Merge "Merge "Merge "Merge "DO NOT MERGE: Bump CTS and CTS Verifier to 7.0_r18" into nougat-cts-dev am: 574ee81d02  -s ours" into nougat-mr1-cts-dev am: 65639e8ba8  -s ours" into oreo-cts-dev am: 55143364fc  -s ours" into oc-dev am: 0c29e60748 am: 78f4674692 am: b9764176ab
am: 16b9a9edfa  -s ours

Change-Id: I280918e30b752866388b9563ee3496802f11aa52
diff --git a/apps/CameraITS/pymodules/its/cv2image.py b/apps/CameraITS/pymodules/its/cv2image.py
index 83e654e..3020548 100644
--- a/apps/CameraITS/pymodules/its/cv2image.py
+++ b/apps/CameraITS/pymodules/its/cv2image.py
@@ -12,27 +12,37 @@
 # See the License for the specific language governing permissions and
 # limitations under the License.
 
-import matplotlib
-matplotlib.use('Agg')
-
-import its.error
-from matplotlib import pylab
-import sys
-from PIL import Image
-import numpy
-import math
-import unittest
-import cStringIO
-import scipy.stats
-import copy
-import cv2
 import os
+import unittest
+
+import cv2
+import its.caps
+import its.device
+import its.error
+import numpy
+
+VGA_HEIGHT = 480
+VGA_WIDTH = 640
+
 
 def scale_img(img, scale=1.0):
     """Scale and image based on a real number scale factor."""
     dim = (int(img.shape[1]*scale), int(img.shape[0]*scale))
     return cv2.resize(img.copy(), dim, interpolation=cv2.INTER_AREA)
 
+
+def gray_scale_img(img):
+    """Return gray scale version of image."""
+    if len(img.shape) == 2:
+        img_gray = img.copy()
+    elif len(img.shape) == 3:
+        if img.shape[2] == 1:
+            img_gray = img[:, :, 0].copy()
+        else:
+            img_gray = cv2.cvtColor(img, cv2.COLOR_RGB2GRAY)
+    return img_gray
+
+
 class Chart(object):
     """Definition for chart object.
 
@@ -57,6 +67,23 @@
         self._scale_start = scale_start
         self._scale_stop = scale_stop
         self._scale_step = scale_step
+        self.xnorm, self.ynorm, self.wnorm, self.hnorm, self.scale = its.image.chart_located_per_argv()
+        if not self.xnorm:
+            with its.device.ItsSession() as cam:
+                props = cam.get_camera_properties()
+                if its.caps.read_3a(props):
+                    self.locate(cam, props)
+                else:
+                    print 'Chart locator skipped.'
+                    self._set_scale_factors_to_one()
+
+    def _set_scale_factors_to_one(self):
+        """Set scale factors to 1.0 for skipped tests."""
+        self.wnorm = 1.0
+        self.hnorm = 1.0
+        self.xnorm = 0.0
+        self.ynorm = 0.0
+        self.scale = 1.0
 
     def _calc_scale_factors(self, cam, props, fmt, s, e, fd):
         """Take an image with s, e, & fd to find the chart location.
@@ -79,7 +106,7 @@
         req['android.lens.focusDistance'] = fd
         cap_chart = its.image.stationary_lens_cap(cam, req, fmt)
         img_3a = its.image.convert_capture_to_rgb_image(cap_chart, props)
-        img_3a = its.image.flip_mirror_img_per_argv(img_3a)
+        img_3a = its.image.rotate_img_per_argv(img_3a)
         its.image.write_image(img_3a, 'af_scene.jpg')
         template = cv2.imread(self._file, cv2.IMREAD_ANYDEPTH)
         focal_l = cap_chart['metadata']['android.lens.focalLength']
@@ -95,41 +122,38 @@
         print 'Chart/image scale factor = %.2f' % scale_factor
         return template, img_3a, scale_factor
 
-    def locate(self, cam, props, fmt, s, e, fd):
-        """Find the chart in the image.
+    def locate(self, cam, props):
+        """Find the chart in the image, and append location to chart object.
 
-        Args:
-            cam:            An open device session
-            props:          Properties of cam
-            fmt:            Image format for the capture
-            s:              Sensitivity for the AF request as defined in
-                            android.sensor.sensitivity
-            e:              Exposure time for the AF request as defined in
-                            android.sensor.exposureTime
-            fd:             float; autofocus lens position
-
-        Returns:
+        The values appended are:
             xnorm:          float; [0, 1] left loc of chart in scene
             ynorm:          float; [0, 1] top loc of chart in scene
             wnorm:          float; [0, 1] width of chart in scene
             hnorm:          float; [0, 1] height of chart in scene
+            scale:          float; scale factor to extract chart
+
+        Args:
+            cam:            An open device session
+            props:          Camera properties
         """
-        chart, scene, s_factor = self._calc_scale_factors(cam, props, fmt,
-                                                          s, e, fd)
+        if its.caps.read_3a(props):
+            s, e, _, _, fd = cam.do_3a(get_results=True)
+            fmt = {'format': 'yuv', 'width': VGA_WIDTH, 'height': VGA_HEIGHT}
+            chart, scene, s_factor = self._calc_scale_factors(cam, props, fmt,
+                                                              s, e, fd)
+        else:
+            print 'Chart locator skipped.'
+            self._set_scale_factors_to_one()
+            return
         scale_start = self._scale_start * s_factor
         scale_stop = self._scale_stop * s_factor
         scale_step = self._scale_step * s_factor
+        self.scale = s_factor
         max_match = []
         # check for normalized image
         if numpy.amax(scene) <= 1.0:
             scene = (scene * 255.0).astype(numpy.uint8)
-        if len(scene.shape) == 2:
-            scene_gray = scene.copy()
-        elif len(scene.shape) == 3:
-            if scene.shape[2] == 1:
-                scene_gray = scene[:, :, 0]
-            else:
-                scene_gray = cv2.cvtColor(scene.copy(), cv2.COLOR_RGB2GRAY)
+        scene_gray = gray_scale_img(scene)
         print 'Finding chart in scene...'
         for scale in numpy.arange(scale_start, scale_stop, scale_step):
             scene_scaled = scale_img(scene_gray, scale)
@@ -142,26 +166,33 @@
         # determine if optimization results are valid
         opt_values = [x[0] for x in max_match]
         if 2.0*min(opt_values) > max(opt_values):
-            estring = ('Unable to find chart in scene!\n'
+            estring = ('Warning: unable to find chart in scene!\n'
                        'Check camera distance and self-reported '
                        'pixel pitch, focal length and hyperfocal distance.')
-            raise its.error.Error(estring)
-        # find max and draw bbox
-        match_index = max_match.index(max(max_match, key=lambda x: x[0]))
-        scale = scale_start + scale_step * match_index
-        print 'Optimum scale factor: %.3f' %  scale
-        top_left_scaled = max_match[match_index][1]
-        h, w = chart.shape
-        bottom_right_scaled = (top_left_scaled[0] + w, top_left_scaled[1] + h)
-        top_left = (int(top_left_scaled[0]/scale),
-                    int(top_left_scaled[1]/scale))
-        bottom_right = (int(bottom_right_scaled[0]/scale),
-                        int(bottom_right_scaled[1]/scale))
-        wnorm = float((bottom_right[0]) - top_left[0]) / scene.shape[1]
-        hnorm = float((bottom_right[1]) - top_left[1]) / scene.shape[0]
-        xnorm = float(top_left[0]) / scene.shape[1]
-        ynorm = float(top_left[1]) / scene.shape[0]
-        return xnorm, ynorm, wnorm, hnorm
+            print estring
+            self._set_scale_factors_to_one()
+        else:
+            if (max(opt_values) == opt_values[0] or
+                        max(opt_values) == opt_values[len(opt_values)-1]):
+                estring = ('Warning: chart is at extreme range of locator '
+                           'check.\n')
+                print estring
+            # find max and draw bbox
+            match_index = max_match.index(max(max_match, key=lambda x: x[0]))
+            self.scale = scale_start + scale_step * match_index
+            print 'Optimum scale factor: %.3f' %  self.scale
+            top_left_scaled = max_match[match_index][1]
+            h, w = chart.shape
+            bottom_right_scaled = (top_left_scaled[0] + w,
+                                   top_left_scaled[1] + h)
+            top_left = (int(top_left_scaled[0]/self.scale),
+                        int(top_left_scaled[1]/self.scale))
+            bottom_right = (int(bottom_right_scaled[0]/self.scale),
+                            int(bottom_right_scaled[1]/self.scale))
+            self.wnorm = float((bottom_right[0]) - top_left[0]) / scene.shape[1]
+            self.hnorm = float((bottom_right[1]) - top_left[1]) / scene.shape[0]
+            self.xnorm = float(top_left[0]) / scene.shape[1]
+            self.ynorm = float(top_left[1]) / scene.shape[0]
 
 
 class __UnitTest(unittest.TestCase):
@@ -186,7 +217,8 @@
             blur = cv2.blur(chart, (j, j))
             blur = blur[:, :, numpy.newaxis]
             sharpness[j] = (yuv_full_scale *
-                    its.image.compute_image_sharpness(blur / white_level))
+                            its.image.compute_image_sharpness(blur /
+                                                              white_level))
         self.assertTrue(numpy.isclose(sharpness[2]/sharpness[4],
                                       numpy.sqrt(2), atol=0.1))
         self.assertTrue(numpy.isclose(sharpness[4]/sharpness[8],
diff --git a/apps/CameraITS/pymodules/its/device.py b/apps/CameraITS/pymodules/its/device.py
index 26af162..66d28c2 100644
--- a/apps/CameraITS/pymodules/its/device.py
+++ b/apps/CameraITS/pymodules/its/device.py
@@ -207,7 +207,7 @@
               'com.android.cts.verifier/.camera.its.ItsTestActivity '
               '--activity-brought-to-front') % self.adb)
         time.sleep(CMD_DELAY)
-        _run(('%s shell am startservice --user 0 -t text/plain '
+        _run(('%s shell am start-foreground-service --user 0 -t text/plain '
               '-a %s') % (self.adb, self.INTENT_START))
 
         # Wait until the socket is ready to accept a connection.
diff --git a/apps/CameraITS/pymodules/its/image.py b/apps/CameraITS/pymodules/its/image.py
index 24b48bb..3fe7f15 100644
--- a/apps/CameraITS/pymodules/its/image.py
+++ b/apps/CameraITS/pymodules/its/image.py
@@ -646,7 +646,10 @@
     ytile = math.ceil(ynorm * hfull)
     wtile = math.floor(wnorm * wfull)
     htile = math.floor(hnorm * hfull)
-    return img[ytile:ytile+htile,xtile:xtile+wtile,:].copy()
+    if len(img.shape)==2:
+        return img[ytile:ytile+htile,xtile:xtile+wtile].copy()
+    else:
+        return img[ytile:ytile+htile,xtile:xtile+wtile,:].copy()
 
 
 def compute_image_means(img):
@@ -697,6 +700,22 @@
     return snr
 
 
+def compute_image_max_gradients(img):
+    """Calculate the maximum gradient of each color channel in the image.
+
+    Args:
+        img: Numpy float image array, with pixel values in [0,1].
+
+    Returns:
+        A list of gradient max values, one per color channel in the image.
+    """
+    grads = []
+    chans = img.shape[2]
+    for i in xrange(chans):
+        grads.append(numpy.amax(numpy.gradient(img[:, :, i])))
+    return grads
+
+
 def write_image(img, fname, apply_gamma=False):
     """Save a float-3 numpy array image to a file.
 
@@ -780,6 +799,7 @@
     [gy, gx] = numpy.gradient(luma)
     return numpy.average(numpy.sqrt(gy*gy + gx*gx))
 
+
 def normalize_img(img):
     """Normalize the image values to between 0 and 1.
 
@@ -790,21 +810,39 @@
     """
     return (img - numpy.amin(img))/(numpy.amax(img) - numpy.amin(img))
 
-def flip_mirror_img_per_argv(img):
-    """Flip/mirror an image if "flip" or "mirror" is in argv
+
+def chart_located_per_argv():
+    """Determine if chart already located outside of test.
+
+    If chart info provided, return location and size. If not, return None.
+
+    Args:
+        None
+    Returns:
+        chart_loc:  float converted xnorm,ynorm,wnorm,hnorm,scale from argv text.
+                    argv is of form 'chart_loc=0.45,0.45,0.1,0.1,1.0'
+    """
+    for s in sys.argv[1:]:
+        if s[:10] == "chart_loc=" and len(s) > 10:
+            chart_loc = s[10:].split(",")
+            return map(float, chart_loc)
+    return None, None, None, None, None
+
+
+def rotate_img_per_argv(img):
+    """Rotate an image 180 degrees if "rotate" is in argv
 
     Args:
         img: 2-D numpy array of image values
     Returns:
-        Flip/mirrored image
+        Rotated image
     """
     img_out = img
-    if "flip" in sys.argv:
-        img_out = numpy.flipud(img_out)
-    if "mirror" in sys.argv:
-        img_out = numpy.fliplr(img_out)
+    if "rotate180" in sys.argv:
+        img_out = numpy.fliplr(numpy.flipud(img_out))
     return img_out
 
+
 def stationary_lens_cap(cam, req, fmt):
     """Take up to NUM_TRYS caps and save the 1st one with lens stationary.
 
@@ -829,6 +867,7 @@
             raise its.error.Error('Cannot settle lens after %d trys!' % trys)
     return cap[NUM_FRAMES-1]
 
+
 class __UnitTest(unittest.TestCase):
     """Run a suite of unit tests on this module.
     """
diff --git a/apps/CameraITS/tests/inprog/test_test_patterns.py b/apps/CameraITS/tests/inprog/test_test_patterns.py
deleted file mode 100644
index f75b141..0000000
--- a/apps/CameraITS/tests/inprog/test_test_patterns.py
+++ /dev/null
@@ -1,41 +0,0 @@
-# Copyright 2014 The Android Open Source Project
-#
-# Licensed under the Apache License, Version 2.0 (the "License");
-# you may not use this file except in compliance with the License.
-# You may obtain a copy of the License at
-#
-#      http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing, software
-# distributed under the License is distributed on an "AS IS" BASIS,
-# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-# See the License for the specific language governing permissions and
-# limitations under the License.
-
-import its.image
-import its.device
-import its.objects
-import os.path
-
-def main():
-    """Test sensor test patterns.
-    """
-    NAME = os.path.basename(__file__).split(".")[0]
-
-    with its.device.ItsSession() as cam:
-        caps = []
-        for i in range(1,6):
-            req = its.objects.manual_capture_request(100, 10*1000*1000)
-            req['android.sensor.testPatternData'] = [40, 100, 160, 220]
-            req['android.sensor.testPatternMode'] = i
-
-            # Capture the shot twice, and use the second one, so the pattern
-            # will have stabilized.
-            caps = cam.do_capture([req]*2)
-
-            img = its.image.convert_capture_to_rgb_image(caps[1])
-            its.image.write_image(img, "%s_pattern=%d.jpg" % (NAME, i))
-
-if __name__ == '__main__':
-    main()
-
diff --git a/apps/CameraITS/tests/scene0/test_metadata.py b/apps/CameraITS/tests/scene0/test_metadata.py
index 752e02b..e78488e 100644
--- a/apps/CameraITS/tests/scene0/test_metadata.py
+++ b/apps/CameraITS/tests/scene0/test_metadata.py
@@ -90,8 +90,8 @@
         pixel_pitch_w = (sensor_size["width"] / fmts[0]["width"] * 1E3)
         print "Assert pixel_pitch WxH: %.2f um, %.2f um" % (pixel_pitch_w,
                                                             pixel_pitch_h)
-        assert 1.0 <= pixel_pitch_w <= 10
-        assert 1.0 <= pixel_pitch_h <= 10
+        assert 0.9 <= pixel_pitch_w <= 10
+        assert 0.9 <= pixel_pitch_h <= 10
         assert 0.333 <= pixel_pitch_w/pixel_pitch_h <= 3.0
 
         diag = math.sqrt(sensor_size["height"] ** 2 +
diff --git a/apps/CameraITS/tests/scene0/test_param_sensitivity_burst.py b/apps/CameraITS/tests/scene0/test_param_sensitivity_burst.py
index c3c2147..7594f9f 100644
--- a/apps/CameraITS/tests/scene0/test_param_sensitivity_burst.py
+++ b/apps/CameraITS/tests/scene0/test_param_sensitivity_burst.py
@@ -12,19 +12,21 @@
 # See the License for the specific language governing permissions and
 # limitations under the License.
 
-import its.image
 import its.caps
 import its.device
+import its.image
 import its.objects
 import its.target
 
-def main():
-    """Test that the android.sensor.sensitivity parameter is applied properly
-    within a burst. Inspects the output metadata only (not the image data).
-    """
+NUM_STEPS = 3
+ERROR_TOLERANCE = 0.97  # Allow ISO to be rounded down by 3%
 
-    NUM_STEPS = 3
-    ERROR_TOLERANCE = 0.97 # Allow ISO to be rounded down by 3%
+
+def main():
+    """Test android.sensor.sensitivity parameter applied properly in burst.
+
+    Inspects the output metadata only (not the image data).
+    """
 
     with its.device.ItsSession() as cam:
         props = cam.get_camera_properties()
@@ -35,15 +37,16 @@
         sens_step = (sens_range[1] - sens_range[0]) / NUM_STEPS
         sens_list = range(sens_range[0], sens_range[1], sens_step)
         e = min(props['android.sensor.info.exposureTimeRange'])
-        reqs = [its.objects.manual_capture_request(s,e) for s in sens_list]
-        _,fmt = its.objects.get_fastest_manual_capture_settings(props)
+        reqs = [its.objects.manual_capture_request(s, e) for s in sens_list]
+        _, fmt = its.objects.get_fastest_manual_capture_settings(props)
 
         caps = cam.do_capture(reqs, fmt)
-        for i,cap in enumerate(caps):
+        for i, cap in enumerate(caps):
             s_req = sens_list[i]
-            s_res = cap["metadata"]["android.sensor.sensitivity"]
-            assert(s_req >= s_res)
-            assert(s_res/float(s_req) > ERROR_TOLERANCE)
+            s_res = cap['metadata']['android.sensor.sensitivity']
+            s_values = 's_write: %d, s_read: %d' % (s_req, s_res)
+            assert s_req >= s_res, s_values
+            assert s_res/float(s_req) > ERROR_TOLERANCE, ERROR_TOLERANCE
 
 if __name__ == '__main__':
     main()
diff --git a/apps/CameraITS/tests/scene0/test_test_patterns.py b/apps/CameraITS/tests/scene0/test_test_patterns.py
new file mode 100644
index 0000000..a1d9cb8
--- /dev/null
+++ b/apps/CameraITS/tests/scene0/test_test_patterns.py
@@ -0,0 +1,174 @@
+# Copyright 2013 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#      http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+import os
+
+import its.caps
+import its.device
+import its.image
+import its.objects
+import numpy as np
+
+NAME = os.path.basename(__file__).split('.')[0]
+PATTERNS = [1, 2]
+COLOR_BAR_ORDER = ['WHITE', 'YELLOW', 'CYAN', 'GREEN', 'MAGENTA', 'RED',
+                   'BLUE', 'BLACK']
+COLOR_CHECKER = {'BLACK': [0, 0, 0], 'RED': [1, 0, 0], 'GREEN': [0, 1, 0],
+                 'BLUE': [0, 0, 1], 'MAGENTA': [1, 0, 1], 'CYAN': [0, 1, 1],
+                 'YELLOW': [1, 1, 0], 'WHITE': [1, 1, 1]}
+CH_TOL = 2E-3  # 1/2 DN in [0:1]
+LSFR_COEFFS = 0b100010000  # PN9
+
+
+def check_solid_color(cap, props):
+    """Simple test for solid color.
+
+    Args:
+        cap: capture element
+        props: capture properties
+    Returns:
+        True/False
+    """
+    print 'Checking solid TestPattern...'
+    r, gr, gb, b = its.image.convert_capture_to_planes(cap, props)
+    r_tile = its.image.get_image_patch(r, 0.0, 0.0, 1.0, 1.0)
+    gr_tile = its.image.get_image_patch(gr, 0.0, 0.0, 1.0, 1.0)
+    gb_tile = its.image.get_image_patch(gb, 0.0, 0.0, 1.0, 1.0)
+    b_tile = its.image.get_image_patch(b, 0.0, 0.0, 1.0, 1.0)
+    var_max = max(np.amax(r_tile), np.amax(gr_tile), np.amax(gb_tile),
+                  np.amax(b_tile))
+    var_min = min(np.amin(r_tile), np.amin(gr_tile), np.amin(gb_tile),
+                  np.amin(b_tile))
+    white_level = int(props['android.sensor.info.whiteLevel'])
+    print ' pixel min: %.f, pixel max: %.f' % (white_level*var_min,
+                                               white_level*var_max)
+    return np.isclose(var_max, var_min, atol=CH_TOL)
+
+
+def check_color_bars(cap, props, mirror=False):
+    """Test image for color bars.
+
+    Compute avg of bars and compare to ideal
+
+    Args:
+        cap:            capture element
+        props:          capture properties
+        mirror (bool):  whether to mirror image or not
+    Returns:
+        True/False
+    """
+    print 'Checking color bar TestPattern...'
+    delta = 0.0005
+    num_bars = len(COLOR_BAR_ORDER)
+    color_match = []
+    img = its.image.convert_capture_to_rgb_image(cap, props=props)
+    if mirror:
+        print ' Image mirrored'
+        img = np.fliplr(img)
+    for i, color in enumerate(COLOR_BAR_ORDER):
+        tile = its.image.get_image_patch(img, float(i)/num_bars+delta,
+                                         0.0, 1.0/num_bars-2*delta, 1.0)
+        color_match.append(np.allclose(its.image.compute_image_means(tile),
+                                       COLOR_CHECKER[color], atol=CH_TOL))
+    print COLOR_BAR_ORDER
+    print color_match
+    return all(color_match)
+
+
+def check_pattern(cap, props, pattern):
+    """Simple tests for pattern correctness.
+
+    Args:
+        cap: capture element
+        props: capture properties
+        pattern (int): valid number for pattern
+    Returns:
+        boolean
+    """
+
+    # white_level = int(props['android.sensor.info.whiteLevel'])
+    if pattern == 1:  # solid color
+        return check_solid_color(cap, props)
+
+    elif pattern == 2:  # color bars
+        striped = check_color_bars(cap, props, mirror=False)
+        # check mirrored version in case image rotated from sensor orientation
+        if not striped:
+            striped = check_color_bars(cap, props, mirror=True)
+        return striped
+
+    else:
+        print 'No specific test for TestPattern %d' % pattern
+        return True
+
+
+def test_test_patterns(cam, props, af_fd):
+    """test image sensor test patterns.
+
+    Args:
+        cam: An open device session.
+        props: Properties of cam
+        af_fd: Focus distance
+    """
+
+    avail_patterns = props['android.sensor.availableTestPatternModes']
+    print 'avail_patterns: ', avail_patterns
+    sens_min, _ = props['android.sensor.info.sensitivityRange']
+    exposure = min(props['android.sensor.info.exposureTimeRange'])
+
+    for pattern in PATTERNS:
+        if pattern in avail_patterns:
+            req = its.objects.manual_capture_request(int(sens_min),
+                                                     exposure)
+            req['android.lens.focusDistance'] = af_fd
+            req['android.sensor.testPatternMode'] = pattern
+            fmt = {'format': 'raw'}
+            cap = cam.do_capture(req, fmt)
+            img = its.image.convert_capture_to_rgb_image(cap, props=props)
+
+            # Save pattern
+            its.image.write_image(img, '%s_%d.jpg' % (NAME, pattern), True)
+
+            # Check pattern for correctness
+            assert check_pattern(cap, props, pattern)
+        else:
+            print 'Pattern not in android.sensor.availableTestPatternModes.'
+
+
+def main():
+    """Test pattern generation test.
+
+    Test: capture frames for each valid test pattern and check if
+    generated correctly.
+    android.sensor.testPatternMode
+    0: OFF
+    1: SOLID_COLOR
+    2: COLOR_BARS
+    3: COLOR_BARS_FADE_TO_GREY
+    4: PN9
+    """
+
+    print '\nStarting %s' % NAME
+    with its.device.ItsSession() as cam:
+        props = cam.get_camera_properties()
+        its.caps.skip_unless(its.caps.raw16(props) and
+                             its.caps.manual_sensor(props) and
+                             its.caps.per_frame_control(props))
+
+        # For test pattern, use min_fd
+        fd = props['android.lens.info.minimumFocusDistance']
+        test_test_patterns(cam, props, fd)
+
+if __name__ == '__main__':
+    main()
diff --git a/apps/CameraITS/tests/scene1/scene1_0.67_scaled.pdf b/apps/CameraITS/tests/scene1/scene1_0.67_scaled.pdf
new file mode 100644
index 0000000..3103cd8
--- /dev/null
+++ b/apps/CameraITS/tests/scene1/scene1_0.67_scaled.pdf
Binary files differ
diff --git a/apps/CameraITS/tests/scene1/test_3a.py b/apps/CameraITS/tests/scene1/test_3a.py
index 08cd747..2c5dcfe 100644
--- a/apps/CameraITS/tests/scene1/test_3a.py
+++ b/apps/CameraITS/tests/scene1/test_3a.py
@@ -12,8 +12,11 @@
 # See the License for the specific language governing permissions and
 # limitations under the License.
 
-import its.device
 import its.caps
+import its.device
+
+import numpy as np
+
 
 def main():
     """Basic test for bring-up of 3A.
@@ -26,14 +29,18 @@
         its.caps.skip_unless(its.caps.read_3a(props))
 
         sens, exp, gains, xform, focus = cam.do_3a(get_results=True)
-        print "AE: sensitivity %d, exposure %dms" % (sens, exp/1000000)
-        print "AWB: gains", gains, "transform", xform
-        print "AF: distance", focus
-        assert(sens > 0)
-        assert(exp > 0)
-        assert(len(gains) == 4)
-        assert(len(xform) == 9)
-        assert(focus >= 0)
+        print 'AE: sensitivity %d, exposure %dms' % (sens, exp/1000000)
+        print 'AWB: gains', gains, 'transform', xform
+        print 'AF: distance', focus
+        assert sens > 0
+        assert exp > 0
+        assert len(gains) == 4
+        for g in gains:
+            assert not np.isnan(g)
+        assert len(xform) == 9
+        for x in xform:
+            assert not np.isnan(x)
+        assert focus >= 0
 
 if __name__ == '__main__':
     main()
diff --git a/apps/CameraITS/tests/scene1/test_ae_af.py b/apps/CameraITS/tests/scene1/test_ae_af.py
index 626a475..ed94017 100644
--- a/apps/CameraITS/tests/scene1/test_ae_af.py
+++ b/apps/CameraITS/tests/scene1/test_ae_af.py
@@ -16,7 +16,7 @@
 import its.device
 import its.target
 
-import numpy
+import numpy as np
 
 GAIN_LENGTH = 4
 TRANSFORM_LENGTH = 9
@@ -39,12 +39,12 @@
         for k, v in sorted(SINGLE_A.items()):
             print k
             try:
-                s, e, g, xform, fd = cam.do_3a(get_results=True,
-                                               do_ae=v[0],
-                                               do_af=v[1],
-                                               do_awb=v[2])
+                s, e, gains, xform, fd = cam.do_3a(get_results=True,
+                                                   do_ae=v[0],
+                                                   do_af=v[1],
+                                                   do_awb=v[2])
                 print ' sensitivity', s, 'exposure', e
-                print ' gains', g, 'transform', xform
+                print ' gains', gains, 'transform', xform
                 print ' fd', fd
                 print ''
             except its.error.Error:
@@ -52,10 +52,14 @@
             if k == 'full_3a':
                 assert s > 0
                 assert e > 0
-                assert len(g) == 4
+                assert len(gains) == 4
+                for g in gains:
+                    assert not np.isnan(g)
                 assert len(xform) == 9
+                for x in xform:
+                    assert not np.isnan(x)
                 assert fd >= 0
-                assert numpy.isclose(g[2], GREEN_GAIN, GREEN_GAIN_TOL)
+                assert np.isclose(gains[2], GREEN_GAIN, GREEN_GAIN_TOL)
 
 if __name__ == '__main__':
     main()
diff --git a/apps/CameraITS/tests/scene1/test_param_flash_mode.py b/apps/CameraITS/tests/scene1/test_param_flash_mode.py
index 2d8c678..94cef80 100644
--- a/apps/CameraITS/tests/scene1/test_param_flash_mode.py
+++ b/apps/CameraITS/tests/scene1/test_param_flash_mode.py
@@ -12,17 +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
-import os.path
+
+NAME = os.path.basename(__file__).split('.')[0]
+Y_DELTA = 0.1
+GRADIENT_DELTA = 0.1
+
 
 def main():
-    """Test that the android.flash.mode parameter is applied.
-    """
-    NAME = os.path.basename(__file__).split(".")[0]
+    """Test that the android.flash.mode parameter is applied."""
 
     with its.device.ItsSession() as cam:
         props = cam.get_camera_properties()
@@ -32,7 +35,8 @@
 
         flash_modes_reported = []
         flash_states_reported = []
-        g_means = []
+        means = []
+        grads = []
 
         # Manually set the exposure to be a little on the dark side, so that
         # it should be obvious whether the flash fired or not, and use a
@@ -45,29 +49,31 @@
             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"]
+        e, s = its.target.get_target_exposure_combos(cam)['midExposureTime']
         e /= 4
         req = its.objects.manual_capture_request(s, e, 0.0, True, props)
 
-        for f in [0,1,2]:
-            req["android.flash.mode"] = f
+        for f in [0, 1, 2]:
+            req['android.flash.mode'] = f
             cap = cam.do_capture(req, fmt)
-            flash_modes_reported.append(cap["metadata"]["android.flash.mode"])
-            flash_states_reported.append(cap["metadata"]["android.flash.state"])
-            img = its.image.convert_capture_to_rgb_image(cap)
-            its.image.write_image(img, "%s_mode=%d.jpg" % (NAME, f))
-            tile = its.image.get_image_patch(img, 0.45, 0.45, 0.1, 0.1)
-            rgb = its.image.compute_image_means(tile)
-            g_means.append(rgb[1])
+            flash_modes_reported.append(cap['metadata']['android.flash.mode'])
+            flash_states_reported.append(cap['metadata']['android.flash.state'])
+            y, _, _ = its.image.convert_capture_to_planes(cap, props)
+            its.image.write_image(y, '%s_%d.jpg' % (NAME, f))
+            tile = its.image.get_image_patch(y, 0.375, 0.375, 0.25, 0.25)
+            its.image.write_image(tile, '%s_%d_tile.jpg' % (NAME, f))
+            means.append(its.image.compute_image_means(tile)[0])
+            grads.append(its.image.compute_image_max_gradients(tile)[0])
 
-        assert(flash_modes_reported == [0,1,2])
-        assert(flash_states_reported[0] not in [3,4])
-        assert(flash_states_reported[1] in [3,4])
-        assert(flash_states_reported[2] in [3,4])
+        assert flash_modes_reported == [0, 1, 2]
+        assert flash_states_reported[0] not in [3, 4]
+        assert flash_states_reported[1] in [3, 4]
+        assert flash_states_reported[2] in [3, 4]
 
-        print "G brightnesses:", g_means
-        assert(g_means[1] > g_means[0])
-        assert(g_means[2] > g_means[0])
+        print 'Brightnesses:', means
+        print 'Max gradients: ', grads
+        assert means[1]-means[0] > Y_DELTA or grads[1]-grads[0] > GRADIENT_DELTA
+        assert means[2]-means[0] > Y_DELTA or grads[2]-grads[0] > GRADIENT_DELTA
 
 if __name__ == '__main__':
     main()
diff --git a/apps/CameraITS/tests/scene1/test_yuv_plus_raw.py b/apps/CameraITS/tests/scene1/test_yuv_plus_raw.py
index 343c960..dd7ef21 100644
--- a/apps/CameraITS/tests/scene1/test_yuv_plus_raw.py
+++ b/apps/CameraITS/tests/scene1/test_yuv_plus_raw.py
@@ -12,20 +12,20 @@
 # See the License for the specific language governing permissions and
 # limitations under the License.
 
-import its.image
+import math
+import os.path
 import its.caps
 import its.device
+import its.image
 import its.objects
 import its.target
-import os.path
-import math
+
+NAME = os.path.basename(__file__).split(".")[0]
+THRESHOLD_MAX_RMS_DIFF = 0.035
+
 
 def main():
-    """Test capturing a single frame as both RAW and YUV outputs.
-    """
-    NAME = os.path.basename(__file__).split(".")[0]
-
-    THRESHOLD_MAX_RMS_DIFF = 0.035
+    """Test capturing a single frame as both RAW and YUV outputs."""
 
     with its.device.ItsSession() as cam:
         props = cam.get_camera_properties()
@@ -38,34 +38,35 @@
         e, s = its.target.get_target_exposure_combos(cam)["midExposureTime"]
         req = its.objects.manual_capture_request(s, e, 0.0, True, props)
 
-        if 0 in props['android.shading.availableModes']:
-            req["android.shading.mode"] = 0
+        mode = req["android.shading.mode"]
+        print "shading mode:", mode
 
-        max_raw_size = \
-                its.objects.get_available_output_sizes("raw", props)[0]
-        w,h = its.objects.get_available_output_sizes(
+        max_raw_size = its.objects.get_available_output_sizes("raw", props)[0]
+        w, h = its.objects.get_available_output_sizes(
                 "yuv", props, (1920, 1080), max_raw_size)[0]
-        out_surfaces = [{"format":"raw"},
-                        {"format":"yuv", "width":w, "height":h}]
+        out_surfaces = [{"format": "raw"},
+                        {"format": "yuv", "width": w, "height": h}]
         cap_raw, cap_yuv = cam.do_capture(req, out_surfaces)
 
         img = its.image.convert_capture_to_rgb_image(cap_yuv)
-        its.image.write_image(img, "%s_yuv.jpg" % (NAME), True)
+        its.image.write_image(img, "%s_shading=%d_yuv.jpg" % (NAME, mode), True)
         tile = its.image.get_image_patch(img, 0.45, 0.45, 0.1, 0.1)
         rgb0 = its.image.compute_image_means(tile)
 
         # Raw shots are 1/2 x 1/2 smaller after conversion to RGB, but tile
         # cropping is relative.
         img = its.image.convert_capture_to_rgb_image(cap_raw, props=props)
-        its.image.write_image(img, "%s_raw.jpg" % (NAME), True)
+        its.image.write_image(img, "%s_shading=%d_raw.jpg" % (NAME, mode), True)
         tile = its.image.get_image_patch(img, 0.45, 0.45, 0.1, 0.1)
         rgb1 = its.image.compute_image_means(tile)
 
         rms_diff = math.sqrt(
                 sum([pow(rgb0[i] - rgb1[i], 2.0) for i in range(3)]) / 3.0)
-        print "RMS difference:", rms_diff
-        assert(rms_diff < THRESHOLD_MAX_RMS_DIFF)
+        msg = "RMS difference: %.4f, spec: %.3f" % (rms_diff,
+                                                    THRESHOLD_MAX_RMS_DIFF)
+        print msg
+        assert rms_diff < THRESHOLD_MAX_RMS_DIFF, msg
 
-if __name__ == '__main__':
+if __name__ == "__main__":
     main()
 
diff --git a/apps/CameraITS/tests/scene1/test_yuv_plus_raw10.py b/apps/CameraITS/tests/scene1/test_yuv_plus_raw10.py
index 6ecdca7..9c0c69b 100644
--- a/apps/CameraITS/tests/scene1/test_yuv_plus_raw10.py
+++ b/apps/CameraITS/tests/scene1/test_yuv_plus_raw10.py
@@ -12,20 +12,20 @@
 # See the License for the specific language governing permissions and
 # limitations under the License.
 
-import its.image
+import math
+import os.path
 import its.caps
 import its.device
+import its.image
 import its.objects
 import its.target
-import os.path
-import math
+
+NAME = os.path.basename(__file__).split(".")[0]
+THRESHOLD_MAX_RMS_DIFF = 0.035
+
 
 def main():
-    """Test capturing a single frame as both RAW10 and YUV outputs.
-    """
-    NAME = os.path.basename(__file__).split(".")[0]
-
-    THRESHOLD_MAX_RMS_DIFF = 0.035
+    """Test capturing a single frame as both RAW10 and YUV outputs."""
 
     with its.device.ItsSession() as cam:
         props = cam.get_camera_properties()
@@ -38,34 +38,36 @@
         e, s = its.target.get_target_exposure_combos(cam)["midExposureTime"]
         req = its.objects.manual_capture_request(s, e, 0.0, True, props)
 
-        if 0 in props['android.shading.availableModes']:
-            req["android.shading.mode"] = 0
+        mode = req["android.shading.mode"]
+        print "shading mode:", mode
 
-        max_raw10_size = \
-                its.objects.get_available_output_sizes("raw10", props)[0]
-        w,h = its.objects.get_available_output_sizes(
+        max_raw10_size = its.objects.get_available_output_sizes("raw10",
+                                                                props)[0]
+        w, h = its.objects.get_available_output_sizes(
                 "yuv", props, (1920, 1080), max_raw10_size)[0]
-        cap_raw, cap_yuv = cam.do_capture(req,
-                [{"format":"raw10"},
-                 {"format":"yuv", "width":w, "height":h}])
+        out_surfaces = [{"format": "raw10"},
+                        {"format": "yuv", "width": w, "height": h}]
+        cap_raw, cap_yuv = cam.do_capture(req, out_surfaces)
 
         img = its.image.convert_capture_to_rgb_image(cap_yuv)
-        its.image.write_image(img, "%s_yuv.jpg" % (NAME), True)
+        its.image.write_image(img, "%s_shading=%d_yuv.jpg" % (NAME, mode), True)
         tile = its.image.get_image_patch(img, 0.45, 0.45, 0.1, 0.1)
         rgb0 = its.image.compute_image_means(tile)
 
         # Raw shots are 1/2 x 1/2 smaller after conversion to RGB, but tile
         # cropping is relative.
         img = its.image.convert_capture_to_rgb_image(cap_raw, props=props)
-        its.image.write_image(img, "%s_raw.jpg" % (NAME), True)
+        its.image.write_image(img, "%s_shading=%d_raw.jpg" % (NAME, mode), True)
         tile = its.image.get_image_patch(img, 0.45, 0.45, 0.1, 0.1)
         rgb1 = its.image.compute_image_means(tile)
 
         rms_diff = math.sqrt(
                 sum([pow(rgb0[i] - rgb1[i], 2.0) for i in range(3)]) / 3.0)
-        print "RMS difference:", rms_diff
-        assert(rms_diff < THRESHOLD_MAX_RMS_DIFF)
+        msg = "RMS difference: %.4f, spec: %.3f" % (rms_diff,
+                                                    THRESHOLD_MAX_RMS_DIFF)
+        print msg
+        assert rms_diff < THRESHOLD_MAX_RMS_DIFF, msg
 
-if __name__ == '__main__':
+if __name__ == "__main__":
     main()
 
diff --git a/apps/CameraITS/tests/scene2/scene2_0.67_scaled.pdf b/apps/CameraITS/tests/scene2/scene2_0.67_scaled.pdf
new file mode 100644
index 0000000..5e7128b
--- /dev/null
+++ b/apps/CameraITS/tests/scene2/scene2_0.67_scaled.pdf
Binary files differ
diff --git a/apps/CameraITS/tests/scene2/test_faces.py b/apps/CameraITS/tests/scene2/test_faces.py
index 4e30fc1..2acce85 100644
--- a/apps/CameraITS/tests/scene2/test_faces.py
+++ b/apps/CameraITS/tests/scene2/test_faces.py
@@ -60,7 +60,7 @@
                 # but it should detect at least one face in last frame
                 if i == NUM_TEST_FRAMES - 1:
                     img = its.image.convert_capture_to_rgb_image(cap, props=props)
-                    img = its.image.flip_mirror_img_per_argv(img)
+                    img = its.image.rotate_img_per_argv(img)
                     img_name = "%s_fd_mode_%s.jpg" % (NAME, fd_mode)
                     its.image.write_image(img, img_name)
                     if len(faces) == 0:
diff --git a/apps/CameraITS/tests/scene3/scene3_0.67_scaled.pdf b/apps/CameraITS/tests/scene3/scene3_0.67_scaled.pdf
new file mode 100644
index 0000000..a3e18e2
--- /dev/null
+++ b/apps/CameraITS/tests/scene3/scene3_0.67_scaled.pdf
Binary files differ
diff --git a/apps/CameraITS/tests/scene3/test_3a_consistency.py b/apps/CameraITS/tests/scene3/test_3a_consistency.py
index f43b3eb..2dbee4c 100644
--- a/apps/CameraITS/tests/scene3/test_3a_consistency.py
+++ b/apps/CameraITS/tests/scene3/test_3a_consistency.py
@@ -41,14 +41,14 @@
         fds = []
         for _ in range(NUM_TEST_ITERATIONS):
             try:
-                s, e, g, xform, fd = cam.do_3a(get_results=True)
+                s, e, gains, xform, fd = cam.do_3a(get_results=True)
                 print ' sensitivity', s, 'exposure', e
-                print ' gains', g, 'transform', xform
+                print ' gains', gains, 'transform', xform
                 print ' fd', fd
                 print ''
                 exps.append(e)
                 senses.append(s)
-                g_gains.append(g[2])
+                g_gains.append(gains[2])
                 fds.append(fd)
             except its.error.Error:
                 print ' FAIL\n'
@@ -57,6 +57,10 @@
         assert np.isclose(np.amax(senses), np.amin(senses), SENS_TOL)
         assert np.isclose(np.amax(g_gains), np.amin(g_gains), GGAIN_TOL)
         assert np.isclose(np.amax(fds), np.amin(fds), FD_TOL)
+        for g in gains:
+            assert not np.isnan(g)
+        for x in xform:
+            assert not np.isnan(x)
 
 if __name__ == '__main__':
     main()
diff --git a/apps/CameraITS/tests/scene3/test_flip_mirror.py b/apps/CameraITS/tests/scene3/test_flip_mirror.py
new file mode 100644
index 0000000..f0d17ec
--- /dev/null
+++ b/apps/CameraITS/tests/scene3/test_flip_mirror.py
@@ -0,0 +1,135 @@
+# Copyright 2016 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the 'License');
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#      http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an 'AS IS' BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+import os
+import cv2
+
+import its.caps
+import its.cv2image
+import its.device
+import its.image
+import its.objects
+import numpy as np
+
+NAME = os.path.basename(__file__).split('.')[0]
+CHART_FILE = os.path.join(os.environ['CAMERA_ITS_TOP'], 'pymodules', 'its',
+                          'test_images', 'ISO12233.png')
+CHART_HEIGHT = 13.5  # cm
+CHART_DISTANCE = 30.0  # cm
+CHART_SCALE_START = 0.65
+CHART_SCALE_STOP = 1.35
+CHART_SCALE_STEP = 0.025
+CHART_ORIENTATIONS = ['nominal', 'flip', 'mirror', 'rotate']
+VGA_WIDTH = 640
+VGA_HEIGHT = 480
+(X_CROP, Y_CROP) = (0.5, 0.5)  # crop center area of ISO12233 chart
+
+
+def test_flip_mirror(cam, props, fmt, chart):
+    """Return if image is flipped or mirrored.
+
+    Args:
+        cam (class): An open device session
+        props (class): Properties of cam
+        fmt (dict): Capture format
+        chart (class): Object with chart properties
+
+    Returns:
+        boolean: True if flipped, False if not
+    """
+
+    # determine if in debug mode
+    debug = its.caps.debug_mode()
+
+    # get a local copy of the chart template
+    template = cv2.imread(CHART_FILE, cv2.IMREAD_ANYDEPTH)
+
+    # take img, crop chart, scale and prep for cv2 template match
+    s, e, _, _, fd = cam.do_3a(get_results=True)
+    req = its.objects.manual_capture_request(s, e, fd)
+    cap = cam.do_capture(req, fmt)
+    y, _, _ = its.image.convert_capture_to_planes(cap, props)
+    y = its.image.rotate_img_per_argv(y)
+    patch = its.image.get_image_patch(y, chart.xnorm, chart.ynorm,
+                                      chart.wnorm, chart.hnorm)
+    patch = 255 * its.cv2image.gray_scale_img(patch)
+    patch = its.cv2image.scale_img(patch.astype(np.uint8), chart.scale)
+
+    # sanity check on image
+    assert np.max(patch)-np.min(patch) > 255/8
+
+    # save full images if in debug
+    if debug:
+        its.image.write_image(template[:, :, np.newaxis]/255.0,
+                              '%s_template.jpg' % NAME)
+
+    # save patch
+    its.image.write_image(patch[:, :, np.newaxis]/255.0,
+                          '%s_scene_patch.jpg' % NAME)
+
+    # crop center areas and strip off any extra rows/columns
+    template = its.image.get_image_patch(template, (1-X_CROP)/2, (1-Y_CROP)/2,
+                                         X_CROP, Y_CROP)
+    patch = its.image.get_image_patch(patch, (1-X_CROP)/2,
+                                      (1-Y_CROP)/2, X_CROP, Y_CROP)
+    patch = patch[0:min(patch.shape[0], template.shape[0]),
+                  0:min(patch.shape[1], template.shape[1])]
+    comp_chart = patch
+
+    # determine optimum orientation
+    opts = []
+    for orientation in CHART_ORIENTATIONS:
+        if orientation == 'flip':
+            comp_chart = np.flipud(patch)
+        elif orientation == 'mirror':
+            comp_chart = np.fliplr(patch)
+        elif orientation == 'rotate':
+            comp_chart = np.flipud(np.fliplr(patch))
+        correlation = cv2.matchTemplate(comp_chart, template, cv2.TM_CCOEFF)
+        _, opt_val, _, _ = cv2.minMaxLoc(correlation)
+        if debug:
+            cv2.imwrite('%s_%s.jpg' % (NAME, orientation), comp_chart)
+        print ' %s correlation value: %d' % (orientation, opt_val)
+        opts.append(opt_val)
+
+    # determine if 'nominal' or 'rotated' is best orientation
+    assert_flag = (opts[0] == max(opts) or opts[3] == max(opts))
+    assert assert_flag, ('Optimum orientation is %s' %
+                         CHART_ORIENTATIONS[np.argmax(opts)])
+    # print warning if rotated
+    if opts[3] == max(opts):
+        print 'Image is rotated 180 degrees. Try "rotate" flag.'
+
+
+def main():
+    """Test if image is properly oriented."""
+
+    print '\nStarting test_flip_mirror.py'
+
+    # initialize chart class and locate chart in scene
+    chart = its.cv2image.Chart(CHART_FILE, CHART_HEIGHT, CHART_DISTANCE,
+                               CHART_SCALE_START, CHART_SCALE_STOP,
+                               CHART_SCALE_STEP)
+
+    with its.device.ItsSession() as cam:
+        props = cam.get_camera_properties()
+        its.caps.skip_unless(its.caps.read_3a(props))
+        fmt = {'format': 'yuv', 'width': VGA_WIDTH, 'height': VGA_HEIGHT}
+
+        # test that image is not flipped, mirrored, or rotated
+        test_flip_mirror(cam, props, fmt, chart)
+
+
+if __name__ == '__main__':
+    main()
diff --git a/apps/CameraITS/tests/scene3/test_lens_movement_reporting.py b/apps/CameraITS/tests/scene3/test_lens_movement_reporting.py
index cd563be..aaa8484 100644
--- a/apps/CameraITS/tests/scene3/test_lens_movement_reporting.py
+++ b/apps/CameraITS/tests/scene3/test_lens_movement_reporting.py
@@ -37,19 +37,20 @@
 CHART_SCALE_STEP = 0.025
 
 
-def test_lens_movement_reporting(cam, props, fmt, sensitivity, exp, af_fd):
+def test_lens_movement_reporting(cam, props, fmt, gain, exp, af_fd, chart):
     """Return fd, sharpness, lens state of the output images.
 
     Args:
         cam: An open device session.
         props: Properties of cam
         fmt: dict; capture format
-        sensitivity: Sensitivity for the 3A request as defined in
+        gain: Sensitivity for the 3A request as defined in
             android.sensor.sensitivity
         exp: Exposure time for the 3A request as defined in
             android.sensor.exposureTime
         af_fd: Focus distance for the 3A request as defined in
             android.lens.focusDistance
+        chart: Object that contains chart information
 
     Returns:
         Object containing reported sharpness of the output image, keyed by
@@ -57,15 +58,6 @@
             'sharpness'
     """
 
-    # initialize chart class
-    chart = its.cv2image.Chart(CHART_FILE, CHART_HEIGHT, CHART_DISTANCE,
-                               CHART_SCALE_START, CHART_SCALE_STOP,
-                               CHART_SCALE_STEP)
-
-    # find chart location
-    xnorm, ynorm, wnorm, hnorm = chart.locate(cam, props, fmt, sensitivity,
-                                              exp, af_fd)
-
     # initialize variables and take data sets
     data_set = {}
     white_level = int(props['android.sensor.info.whiteLevel'])
@@ -74,7 +66,7 @@
     fds = sorted(fds * NUM_IMGS)
     reqs = []
     for i, fd in enumerate(fds):
-        reqs.append(its.objects.manual_capture_request(sensitivity, exp))
+        reqs.append(its.objects.manual_capture_request(gain, exp))
         reqs[i]['android.lens.focusDistance'] = fd
     caps = cam.do_capture(reqs, fmt)
     for i, cap in enumerate(caps):
@@ -92,12 +84,12 @@
         print ' current lens location (diopters): %.3f' % data['loc']
         print ' lens moving %r' % data['lens_moving']
         y, _, _ = its.image.convert_capture_to_planes(cap, props)
-        y = its.image.flip_mirror_img_per_argv(y)
-        chart = its.image.normalize_img(its.image.get_image_patch(y,
-                                                                  xnorm, ynorm,
-                                                                  wnorm, hnorm))
-        its.image.write_image(chart, '%s_i=%d_chart.jpg' % (NAME, i))
-        data['sharpness'] = white_level*its.image.compute_image_sharpness(chart)
+        y = its.image.rotate_img_per_argv(y)
+        chart.img = its.image.normalize_img(its.image.get_image_patch(
+                y, chart.xnorm, chart.ynorm, chart.wnorm, chart.hnorm))
+        its.image.write_image(chart.img, '%s_i=%d_chart.jpg' % (NAME, i))
+        data['sharpness'] = white_level*its.image.compute_image_sharpness(
+                chart.img)
         print 'Chart sharpness: %.1f\n' % data['sharpness']
         data_set[i] = data
     return data_set
@@ -110,10 +102,16 @@
     """
 
     print '\nStarting test_lens_movement_reporting.py'
+    # initialize chart class
+    chart = its.cv2image.Chart(CHART_FILE, CHART_HEIGHT, CHART_DISTANCE,
+                               CHART_SCALE_START, CHART_SCALE_STOP,
+                               CHART_SCALE_STEP)
+
     with its.device.ItsSession() as cam:
         props = cam.get_camera_properties()
         its.caps.skip_unless(not its.caps.fixed_focus(props))
-        its.caps.skip_unless(its.caps.lens_approx_calibrated(props))
+        its.caps.skip_unless(its.caps.read_3a(props) and
+                             its.caps.lens_approx_calibrated(props))
         min_fd = props['android.lens.info.minimumFocusDistance']
         fmt = {'format': 'yuv', 'width': VGA_WIDTH, 'height': VGA_HEIGHT}
 
@@ -121,7 +119,7 @@
         s, e, _, _, fd = cam.do_3a(get_results=True)
 
         # Get sharpness for each focal distance
-        d = test_lens_movement_reporting(cam, props, fmt, s, e, fd)
+        d = test_lens_movement_reporting(cam, props, fmt, s, e, fd, chart)
         for k in sorted(d):
             print ('i: %d\tfd: %.3f\tlens location (diopters): %.3f \t'
                    'sharpness: %.1f  \tlens_moving: %r \t'
diff --git a/apps/CameraITS/tests/scene3/test_lens_position.py b/apps/CameraITS/tests/scene3/test_lens_position.py
index f850e3d..feb28a8 100644
--- a/apps/CameraITS/tests/scene3/test_lens_position.py
+++ b/apps/CameraITS/tests/scene3/test_lens_position.py
@@ -38,7 +38,7 @@
 CHART_SCALE_STEP = 0.025
 
 
-def test_lens_position(cam, props, fmt, sensitivity, exp, af_fd):
+def test_lens_position(cam, props, fmt, sensitivity, exp, chart):
     """Return fd, sharpness, lens state of the output images.
 
     Args:
@@ -49,8 +49,7 @@
             android.sensor.sensitivity
         exp: Exposure time for the 3A request as defined in
             android.sensor.exposureTime
-        af_fd: Focus distance for the 3A request as defined in
-            android.lens.focusDistance
+        chart: Object with chart properties
 
     Returns:
         Dictionary of results for different focal distance captures
@@ -58,15 +57,6 @@
         d_static, d_moving
     """
 
-    # initialize chart class
-    chart = its.cv2image.Chart(CHART_FILE, CHART_HEIGHT, CHART_DISTANCE,
-                                CHART_SCALE_START, CHART_SCALE_STOP,
-                                CHART_SCALE_STEP)
-
-    # find chart location
-    xnorm, ynorm, wnorm, hnorm = chart.locate(cam, props, fmt, sensitivity,
-                                              exp, af_fd)
-
     # initialize variables and take data sets
     data_static = {}
     data_moving = {}
@@ -89,11 +79,11 @@
         print ' focus distance (diopters): %.3f' % data['fd']
         print ' current lens location (diopters): %.3f' % data['loc']
         y, _, _ = its.image.convert_capture_to_planes(cap, props)
-        chart = its.image.normalize_img(its.image.get_image_patch(y,
-                                                                  xnorm, ynorm,
-                                                                  wnorm, hnorm))
-        its.image.write_image(chart, '%s_stat_i=%d_chart.jpg' % (NAME, i))
-        data['sharpness'] = white_level*its.image.compute_image_sharpness(chart)
+        chart.img = its.image.normalize_img(its.image.get_image_patch(
+                y, chart.xnorm, chart.ynorm, chart.wnorm, chart.hnorm))
+        its.image.write_image(chart.img, '%s_stat_i=%d_chart.jpg' % (NAME, i))
+        data['sharpness'] = white_level*its.image.compute_image_sharpness(
+                chart.img)
         print 'Chart sharpness: %.1f\n' % data['sharpness']
         data_static[i] = data
     # take moving data set
@@ -115,12 +105,12 @@
         print ' focus distance (diopters): %.3f' % data['fd']
         print ' current lens location (diopters): %.3f' % data['loc']
         y, _, _ = its.image.convert_capture_to_planes(cap, props)
-        y = its.image.flip_mirror_img_per_argv(y)
-        chart = its.image.normalize_img(its.image.get_image_patch(y,
-                                                                  xnorm, ynorm,
-                                                                  wnorm, hnorm))
-        its.image.write_image(chart, '%s_move_i=%d_chart.jpg' % (NAME, i))
-        data['sharpness'] = white_level*its.image.compute_image_sharpness(chart)
+        y = its.image.rotate_img_per_argv(y)
+        chart.img = its.image.normalize_img(its.image.get_image_patch(
+                y, chart.xnorm, chart.ynorm, chart.wnorm, chart.hnorm))
+        its.image.write_image(chart.img, '%s_move_i=%d_chart.jpg' % (NAME, i))
+        data['sharpness'] = white_level*its.image.compute_image_sharpness(
+                chart.img)
         print 'Chart sharpness: %.1f\n' % data['sharpness']
         data_moving[i] = data
     return data_static, data_moving
@@ -128,19 +118,24 @@
 
 def main():
     """Test if focus position is properly reported for moving lenses."""
-
     print '\nStarting test_lens_position.py'
+    # initialize chart class
+    chart = its.cv2image.Chart(CHART_FILE, CHART_HEIGHT, CHART_DISTANCE,
+                               CHART_SCALE_START, CHART_SCALE_STOP,
+                               CHART_SCALE_STEP)
+
     with its.device.ItsSession() as cam:
         props = cam.get_camera_properties()
         its.caps.skip_unless(not its.caps.fixed_focus(props))
-        its.caps.skip_unless(its.caps.lens_calibrated(props))
+        its.caps.skip_unless(its.caps.read_3a(props) and
+                             its.caps.lens_calibrated(props))
         fmt = {'format': 'yuv', 'width': VGA_WIDTH, 'height': VGA_HEIGHT}
 
-        # Get proper sensitivity, exposure time, and focus distance with 3A.
-        s, e, _, _, fd = cam.do_3a(get_results=True)
+        # Get proper sensitivity and exposure time with 3A
+        s, e, _, _, _ = cam.do_3a(get_results=True)
 
         # Get sharpness for each focal distance
-        d_stat, d_move = test_lens_position(cam, props, fmt, s, e, fd)
+        d_stat, d_move = test_lens_position(cam, props, fmt, s, e, chart)
         print 'Lens stationary'
         for k in sorted(d_stat):
             print ('i: %d\tfd: %.3f\tlens location (diopters): %.3f \t'
diff --git a/apps/CameraITS/tests/scene4/scene4_0.67_scaled.pdf b/apps/CameraITS/tests/scene4/scene4_0.67_scaled.pdf
new file mode 100644
index 0000000..7fb1e42
--- /dev/null
+++ b/apps/CameraITS/tests/scene4/scene4_0.67_scaled.pdf
Binary files differ
diff --git a/apps/CameraITS/tests/sensor_fusion/test_sensor_fusion.py b/apps/CameraITS/tests/sensor_fusion/test_sensor_fusion.py
index e8a5b81..faacaf9 100644
--- a/apps/CameraITS/tests/sensor_fusion/test_sensor_fusion.py
+++ b/apps/CameraITS/tests/sensor_fusion/test_sensor_fusion.py
@@ -116,7 +116,7 @@
             # Split by comma and convert each dimension to int.
             [w, h] = map(int, s[9:].split(","))
         elif s[:12] == "test_length=" and len(s) > 12:
-            test_length = int(s[12:])
+            test_length = float(s[12:])
 
     # Collect or load the camera+gyro data. All gyro events as well as camera
     # timestamps are in the "events" dictionary, and "frames" is a list of
@@ -345,7 +345,7 @@
         if num_features < MIN_FEATURE_PTS:
             print "Not enough feature points in frame", i
             print "Need at least %d features, got %d" % (
-                    MIN_FEATURE_PTS, num_features)
+                MIN_FEATURE_PTS, num_features)
             assert 0
         else:
             print "Number of features in frame %d is %d" % (i, num_features)
@@ -447,13 +447,12 @@
         fmt = {"format": "yuv", "width": w, "height": h}
         s, e, _, _, _ = cam.do_3a(get_results=True, do_af=False)
         req = its.objects.manual_capture_request(s, e)
-        fps = 30
         req["android.lens.focusDistance"] = 1 / (CHART_DISTANCE * CM_TO_M)
         req["android.control.aeTargetFpsRange"] = [fps, fps]
         req["android.sensor.frameDuration"] = int(1000.0/fps * MSEC_TO_NSEC)
-        print "Capturing %dx%d with sens. %d, exp. time %.1fms" % (
-            w, h, s, e*NSEC_TO_MSEC)
-        caps = cam.do_capture([req]*fps*test_length, fmt)
+        print "Capturing %dx%d with sens. %d, exp. time %.1fms at %dfps" % (
+            w, h, s, e*NSEC_TO_MSEC, fps)
+        caps = cam.do_capture([req]*int(fps*test_length), fmt)
 
         # Get the gyro events.
         print "Reading out sensor events"
diff --git a/apps/CameraITS/tools/load_scene.py b/apps/CameraITS/tools/load_scene.py
index 4e245f4..b46a311 100644
--- a/apps/CameraITS/tools/load_scene.py
+++ b/apps/CameraITS/tools/load_scene.py
@@ -21,13 +21,16 @@
 
 def main():
     """Load charts on device and display."""
-    camera_id = -1
     scene = None
     for s in sys.argv[1:]:
         if s[:6] == 'scene=' and len(s) > 6:
             scene = s[6:]
         elif s[:7] == 'screen=' and len(s) > 7:
             screen_id = s[7:]
+        elif s[:5] == 'dist=' and len(s) > 5:
+            chart_distance = float(re.sub('cm', '', s[5:]))
+        elif s[:4] == 'fov=' and len(s) > 4:
+            camera_fov = float(s[4:])
 
     cmd = ('adb -s %s shell am force-stop com.google.android.apps.docs' %
            screen_id)
@@ -43,8 +46,13 @@
 
     remote_scene_file = '/sdcard/Download/%s.pdf' % scene
     local_scene_file = os.path.join(os.environ['CAMERA_ITS_TOP'], 'tests',
-                                    scene, scene+'.pdf')
-    print 'Loading %s on %s' % (remote_scene_file, screen_id)
+                                    scene)
+    if chart_distance == 20 and camera_fov < 90:
+        local_scene_file = os.path.join(local_scene_file,
+                                        scene+'_0.67_scaled.pdf')
+    else:
+        local_scene_file = os.path.join(local_scene_file, scene+'.pdf')
+    print 'Loading %s on %s' % (local_scene_file, screen_id)
     cmd = 'adb -s %s push %s /mnt%s' % (screen_id, local_scene_file,
                                         remote_scene_file)
     subprocess.Popen(cmd.split())
diff --git a/apps/CameraITS/tools/run_all_tests.py b/apps/CameraITS/tools/run_all_tests.py
index 47f7296..17d49ec 100644
--- a/apps/CameraITS/tools/run_all_tests.py
+++ b/apps/CameraITS/tools/run_all_tests.py
@@ -13,21 +13,50 @@
 # limitations under the License.
 
 import copy
+import math
 import os
 import os.path
-import tempfile
+import re
 import subprocess
-import time
 import sys
+import tempfile
+import time
 
 import its.caps
+import its.cv2image
 import its.device
 from its.device import ItsSession
+import its.image
 
 CHART_DELAY = 1  # seconds
+CHART_DISTANCE = 30.0  # cm
+CHART_HEIGHT = 13.5  # cm
+CHART_SCALE_START = 0.65
+CHART_SCALE_STOP = 1.35
+CHART_SCALE_STEP = 0.025
 FACING_EXTERNAL = 2
 NUM_TRYS = 2
+SCENE3_FILE = os.path.join(os.environ['CAMERA_ITS_TOP'], 'pymodules', 'its',
+                           'test_images', 'ISO12233.png')
 SKIP_RET_CODE = 101  # note this must be same as tests/scene*/test_*
+VGA_HEIGHT = 480
+VGA_WIDTH = 640
+
+
+def calc_camera_fov():
+    """Determine the camera field of view from internal params."""
+    with ItsSession() as cam:
+        props = cam.get_camera_properties()
+    try:
+        focal_l = props['android.lens.info.availableFocalLengths'][0]
+        sensor_size = props['android.sensor.info.physicalSize']
+        diag = math.sqrt(sensor_size['height'] ** 2 +
+                         sensor_size['width'] ** 2)
+        fov = str(round(2 * math.degrees(math.atan(diag / (2 * focal_l))), 2))
+    except ValueError:
+        fov = str(0)
+    print 'Calculated FoV: %s' % fov
+    return fov
 
 
 def evaluate_socket_failure(err_file_path):
@@ -79,6 +108,7 @@
         tmp_dir: location of temp directory for output files
         skip_scene_validation: force skip scene validation. Used when test scene
                  is setup up front and don't require tester validation.
+        dist:    [Experimental] chart distance in cm.
     """
 
     # Not yet mandated tests
@@ -100,6 +130,7 @@
             ],
         "scene3": [
             "test_3a_consistency",
+            "test_flip_mirror",
             "test_lens_movement_reporting",
             "test_lens_position"
             ],
@@ -139,6 +170,10 @@
     rot_rig_id = None
     tmp_dir = None
     skip_scene_validation = False
+    chart_distance = CHART_DISTANCE
+    chart_height = CHART_HEIGHT
+    approx_equal = lambda a, b, t: abs(a - b) < t
+
     for s in sys.argv[1:]:
         if s[:7] == "camera=" and len(s) > 7:
             camera_ids = s[7:].split(',')
@@ -155,6 +190,8 @@
             tmp_dir = s[8:]
         elif s == 'skip_scene_validation':
             skip_scene_validation = True
+        elif s[:5] == 'dist=' and len(s) > 5:
+            chart_distance = float(re.sub('cm', '', s[5:]))
 
     auto_scene_switch = chart_host_id is not None
     merge_result_switch = result_device_id is not None
@@ -183,7 +220,7 @@
                     break
 
         if not valid_scenes:
-            print "Unknown scene specifiied:", s
+            print 'Unknown scene specified:', s
             assert False
         scenes = temp_scenes
 
@@ -245,6 +282,7 @@
             assert wake_code == 0
 
     for camera_id in camera_ids:
+        camera_fov = calc_camera_fov()
         # Loop capturing images until user confirm test scene is correct
         camera_id_arg = "camera=" + camera_id
         print "Preparing to run ITS on camera", camera_id
@@ -280,9 +318,11 @@
                     if (not merge_result_switch or
                             (merge_result_switch and camera_ids[0] == '0')):
                         scene_arg = 'scene=' + scene
+                        chart_dist_arg = 'dist= ' + str(chart_distance)
+                        fov_arg = 'fov=' + camera_fov
                         cmd = ['python',
                                os.path.join(os.getcwd(), 'tools/load_scene.py'),
-                               scene_arg, screen_id_arg]
+                               scene_arg, chart_dist_arg, fov_arg, screen_id_arg]
                     else:
                         time.sleep(CHART_DELAY)
                 else:
@@ -299,6 +339,18 @@
                     valid_scene_code = subprocess.call(cmd, cwd=topdir)
                     assert valid_scene_code == 0
             print "Start running ITS on camera %s, %s" % (camera_id, scene)
+            # Extract chart from scene for scene3 once up front
+            chart_loc_arg = ''
+            if scene == 'scene3':
+                if float(camera_fov) < 90 and approx_equal(chart_distance, 20,
+                                                           1E-6):
+                    chart_height *= 0.67
+                chart = its.cv2image.Chart(SCENE3_FILE, chart_height,
+                                           chart_distance, CHART_SCALE_START,
+                                           CHART_SCALE_STOP, CHART_SCALE_STEP)
+                chart_loc_arg = 'chart_loc=%.2f,%.2f,%.2f,%.2f,%.3f' % (
+                        chart.xnorm, chart.ynorm, chart.wnorm, chart.hnorm,
+                        chart.scale)
             # Run each test, capturing stdout and stderr.
             for (testname, testpath) in tests:
                 if auto_scene_switch:
@@ -330,7 +382,7 @@
                             test_code = skip_code
                     if skip_code is not SKIP_RET_CODE:
                         cmd = ['python', os.path.join(os.getcwd(), testpath)]
-                        cmd += sys.argv[1:] + [camera_id_arg]
+                        cmd += sys.argv[1:] + [camera_id_arg] + [chart_loc_arg]
                         with open(outpath, 'w') as fout, open(errpath, 'w') as ferr:
                             test_code = subprocess.call(
                                 cmd, stderr=ferr, stdout=fout, cwd=outdir)
diff --git a/apps/CameraITS/tools/run_sensor_fusion_box.py b/apps/CameraITS/tools/run_sensor_fusion_box.py
index ec16b3d..41f1180 100644
--- a/apps/CameraITS/tools/run_sensor_fusion_box.py
+++ b/apps/CameraITS/tools/run_sensor_fusion_box.py
@@ -82,10 +82,6 @@
         elif s[:8] == 'tmp_dir=' and len(s) > 8:
             tmp_dir = s[8:]
 
-    if camera_id not in ['0', '1']:
-        print 'Need to specify camera 0 or 1'
-        sys.exit()
-
     # Make output directories to hold the generated files.
     tmpdir = tempfile.mkdtemp(dir=tmp_dir)
     print 'Saving output files to:', tmpdir, '\n'
@@ -94,6 +90,12 @@
     device_id_arg = 'device=' + device_id
     print 'Testing device ' + device_id
 
+    # ensure camera_id is valid
+    avail_camera_ids = find_avail_camera_ids(device_id_arg, tmpdir)
+    if camera_id not in avail_camera_ids:
+        print 'Need to specify valid camera_id in ', avail_camera_ids
+        sys.exit()
+
     camera_id_arg = 'camera=' + camera_id
     if rotator_ids:
         rotator_id_arg = 'rotator=' + rotator_ids
@@ -104,6 +106,7 @@
 
     fps_arg = 'fps=' + fps
     test_length_arg = 'test_length=' + test_length
+    print 'Capturing at %sfps' % fps
 
     os.mkdir(os.path.join(tmpdir, camera_id))
 
@@ -217,6 +220,28 @@
                 return line
     return None
 
+def find_avail_camera_ids(device_id_arg, tmpdir):
+    """Find the available camera IDs.
+
+    Args:
+        devices_id_arg(str):    device=###
+        tmpdir(str):            generated tmp dir for run
+    Returns:
+        list of available cameras
+    """
+    avail_camera_ids = []
+    camera_ids_path = os.path.join(tmpdir, 'camera_ids.txt')
+    out_arg = 'out=' + camera_ids_path
+    cmd = ['python',
+           os.path.join(os.getcwd(), 'tools/get_camera_ids.py'), out_arg,
+           device_id_arg]
+    cam_code = subprocess.call(cmd, cwd=tmpdir)
+    assert cam_code == 0
+    with open(camera_ids_path, "r") as f:
+        for line in f:
+            avail_camera_ids.append(line.replace('\n', ''))
+    return avail_camera_ids
+
 
 if __name__ == '__main__':
     main()
diff --git a/apps/CtsVerifier/Android.mk b/apps/CtsVerifier/Android.mk
index 9fcbdb1..10c33ba 100644
--- a/apps/CtsVerifier/Android.mk
+++ b/apps/CtsVerifier/Android.mk
@@ -38,9 +38,14 @@
                                mockito-target-minus-junit4 \
                                mockwebserver \
                                compatibility-device-util \
-                               platform-test-annotations
+                               platform-test-annotations \
+                               cts-security-test-support-library
 
-LOCAL_JAVA_LIBRARIES := legacy-android-test
+LOCAL_JAVA_LIBRARIES += telephony-common
+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_PACKAGE_NAME := CtsVerifier
 
diff --git a/apps/CtsVerifier/AndroidManifest.xml b/apps/CtsVerifier/AndroidManifest.xml
index 514056a..5f7ee79 100644
--- a/apps/CtsVerifier/AndroidManifest.xml
+++ b/apps/CtsVerifier/AndroidManifest.xml
@@ -67,6 +67,12 @@
     <uses-permission android:name="android.permission.CHANGE_WIFI_STATE" />
     <uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION" />
 
+    <uses-permission android:name="android.permission.READ_PHONE_STATE"/>
+    <uses-permission android:name="android.permission.READ_SMS"/>
+    <uses-permission android:name="android.permission.READ_PHONE_NUMBERS"/>
+    <uses-permission android:name="android.permission.RECEIVE_SMS" />
+    <uses-permission android:name="android.permission.SEND_SMS" />
+
     <!-- Needed by UsbTest tapjacking -->
     <uses-permission android:name="android.permission.SYSTEM_ALERT_WINDOW" />
 
@@ -82,6 +88,11 @@
             android:largeHeap="true"
             android:theme="@android:style/Theme.DeviceDefault">
 
+        <provider android:name="android.location.cts.MmsPduProvider"
+                android:authorities="emergencycallverifier"
+                android:grantUriPermissions="true" />
+        <uses-library android:name="android.test.runner" />
+
         <meta-data android:name="SuiteName" android:value="CTS_VERIFIER" />
 
         <meta-data android:name="android.telephony.HIDE_VOICEMAIL_SETTINGS_MENU"
@@ -167,8 +178,13 @@
                        android:value="android.software.companion_device_setup" />
         </activity>
 
-        <!-- A generic activity for intent based tests -->
-        <activity android:name=".IntentDrivenTestActivity"/>
+        <!-- A generic activity for intent based tests.
+        stateNotNeeded is defined ot prevent IntentDrivenTestActivity from being killed when
+        switching users. IntentDrivenTestActivity does not implement onSaveInstanceState() so it is
+        fine to ignore onSaveInstanceState() not being called.
+        -->
+        <activity android:name=".IntentDrivenTestActivity"
+                android:stateNotNeeded="true"/>
 
         <activity android:name=".admin.DeviceAdminKeyguardDisabledFeaturesActivity"
                 android:label="@string/da_kg_disabled_features_test"
@@ -1099,6 +1115,43 @@
             <meta-data android:name="test_required_features" android:value="android.hardware.location.gps" />
         </activity>
 
+        <activity android:name=".location.EmergencyCallWifiTestsActivity"
+            android:label="@string/location_emergency_call_wifi_test"
+            android:screenOrientation="locked">
+            <intent-filter>
+                <action android:name="android.intent.action.MAIN"/>
+                <category android:name="android.cts.intent.category.MANUAL_TEST"/>
+            </intent-filter>
+            <meta-data android:name="test_category" android:value="@string/test_category_hardware"/>
+            <meta-data android:name="test_required_features" android:value="android.hardware.location.gps" />
+            <meta-data android:name="test_required_features" android:value="android.hardware.wifi" />
+        </activity>
+
+        <activity android:name=".location.EmergencyCallMessageTestsActivity"
+            android:label="@string/location_emergency_call_message_test"
+            android:screenOrientation="locked">
+            <intent-filter>
+                <action android:name="android.intent.action.MAIN"/>
+                <category android:name="android.cts.intent.category.MANUAL_TEST"/>
+            </intent-filter>
+            <meta-data android:name="test_category" android:value="@string/test_category_hardware"/>
+            <meta-data android:name="test_required_features" android:value="android.hardware.location.gps" />
+            <meta-data android:name="test_required_features" android:value="android.hardware.wifi" />
+            <meta-data android:name="test_required_features" android:value="android.hardware.telephony"/>
+        </activity>
+
+        <activity android:name=".location.EmergencyCallGNSSTestsActivity"
+            android:label="@string/location_emergency_call_gps_test"
+            android:screenOrientation="locked">
+            <intent-filter>
+                <action android:name="android.intent.action.MAIN"/>
+                <category android:name="android.cts.intent.category.MANUAL_TEST"/>
+            </intent-filter>
+            <meta-data android:name="test_category" android:value="@string/test_category_hardware"/>
+            <meta-data android:name="test_required_features" android:value="android.hardware.location.gps" />
+            <meta-data android:name="test_required_features" android:value="android.hardware.wifi" />
+        </activity>
+
         <activity android:name=".location.GnssMeasurementWhenNoLocationTestsActivity"
             android:label="@string/location_gnss_measure_no_location_test"
             android:screenOrientation="locked">
@@ -1829,6 +1882,13 @@
             <meta-data android:name="test_category" android:value="@string/test_category_notifications" />
         </activity>
 
+        <receiver android:name=".notifications.BlockChangeReceiver">
+            <intent-filter>
+                <action android:name="android.app.action.NOTIFICATION_CHANNEL_BLOCK_STATE_CHANGED"/>
+                <action android:name="android.app.action.NOTIFICATION_CHANNEL_GROUP_BLOCK_STATE_CHANGED"/>
+            </intent-filter>
+        </receiver>
+
         <activity android:name=".notifications.ConditionProviderVerifierActivity"
                   android:label="@string/cp_test">
             <intent-filter>
@@ -2266,6 +2326,16 @@
             <meta-data android:name="test_required_features" android:value="android.software.device_admin" />
         </activity>
 
+        <activity android:name=".managedprovisioning.ManagedUserPositiveTestActivity"
+                  android:label="@string/managed_user_test">
+            <intent-filter>
+                <action android:name="android.intent.action.MAIN" />
+                <action android:name="com.android.cts.verifier.managedprovisioning.action.CHECK_AFFILIATED_PROFILE_OWNER" />
+                <category android:name="android.intent.category.DEFAULT" />
+                <category android:name="android.cts.intent.category.MANUAL_TEST" />
+            </intent-filter>
+        </activity>
+
         <activity android:name=".managedprovisioning.DeviceOwnerRequestingBugreportTestActivity"
                 android:label="@string/device_owner_requesting_bugreport_tests">
             <intent-filter>
@@ -2278,14 +2348,6 @@
             <meta-data android:name="test_required_features" android:value="android.software.device_admin" />
         </activity>
 
-        <activity android:name=".managedprovisioning.DeviceOwnerPositiveTestActivity$CommandReceiver"
-                android:exported="false"
-                android:theme="@android:style/Theme.NoDisplay"
-                android:noHistory="true"
-                android:autoRemoveFromRecents="true"
-                android:stateNotNeeded="true">
-        </activity>
-
         <activity android:name=".managedprovisioning.KeyguardDisabledFeaturesActivity"
                 android:label="@string/provisioning_byod_keyguard_disabled_features">
         </activity>
@@ -2294,6 +2356,14 @@
                 android:label="@string/provisioning_byod_disallow_apps_control">
         </activity>
 
+        <activity android:name=".managedprovisioning.LockTaskUiTestActivity"
+                android:label="@string/device_owner_lock_task_ui_test">
+            <intent-filter>
+                <action android:name="com.android.cts.verifier.managedprovisioning.action.STOP_LOCK_TASK" />
+                <category android:name="android.intent.category.DEFAULT" />
+            </intent-filter>
+        </activity>
+
         <activity android:name=".managedprovisioning.WifiLockdownTestActivity"
                 android:label="@string/device_owner_wifi_lockdown_test">
         </activity>
@@ -2321,6 +2391,14 @@
             </intent-filter>
         </activity>
 
+        <activity android:name=".managedprovisioning.KeyChainTestActivity"
+                android:label="@string/provisioning_byod_keychain">
+            <intent-filter>
+                <action android:name="com.android.cts.verifier.managedprovisioning.KEYCHAIN" />
+                <category android:name="android.intent.category.DEFAULT" />
+            </intent-filter>
+        </activity>
+
         <activity android:name=".managedprovisioning.PermissionLockdownTestActivity"
                 android:label="@string/device_profile_owner_permission_lockdown_test">
             <intent-filter>
diff --git a/apps/CtsVerifier/res/layout-small/positive_managed_user.xml b/apps/CtsVerifier/res/layout-small/positive_managed_user.xml
new file mode 100644
index 0000000..84fa363
--- /dev/null
+++ b/apps/CtsVerifier/res/layout-small/positive_managed_user.xml
@@ -0,0 +1,45 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2017 The Android Open Source Project
+
+     Licensed under the Apache License, Version 2.0 (the "License");
+     you may not use this file except in compliance with the License.
+     You may obtain a copy of the License at
+
+          http://www.apache.org/licenses/LICENSE-2.0
+
+     Unless required by applicable law or agreed to in writing, software
+     distributed under the License is distributed on an "AS IS" BASIS,
+     WITHOUT 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"
+    style="@style/RootLayoutPadding"
+    android:layout_width="match_parent"
+    android:layout_height="match_parent"
+    android:orientation="vertical">
+
+    <ScrollView
+        android:layout_width="match_parent"
+        android:layout_height="match_parent">
+        <LinearLayout
+            android:layout_width="match_parent"
+            android:layout_height="match_parent"
+            android:orientation="vertical">
+
+            <TextView
+                android:id="@+id/positive_managed_user_instructions"
+                android:layout_width="match_parent"
+                android:layout_height="wrap_content"
+                android:text="@string/managed_user_positive_tests_instructions"
+                android:textSize="16dip" />
+
+            <ListView
+                android:id="@+id/android:list"
+                android:layout_width="match_parent"
+                android:layout_height="800dip" />
+
+            <include layout="@layout/pass_fail_buttons" />
+        </LinearLayout>
+    </ScrollView>
+</LinearLayout>
diff --git a/apps/CtsVerifier/res/layout/device_owner_lock_task_ui.xml b/apps/CtsVerifier/res/layout/device_owner_lock_task_ui.xml
new file mode 100644
index 0000000..00dee1a
--- /dev/null
+++ b/apps/CtsVerifier/res/layout/device_owner_lock_task_ui.xml
@@ -0,0 +1,53 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2017 The Android Open Source Project
+
+     Licensed under the Apache License, Version 2.0 (the "License");
+     you may not use this file except in compliance with the License.
+     You may obtain a copy of the License at
+
+          http://www.apache.org/licenses/LICENSE-2.0
+
+     Unless required by applicable law or agreed to in writing, software
+     distributed under the License is distributed on an "AS IS" BASIS,
+     WITHOUT 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"
+              style="@style/RootLayoutPadding"
+              android:layout_width="match_parent"
+              android:layout_height="match_parent"
+              android:orientation="vertical">
+
+    <ScrollView
+        android:layout_width="match_parent"
+        android:layout_height="match_parent"
+        android:fillViewport="true">
+        <LinearLayout
+            android:layout_width="match_parent"
+            android:layout_height="match_parent"
+            android:orientation="vertical">
+
+            <TextView
+                android:id="@+id/device_owner_lock_task_ui_instructions"
+                android:layout_width="match_parent"
+                android:layout_height="wrap_content"
+                android:text="@string/device_owner_lock_task_ui_test_info"
+                android:textSize="18dip" />
+
+            <Button
+                android:id="@+id/start_lock_task_button"
+                android:layout_width="match_parent"
+                android:layout_height="wrap_content"
+                android:text="@string/start_lock_task_button_label" />
+
+            <ListView
+                android:id="@+id/android:list"
+                android:layout_width="match_parent"
+                android:layout_height="0dp"
+                android:layout_weight="1" />
+
+            <include layout="@layout/pass_fail_buttons" />
+        </LinearLayout>
+    </ScrollView>
+</LinearLayout>
diff --git a/apps/CtsVerifier/res/layout/emergency_call_confirm_dialog.xml b/apps/CtsVerifier/res/layout/emergency_call_confirm_dialog.xml
new file mode 100644
index 0000000..fae5167
--- /dev/null
+++ b/apps/CtsVerifier/res/layout/emergency_call_confirm_dialog.xml
@@ -0,0 +1,31 @@
+<?xml version="1.0" encoding="utf-8"?>
+    <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
+        android:layout_width="match_parent"
+        android:layout_height="match_parent"
+        android:orientation="vertical"
+        style="@style/RootLayoutPadding">
+
+        <TextView android:id="@+id/info"
+            android:layout_width="match_parent"
+            android:layout_height="wrap_content"
+            android:layout_weight="1"
+            android:textSize="18sp"
+            android:padding="5dp"
+            android:text="@string/emergency_call_confirm_info"/>
+        <EditText
+            android:id="@+id/emergency_number"
+            android:layout_height="wrap_content"
+            android:layout_width="match_parent"
+            android:hint="@string/emergency_call_emergency_number_hint_text"
+            android:inputType="phone"
+            android:text="@string/emergency_call_emergency_number_text"/>
+        <Button
+            android:id="@+id/dial_button"
+            android:layout_width="200px"
+            android:layout_height="wrap_content"
+            android:text="@string/emergency_call_dial_text"
+            android:paddingLeft="5dp"
+            android:paddingRight="5dp"
+            android:layout_marginTop="5dp"
+            android:layout_marginRight="5dp"/>
+    </LinearLayout>
diff --git a/apps/CtsVerifier/res/layout/emergency_call_msg_test_confirm_dialog.xml b/apps/CtsVerifier/res/layout/emergency_call_msg_test_confirm_dialog.xml
new file mode 100644
index 0000000..071e2bf
--- /dev/null
+++ b/apps/CtsVerifier/res/layout/emergency_call_msg_test_confirm_dialog.xml
@@ -0,0 +1,37 @@
+<?xml version="1.0" encoding="utf-8"?>
+    <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
+        android:layout_width="match_parent"
+        android:layout_height="match_parent"
+        android:orientation="vertical"
+        style="@style/RootLayoutPadding">
+
+        <TextView android:id="@+id/info"
+            android:layout_width="match_parent"
+            android:layout_height="wrap_content"
+            android:layout_weight="1"
+            android:textSize="18sp"
+            android:padding="5dp"
+            android:text="@string/emergency_call_confirm_info"/>
+        <EditText
+            android:id="@+id/emergency_number"
+            android:layout_height="wrap_content"
+            android:layout_width="match_parent"
+            android:hint="@string/emergency_call_emergency_number_hint_text"
+            android:inputType="phone"
+            android:text="@string/emergency_call_emergency_number_text"/>
+        <EditText
+            android:id="@+id/local_phone_number"
+            android:layout_height="wrap_content"
+            android:layout_width="match_parent"
+            android:hint="@string/emergency_call_current_number_hint_text"
+            android:inputType="phone"/>
+        <Button
+            android:id="@+id/dial_button"
+            android:layout_width="200px"
+            android:layout_height="wrap_content"
+            android:text="@string/emergency_call_dial_text"
+            android:paddingLeft="5dp"
+            android:paddingRight="5dp"
+            android:layout_marginTop="5dp"
+            android:layout_marginRight="5dp"/>
+    </LinearLayout>
diff --git a/apps/CtsVerifier/res/layout/keychain_test.xml b/apps/CtsVerifier/res/layout/keychain_test.xml
new file mode 100644
index 0000000..09708c7
--- /dev/null
+++ b/apps/CtsVerifier/res/layout/keychain_test.xml
@@ -0,0 +1,62 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2018 The Android Open Source Project
+
+     Licensed under the Apache License, Version 2.0 (the "License");
+     you may not use this file except in compliance with the License.
+     You may obtain a copy of the License at
+
+          http://www.apache.org/licenses/LICENSE-2.0
+
+     Unless required by applicable law or agreed to in writing, software
+     distributed under the License is distributed on an "AS IS" BASIS,
+     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+     See the License for the specific language governing permissions and
+     limitations under the License.
+-->
+<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
+        android:orientation="vertical"
+        android:layout_width="match_parent"
+        android:layout_height="match_parent">
+
+    <ScrollView
+            android:layout_width="match_parent"
+            android:layout_height="320dip"
+            android:layout_weight="2">
+        <TextView
+                android:id="@+id/provisioning_byod_keychain_instructions"
+                android:layout_width="match_parent"
+                android:layout_height="wrap_content"
+                android:padding="10dip"
+                android:text="@string/provisioning_byod_keychain_info_start"
+                android:textSize="18dip" />
+    </ScrollView>
+
+    <TextView
+          android:id="@+id/provisioning_byod_keychain_test_log"
+          android:layout_width="match_parent"
+          android:layout_height="wrap_content"
+          android:padding="10dip"
+          android:textSize="18dip" />
+
+    <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
+        android:layout_width="match_parent"
+        android:layout_height="wrap_content"
+        android:orientation="horizontal">
+
+        <Button
+            android:id="@+id/prepare_test_button"
+            android:layout_width="204dip"
+            android:layout_height="wrap_content"
+            android:text="@string/provisioning_byod_keyguard_disabled_features_prepare_button"/>
+
+        <Button
+            android:id="@+id/run_test_button"
+            android:layout_width="204dip"
+            android:layout_height="wrap_content"
+            android:text="@string/go_button_text"/>
+
+    </LinearLayout>
+
+    <include layout="@layout/pass_fail_buttons" />
+
+</LinearLayout>
diff --git a/apps/CtsVerifier/res/layout/positive_managed_user.xml b/apps/CtsVerifier/res/layout/positive_managed_user.xml
new file mode 100644
index 0000000..4afdf63
--- /dev/null
+++ b/apps/CtsVerifier/res/layout/positive_managed_user.xml
@@ -0,0 +1,47 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2017 The Android Open Source Project
+
+     Licensed under the Apache License, Version 2.0 (the "License");
+     you may not use this file except in compliance with the License.
+     You may obtain a copy of the License at
+
+          http://www.apache.org/licenses/LICENSE-2.0
+
+     Unless required by applicable law or agreed to in writing, software
+     distributed under the License is distributed on an "AS IS" BASIS,
+     WITHOUT 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"
+    style="@style/RootLayoutPadding"
+    android:layout_width="match_parent"
+    android:layout_height="match_parent"
+    android:orientation="vertical">
+
+    <ScrollView
+        android:layout_width="match_parent"
+        android:layout_height="match_parent"
+        android:fillViewport="true">
+        <LinearLayout
+            android:layout_width="match_parent"
+            android:layout_height="match_parent"
+            android:orientation="vertical">
+
+            <TextView
+                android:id="@+id/positive_managed_user_instructions"
+                android:layout_width="match_parent"
+                android:layout_height="wrap_content"
+                android:text="@string/managed_user_positive_tests_instructions"
+                android:textSize="18dip" />
+
+            <ListView
+                android:id="@+id/android:list"
+                android:layout_width="match_parent"
+                android:layout_height="0dp"
+                android:layout_weight="1" />
+
+            <include layout="@layout/pass_fail_buttons" />
+        </LinearLayout>
+    </ScrollView>
+</LinearLayout>
diff --git a/apps/CtsVerifier/res/values/strings.xml b/apps/CtsVerifier/res/values/strings.xml
index 2ca58ab..14706dd 100755
--- a/apps/CtsVerifier/res/values/strings.xml
+++ b/apps/CtsVerifier/res/values/strings.xml
@@ -566,10 +566,32 @@
         (for example, stationary on a windowsill.  If needed, try again, outside, also with the
         device stationary, and with at least some view of the sky.) and then press Next to run
         the automated tests.</string>
+    <string name="location_emergency_call_test_info">This test verifies whether basic features
+        (wifi, sms, gps) works correctly during the emergency call. Make sure the device is using
+        a special white listed sim card. The wifi and GPS should be on and have internet connection.
+    </string>
+    <string name="emergency_call_confirm_info">This test will dial 911! Please make sure to use a
+        whitelisted sim card to run this test!
+    </string>
+    <string name="emergency_call_emergency_number_hint_text">
+        Emergency Number:
+    </string>
+    <string name="emergency_call_current_number_hint_text">
+        Current Number:
+    </string>
+    <string name="emergency_call_dial_text">
+        Dial 911
+    </string>
+    <string name="emergency_call_emergency_number_text">
+        911
+    </string>
     <string name="location_gnss_test_retry_info">If this test fails, please make sure the device
         has line of sight to GNSS satellites (for example, stationary on a windowsill. If needed,
         try again, outside, also with the device stationary, with as much view of the sky as
         possible.) </string>
+    <string name="location_emergency_call_gps_test">Emergency Call GPS Test</string>
+    <string name="location_emergency_call_message_test">Emergency Call Message Test</string>
+    <string name="location_emergency_call_wifi_test">Emergency Call Wifi Test</string>
 
     <!-- Strings for net.ConnectivityScreenOffTestActivity -->
     <string name="network_screen_off_test">Network Connectivity Screen Off Test</string>
@@ -1523,8 +1545,11 @@
     <string name="attention_phone_order">Check that ranker respects telephone URIs for contacts.</string>
     <string name="attention_interruption_order">Check that ranker temporarily boosts interruptions.
     This test takes 30 seconds to complete.</string>
-    <string name="attention_none_are_filtered">Check that \"All\" mode doesn\'t filter any notifications.</string>
-    <string name="attention_some_are_filtered">Check that \"Priority\" mode doesn\'t filter priority notifications.</string>
+    <string name="attention_none_are_filtered_messages">Check that \"All\" mode doesn\'t filter any notifications (messages).</string>
+    <string name="attention_none_are_filtered_diff_categories">Check that \"All\" mode doesn\'t filter any notifications (event, reminder, alarm).</string>
+    <string name="attention_some_are_filtered_messages">Check that \"Priority\" mode doesn\'t filter priority notifications (messages from starred contacts).</string>
+    <string name="attention_some_are_filtered_alarms">Check that \"Priority\" mode doesn\'t filter priority notifications (alarms).</string>
+    <string name="attention_some_are_filtered_media_system_other">Check that \"Priority\" mode doesn\'t filter priority notifications (media, system, other).</string>
     <string name="attention_all_are_filtered">Check that \"None\" mode filters all notifications.</string>
     <string name="nls_test">Notification Listener Test</string>
     <string name="nas_test">Notification Assistant Test</string>
@@ -1562,6 +1587,10 @@
         under Apps > Gear Icon > Default > Notification Assistant and return here.</string>
     <string name="nls_enable_service">Please enable \"Notification Listener for CTS Verifier\"
         under Apps > Gear Icon > Special Access > Notification Access and return here.</string>
+    <string name="nls_block_channel">Please block the linked notification channel and return here.</string>
+    <string name="nls_block_group">Please block the linked notification channel group and return here.</string>
+    <string name="nls_cannot_enable_service">Please make sure you cannot enable
+        \"Notification Listener for CTS Verifier\" and return here.</string>
     <string name="nls_disable_service">Please disable \"Notification Listener for CTS Verifier\"
         under Apps > Gear Icon > Special Access > Notification Access and return here.</string>
     <string name="nls_start_settings">Launch Settings</string>
@@ -1578,6 +1607,7 @@
     <string name="nas_snooze_context">Check that the Assistant can snooze a notification until a given context.</string>
     <string name="nls_clear_one">Check that service can clear a notification.</string>
     <string name="nls_clear_one_reason">Check that service can clear a notification and receive the correct reason for dismissal.</string>
+    <string name="nls_clear_one_stats">Check that service does not receive notification stats.</string>
     <string name="nls_clear_all">Check that service can clear all notifications.</string>
     <string name="nls_service_stopped">Service should stop once disabled.</string>
     <string name="nls_note_missed">Check that notification was not received.</string>
@@ -1591,10 +1621,11 @@
     <string name="nas_note_missed_enqueued">Check that notification was not enqueued.</string>
     <string name="cp_test">Condition Provider test</string>
     <string name="cp_service_name">Condition Provider for CTS Verifier</string>
-    <string name="cp_info">This test checks that a ConditionProviderService can be enabled
+    <string name="cp_info">This test checks that on non-low ram a ConditionProviderService can be enabled
         and disabled, and that once enabled the service is able to create, query, edit, and delete
-        automatic zen rules.
+        automatic zen rules. On low ram devices condition providers should not be bound.
     </string>
+    <string name="cp_cannot_enable_service">Please make sure you cannot enable \"CTS Verifier\" under Do Not Disturb access and return here.</string>
     <string name="cp_enable_service">Please enable \"CTS Verifier\" under Do Not Disturb access and return here.</string>
     <string name="cp_disable_service">Please disable \"CTS Verifier\" under Do Not Disturb access and return here.</string>
     <string name="cp_start_settings">Launch Settings</string>
@@ -1874,7 +1905,8 @@
         This test verifies that if a work profile is locked with a separate password, Recents views
         for applications in the work profile are redacted.\n
         Some devices may not lock as soon as the screen is turned off by default. On such devices,
-        use the button below when requested to lock the work profile.
+        use the button below when requested to lock the work profile. Please skip these tests if
+        "Recents" is absent.
     </string>
     <string name="provisioning_byod_recents_lock_now">Lock now</string>
 
@@ -1903,6 +1935,35 @@
         The work profile still has a separate password. Please remove this before continuing.
     </string>
 
+    <string name="provisioning_byod_keychain">KeyChain test</string>
+    <string name="provisioning_byod_keychain_info_start">
+        In this test, you\'ll verify that keys generated by KeyChain keys are as usable as keys
+        installed into KeyChain and that they can be hidden from users.\n
+        The test has two parts:\n
+        1) Testing that a generated key can be selectable by the user.\n
+        2) Testing that a generated key can be hidden from users.\n
+        \n
+        Tap \"Prepare Test\" button below to begin.\n
+        \n
+        NOTE: A screen lock must be configured for this test. Otherwise, test preparation
+        will fail to generate a key for use by the test.
+    </string>
+    <string name="provisioning_byod_keychain_info_first_test">
+        Once you press \'Go\', a prompt titled \"Choose certificate\" should appear.\n
+        Verify that the list in this dialog has one item, starting with \'cts-verifier-gen\'.
+        Press \'Select\' to select it.\n
+        If the test passes, you\'ll see the text \"Second test ready\" at the bottom.\n
+        \n
+        Press \'Go\'.\n
+    </string>
+    <string name="provisioning_byod_keychain_info_second_test">
+        Once you press \'Run 2nd test\', the same prompt should appear again.\n
+        This time, verify that the title is \"No certificates found\" and the list is empty,
+        then press \'Cancel\'.\n
+        \n
+        Mark the test as passed if the text at the bottom shows \"PASSED (2/2)\"\n
+    </string>
+
     <!-- Strings for DeskClock -->
     <string name="deskclock_tests">Alarms and Timers Tests</string>
     <string name="deskclock_tests_info">
@@ -2662,6 +2723,142 @@
     </string>
     <string name="device_owner_disable_keyguard_button">Disable keyguard</string>
     <string name="device_owner_reenable_keyguard_button">Reenable keyguard</string>
+    <string name="device_owner_lock_task_ui_test">LockTask UI</string>
+    <string name="device_owner_lock_task_ui_test_info">
+            The following tests verify the configurable UI during LockTask, a special mode that
+            prevents the user from leaving the current application.\n\n
+            Please make sure the lock screen is turned on before the test. Press the button below to
+            start LockTask mode. Then mark each item as \'pass\' or \'fail\' according to the
+            instructions.\n\n
+            Finally, execute the last test item to leave LockTask mode.
+    </string>
+    <string name="start_lock_task_button_label">Start LockTask mode</string>
+    <string name="device_owner_lock_task_ui_default_test">Default LockTask UI</string>
+    <string name="device_owner_lock_task_ui_default_test_info">
+            Observe the following UI restrictions. Mark the test as \'pass\' only if ALL of the
+            requirements below are met.\n\n
+            1) Nothing is shown in the status bar, including notification icons, connectivity icons,
+            battery status, clock, etc.\n
+            2) The status bar can\'t be expanded. That is, the \'swipe-down\' gesture doesn\'t work
+            for the status bar.\n
+            3) The Home button is hidden.\n
+            4) The Recents button is hidden.\n
+            5) Long-press the power button. The power button menu, which usually shows the power-off
+            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.
+    </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">
+            Press the button below to enable system info. Observe the system info area of the status
+            bar is now enabled. This includes the clock, connectivity info, battery info, etc.\n\n
+            The rest of the UI restrictions should still apply:\n
+            1) Notification icons are still hidden on the status bar.\n
+            2) The status bar can\'t be expanded. That is, the \'swipe-down\' gesture doesn\'t work
+            for the status bar.\n
+            3) The Home button is hidden.\n
+            4) The Recents button is hidden.\n
+            5) Long-press the power button. The power button menu, which usually shows the power-off
+            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
+            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>
+    <string name="device_owner_lock_task_ui_notifications_test_info">
+            Press the button below to enable notifications. Observe the notification icons on the
+            status bar are now enabled. The status bar can also be expanded to show the
+            notifications. However, all Settings UI should remain invisible, including Quick
+            Settings and any link to the Settings app.\n\n
+            The rest of the UI restrictions should still apply:\n
+            1) System info area is still hidden on the status bar.\n
+            2) The Home button is hidden.\n
+            3) The Recents button is hidden.\n
+            4) Long-press the power button. The power button menu, which usually shows the power-off
+            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
+            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>
+    <string name="device_owner_lock_task_ui_home_test_info">
+            Press the button below to enable the Home button. Observe the Home button is now
+            enabled.\n\n
+            The rest of the UI restrictions should still apply:\n
+            1) Nothing is shown in the status bar, including notification icons, connectivity icons,
+            battery status, clock, etc.\n
+            2) The status bar can\'t be expanded. That is, the \'swipe-down\' gesture doesn\'t work
+            for the status bar.\n
+            3) The Recents button is hidden.\n
+            4) Long-press the power button. The power button menu, which usually shows the power-off
+            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
+            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 Recents button</string>
+    <string name="device_owner_lock_task_ui_recents_test_info">
+            Press the button below to enable the Recents button. Observe the Recents button is now
+            enabled. Press the Recents button and verify the Recents view can be opened.\n\n
+            The rest of the UI restrictions should still apply:\n
+            1) Nothing is shown in the status bar, including notification icons, connectivity icons,
+            battery status, clock, etc.\n
+            2) The status bar can\'t be expanded. That is, the \'swipe-down\' gesture doesn\'t work
+            for the status bar.\n
+            3) The Home button is hidden.\n
+            4) Long-press the power button. The power button menu, which usually shows the power-off
+            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
+            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>
+    <string name="device_owner_lock_task_ui_global_actions_test_info">
+            Press the button below to enable global actions (a.k.a. power button menu). Long-press
+            the power button and verify a menu containing power-off and restart buttons is shown.
+            This menu can\'t contain any UI that allows the user to change system settings (such as
+            airplane mode switch) or access the Settings app.\n\n
+            The rest of the UI restrictions should still apply:\n
+            1) Nothing is shown in the status bar, including notification icons, connectivity icons,
+            battery status, clock, etc.\n
+            2) The status bar can\'t be expanded. That is, the \'swipe-down\' gesture doesn\'t work
+            for the status bar.\n
+            3) The Home button is hidden.\n
+            4) The Recents button is hidden.\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
+            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>
+    <string name="device_owner_lock_task_ui_keyguard_test_info">
+            Press the button below to enable keyguard. Press the power button to turn off the screen
+            and press it again to turn the screen back on. Verify that the lock screen is shown.\n\n
+            The rest of the UI restrictions should still apply, both on the lock screen and after
+            the lock screen is dismissed:\n
+            1) Nothing is shown in the status bar, including notification icons, connectivity icons,
+            battery status, clock, etc.\n
+            2) The status bar can\'t be expanded. That is, the \'swipe-down\' gesture doesn\'t work
+            for the status bar, even on the lock screen.\n
+            3) The Home button is hidden.\n
+            4) The Recents button is hidden.\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
+            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>
+    <string name="device_owner_lock_task_ui_stop_lock_task_test_info">
+            Press the button below to exit LockTask mode.\n\n
+            Observe that the UI has returned to the normal, unrestricted state, and is no longer
+            subject to any LockTask restriction.\n\n
+            Mark the test as \'pass\' or \'fail\' accordingly.
+    </string>
     <string name="device_owner_lockscreen_secure">Please remove lockscreen password</string>
     <string name="device_profile_owner_permission_lockdown_test">Permissions lockdown</string>
     <string name="device_profile_owner_permission_lockdown_test_instructions">
@@ -2968,7 +3165,9 @@
     <string name="disallow_share_location">Disallow share location</string>
     <string name="disallow_share_location_action">Turning on location sharing</string>
     <string name="disallow_uninstall_apps">Disallow uninstall apps</string>
-    <string name="disallow_uninstall_apps_action">Uninstalling applications other CtsVerifier</string>
+    <string name="disallow_uninstall_apps_action">Uninstalling applications from the work profile (badged applications) other than CtsVerifier</string>
+    <string name="disallow_unified_challenge">Disallow unified challenge</string>
+    <string name="disallow_unified_challenge_action">Setting one lock for both personal and work profiles. IMPORTANT: Separate work lock should be set prior to this test in Set work lock test</string>
     <string name="disallow_keyguard_unredacted_notifications">Disallow lockscreen unredacted notification</string>
     <string name="disallow_keyguard_unredacted_notifications_set_step">Disallow unredacted notifications when device is locked by turning on the switch below</string>
     <string name="disallow_keyguard_unredacted_notifications_action">Selecting show all notification content when device is locked</string>
@@ -3014,7 +3213,7 @@
         Check that \'Dummy Input method\' is not enabled in Settings and disallow \'Dummy Input method\' from permitted input methods by turning on the switch below.
     </string>
     <string name="set_permitted_input_methods_action">
-        Enabling \'Dummy Input method\' in the list of accessibility services
+        Enabling \'Dummy Input method\' in the list of input methods
     </string>
     <string name="set_permitted_input_methods_widget_label">
         Allow only system input methods:
@@ -3326,6 +3525,23 @@
     <string name="comp_provision_profile_dialog_title">Provision work profile</string>
     <string name="comp_provision_profile_dialog_text">Press the OK button to start the managed provisioning flow, and complete the flow to create a work profile</string>
 
+    <string name="managed_user_test">Managed User</string>
+    <string name="managed_user_positive_tests">Managed User positive tests</string>
+    <string name="managed_user_positive_tests_instructions">
+        The positive managed user tests verify policies on a managed user created by a device owner.
+        \n
+        Press Go button to create a managed user, and you will be switched to the managed user
+        automatically. Dismiss the keyguard and a \'Managed User Tests\' should launch.\n
+        Follow the test instructions and press \'pass\' or \'fail\' to return to this screen.\n
+    </string>
+    <string name="managed_user_positive_tests_info">
+        The positive managed user tests verify policies on a managed user created by a device owner.
+        Proceed to the test cases, then press \'pass\' or \'fail\' to finish this test.
+    </string>
+    <string name="managed_user_positive_category">Managed User Tests</string>
+    <string name="managed_user_check_managed_user_test">Check affiliated profile owner</string>
+    <string name="managed_user_incorrect_managed_user">Missing or incorrect affiliated profile owner: CTSVerifier is not affilaited PO!</string>
+
     <!-- Strings for JobScheduler Tests -->
     <string name="js_test_description">This test is mostly automated, but requires some user interaction. You can pass this test once the list items below are checked.</string>
 
diff --git a/apps/CtsVerifier/src/com/android/cts/verifier/TestResultsReport.java b/apps/CtsVerifier/src/com/android/cts/verifier/TestResultsReport.java
index 8c779c5..8893e0d 100644
--- a/apps/CtsVerifier/src/com/android/cts/verifier/TestResultsReport.java
+++ b/apps/CtsVerifier/src/com/android/cts/verifier/TestResultsReport.java
@@ -103,7 +103,7 @@
         DevicePropertyInfo devicePropertyInfo = new DevicePropertyInfo(Build.CPU_ABI,
                 Build.CPU_ABI2, abis, abis32, abis64, Build.BOARD, Build.BRAND, Build.DEVICE,
                 Build.FINGERPRINT, Build.ID, Build.MANUFACTURER, Build.MODEL, Build.PRODUCT,
-                referenceFingerprint, Build.SERIAL, Build.TAGS, Build.TYPE, versionBaseOs,
+                referenceFingerprint, Build.getSerial(), Build.TAGS, Build.TYPE, versionBaseOs,
                 Build.VERSION.RELEASE, Integer.toString(Build.VERSION.SDK_INT),
                 versionSecurityPatch, Build.VERSION.INCREMENTAL);
 
diff --git a/apps/CtsVerifier/src/com/android/cts/verifier/camera/its/ItsService.java b/apps/CtsVerifier/src/com/android/cts/verifier/camera/its/ItsService.java
index 697ad93..febaf08 100644
--- a/apps/CtsVerifier/src/com/android/cts/verifier/camera/its/ItsService.java
+++ b/apps/CtsVerifier/src/com/android/cts/verifier/camera/its/ItsService.java
@@ -16,6 +16,9 @@
 
 package com.android.cts.verifier.camera.its;
 
+import android.app.Notification;
+import android.app.NotificationChannel;
+import android.app.NotificationManager;
 import android.app.Service;
 import android.content.Context;
 import android.content.Intent;
@@ -59,6 +62,7 @@
 import com.android.ex.camera2.blocking.BlockingSessionCallback;
 
 import com.android.cts.verifier.camera.its.StatsImage;
+import com.android.cts.verifier.R;
 
 import org.json.JSONArray;
 import org.json.JSONObject;
@@ -93,6 +97,9 @@
 public class ItsService extends Service implements SensorEventListener {
     public static final String TAG = ItsService.class.getSimpleName();
 
+    private final int SERVICE_NOTIFICATION_ID = 37; // random int that is unique within app
+    private NotificationChannel mChannel;
+
     // Timeouts, in seconds.
     private static final int TIMEOUT_CALLBACK = 20;
     private static final int TIMEOUT_3A = 10;
@@ -270,6 +277,15 @@
         } catch (ItsException e) {
             Logt.e(TAG, "Service failed to start: ", e);
         }
+
+        NotificationManager notificationManager =
+                (NotificationManager) getSystemService(Context.NOTIFICATION_SERVICE);
+        mChannel = new NotificationChannel(
+                "ItsServiceChannel", "ItsService", NotificationManager.IMPORTANCE_LOW);
+        // Configure the notification channel.
+        mChannel.setDescription("ItsServiceChannel");
+        mChannel.enableVibration(false);
+        notificationManager.createNotificationChannel(mChannel);
     }
 
     @Override
@@ -285,6 +301,13 @@
             } else {
                 Logt.e(TAG, "Starting ItsService in bad state");
             }
+
+            Notification notification = new Notification.Builder(this, mChannel.getId())
+                    .setContentTitle("CameraITS Service")
+                    .setContentText("CameraITS Service is running")
+                    .setSmallIcon(R.drawable.icon)
+                    .setOngoing(true).build();
+            startForeground(SERVICE_NOTIFICATION_ID, notification);
         } catch (java.lang.InterruptedException e) {
             Logt.e(TAG, "Error starting ItsService (interrupted)", e);
         }
diff --git a/apps/CtsVerifier/src/com/android/cts/verifier/location/EmergencyCallGNSSTestsActivity.java b/apps/CtsVerifier/src/com/android/cts/verifier/location/EmergencyCallGNSSTestsActivity.java
new file mode 100644
index 0000000..1e24e6a
--- /dev/null
+++ b/apps/CtsVerifier/src/com/android/cts/verifier/location/EmergencyCallGNSSTestsActivity.java
@@ -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
+ */
+
+package com.android.cts.verifier.location;
+
+import android.location.cts.GnssMeasurementValuesTest;
+import com.android.cts.verifier.location.base.EmergencyCallBaseTestActivity;
+import java.util.concurrent.TimeUnit;
+
+/**
+ * Activity to execute CTS GnssMeasurementValuesTest while dialing emergency number.
+ * It is a wrapper for {@link GnssMeasurementValuesTest} running with AndroidJUnitRunner.
+ */
+public class EmergencyCallGNSSTestsActivity extends EmergencyCallBaseTestActivity {
+  // GNSS test has a longer timeout
+  private static final long PHONE_CALL_DURATION_MS = TimeUnit.MINUTES.toMillis(2);
+
+  public EmergencyCallGNSSTestsActivity() {
+    super(GnssMeasurementValuesTest.class);
+  }
+
+  @Override
+  protected long getPhoneCallDurationMs() {
+    return PHONE_CALL_DURATION_MS;
+  }
+
+  @Override
+  protected boolean showLocalNumberInputbox() {
+    return false;
+  }
+}
diff --git a/apps/CtsVerifier/src/com/android/cts/verifier/location/EmergencyCallMessageTestsActivity.java b/apps/CtsVerifier/src/com/android/cts/verifier/location/EmergencyCallMessageTestsActivity.java
new file mode 100644
index 0000000..7377065
--- /dev/null
+++ b/apps/CtsVerifier/src/com/android/cts/verifier/location/EmergencyCallMessageTestsActivity.java
@@ -0,0 +1,42 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License
+ */
+
+package com.android.cts.verifier.location;
+
+import android.location.cts.EmergencyCallMessageTest;
+import com.android.cts.verifier.location.base.EmergencyCallBaseTestActivity;
+import java.util.concurrent.TimeUnit;
+
+/**
+ * Activity to execute CTS EmergencyCallMessageTest while dialing emergency number.
+ * It is a wrapper for {@link EmergencyCallMessageTest} running with AndroidJUnitRunner.
+ */
+public class EmergencyCallMessageTestsActivity extends EmergencyCallBaseTestActivity {
+  private static final long PHONE_CALL_DURATION_MS = TimeUnit.SECONDS.toMillis(35);
+  public EmergencyCallMessageTestsActivity() {
+    super(EmergencyCallMessageTest.class);
+  }
+
+  @Override
+  protected long getPhoneCallDurationMs() {
+    return PHONE_CALL_DURATION_MS;
+  }
+
+  @Override
+  protected boolean showLocalNumberInputbox() {
+    return true;
+  }
+}
diff --git a/apps/CtsVerifier/src/com/android/cts/verifier/location/EmergencyCallWifiTestsActivity.java b/apps/CtsVerifier/src/com/android/cts/verifier/location/EmergencyCallWifiTestsActivity.java
new file mode 100644
index 0000000..634863d
--- /dev/null
+++ b/apps/CtsVerifier/src/com/android/cts/verifier/location/EmergencyCallWifiTestsActivity.java
@@ -0,0 +1,42 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License
+ */
+
+package com.android.cts.verifier.location;
+
+import android.location.cts.EmergencyCallWifiTest;
+import com.android.cts.verifier.location.base.EmergencyCallBaseTestActivity;
+import java.util.concurrent.TimeUnit;
+
+/**
+ * Activity to execute CTS EmergencyCallWifiTest while dialing emergency number.
+ * It is a wrapper for {@link EmergencyCallWifiTest} running with AndroidJUnitRunner.
+ */
+public class EmergencyCallWifiTestsActivity extends EmergencyCallBaseTestActivity {
+    private static final long PHONE_CALL_DURATION_MS = TimeUnit.SECONDS.toMillis(35);
+    public EmergencyCallWifiTestsActivity() {
+        super(EmergencyCallWifiTest.class);
+    }
+
+    @Override
+    protected long getPhoneCallDurationMs() {
+      return PHONE_CALL_DURATION_MS;
+    }
+
+    @Override
+    protected boolean showLocalNumberInputbox() {
+      return false;
+    }
+}
diff --git a/apps/CtsVerifier/src/com/android/cts/verifier/location/base/EmergencyCallBaseTestActivity.java b/apps/CtsVerifier/src/com/android/cts/verifier/location/base/EmergencyCallBaseTestActivity.java
new file mode 100644
index 0000000..ef88e9b
--- /dev/null
+++ b/apps/CtsVerifier/src/com/android/cts/verifier/location/base/EmergencyCallBaseTestActivity.java
@@ -0,0 +1,113 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License
+ */
+
+package com.android.cts.verifier.location.base;
+
+import android.app.AlertDialog;
+import android.content.DialogInterface;
+import android.location.cts.GnssTestCase;
+import android.os.Bundle;
+import android.util.Log;
+import android.view.View;
+import android.view.LayoutInflater;
+import android.widget.Button;
+import android.widget.EditText;
+import android.widget.FrameLayout;
+import com.android.cts.verifier.R;
+import java.util.concurrent.TimeUnit;
+
+/**
+ * An Activity that allows Gnss CTS tests to be executed inside CtsVerifier.
+ *
+ * Sub-classes pass the test class as part of construction.
+ * One JUnit test class is executed per Activity, the test class can still be executed outside
+ * CtsVerifier.
+ */
+public abstract class EmergencyCallBaseTestActivity extends GnssCtsTestActivity {
+    private static final String PHONE_NUMBER_KEY = "android.cts.emergencycall.phonenumber";
+    private static final String defaultPhonePackageName = "com.google.android.dialer";
+
+    /**
+     * Constructor for a CTS test executor. It will execute a standalone CTS test class.
+     *
+     * @param testClass The test class to execute, it must be a subclass of {@link AndroidTestCase}.
+     */
+    protected EmergencyCallBaseTestActivity(Class<? extends GnssTestCase> testClass) {
+        super(testClass);
+    }
+
+    protected abstract long getPhoneCallDurationMs();
+
+    @Override
+    protected void onCreate(Bundle savedInstanceState) {
+        super.onCreate(savedInstanceState);
+        // override the test info
+        mTextView.setText(R.string.location_emergency_call_test_info);
+        EmergencyCallUtil.setDefaultDialer(this, this.getPackageName());
+    }
+
+    @Override
+    protected void onDestroy() {
+        super.onDestroy();
+        EmergencyCallUtil.setDefaultDialer(this, defaultPhonePackageName);
+    }
+
+    protected abstract boolean showLocalNumberInputbox();
+
+    @Override
+    public void onClick(View target) {
+        AlertDialog.Builder builder = new AlertDialog.Builder(this);
+        final FrameLayout frameView = new FrameLayout(this);
+        builder.setView(frameView);
+
+        final boolean enableLocalNumberInputBox = showLocalNumberInputbox();
+        final AlertDialog alertDialog = builder.create();
+        LayoutInflater inflater = alertDialog.getLayoutInflater();
+
+        View dialogView;
+        if (enableLocalNumberInputBox) {
+            dialogView =
+                inflater.inflate(R.layout.emergency_call_msg_test_confirm_dialog, frameView);
+        } else {
+            dialogView = inflater.inflate(R.layout.emergency_call_confirm_dialog, frameView);
+        }
+        final EditText targetNumberEditText =
+            (EditText) dialogView.findViewById(R.id.emergency_number);
+        final Button dialButton = (Button) dialogView.findViewById(R.id.dial_button);
+        dialButton.setOnClickListener(new Button.OnClickListener() {
+            @Override
+            public void onClick(View v) {
+                if (enableLocalNumberInputBox) {
+                    final EditText currentNumberEditText =
+                        (EditText) dialogView.findViewById(R.id.local_phone_number);
+                    String currentNumber = currentNumberEditText.getText().toString();
+                    // pass the number to cts tests for cts verifier UI, through System property.
+                    System.setProperty(PHONE_NUMBER_KEY, currentNumber);
+                }
+                int targetPhoneNumber =
+                    Integer.parseInt(targetNumberEditText.getText().toString());
+                long callDurationMs = EmergencyCallBaseTestActivity.this.getPhoneCallDurationMs();
+                EmergencyCallUtil.makePhoneCall(
+                    EmergencyCallBaseTestActivity.this, targetPhoneNumber);
+                EmergencyCallBaseTestActivity.super.onClick(target);
+                EmergencyCallUtil.endCallWithDelay(
+                    EmergencyCallBaseTestActivity.this.getApplicationContext(), callDurationMs);
+                alertDialog.dismiss();
+            }
+        });
+        alertDialog.show();
+    }
+}
diff --git a/apps/CtsVerifier/src/com/android/cts/verifier/location/base/EmergencyCallUtil.java b/apps/CtsVerifier/src/com/android/cts/verifier/location/base/EmergencyCallUtil.java
new file mode 100644
index 0000000..12b7ac2
--- /dev/null
+++ b/apps/CtsVerifier/src/com/android/cts/verifier/location/base/EmergencyCallUtil.java
@@ -0,0 +1,120 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License
+ */
+
+package com.android.cts.verifier.location.base;
+
+import android.app.Activity;
+import android.app.AlertDialog;
+import android.content.Context;
+import android.content.Intent;
+import android.net.Uri;
+import android.telecom.TelecomManager;
+import android.telephony.TelephonyManager;
+import android.text.InputType;
+import android.util.Log;
+import android.widget.EditText;
+import android.widget.LinearLayout;
+import com.android.cts.verifier.R;
+import java.lang.reflect.Method;
+import java.util.concurrent.TimeUnit;
+
+/**
+ * The EmergencyCallUtil class provides util functions related to the emergency call.
+ */
+public class EmergencyCallUtil {
+    private static final int REQUEST_CODE_SET_DEFAULT_DIALER = 1;
+    private static final String TAG = "EmergencyCallUtil";
+    private static final long WAIT_FOR_CONNECTION_MS = TimeUnit.SECONDS.toMillis(3);
+
+    /*
+     * This method is used to set default dialer app.
+     * To dial 911, it requires to set the dialer to be the system default dial app.
+     *
+     * @param activity current Activity.
+     * @param packageName dialer package name.
+     */
+    public static void setDefaultDialer(Activity activity, String packageName) {
+        final Intent intent = new Intent(TelecomManager.ACTION_CHANGE_DEFAULT_DIALER);
+        intent.putExtra(TelecomManager.EXTRA_CHANGE_DEFAULT_DIALER_PACKAGE_NAME, packageName);
+        activity.startActivityForResult(intent, REQUEST_CODE_SET_DEFAULT_DIALER);
+    }
+
+    public static void makePhoneCall(Activity activity, int phoneNumber) {
+        Intent callIntent = new Intent(Intent.ACTION_CALL);
+        callIntent.setData(Uri.parse("tel:" + phoneNumber));
+        try {
+            callIntent.setFlags(Intent.FLAG_ACTIVITY_NO_USER_ACTION);
+            activity.startActivityForResult(callIntent, REQUEST_CODE_SET_DEFAULT_DIALER);
+        } catch (SecurityException ex) {
+            Log.d(TAG, "Failed to make the phone call: " + ex.toString());
+        }
+        // sleep 3sec to make sure call is connected
+        activity.runOnUiThread(new Runnable() {
+            @Override
+            public void run() {
+                try {
+                    Thread.sleep(WAIT_FOR_CONNECTION_MS);
+                } catch (InterruptedException ex) {
+                    Log.d(TAG, "Failed to make the phone call: " + ex.toString());
+                }
+            }
+        });
+    }
+
+    public static void endCallWithDelay(Context context, long delayMs) {
+        Runnable runnable = new Runnable() {
+            @Override
+            public void run() {
+                try {
+                    Thread.sleep(delayMs);
+                    endCall(context);
+                } catch (InterruptedException ex) {
+                    Log.d(TAG, "Failed to make the phone call: " + ex.toString());
+                }
+            }
+        };
+        new Thread(runnable).start();
+    }
+
+    private static void endCall(Context context) {
+        try {
+            TelephonyManager telephonyManager =
+                (TelephonyManager) context.getSystemService(Context.TELEPHONY_SERVICE);
+
+            Class<?> classTelephony = Class.forName(telephonyManager.getClass().getName());
+            Method methodGetITelephony = classTelephony.getDeclaredMethod("getITelephony");
+            methodGetITelephony.setAccessible(true);
+
+            Object telephonyInterface = methodGetITelephony.invoke(telephonyManager);
+
+            Class<?> telephonyInterfaceClass =
+                Class.forName(telephonyInterface.getClass().getName());
+            Method methodEndCall = telephonyInterfaceClass.getDeclaredMethod("endCall");
+
+            methodEndCall.invoke(telephonyInterface);
+
+        } catch (Exception e) {
+            Log.d(TAG, "Failed to cancel the call: " + e.toString());
+        }
+    }
+
+    private static String getCurrentPhoneNumber(Activity activity) {
+        TelephonyManager tMgr =
+            (TelephonyManager)activity.getSystemService(Context.TELEPHONY_SERVICE);
+        return tMgr.getLine1Number();
+    }
+
+}
\ No newline at end of file
diff --git a/apps/CtsVerifier/src/com/android/cts/verifier/managedprovisioning/ByodFlowTestActivity.java b/apps/CtsVerifier/src/com/android/cts/verifier/managedprovisioning/ByodFlowTestActivity.java
index 15808a7..d7a5033 100644
--- a/apps/CtsVerifier/src/com/android/cts/verifier/managedprovisioning/ByodFlowTestActivity.java
+++ b/apps/CtsVerifier/src/com/android/cts/verifier/managedprovisioning/ByodFlowTestActivity.java
@@ -101,6 +101,7 @@
     private DialogTestListItem mConfirmWorkCredentials;
     private DialogTestListItem mParentProfilePassword;
     private TestListItem mVpnTest;
+    private TestListItem mKeyChainTest;
     private TestListItem mAlwaysOnVpnSettingsTest;
     private TestListItem mRecentsTest;
     private TestListItem mDisallowAppsControlTest;
@@ -415,6 +416,12 @@
                 new Intent(this, OrganizationInfoTestActivity.class),
                 null);
 
+        mKeyChainTest = TestListItem.newTest(this,
+                R.string.provisioning_byod_keychain,
+                KeyChainTestActivity.class.getName(),
+                new Intent(KeyChainTestActivity.ACTION_KEYCHAIN),
+                null);
+
         mParentProfilePassword = new DialogTestListItem(this,
                 R.string.provisioning_byod_parent_profile_password,
                 "BYOD_ParentProfilePasswordTest",
@@ -563,6 +570,7 @@
                 }
             };
             adapter.add(mDisableNfcBeamTest);
+            adapter.add(mKeyChainTest);
         }
 
         /* If there is an application that handles RECORD_SOUND_ACTION, test that it handles it
diff --git a/apps/CtsVerifier/src/com/android/cts/verifier/managedprovisioning/ByodFlowTestHelper.java b/apps/CtsVerifier/src/com/android/cts/verifier/managedprovisioning/ByodFlowTestHelper.java
index 5053a90..bfa65b7 100644
--- a/apps/CtsVerifier/src/com/android/cts/verifier/managedprovisioning/ByodFlowTestHelper.java
+++ b/apps/CtsVerifier/src/com/android/cts/verifier/managedprovisioning/ByodFlowTestHelper.java
@@ -42,7 +42,8 @@
                 AlwaysOnVpnSettingsTestActivity.class.getName(),
                 RecentsRedactionActivity.class.getName(),
                 CommandReceiverActivity.class.getName(),
-                SetSupportMessageActivity.class.getName()
+                SetSupportMessageActivity.class.getName(),
+                KeyChainTestActivity.class.getName()
         };
         for (String component : components) {
             mPackageManager.setComponentEnabledSetting(new ComponentName(mContext, component),
diff --git a/apps/CtsVerifier/src/com/android/cts/verifier/managedprovisioning/CommandReceiverActivity.java b/apps/CtsVerifier/src/com/android/cts/verifier/managedprovisioning/CommandReceiverActivity.java
index 1d4d13a..5abc2ea 100644
--- a/apps/CtsVerifier/src/com/android/cts/verifier/managedprovisioning/CommandReceiverActivity.java
+++ b/apps/CtsVerifier/src/com/android/cts/verifier/managedprovisioning/CommandReceiverActivity.java
@@ -16,6 +16,10 @@
 
 package com.android.cts.verifier.managedprovisioning;
 
+import static android.app.admin.DevicePolicyManager.MAKE_USER_EPHEMERAL;
+import static android.app.admin.DevicePolicyManager.SKIP_SETUP_WIZARD;
+import static android.app.admin.DevicePolicyManager.START_USER_IN_BACKGROUND;
+
 import android.Manifest;
 import android.app.Activity;
 import android.app.KeyguardManager;
@@ -30,24 +34,28 @@
 import android.graphics.BitmapFactory;
 import android.net.ProxyInfo;
 import android.os.Bundle;
+import android.os.PersistableBundle;
 import android.os.UserHandle;
 import android.os.UserManager;
 import android.provider.ContactsContract;
 import android.provider.MediaStore;
 import android.provider.Settings;
 import android.util.Log;
+import android.view.inputmethod.InputMethodInfo;
+import android.view.inputmethod.InputMethodManager;
 import android.widget.Toast;
 
 import com.android.cts.verifier.R;
-import com.android.cts.verifier.managedprovisioning.Utils;
 
 import java.io.File;
 import java.io.FileInputStream;
 import java.io.InputStream;
 import java.io.OutputStream;
 import java.util.ArrayList;
+import java.util.Collections;
 import java.util.List;
 import java.util.concurrent.TimeUnit;
+import java.util.stream.Collectors;
 
 public class CommandReceiverActivity extends Activity {
     private static final String TAG = "CommandReceiverActivity";
@@ -68,6 +76,7 @@
     public static final String COMMAND_SET_KEYGUARD_DISABLED = "set-keyguard-disabled";
     public static final String COMMAND_SET_LOCK_SCREEN_INFO = "set-lock-screen-info";
     public static final String COMMAND_SET_STATUSBAR_DISABLED = "set-statusbar-disabled";
+    public static final String COMMAND_SET_LOCK_TASK_FEATURES = "set-lock-task-features";
     public static final String COMMAND_ALLOW_ONLY_SYSTEM_INPUT_METHODS =
             "allow-only-system-input-methods";
     public static final String COMMAND_ALLOW_ONLY_SYSTEM_ACCESSIBILITY_SERVICES =
@@ -102,6 +111,7 @@
             "clear-maximum-password-attempts";
     public static final String COMMAND_SET_DEFAULT_IME = "set-default-ime";
     public static final String COMMAND_CLEAR_DEFAULT_IME = "clear-default-ime";
+    public static final String COMMAND_CREATE_MANAGED_USER = "create-managed-user";
 
     public static final String EXTRA_USER_RESTRICTION =
             "com.android.cts.verifier.managedprovisioning.extra.USER_RESTRICTION";
@@ -171,9 +181,8 @@
                     Context.DEVICE_POLICY_SERVICE);
             mUm = (UserManager) getSystemService(Context.USER_SERVICE);
             mAdmin = DeviceAdminTestReceiver.getReceiverComponentName();
-            Log.i(TAG, "Command: " + intent);
-
             final String command = getIntent().getStringExtra(EXTRA_COMMAND);
+            Log.i(TAG, "Command: " + command);
             switch (command) {
                 case COMMAND_SET_USER_RESTRICTION: {
                     String restrictionKey = intent.getStringExtra(EXTRA_USER_RESTRICTION);
@@ -220,9 +229,15 @@
                     boolean enforced = intent.getBooleanExtra(EXTRA_ENFORCED, false);
                     mDpm.setStatusBarDisabled(mAdmin, enforced);
                 } break;
+                case COMMAND_SET_LOCK_TASK_FEATURES: {
+                    int flags = intent.getIntExtra(EXTRA_VALUE,
+                            DevicePolicyManager.LOCK_TASK_FEATURE_NONE);
+                    mDpm.setLockTaskFeatures(mAdmin, flags);
+                } break;
                 case COMMAND_ALLOW_ONLY_SYSTEM_INPUT_METHODS: {
                     boolean enforced = intent.getBooleanExtra(EXTRA_ENFORCED, false);
-                    mDpm.setPermittedInputMethods(mAdmin, enforced ? new ArrayList() : null);
+                    mDpm.setPermittedInputMethods(mAdmin,
+                            enforced ? getEnabledNonSystemImes() : null);
                 } break;
                 case COMMAND_ALLOW_ONLY_SYSTEM_ACCESSIBILITY_SERVICES: {
                     boolean enforced = intent.getBooleanExtra(EXTRA_ENFORCED, false);
@@ -458,7 +473,20 @@
                         return;
                     }
                     mDpm.setSecureSetting(mAdmin, Settings.Secure.DEFAULT_INPUT_METHOD, null);
-                }
+                } break;
+                case COMMAND_CREATE_MANAGED_USER:{
+                    if (!mDpm.isDeviceOwnerApp(getPackageName())) {
+                        return;
+                    }
+                    PersistableBundle extras = new PersistableBundle();
+                    extras.putBoolean(DeviceAdminTestReceiver.EXTRA_MANAGED_USER_TEST, true);
+                    UserHandle userHandle = mDpm.createAndManageUser(mAdmin, "managed user", mAdmin,
+                            extras,
+                            SKIP_SETUP_WIZARD | MAKE_USER_EPHEMERAL | START_USER_IN_BACKGROUND);
+                    mDpm.setAffiliationIds(mAdmin,
+                            Collections.singleton(DeviceAdminTestReceiver.AFFILIATION_ID));
+                    mDpm.switchUser(mAdmin, userHandle);
+                } break;
             }
         } catch (Exception e) {
             Log.e(TAG, "Failed to execute command: " + intent, e);
@@ -562,4 +590,26 @@
             mDpm.removeUser(mAdmin, userHandle);
         }
     }
+
+    public static Intent createSetUserRestrictionIntent(String restriction, boolean enforced) {
+        return new Intent(ACTION_EXECUTE_COMMAND)
+                .putExtra(EXTRA_COMMAND,COMMAND_SET_USER_RESTRICTION)
+                .putExtra(EXTRA_USER_RESTRICTION, restriction)
+                .putExtra(EXTRA_ENFORCED, enforced);
+    }
+
+    private List<String> getEnabledNonSystemImes() {
+        InputMethodManager inputMethodManager = getSystemService(InputMethodManager.class);
+        final List<InputMethodInfo> inputMethods = inputMethodManager.getEnabledInputMethodList();
+        return inputMethods.stream()
+                .filter(inputMethodInfo -> !isSystemInputMethodInfo(inputMethodInfo))
+                .map(inputMethodInfo -> inputMethodInfo.getPackageName())
+                .filter(packageName -> !packageName.equals(getPackageName()))
+                .distinct()
+                .collect(Collectors.toList());
+    }
+
+    private boolean isSystemInputMethodInfo(InputMethodInfo inputMethodInfo) {
+        return inputMethodInfo.getServiceInfo().applicationInfo.isSystemApp();
+    }
 }
diff --git a/apps/CtsVerifier/src/com/android/cts/verifier/managedprovisioning/DeviceAdminTestReceiver.java b/apps/CtsVerifier/src/com/android/cts/verifier/managedprovisioning/DeviceAdminTestReceiver.java
index f54e567..8486765 100644
--- a/apps/CtsVerifier/src/com/android/cts/verifier/managedprovisioning/DeviceAdminTestReceiver.java
+++ b/apps/CtsVerifier/src/com/android/cts/verifier/managedprovisioning/DeviceAdminTestReceiver.java
@@ -25,11 +25,14 @@
 import android.content.Intent;
 import android.content.IntentFilter;
 import android.os.PersistableBundle;
+import android.support.v4.content.LocalBroadcastManager;
 import android.util.Log;
 
 import com.android.cts.verifier.R;
 import com.android.cts.verifier.location.LocationListenerActivity;
 
+import java.util.Collections;
+
 /**
  * Profile owner receiver for BYOD flow test.
  * Setup cross-profile intent filter after successful provisioning.
@@ -43,6 +46,9 @@
             DEVICE_OWNER_PKG + ".managedprovisioning.DeviceAdminTestReceiver";
     private static final ComponentName RECEIVER_COMPONENT_NAME = new ComponentName(
             DEVICE_OWNER_PKG, ADMIN_RECEIVER_TEST_CLASS);
+    public static final String EXTRA_MANAGED_USER_TEST =
+            "com.android.cts.verifier.managedprovisioning.extra.MANAGED_USER_TEST";
+    public static final String AFFILIATION_ID = "affiliationId";
 
     public static ComponentName getReceiverComponentName() {
         return RECEIVER_COMPONENT_NAME;
@@ -76,6 +82,32 @@
                 R.string.bugreport_failed_completing), Utils.BUGREPORT_NOTIFICATION_ID);
     }
 
+    @Override
+    public void onLockTaskModeEntering(Context context, Intent intent, String pkg) {
+        Log.i(TAG, "Entering LockTask mode: " + pkg);
+        LocalBroadcastManager.getInstance(context)
+                .sendBroadcast(new Intent(LockTaskUiTestActivity.ACTION_LOCK_TASK_STARTED));
+    }
+
+    @Override
+    public void onLockTaskModeExiting(Context context, Intent intent) {
+        Log.i(TAG, "Exiting LockTask mode");
+        LocalBroadcastManager.getInstance(context)
+                .sendBroadcast(new Intent(LockTaskUiTestActivity.ACTION_LOCK_TASK_STOPPED));
+    }
+
+    @Override
+    public void onEnabled(Context context, Intent intent) {
+        Log.i(TAG, "Device admin enabled");
+        if (intent.getBooleanExtra(EXTRA_MANAGED_USER_TEST, false)) {
+            DevicePolicyManager dpm = context.getSystemService(DevicePolicyManager.class);
+            ComponentName admin = DeviceAdminTestReceiver.getReceiverComponentName();
+            dpm.setAffiliationIds(admin,
+                    Collections.singleton(DeviceAdminTestReceiver.AFFILIATION_ID));
+            context.startActivity(new Intent(context, ManagedUserPositiveTestActivity.class));
+        }
+    }
+
     private void setupProfile(Context context) {
         DevicePolicyManager dpm = (DevicePolicyManager) context.getSystemService(Context.DEVICE_POLICY_SERVICE);
         dpm.setProfileEnabled(new ComponentName(context.getApplicationContext(), getClass()));
@@ -117,6 +149,7 @@
         filter.addAction(ByodHelperActivity.ACTION_SET_ORGANIZATION_INFO);
         filter.addAction(ByodHelperActivity.ACTION_TEST_PARENT_PROFILE_PASSWORD);
         filter.addAction(SetSupportMessageActivity.ACTION_SET_SUPPORT_MSG);
+        filter.addAction(KeyChainTestActivity.ACTION_KEYCHAIN);
         filter.addAction(CommandReceiverActivity.ACTION_EXECUTE_COMMAND);
         dpm.addCrossProfileIntentFilter(getWho(context), filter,
                 DevicePolicyManager.FLAG_MANAGED_CAN_ACCESS_PARENT);
diff --git a/apps/CtsVerifier/src/com/android/cts/verifier/managedprovisioning/DeviceOwnerPositiveTestActivity.java b/apps/CtsVerifier/src/com/android/cts/verifier/managedprovisioning/DeviceOwnerPositiveTestActivity.java
index 0cae6be..6c87b84 100644
--- a/apps/CtsVerifier/src/com/android/cts/verifier/managedprovisioning/DeviceOwnerPositiveTestActivity.java
+++ b/apps/CtsVerifier/src/com/android/cts/verifier/managedprovisioning/DeviceOwnerPositiveTestActivity.java
@@ -21,7 +21,6 @@
 import android.app.Activity;
 import android.app.AlertDialog;
 import android.app.admin.DevicePolicyManager;
-import android.content.ComponentName;
 import android.content.Context;
 import android.content.Intent;
 import android.content.pm.PackageManager;
@@ -29,12 +28,10 @@
 import android.os.Bundle;
 import android.os.UserManager;
 import android.provider.Settings;
-import android.util.Log;
 import android.view.View;
 import android.view.View.OnClickListener;
 
 import com.android.cts.verifier.ArrayTestListAdapter;
-import com.android.cts.verifier.IntentDrivenTestActivity;
 import com.android.cts.verifier.IntentDrivenTestActivity.ButtonInfo;
 import com.android.cts.verifier.PassFailButtons;
 import com.android.cts.verifier.R;
@@ -60,6 +57,7 @@
     private static final String WIFI_LOCKDOWN_TEST_ID = WifiLockdownTestActivity.class.getName();
     private static final String DISABLE_STATUS_BAR_TEST_ID = "DISABLE_STATUS_BAR";
     private static final String DISABLE_KEYGUARD_TEST_ID = "DISABLE_KEYGUARD";
+    private static final String LOCK_TASK_UI_TEST_ID = "LOCK_TASK_UI";
     private static final String CHECK_PERMISSION_LOCKDOWN_TEST_ID =
             PermissionLockdownTestActivity.class.getName();
     private static final String DISALLOW_CONFIG_BT_ID = "DISALLOW_CONFIG_BT";
@@ -72,7 +70,8 @@
     private static final String POLICY_TRANSPARENCY_TEST_ID = "POLICY_TRANSPARENCY";
     private static final String ENTERPRISE_PRIVACY_TEST_ID = "ENTERPRISE_PRIVACY";
     private static final String NETWORK_LOGGING_UI_TEST_ID = "NETWORK_LOGGING_UI";
-    public static final String COMP_TEST_ID = "COMP_UI";
+    private static final String COMP_TEST_ID = "COMP_UI";
+    private static final String MANAGED_USER_TEST_ID = "MANAGED_USER_UI";
     private static final String REMOVE_DEVICE_OWNER_TEST_ID = "REMOVE_DEVICE_OWNER";
 
     @Override
@@ -171,8 +170,8 @@
                     new ButtonInfo[] {
                             new ButtonInfo(
                                     R.string.device_owner_user_restriction_set,
-                                    createSetUserRestrictionIntent(
-                                            UserManager.DISALLOW_CONFIG_WIFI)),
+                                    CommandReceiverActivity.createSetUserRestrictionIntent(
+                                            UserManager.DISALLOW_CONFIG_WIFI, true)),
                             new ButtonInfo(
                                     R.string.device_owner_settings_go,
                                     new Intent(Settings.ACTION_WIFI_SETTINGS))}));
@@ -185,8 +184,8 @@
                 new ButtonInfo[] {
                         new ButtonInfo(
                                 R.string.device_owner_user_vpn_restriction_set,
-                                createSetUserRestrictionIntent(
-                                        UserManager.DISALLOW_CONFIG_VPN)),
+                                CommandReceiverActivity.createSetUserRestrictionIntent(
+                                        UserManager.DISALLOW_CONFIG_VPN, true)),
                         new ButtonInfo(
                                 R.string.device_owner_settings_go,
                                 new Intent(Settings.ACTION_VPN_SETTINGS)),
@@ -202,8 +201,8 @@
                     new ButtonInfo[] {
                             new ButtonInfo(
                                     R.string.device_owner_user_restriction_set,
-                                    createSetUserRestrictionIntent(
-                                            UserManager.DISALLOW_DATA_ROAMING)),
+                                    CommandReceiverActivity.createSetUserRestrictionIntent(
+                                            UserManager.DISALLOW_DATA_ROAMING, true)),
                             new ButtonInfo(
                                     R.string.device_owner_settings_go,
                                     new Intent(Settings.ACTION_DATA_ROAMING_SETTINGS))}));
@@ -216,8 +215,8 @@
                 new ButtonInfo[] {
                         new ButtonInfo(
                                 R.string.device_owner_user_restriction_set,
-                                createSetUserRestrictionIntent(
-                                        UserManager.DISALLOW_FACTORY_RESET))}));
+                                CommandReceiverActivity.createSetUserRestrictionIntent(
+                                        UserManager.DISALLOW_FACTORY_RESET, true))}));
 
         // DISALLOW_CONFIG_BLUETOOTH
         if (packageManager.hasSystemFeature(PackageManager.FEATURE_BLUETOOTH)) {
@@ -227,8 +226,8 @@
                     new ButtonInfo[] {
                             new ButtonInfo(
                                     R.string.device_owner_user_restriction_set,
-                                    createSetUserRestrictionIntent(
-                                            UserManager.DISALLOW_CONFIG_BLUETOOTH)),
+                                    CommandReceiverActivity.createSetUserRestrictionIntent(
+                                            UserManager.DISALLOW_CONFIG_BLUETOOTH, true)),
                             new ButtonInfo(
                                     R.string.device_owner_settings_go,
                                     new Intent(Settings.ACTION_BLUETOOTH_SETTINGS))}));
@@ -241,8 +240,8 @@
                 new ButtonInfo[] {
                         new ButtonInfo(
                                 R.string.device_owner_user_restriction_set,
-                                createSetUserRestrictionIntent(
-                                        UserManager.DISALLOW_USB_FILE_TRANSFER)),
+                                CommandReceiverActivity.createSetUserRestrictionIntent(
+                                        UserManager.DISALLOW_USB_FILE_TRANSFER, true)),
                 }));
 
         // DISABLE_STATUS_BAR_TEST
@@ -279,6 +278,14 @@
                                         CommandReceiverActivity.COMMAND_SET_KEYGUARD_DISABLED,
                                                 false))}));
 
+        // setLockTaskFeatures
+        final Intent lockTaskUiTestIntent = new Intent(this, LockTaskUiTestActivity.class);
+        lockTaskUiTestIntent.putExtra(LockTaskUiTestActivity.EXTRA_TEST_ID, LOCK_TASK_UI_TEST_ID);
+        adapter.add(createTestItem(this, LOCK_TASK_UI_TEST_ID,
+                R.string.device_owner_lock_task_ui_test,
+                lockTaskUiTestIntent));
+
+        // setUserIcon
         adapter.add(createInteractiveTestItem(this, SET_USER_ICON_TEST_ID,
                 R.string.device_owner_set_user_icon,
                 R.string.device_owner_set_user_icon_instruction,
@@ -317,6 +324,7 @@
                 R.string.enterprise_privacy_test,
                 enterprisePolicyTestIntent));
 
+        // COMP
         if (packageManager.hasSystemFeature(PackageManager.FEATURE_MANAGED_USERS)) {
             Intent compIntent = new Intent(this, CompTestActivity.class)
                     .putExtra(PolicyTransparencyTestActivity.EXTRA_TEST_ID, COMP_TEST_ID);
@@ -325,6 +333,18 @@
                     compIntent));
         }
 
+        // Managed user
+        if (packageManager.hasSystemFeature(PackageManager.FEATURE_MANAGED_USERS)
+                && UserManager.supportsMultipleUsers()) {
+            adapter.add(createInteractiveTestItem(this, MANAGED_USER_TEST_ID,
+                    R.string.managed_user_test,
+                    R.string.managed_user_positive_tests_instructions,
+                    new ButtonInfo[]{
+                            new ButtonInfo(
+                                    R.string.device_owner_settings_go,
+                                    createCreateManagedUserIntent())}));
+        }
+
         // Network logging UI
         adapter.add(createInteractiveTestItem(this, NETWORK_LOGGING_UI_TEST_ID,
                 R.string.device_owner_network_logging_ui,
@@ -365,14 +385,6 @@
                 .putExtra(CommandReceiverActivity.EXTRA_ENFORCED, value);
     }
 
-    private Intent createSetUserRestrictionIntent(String restriction) {
-        return new Intent(this, CommandReceiverActivity.class)
-                .putExtra(CommandReceiverActivity.EXTRA_COMMAND,
-                        CommandReceiverActivity.COMMAND_SET_USER_RESTRICTION)
-                .putExtra(CommandReceiverActivity.EXTRA_USER_RESTRICTION, restriction)
-                .putExtra(CommandReceiverActivity.EXTRA_ENFORCED, true);
-    }
-
     private Intent createSetUserIconIntent() {
         return new Intent(this, CommandReceiverActivity.class)
                 .putExtra(CommandReceiverActivity.EXTRA_COMMAND,
@@ -391,6 +403,12 @@
                         CommandReceiverActivity.COMMAND_DISABLE_NETWORK_LOGGING);
     }
 
+    private Intent createCreateManagedUserIntent() {
+        return new Intent(this, CommandReceiverActivity.class)
+                .putExtra(CommandReceiverActivity.EXTRA_COMMAND,
+                        CommandReceiverActivity.COMMAND_CREATE_MANAGED_USER);
+    }
+
     private boolean isStatusBarEnabled() {
       // Watches don't support the status bar so this is an ok proxy, but this is not the most
       // general test for that. TODO: add a test API to do a real check for status bar support.
diff --git a/apps/CtsVerifier/src/com/android/cts/verifier/managedprovisioning/EnterprisePrivacyTestListActivity.java b/apps/CtsVerifier/src/com/android/cts/verifier/managedprovisioning/EnterprisePrivacyTestListActivity.java
index 71c4421..3a16297 100644
--- a/apps/CtsVerifier/src/com/android/cts/verifier/managedprovisioning/EnterprisePrivacyTestListActivity.java
+++ b/apps/CtsVerifier/src/com/android/cts/verifier/managedprovisioning/EnterprisePrivacyTestListActivity.java
@@ -269,19 +269,18 @@
                                             CommandReceiverActivity.
                                             COMMAND_REMOVE_MANAGED_PROFILE))}));
         }
-        // Disabled for API 26 due to b/63696536.
-        // adapter.add(createInteractiveTestItem(this, ENTERPRISE_PRIVACY_FAILED_PASSWORD_WIPE,
-        //         R.string.enterprise_privacy_failed_password_wipe,
-        //         R.string.enterprise_privacy_failed_password_wipe_info,
-        //         new ButtonInfo[] {
-        //                 new ButtonInfo(R.string.enterprise_privacy_open_settings,
-        //                         new Intent(Settings.ACTION_ENTERPRISE_PRIVACY_SETTINGS)),
-        //                 new ButtonInfo(R.string.enterprise_privacy_set_limit,
-        //                         buildCommandIntent(CommandReceiverActivity
-        //                                 .COMMAND_SET_MAXIMUM_PASSWORD_ATTEMPTS)),
-        //                 new ButtonInfo(R.string.enterprise_privacy_finish,
-        //                         buildCommandIntent(CommandReceiverActivity
-        //                                 .COMMAND_CLEAR_MAXIMUM_PASSWORD_ATTEMPTS))}));
+        adapter.add(createInteractiveTestItem(this, ENTERPRISE_PRIVACY_FAILED_PASSWORD_WIPE,
+                R.string.enterprise_privacy_failed_password_wipe,
+                R.string.enterprise_privacy_failed_password_wipe_info,
+                new ButtonInfo[] {
+                        new ButtonInfo(R.string.enterprise_privacy_open_settings,
+                                new Intent(Settings.ACTION_ENTERPRISE_PRIVACY_SETTINGS)),
+                        new ButtonInfo(R.string.enterprise_privacy_set_limit,
+                                buildCommandIntent(CommandReceiverActivity
+                                        .COMMAND_SET_MAXIMUM_PASSWORD_ATTEMPTS)),
+                        new ButtonInfo(R.string.enterprise_privacy_finish,
+                                buildCommandIntent(CommandReceiverActivity
+                                        .COMMAND_CLEAR_MAXIMUM_PASSWORD_ATTEMPTS))}));
         if (getPackageManager().hasSystemFeature(PackageManager.FEATURE_MANAGED_USERS)) {
             adapter.add(createInteractiveTestItem(this,
                     ENTERPRISE_PRIVACY_COMP_FAILED_PASSWORD_WIPE,
diff --git a/apps/CtsVerifier/src/com/android/cts/verifier/managedprovisioning/KeyChainTestActivity.java b/apps/CtsVerifier/src/com/android/cts/verifier/managedprovisioning/KeyChainTestActivity.java
new file mode 100644
index 0000000..a59261c
--- /dev/null
+++ b/apps/CtsVerifier/src/com/android/cts/verifier/managedprovisioning/KeyChainTestActivity.java
@@ -0,0 +1,314 @@
+/*
+ * 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.managedprovisioning;
+
+import static android.keystore.cts.CertificateUtils.createCertificate;
+
+import android.app.admin.DevicePolicyManager;
+import android.content.Context;
+import android.os.AsyncTask;
+import android.os.Bundle;
+import android.security.AttestedKeyPair;
+import android.security.KeyChain;
+import android.security.KeyChainAliasCallback;
+import android.security.KeyChainException;
+import android.security.keystore.KeyGenParameterSpec;
+import android.security.keystore.KeyProperties;
+import android.text.method.ScrollingMovementMethod;
+import android.util.Log;
+import android.view.View;
+import android.widget.Button;
+import android.widget.TextView;
+import com.android.cts.verifier.PassFailButtons;
+import com.android.cts.verifier.R;
+import java.security.GeneralSecurityException;
+import java.security.Principal;
+import java.security.PrivateKey;
+import java.security.Signature;
+import java.security.cert.X509Certificate;
+import java.util.Arrays;
+import javax.security.auth.x500.X500Principal;
+
+/**
+ * Activity to test KeyChain key generation. The following flows are tested: * Generating a key. *
+ * Installing a (self-signed) certificate associated with the key, visible to users. * Setting
+ * visibility of the certificate to not be visible to user.
+ *
+ * <p>After the key generation and certificate installation, it should be possible for a user to
+ * select the key from the certificate selection prompt when {@code KeyChain.choosePrivateKeyAlias}
+ * is called. The test then tests that the key is indeed usable for signing.
+ *
+ * <p>After the visibility is set to not-user-visible, the prompt is shown again, this time the
+ * testes is asked to verify no keys are selectable and cancel the dialog.
+ */
+public class KeyChainTestActivity extends PassFailButtons.Activity {
+    private static final String TAG = "ByodKeyChainActivity";
+
+    public static final String ACTION_KEYCHAIN =
+            "com.android.cts.verifier.managedprovisioning.KEYCHAIN";
+
+    public static final String ALIAS = "cts-verifier-gen-rsa-1";
+    public static final String KEY_ALGORITHM = "RSA";
+
+    private DevicePolicyManager mDevicePolicyManager;
+    private AttestedKeyPair mAttestedKeyPair;
+    private X509Certificate mCert;
+    private TextView mLogView;
+    private TextView mInstructionsView;
+    private Button mSetupButton;
+    private Button mGoButton;
+
+    // Callback interface for when a key is generated.
+    static interface KeyGenerationListener {
+        void onKeyPairGenerated(AttestedKeyPair keyPair);
+    }
+
+    // Task for generating a key pair using {@code DevicePolicyManager.generateKeyPair}.
+    // The listener, if provided, will be invoked after the key has been generated successfully.
+    class GenerateKeyTask extends AsyncTask<KeyGenParameterSpec, Integer, AttestedKeyPair> {
+        KeyGenerationListener mListener;
+
+        public GenerateKeyTask(KeyGenerationListener listener) {
+            mListener = listener;
+        }
+
+        @Override
+        protected AttestedKeyPair doInBackground(KeyGenParameterSpec... specs) {
+            Log.i(TAG, "Generating key pair.");
+            try {
+                AttestedKeyPair kp =
+                        mDevicePolicyManager.generateKeyPair(
+                                DeviceAdminTestReceiver.getReceiverComponentName(),
+                                KEY_ALGORITHM,
+                                specs[0],
+                                0);
+                if (kp != null) {
+                    mLogView.setText("Key generated successfully.");
+                } else {
+                    mLogView.setText("Failed generating key.");
+                }
+                return kp;
+            } catch (SecurityException e) {
+                mLogView.setText("Security exception while generating key.");
+                Log.w(TAG, "Security exception", e);
+            }
+
+            return null;
+        }
+
+        @Override
+        protected void onPostExecute(AttestedKeyPair kp) {
+            super.onPostExecute(kp);
+            if (mListener != null && kp != null) {
+                mListener.onKeyPairGenerated(kp);
+            }
+        }
+    }
+
+    // Helper for generating and installing a self-signed certificate.
+    class CertificateInstaller implements KeyGenerationListener {
+        @Override
+        public void onKeyPairGenerated(AttestedKeyPair keyPair) {
+            mAttestedKeyPair = keyPair;
+            X500Principal issuer = new X500Principal("CN=SelfSigned, O=Android, C=US");
+            X500Principal subject = new X500Principal("CN=Subject, O=Android, C=US");
+            try {
+                mCert = createCertificate(mAttestedKeyPair.getKeyPair(), subject, issuer);
+                boolean installResult = installCertificate(mCert, true);
+                // called from onPostExecute so safe to interact with the UI here.
+                if (installResult) {
+                    mLogView.setText("Test ready");
+                    mInstructionsView.setText(R.string.provisioning_byod_keychain_info_first_test);
+                    mGoButton.setEnabled(true);
+                } else {
+                    mLogView.setText("FAILED certificate installation.");
+                }
+            } catch (Exception e) {
+                Log.w(TAG, "Failed installing certificate", e);
+                mLogView.setText("Error generating a certificate.");
+            }
+        }
+    }
+
+    // Helper for calling {@code DevicePolicyManager.setKeyPairCertificate} with the user-visibility
+    // specified in the constructor. Returns true if the call was successful (and no exceptions
+    // were thrown).
+    protected boolean installCertificate(X509Certificate cert, boolean isUserVisible) {
+        try {
+            return mDevicePolicyManager.setKeyPairCertificate(
+                    DeviceAdminTestReceiver.getReceiverComponentName(),
+                    ALIAS,
+                    Arrays.asList(new X509Certificate[] {cert}),
+                    isUserVisible);
+        } catch (SecurityException e) {
+            logStatus("Security exception while installing cert.");
+            Log.w(TAG, "Security exception", e);
+        }
+        return false;
+    }
+
+    // Invokes choosePrivateKeyAlias.
+    void selectCertificate(KeyChainAliasCallback callback) {
+        String[] keyTypes = new String[] {KEY_ALGORITHM};
+        Principal[] issuers = new Principal[0];
+        KeyChain.choosePrivateKeyAlias(
+                KeyChainTestActivity.this, callback, keyTypes, issuers, null, null);
+    }
+
+    class TestPreparator implements View.OnClickListener {
+        @Override
+        public void onClick(View v) {
+            mLogView.setText("Starting key generation");
+            KeyGenParameterSpec spec =
+                    new KeyGenParameterSpec.Builder(
+                                    ALIAS,
+                                    KeyProperties.PURPOSE_SIGN | KeyProperties.PURPOSE_VERIFY)
+                            .setKeySize(2048)
+                            .setDigests(KeyProperties.DIGEST_SHA256)
+                            .setSignaturePaddings(
+                                    KeyProperties.SIGNATURE_PADDING_RSA_PSS,
+                                    KeyProperties.SIGNATURE_PADDING_RSA_PKCS1)
+                            .build();
+            new GenerateKeyTask(new CertificateInstaller()).execute(spec);
+        }
+    }
+
+    class SelectCertificate implements View.OnClickListener, KeyChainAliasCallback {
+        @Override
+        public void onClick(View v) {
+            Log.i(TAG, "Selecting certificate");
+            mLogView.setText("Waiting for prompt");
+            selectCertificate(this);
+        }
+
+        @Override
+        public void alias(String alias) {
+            Log.i(TAG, "Got alias: " + alias);
+            if (alias == null) {
+                logStatus("FAILED (no alias)");
+                return;
+            } else if (!alias.equals(ALIAS)) {
+                logStatus("FAILED (wrong alias)");
+                return;
+            }
+            logStatus("Got right alias.");
+            try {
+                PrivateKey privateKey = KeyChain.getPrivateKey(KeyChainTestActivity.this, alias);
+                byte[] data = new String("hello").getBytes();
+                Signature sign = Signature.getInstance("SHA256withRSA");
+                sign.initSign(privateKey);
+                sign.update(data);
+                if (sign.sign() != null) {
+                    prepareSecondTest();
+                } else {
+                    logStatus("FAILED (cannot sign)");
+                }
+            } catch (GeneralSecurityException | KeyChainException | InterruptedException e) {
+                Log.w(TAG, "Failed using the key", e);
+                logStatus("FAILED (key unusable)");
+            }
+        }
+    }
+
+    class SelectCertificateExpectingNone implements View.OnClickListener, KeyChainAliasCallback {
+        @Override
+        public void onClick(View v) {
+            Log.i(TAG, "Selecting certificate");
+            mLogView.setText("Waiting for prompt");
+            selectCertificate(this);
+        }
+
+        @Override
+        public void alias(String alias) {
+            Log.i(TAG, "Got alias: " + alias);
+            if (alias != null) {
+                logStatus("FAILED: Should have no certificate.");
+            } else {
+                logStatus("PASSED (2/2)");
+                runOnUiThread(
+                        () -> {
+                            getPassButton().setEnabled(true);
+                        });
+            }
+        }
+    }
+
+    @Override
+    protected void onCreate(Bundle savedInstanceState) {
+        super.onCreate(savedInstanceState);
+        setContentView(R.layout.keychain_test);
+        setPassFailButtonClickListeners();
+        mDevicePolicyManager =
+                (DevicePolicyManager) getSystemService(Context.DEVICE_POLICY_SERVICE);
+
+        mLogView = (TextView) findViewById(R.id.provisioning_byod_keychain_test_log);
+        mLogView.setMovementMethod(new ScrollingMovementMethod());
+
+        mInstructionsView = (TextView) findViewById(R.id.provisioning_byod_keychain_instructions);
+
+        mSetupButton = (Button) findViewById(R.id.prepare_test_button);
+        mSetupButton.setOnClickListener(new TestPreparator());
+
+        mGoButton = (Button) findViewById(R.id.run_test_button);
+        mGoButton.setOnClickListener(new SelectCertificate());
+        mGoButton.setEnabled(false);
+
+        // Disable the pass button here, only enable it when the 2nd test passes.
+        getPassButton().setEnabled(false);
+    }
+
+    protected void prepareSecondTest() {
+        Runnable uiChanges;
+        if (installCertificate(mCert, false)) {
+            uiChanges =
+                    () -> {
+                        mLogView.setText("Second test ready.");
+                        mInstructionsView.setText(
+                                R.string.provisioning_byod_keychain_info_second_test);
+                        mGoButton.setText("Run 2nd test");
+                        mGoButton.setOnClickListener(new SelectCertificateExpectingNone());
+                    };
+        } else {
+            uiChanges =
+                    () -> {
+                        mLogView.setText("FAILED second test setup.");
+                        mGoButton.setEnabled(false);
+                    };
+        }
+
+        runOnUiThread(uiChanges);
+    }
+
+    @Override
+    public void finish() {
+        super.finish();
+        try {
+            mDevicePolicyManager.removeKeyPair(
+                    DeviceAdminTestReceiver.getReceiverComponentName(), ALIAS);
+            Log.i(TAG, "Deleted alias " + ALIAS);
+        } catch (SecurityException e) {
+            Log.w(TAG, "Failed deleting alias", e);
+        }
+    }
+
+    private void logStatus(String status) {
+        runOnUiThread(
+                () -> {
+                    mLogView.setText(status);
+                });
+    }
+}
diff --git a/apps/CtsVerifier/src/com/android/cts/verifier/managedprovisioning/LockTaskUiTestActivity.java b/apps/CtsVerifier/src/com/android/cts/verifier/managedprovisioning/LockTaskUiTestActivity.java
new file mode 100644
index 0000000..a6e6b2f
--- /dev/null
+++ b/apps/CtsVerifier/src/com/android/cts/verifier/managedprovisioning/LockTaskUiTestActivity.java
@@ -0,0 +1,355 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.cts.verifier.managedprovisioning;
+
+import static android.app.admin.DevicePolicyManager.LOCK_TASK_FEATURE_GLOBAL_ACTIONS;
+import static android.app.admin.DevicePolicyManager.LOCK_TASK_FEATURE_HOME;
+import static android.app.admin.DevicePolicyManager.LOCK_TASK_FEATURE_KEYGUARD;
+import static android.app.admin.DevicePolicyManager.LOCK_TASK_FEATURE_NONE;
+import static android.app.admin.DevicePolicyManager.LOCK_TASK_FEATURE_NOTIFICATIONS;
+import static android.app.admin.DevicePolicyManager.LOCK_TASK_FEATURE_RECENTS;
+import static android.app.admin.DevicePolicyManager.LOCK_TASK_FEATURE_SYSTEM_INFO;
+
+import static com.android.cts.verifier.managedprovisioning.Utils.createInteractiveTestItem;
+
+import android.app.Activity;
+import android.app.ActivityManager;
+import android.app.Notification;
+import android.app.NotificationChannel;
+import android.app.NotificationManager;
+import android.app.admin.DevicePolicyManager;
+import android.content.BroadcastReceiver;
+import android.content.ComponentName;
+import android.content.Context;
+import android.content.Intent;
+import android.content.IntentFilter;
+import android.database.DataSetObserver;
+import android.os.AsyncTask;
+import android.os.Bundle;
+import android.support.v4.content.LocalBroadcastManager;
+import android.util.Log;
+import android.widget.Button;
+import android.widget.Toast;
+
+import com.android.cts.verifier.ArrayTestListAdapter;
+import com.android.cts.verifier.IntentDrivenTestActivity.ButtonInfo;
+import com.android.cts.verifier.PassFailButtons;
+import com.android.cts.verifier.R;
+import com.android.cts.verifier.TestListAdapter.TestListItem;
+import com.android.cts.verifier.TestResult;
+
+import java.util.concurrent.CountDownLatch;
+import java.util.concurrent.TimeUnit;
+
+/**
+ * Tests for {@link DevicePolicyManager#setLockTaskFeatures(ComponentName, int)}.
+ */
+public class LockTaskUiTestActivity extends PassFailButtons.TestListActivity {
+
+    private static final String TAG = LockTaskUiTestActivity.class.getSimpleName();
+
+    public static final String EXTRA_TEST_ID =
+            "com.android.cts.verifier.managedprovisioning.extra.TEST_ID";
+
+    /** Broadcast action sent by {@link DeviceAdminTestReceiver} when LockTask starts. */
+    static final String ACTION_LOCK_TASK_STARTED =
+            "com.android.cts.verifier.managedprovisioning.action.LOCK_TASK_STARTED";
+    /** Broadcast action sent by {@link DeviceAdminTestReceiver} when LockTask stops. */
+    static final String ACTION_LOCK_TASK_STOPPED =
+            "com.android.cts.verifier.managedprovisioning.action.LOCK_TASK_STOPPED";
+
+    private static final ComponentName ADMIN_RECEIVER =
+            DeviceAdminTestReceiver.getReceiverComponentName();
+    private static final String TEST_PACKAGE_NAME = "com.android.cts.verifier";
+    private static final String ACTION_STOP_LOCK_TASK =
+            "com.android.cts.verifier.managedprovisioning.action.STOP_LOCK_TASK";
+
+    private static final String TEST_ID_DEFAULT = "lock-task-ui-default";
+    private static final String TEST_ID_SYSTEM_INFO = "lock-task-ui-system-info";
+    private static final String TEST_ID_NOTIFICATIONS = "lock-task-ui-notifications";
+    private static final String TEST_ID_HOME = "lock-task-ui-home";
+    private static final String TEST_ID_RECENTS = "lock-task-ui-recents";
+    private static final String TEST_ID_GLOBAL_ACTIONS = "lock-task-ui-global-actions";
+    private static final String TEST_ID_KEYGUARD = "lock-task-ui-keyguard";
+    private static final String TEST_ID_STOP_LOCK_TASK = "lock-task-ui-stop-lock-task";
+
+    private DevicePolicyManager mDpm;
+    private ActivityManager mAm;
+    private NotificationManager mNotifyMgr;
+
+    private LockTaskStateChangedReceiver mStateChangedReceiver;
+    private CountDownLatch mLockTaskStartedLatch;
+    private CountDownLatch mLockTaskStoppedLatch;
+
+    @Override
+    protected void onCreate(Bundle savedInstanceState) {
+        super.onCreate(savedInstanceState);
+        setContentView(R.layout.device_owner_lock_task_ui);
+        setPassFailButtonClickListeners();
+
+        mDpm = getSystemService(DevicePolicyManager.class);
+        mAm = getSystemService(ActivityManager.class);
+        mNotifyMgr = getSystemService(NotificationManager.class);
+
+        final ArrayTestListAdapter adapter = new ArrayTestListAdapter(this);
+        addTestsToAdapter(adapter);
+        adapter.registerDataSetObserver(new DataSetObserver() {
+            @Override
+            public void onChanged() {
+                updatePassButton();
+            }
+        });
+        setTestListAdapter(adapter);
+
+        Button startLockTaskButton = findViewById(R.id.start_lock_task_button);
+        startLockTaskButton.setOnClickListener((view) -> startLockTaskMode());
+
+        if (ACTION_STOP_LOCK_TASK.equals(getIntent().getAction())) {
+            // This means we're started by the "stop LockTask mode" test activity (the last one in
+            // the list) in order to stop LockTask.
+            stopLockTaskMode();
+        }
+    }
+
+    private void addTestsToAdapter(final ArrayTestListAdapter adapter) {
+        adapter.add(createInteractiveTestItem(this,
+                TEST_ID_DEFAULT,
+                R.string.device_owner_lock_task_ui_default_test,
+                R.string.device_owner_lock_task_ui_default_test_info,
+                new ButtonInfo[]{}));
+
+        adapter.add(createSetLockTaskFeaturesTest(
+                TEST_ID_SYSTEM_INFO,
+                LOCK_TASK_FEATURE_SYSTEM_INFO,
+                R.string.device_owner_lock_task_ui_system_info_test,
+                R.string.device_owner_lock_task_ui_system_info_test_info));
+
+        adapter.add(createSetLockTaskFeaturesTest(
+                TEST_ID_NOTIFICATIONS,
+                LOCK_TASK_FEATURE_NOTIFICATIONS,
+                R.string.device_owner_lock_task_ui_notifications_test,
+                R.string.device_owner_lock_task_ui_notifications_test_info));
+
+        adapter.add(createSetLockTaskFeaturesTest(
+                TEST_ID_HOME,
+                LOCK_TASK_FEATURE_HOME,
+                R.string.device_owner_lock_task_ui_home_test,
+                R.string.device_owner_lock_task_ui_home_test_info));
+
+        adapter.add(createSetLockTaskFeaturesTest(
+                TEST_ID_RECENTS,
+                LOCK_TASK_FEATURE_RECENTS,
+                R.string.device_owner_lock_task_ui_recents_test,
+                R.string.device_owner_lock_task_ui_recents_test_info));
+
+        adapter.add(createSetLockTaskFeaturesTest(
+                TEST_ID_GLOBAL_ACTIONS,
+                LOCK_TASK_FEATURE_GLOBAL_ACTIONS,
+                R.string.device_owner_lock_task_ui_global_actions_test,
+                R.string.device_owner_lock_task_ui_global_actions_test_info));
+
+        adapter.add(createSetLockTaskFeaturesTest(
+                TEST_ID_KEYGUARD,
+                LOCK_TASK_FEATURE_KEYGUARD,
+                R.string.device_owner_lock_task_ui_keyguard_test,
+                R.string.device_owner_lock_task_ui_keyguard_test_info));
+
+        final Intent stopLockTaskIntent = new Intent(this, LockTaskUiTestActivity.class);
+        stopLockTaskIntent.setAction(ACTION_STOP_LOCK_TASK);
+        adapter.add(createInteractiveTestItem(this,
+                TEST_ID_STOP_LOCK_TASK,
+                R.string.device_owner_lock_task_ui_stop_lock_task_test,
+                R.string.device_owner_lock_task_ui_stop_lock_task_test_info,
+                new ButtonInfo(
+                        R.string.device_owner_lock_task_ui_stop_lock_task_test,
+                        stopLockTaskIntent
+                )));
+    }
+
+    /** Receives LockTask start/stop callbacks forwarded by {@link DeviceAdminTestReceiver}. */
+    private final class LockTaskStateChangedReceiver extends BroadcastReceiver {
+        @Override
+        public void onReceive(Context context, Intent intent) {
+            String action = intent.getAction();
+            switch (action) {
+                case ACTION_LOCK_TASK_STARTED:
+                    if (mLockTaskStartedLatch != null) {
+                        mLockTaskStartedLatch.countDown();
+                    }
+                    break;
+                case ACTION_LOCK_TASK_STOPPED:
+                    if (mLockTaskStoppedLatch != null) {
+                        mLockTaskStoppedLatch.countDown();
+                    }
+                    break;
+            }
+        }
+    }
+
+    @Override
+    protected void onResume() {
+        super.onResume();
+        mStateChangedReceiver = new LockTaskStateChangedReceiver();
+        final IntentFilter filter = new IntentFilter();
+        filter.addAction(ACTION_LOCK_TASK_STARTED);
+        filter.addAction(ACTION_LOCK_TASK_STOPPED);
+        LocalBroadcastManager.getInstance(this).registerReceiver(mStateChangedReceiver, filter);
+    }
+
+    @Override
+    protected void onPause() {
+        if (mStateChangedReceiver != null) {
+            LocalBroadcastManager.getInstance(this).unregisterReceiver(mStateChangedReceiver);
+            mStateChangedReceiver = null;
+        }
+        super.onPause();
+    }
+
+    /**
+     * Starts LockTask mode and waits for callback from {@link DeviceAdminTestReceiver} to confirm
+     * LockTask has started successfully. If the callback isn't received, the entire test will be
+     * marked as failed.
+     *
+     * @see LockTaskStateChangedReceiver
+     */
+    private void startLockTaskMode() {
+        if (mAm.getLockTaskModeState() == ActivityManager.LOCK_TASK_MODE_LOCKED) {
+            return;
+        }
+
+        mLockTaskStartedLatch = new CountDownLatch(1);
+        try {
+            mDpm.setLockTaskPackages(ADMIN_RECEIVER, new String[] {TEST_PACKAGE_NAME});
+            mDpm.setLockTaskFeatures(ADMIN_RECEIVER, LOCK_TASK_FEATURE_NONE);
+            startLockTask();
+
+            new CheckLockTaskStateTask() {
+                @Override
+                protected void onPostExecute(Boolean success) {
+                    if (success) {
+                        issueTestNotification();
+                    } else {
+                        notifyFailure(getTestId(), "Failed to start LockTask mode");
+                    }
+                }
+            }.execute(mLockTaskStartedLatch);
+        } catch (SecurityException e) {
+            Log.e(TAG, e.getMessage(), e);
+            Toast.makeText(this, "Failed to run test. Did you set up device owner correctly?",
+                    Toast.LENGTH_SHORT).show();
+        }
+    }
+
+    /**
+     * Stops LockTask mode and waits for callback from {@link DeviceAdminTestReceiver} to confirm
+     * LockTask has stopped successfully. If the callback isn't received, the "Stop LockTask mode"
+     * test case will be marked as failed.
+     *
+     * Note that we {@link #finish()} this activity here, since it's started by the "Stop LockTask
+     * mode" test activity, and shouldn't be exposed to the tester once its job is done.
+     *
+     * @see LockTaskStateChangedReceiver
+     */
+    private void stopLockTaskMode() {
+        if (mAm.getLockTaskModeState() == ActivityManager.LOCK_TASK_MODE_NONE) {
+            finish();
+            return;
+        }
+
+        mLockTaskStoppedLatch = new CountDownLatch(1);
+        try {
+            stopLockTask();
+
+            new CheckLockTaskStateTask() {
+                @Override
+                protected void onPostExecute(Boolean success) {
+                    if (!success) {
+                        notifyFailure(TEST_ID_STOP_LOCK_TASK, "Failed to stop LockTask mode");
+                    }
+                    cancelTestNotification();
+                    mDpm.setLockTaskFeatures(ADMIN_RECEIVER, LOCK_TASK_FEATURE_NONE);
+                    mDpm.setLockTaskPackages(ADMIN_RECEIVER, new String[] {});
+                    LockTaskUiTestActivity.this.finish();
+                }
+            }.execute(mLockTaskStoppedLatch);
+        } catch (SecurityException e) {
+            Log.e(TAG, e.getMessage(), e);
+            Toast.makeText(this, "Failed to finish test. Did you set up device owner correctly?",
+                    Toast.LENGTH_SHORT).show();
+        }
+    }
+
+    private abstract class CheckLockTaskStateTask extends AsyncTask<CountDownLatch, Void, Boolean> {
+        @Override
+        protected Boolean doInBackground(CountDownLatch... latches) {
+            if (latches.length > 0 && latches[0] != null) {
+                try {
+                    return latches[0].await(1, TimeUnit.SECONDS);
+                } catch (InterruptedException e) {
+                    // Fall through
+                }
+            }
+            return false;
+        }
+
+        @Override
+        protected abstract void onPostExecute(Boolean success);
+    }
+
+    private void notifyFailure(String testId, String message) {
+        Log.e(TAG, message);
+        Toast.makeText(this, message, Toast.LENGTH_SHORT).show();
+        TestResult.setFailedResult(this, testId, message);
+    }
+
+    private void issueTestNotification() {
+        String channelId = getTestId();
+        if (mNotifyMgr.getNotificationChannel(channelId) == null) {
+            NotificationChannel channel = new NotificationChannel(
+                    channelId, getTestId(), NotificationManager.IMPORTANCE_HIGH);
+            mNotifyMgr.createNotificationChannel(channel);
+        }
+
+        Notification note = new Notification.Builder(this, channelId)
+                .setContentTitle(getString(R.string.device_owner_lock_task_ui_test))
+                .setSmallIcon(android.R.drawable.sym_def_app_icon)
+                .setOngoing(true)
+                .build();
+
+        mNotifyMgr.notify(0, note);
+    }
+
+    private void cancelTestNotification() {
+        mNotifyMgr.cancelAll();
+    }
+
+    private TestListItem createSetLockTaskFeaturesTest(String testId, int featureFlags,
+            int titleResId, int detailResId) {
+        final Intent commandIntent = new Intent(CommandReceiverActivity.ACTION_EXECUTE_COMMAND);
+        commandIntent.putExtra(CommandReceiverActivity.EXTRA_COMMAND,
+                CommandReceiverActivity.COMMAND_SET_LOCK_TASK_FEATURES);
+        commandIntent.putExtra(CommandReceiverActivity.EXTRA_VALUE, featureFlags);
+
+        return createInteractiveTestItem(this, testId, titleResId, detailResId,
+                new ButtonInfo(titleResId, commandIntent));
+    }
+
+    @Override
+    public String getTestId() {
+        return getIntent().getStringExtra(EXTRA_TEST_ID);
+    }
+}
diff --git a/apps/CtsVerifier/src/com/android/cts/verifier/managedprovisioning/ManagedUserPositiveTestActivity.java b/apps/CtsVerifier/src/com/android/cts/verifier/managedprovisioning/ManagedUserPositiveTestActivity.java
new file mode 100644
index 0000000..1ce0807
--- /dev/null
+++ b/apps/CtsVerifier/src/com/android/cts/verifier/managedprovisioning/ManagedUserPositiveTestActivity.java
@@ -0,0 +1,169 @@
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.cts.verifier.managedprovisioning;
+
+import static com.android.cts.verifier.managedprovisioning.Utils.createInteractiveTestItem;
+
+import android.app.Activity;
+import android.app.admin.DevicePolicyManager;
+import android.content.Intent;
+import android.content.pm.PackageManager;
+import android.database.DataSetObserver;
+import android.os.Bundle;
+import android.provider.Settings;
+
+import com.android.cts.verifier.ArrayTestListAdapter;
+import com.android.cts.verifier.IntentDrivenTestActivity.ButtonInfo;
+import com.android.cts.verifier.PassFailButtons;
+import com.android.cts.verifier.R;
+import com.android.cts.verifier.TestListAdapter.TestListItem;
+import com.android.cts.verifier.TestResult;
+
+/**
+ * Activity that lists all positive managed user tests.
+ */
+public class ManagedUserPositiveTestActivity extends PassFailButtons.TestListActivity {
+    private static final String TAG = "ManagedUserPositiveTestActivity";
+
+    private static final String ACTION_CHECK_AFFILIATED_PROFILE_OWNER =
+            "com.android.cts.verifier.managedprovisioning.action.CHECK_AFFILIATED_PROFILE_OWNER";
+    static final String EXTRA_TEST_ID = "extra-test-id";
+
+    private static final String CHECK_AFFILIATED_PROFILE_OWNER_TEST_ID =
+            "CHECK_AFFILIATED_PROFILE_OWNER";
+    private static final String DEVICE_ADMIN_SETTINGS_ID = "DEVICE_ADMIN_SETTINGS";
+    private static final String DISABLE_STATUS_BAR_TEST_ID = "DISABLE_STATUS_BAR";
+    private static final String DISABLE_KEYGUARD_TEST_ID = "DISABLE_KEYGUARD";
+
+    @Override
+    protected void onCreate(Bundle savedInstanceState) {
+        super.onCreate(savedInstanceState);
+
+        if (ACTION_CHECK_AFFILIATED_PROFILE_OWNER.equals(getIntent().getAction())) {
+            DevicePolicyManager dpm = getSystemService(DevicePolicyManager.class);
+            if (dpm.isProfileOwnerApp(getPackageName()) && dpm.isAffiliatedUser()) {
+                TestResult.setPassedResult(this, getIntent().getStringExtra(EXTRA_TEST_ID),
+                        null, null);
+            } else {
+                TestResult.setFailedResult(this, getIntent().getStringExtra(EXTRA_TEST_ID),
+                        getString(R.string.managed_user_incorrect_managed_user), null);
+            }
+            finish();
+            return;
+        }
+
+        setContentView(R.layout.positive_managed_user);
+        setInfoResources(R.string.managed_user_positive_tests,
+                R.string.managed_user_positive_tests_info, 0);
+        setPassFailButtonClickListeners();
+
+        final ArrayTestListAdapter adapter = new ArrayTestListAdapter(this);
+        adapter.add(TestListItem.newCategory(this, R.string.managed_user_positive_category));
+
+        addTestsToAdapter(adapter);
+
+        adapter.registerDataSetObserver(new DataSetObserver() {
+            @Override
+            public void onChanged() {
+                updatePassButton();
+            }
+        });
+
+        setTestListAdapter(adapter);
+    }
+
+    @Override
+    public void finish() {
+        // If this activity was started for checking profile owner status, then no need to do any
+        // tear down.
+        if (!ACTION_CHECK_AFFILIATED_PROFILE_OWNER.equals(getIntent().getAction())) {
+            // Pass and fail buttons are known to call finish() when clicked,
+            // and this is when we want to remove the managed owner.
+            DevicePolicyManager dpm = getSystemService(DevicePolicyManager.class);
+            dpm.logoutUser(DeviceAdminTestReceiver.getReceiverComponentName());
+        }
+        super.finish();
+    }
+
+    private void addTestsToAdapter(final ArrayTestListAdapter adapter) {
+        adapter.add(createTestItem(this, CHECK_AFFILIATED_PROFILE_OWNER_TEST_ID,
+                R.string.managed_user_check_managed_user_test,
+                new Intent(ACTION_CHECK_AFFILIATED_PROFILE_OWNER)
+                        .putExtra(EXTRA_TEST_ID, getIntent().getStringExtra(EXTRA_TEST_ID))));
+
+        // device admin settings
+        adapter.add(createInteractiveTestItem(this, DEVICE_ADMIN_SETTINGS_ID,
+                R.string.device_owner_device_admin_visible,
+                R.string.device_owner_device_admin_visible_info,
+                new ButtonInfo(
+                        R.string.device_owner_settings_go,
+                        new Intent(Settings.ACTION_SECURITY_SETTINGS))));
+
+        // DISABLE_STATUS_BAR_TEST
+        if (isStatusBarEnabled()) {
+            adapter.add(createInteractiveTestItem(this, DISABLE_STATUS_BAR_TEST_ID,
+                    R.string.device_owner_disable_statusbar_test,
+                    R.string.device_owner_disable_statusbar_test_info,
+                    new ButtonInfo[]{
+                            new ButtonInfo(
+                                    R.string.device_owner_disable_statusbar_button,
+                                    createManagedUserIntentWithBooleanParameter(
+                                            CommandReceiverActivity.COMMAND_SET_STATUSBAR_DISABLED,
+                                            true)),
+                            new ButtonInfo(
+                                    R.string.device_owner_reenable_statusbar_button,
+                                    createManagedUserIntentWithBooleanParameter(
+                                            CommandReceiverActivity.COMMAND_SET_STATUSBAR_DISABLED,
+                                            false))}));
+        }
+
+        // setKeyguardDisabled
+        adapter.add(createInteractiveTestItem(this, DISABLE_KEYGUARD_TEST_ID,
+                R.string.device_owner_disable_keyguard_test,
+                R.string.device_owner_disable_keyguard_test_info,
+                new ButtonInfo[]{
+                        new ButtonInfo(
+                                R.string.device_owner_disable_keyguard_button,
+                                createManagedUserIntentWithBooleanParameter(
+                                        CommandReceiverActivity.COMMAND_SET_KEYGUARD_DISABLED,
+                                        true)),
+                        new ButtonInfo(
+                                R.string.device_owner_reenable_keyguard_button,
+                                createManagedUserIntentWithBooleanParameter(
+                                        CommandReceiverActivity.COMMAND_SET_KEYGUARD_DISABLED,
+                                        false))}));
+    }
+
+
+    static TestListItem createTestItem(Activity activity, String id, int titleRes,
+            Intent intent) {
+        intent.putExtra(EXTRA_TEST_ID, id);
+        return TestListItem.newTest(activity, titleRes, id, intent, null);
+    }
+
+    private Intent createManagedUserIntentWithBooleanParameter(String command, boolean value) {
+        return new Intent(this, CommandReceiverActivity.class)
+                .putExtra(CommandReceiverActivity.EXTRA_COMMAND, command)
+                .putExtra(CommandReceiverActivity.EXTRA_ENFORCED, value);
+    }
+
+    private boolean isStatusBarEnabled() {
+        // Watches don't support the status bar so this is an ok proxy, but this is not the most
+        // general test for that. TODO: add a test API to do a real check for status bar support.
+        return !getPackageManager().hasSystemFeature(PackageManager.FEATURE_WATCH);
+    }
+}
diff --git a/apps/CtsVerifier/src/com/android/cts/verifier/managedprovisioning/PolicyTransparencyTestActivity.java b/apps/CtsVerifier/src/com/android/cts/verifier/managedprovisioning/PolicyTransparencyTestActivity.java
index 0f57b87..e3d6edb 100644
--- a/apps/CtsVerifier/src/com/android/cts/verifier/managedprovisioning/PolicyTransparencyTestActivity.java
+++ b/apps/CtsVerifier/src/com/android/cts/verifier/managedprovisioning/PolicyTransparencyTestActivity.java
@@ -21,7 +21,6 @@
 import android.content.Intent;
 import android.inputmethodservice.InputMethodService;
 import android.os.Bundle;
-import android.os.UserManager;
 import android.util.ArrayMap;
 import android.view.accessibility.AccessibilityEvent;
 import android.view.View;
@@ -235,15 +234,14 @@
 
     @Override
     public void onCheckedChanged(CompoundButton buttonView, boolean isChecked) {
-        final Intent intent = new Intent(CommandReceiverActivity.ACTION_EXECUTE_COMMAND);
+        final Intent intent;
         if (TEST_CHECK_USER_RESTRICTION.equals(mTest)) {
             final String userRestriction = getIntent().getStringExtra(
                     CommandReceiverActivity.EXTRA_USER_RESTRICTION);
-            intent.putExtra(CommandReceiverActivity.EXTRA_COMMAND,
-                    CommandReceiverActivity.COMMAND_SET_USER_RESTRICTION);
-            intent.putExtra(CommandReceiverActivity.EXTRA_USER_RESTRICTION, userRestriction);
-            intent.putExtra(CommandReceiverActivity.EXTRA_ENFORCED, isChecked);
+            intent = CommandReceiverActivity.createSetUserRestrictionIntent(
+                    userRestriction, isChecked);
         } else {
+            intent = new Intent(CommandReceiverActivity.ACTION_EXECUTE_COMMAND);
             final PolicyTestItem testItem = POLICY_TEST_ITEMS.get(mTest);
             intent.putExtra(CommandReceiverActivity.EXTRA_COMMAND, testItem.command);
             intent.putExtra(CommandReceiverActivity.EXTRA_ENFORCED, isChecked);
diff --git a/apps/CtsVerifier/src/com/android/cts/verifier/managedprovisioning/UserRestrictions.java b/apps/CtsVerifier/src/com/android/cts/verifier/managedprovisioning/UserRestrictions.java
index 2c44030..cb25bc6 100644
--- a/apps/CtsVerifier/src/com/android/cts/verifier/managedprovisioning/UserRestrictions.java
+++ b/apps/CtsVerifier/src/com/android/cts/verifier/managedprovisioning/UserRestrictions.java
@@ -49,7 +49,8 @@
         UserManager.DISALLOW_REMOVE_MANAGED_PROFILE,
         UserManager.DISALLOW_REMOVE_USER,
         UserManager.DISALLOW_SHARE_LOCATION,
-        UserManager.DISALLOW_UNINSTALL_APPS
+        UserManager.DISALLOW_UNINSTALL_APPS,
+        UserManager.DISALLOW_UNIFIED_PASSWORD,
     };
 
     private static final ArrayMap<String, UserRestrictionItem> USER_RESTRICTION_ITEMS;
@@ -73,7 +74,8 @@
             R.string.disallow_remove_managed_profile,
             R.string.disallow_remove_user,
             R.string.disallow_share_location,
-            R.string.disallow_uninstall_apps
+            R.string.disallow_uninstall_apps,
+            R.string.disallow_unified_challenge,
         };
 
         final int[] restrictionActions = new int[] {
@@ -95,7 +97,8 @@
             R.string.disallow_remove_managed_profile_action,
             R.string.disallow_remove_user_action,
             R.string.disallow_share_location_action,
-            R.string.disallow_uninstall_apps_action
+            R.string.disallow_uninstall_apps_action,
+            R.string.disallow_unified_challenge_action,
         };
 
         final String[] settingsIntentActions = new String[] {
@@ -118,6 +121,7 @@
             Settings.ACTION_SETTINGS,
             Settings.ACTION_LOCATION_SOURCE_SETTINGS,
             Settings.ACTION_APPLICATION_SETTINGS,
+            Settings.ACTION_SECURITY_SETTINGS,
         };
 
         if (RESTRICTION_IDS_FOR_POLICY_TRANSPARENCY.length != restrictionLabels.length
@@ -143,6 +147,7 @@
         ALSO_VALID_FOR_PO_POLICY_TRANSPARENCY.add(UserManager.DISALLOW_UNINSTALL_APPS);
         ALSO_VALID_FOR_PO_POLICY_TRANSPARENCY.add(UserManager.DISALLOW_MODIFY_ACCOUNTS);
         ALSO_VALID_FOR_PO_POLICY_TRANSPARENCY.add(UserManager.DISALLOW_SHARE_LOCATION);
+        ALSO_VALID_FOR_PO_POLICY_TRANSPARENCY.add(UserManager.DISALLOW_UNIFIED_PASSWORD);
     }
 
     public static String getRestrictionLabel(Context context, String restriction) {
@@ -168,7 +173,8 @@
             ArrayList<String> result = new ArrayList<String>();
             // They are all valid except for DISALLOW_REMOVE_MANAGED_PROFILE
             for (String st : RESTRICTION_IDS_FOR_POLICY_TRANSPARENCY) {
-                if (!st.equals(UserManager.DISALLOW_REMOVE_MANAGED_PROFILE)) {
+                if (!st.equals(UserManager.DISALLOW_REMOVE_MANAGED_PROFILE)
+                        && !st.equals(UserManager.DISALLOW_UNIFIED_PASSWORD)) {
                     result.add(st);
                 }
             }
diff --git a/apps/CtsVerifier/src/com/android/cts/verifier/notifications/AttentionManagementVerifierActivity.java b/apps/CtsVerifier/src/com/android/cts/verifier/notifications/AttentionManagementVerifierActivity.java
index 6a1ce2c..29c8708 100644
--- a/apps/CtsVerifier/src/com/android/cts/verifier/notifications/AttentionManagementVerifierActivity.java
+++ b/apps/CtsVerifier/src/com/android/cts/verifier/notifications/AttentionManagementVerifierActivity.java
@@ -26,6 +26,7 @@
 import android.content.ContentProviderOperation;
 import android.content.OperationApplicationException;
 import android.database.Cursor;
+import android.media.AudioAttributes;
 import android.net.Uri;
 import android.os.RemoteException;
 import android.provider.ContactsContract;
@@ -35,7 +36,9 @@
 import android.util.Log;
 import android.view.View;
 import android.view.ViewGroup;
+
 import com.android.cts.verifier.R;
+
 import org.json.JSONException;
 import org.json.JSONObject;
 
@@ -50,6 +53,8 @@
 
     private static final String NOTIFICATION_CHANNEL_ID = TAG;
     private static final String NOTIFICATION_CHANNEL_ID_NOISY = TAG + "/noisy";
+    private static final String NOTIFICATION_CHANNEL_ID_MEDIA = TAG + "/media";
+    private static final String NOTIFICATION_CHANNEL_ID_GAME = TAG + "/game";
     private static final String ALICE = "Alice";
     private static final String ALICE_PHONE = "+16175551212";
     private static final String ALICE_EMAIL = "alice@_foo._bar";
@@ -91,9 +96,13 @@
         tests.add(new IsEnabledTest());
         tests.add(new ServiceStartedTest());
         tests.add(new InsertContactsTest());
-        tests.add(new NoneInterceptsAllTest());
-        tests.add(new PriorityInterceptsSomeTest());
-        tests.add(new AllInterceptsNothingTest());
+        tests.add(new NoneInterceptsAllMessagesTest());
+        tests.add(new NoneInterceptsAlarmEventReminderCategoriesTest());
+        tests.add(new PriorityInterceptsSomeMessagesTest());
+        tests.add(new PriorityInterceptsAlarmsTest());
+        tests.add(new PriorityInterceptsMediaSystemOtherTest());
+        tests.add(new AllInterceptsNothingMessagesTest());
+        tests.add(new AllInterceptsNothingDiffCategoriesTest());
         tests.add(new DefaultOrderTest());
         tests.add(new PriorityOrderTest());
         tests.add(new InterruptionOrderTest());
@@ -113,11 +122,25 @@
                 NOTIFICATION_CHANNEL_ID_NOISY, NotificationManager.IMPORTANCE_HIGH);
         noisyChannel.enableVibration(true);
         mNm.createNotificationChannel(noisyChannel);
+        NotificationChannel mediaChannel = new NotificationChannel(NOTIFICATION_CHANNEL_ID_MEDIA,
+                NOTIFICATION_CHANNEL_ID_MEDIA, NotificationManager.IMPORTANCE_HIGH);
+        AudioAttributes.Builder aa = new AudioAttributes.Builder()
+                .setUsage(AudioAttributes.USAGE_MEDIA);
+        mediaChannel.setSound(null, aa.build());
+        mNm.createNotificationChannel(mediaChannel);
+        NotificationChannel gameChannel = new NotificationChannel(NOTIFICATION_CHANNEL_ID_GAME,
+                NOTIFICATION_CHANNEL_ID_GAME, NotificationManager.IMPORTANCE_HIGH);
+        AudioAttributes.Builder aa2 = new AudioAttributes.Builder()
+                .setUsage(AudioAttributes.USAGE_GAME);
+        gameChannel.setSound(null, aa2.build());
+        mNm.createNotificationChannel(gameChannel);
     }
 
     private void deleteChannels() {
         mNm.deleteNotificationChannel(NOTIFICATION_CHANNEL_ID);
         mNm.deleteNotificationChannel(NOTIFICATION_CHANNEL_ID_NOISY);
+        mNm.deleteNotificationChannel(NOTIFICATION_CHANNEL_ID_MEDIA);
+        mNm.deleteNotificationChannel(NOTIFICATION_CHANNEL_ID_GAME);
     }
 
     // Tests
@@ -185,7 +208,7 @@
         }
     }
 
-    protected class NoneInterceptsAllTest extends InteractiveTestCase {
+    protected class NoneInterceptsAllMessagesTest extends InteractiveTestCase {
         @Override
         View inflate(ViewGroup parent) {
             return createAutoItem(parent, R.string.attention_all_are_filtered);
@@ -213,7 +236,7 @@
                 try {
                     String tag = payload.getString(JSON_TAG);
                     boolean zen = payload.getBoolean(JSON_MATCHES_ZEN_FILTER);
-                    Log.e(TAG, tag + (zen ? "" : " not") + " intercepted");
+                    Log.e(TAG, tag + (!zen ? "" : " not") + " intercepted");
                     if (found.contains(tag)) {
                         // multiple entries for same notification!
                         pass = false;
@@ -238,24 +261,23 @@
 
         @Override
         void tearDown() {
-            mNm.setInterruptionFilter(NotificationManager.INTERRUPTION_FILTER_ALL);
             mNm.cancelAll();
             deleteChannels();
             MockListener.getInstance().resetData();
         }
-
     }
 
-    protected class AllInterceptsNothingTest extends InteractiveTestCase {
+    protected class NoneInterceptsAlarmEventReminderCategoriesTest extends InteractiveTestCase {
         @Override
         View inflate(ViewGroup parent) {
-            return createAutoItem(parent, R.string.attention_none_are_filtered);
+            return createAutoItem(parent, R.string.attention_all_are_filtered);
         }
 
         @Override
         void setUp() {
+            mNm.setInterruptionFilter(NotificationManager.INTERRUPTION_FILTER_NONE);
             createChannels();
-            sendNotifications(MODE_URI, false, false);
+            sendEventAlarmReminderNotifications(SEND_ALL);
             status = READY;
         }
 
@@ -273,7 +295,66 @@
                 try {
                     String tag = payload.getString(JSON_TAG);
                     boolean zen = payload.getBoolean(JSON_MATCHES_ZEN_FILTER);
-                    Log.e(TAG, tag + (zen ? "" : " not") + " intercepted");
+                    Log.e(TAG, tag + (!zen ? "" : " not") + " intercepted");
+                    if (found.contains(tag)) {
+                        // multiple entries for same notification!
+                        pass = false;
+                    } else if (ALICE.equals(tag)) {
+                        found.add(ALICE);
+                        pass &= !zen;
+                    } else if (BOB.equals(tag)) {
+                        found.add(BOB);
+                        pass &= !zen;
+                    } else if (CHARLIE.equals(tag)) {
+                        found.add(CHARLIE);
+                        pass &= !zen;
+                    }
+                } catch (JSONException e) {
+                    pass = false;
+                    Log.e(TAG, "failed to unpack data from mocklistener", e);
+                }
+            }
+            pass &= found.size() == 3;
+            status = pass ? PASS : FAIL;
+        }
+
+        @Override
+        void tearDown() {
+            mNm.cancelAll();
+            deleteChannels();
+            MockListener.getInstance().resetData();
+        }
+    }
+
+    protected class AllInterceptsNothingMessagesTest extends InteractiveTestCase {
+        @Override
+        View inflate(ViewGroup parent) {
+            return createAutoItem(parent, R.string.attention_none_are_filtered_messages);
+        }
+
+        @Override
+        void setUp() {
+            mNm.setInterruptionFilter(NotificationManager.INTERRUPTION_FILTER_ALL);
+            createChannels();
+            sendNotifications(MODE_URI, false, false); // different messages
+            status = READY;
+        }
+
+        @Override
+        void test() {
+            List<JSONObject> result = new ArrayList<>(MockListener.getInstance().getPosted());
+
+            Set<String> found = new HashSet<String>();
+            if (result.size() == 0) {
+                status = FAIL;
+                return;
+            }
+            boolean pass = true;
+            for (JSONObject payload : result) {
+                try {
+                    String tag = payload.getString(JSON_TAG);
+                    boolean zen = payload.getBoolean(JSON_MATCHES_ZEN_FILTER);
+                    Log.e(TAG, tag + (!zen ? "" : " not") + " intercepted");
                     if (found.contains(tag)) {
                         // multiple entries for same notification!
                         pass = false;
@@ -304,18 +385,77 @@
         }
     }
 
-    protected class PriorityInterceptsSomeTest extends InteractiveTestCase {
+    protected class AllInterceptsNothingDiffCategoriesTest extends InteractiveTestCase {
         @Override
         View inflate(ViewGroup parent) {
-            return createAutoItem(parent, R.string.attention_some_are_filtered);
+            return createAutoItem(parent, R.string.attention_none_are_filtered_diff_categories);
+        }
+
+        @Override
+        void setUp() {
+            mNm.setInterruptionFilter(NotificationManager.INTERRUPTION_FILTER_ALL);
+            createChannels();
+            sendEventAlarmReminderNotifications(SEND_ALL);
+            status = READY;
+        }
+
+        @Override
+        void test() {
+            List<JSONObject> result = new ArrayList<>(MockListener.getInstance().getPosted());
+
+            Set<String> found = new HashSet<String>();
+            if (result.size() == 0) {
+                status = FAIL;
+                return;
+            }
+            boolean pass = true;
+            for (JSONObject payload : result) {
+                try {
+                    String tag = payload.getString(JSON_TAG);
+                    boolean zen = payload.getBoolean(JSON_MATCHES_ZEN_FILTER);
+                    Log.e(TAG, tag + (!zen ? "" : " not") + " intercepted");
+                    if (found.contains(tag)) {
+                        // multiple entries for same notification!
+                        pass = false;
+                    } else if (ALICE.equals(tag)) {
+                        found.add(ALICE);
+                        pass &= zen;
+                    } else if (BOB.equals(tag)) {
+                        found.add(BOB);
+                        pass &= zen;
+                    } else if (CHARLIE.equals(tag)) {
+                        found.add(CHARLIE);
+                        pass &= zen;
+                    }
+                } catch (JSONException e) {
+                    pass = false;
+                    Log.e(TAG, "failed to unpack data from mocklistener", e);
+                }
+            }
+            pass &= found.size() == 3;
+            status = pass ? PASS : FAIL;
+        }
+
+        @Override
+        void tearDown() {
+            mNm.cancelAll();
+            deleteChannels();
+            MockListener.getInstance().resetData();
+        }
+    }
+
+    protected class PriorityInterceptsSomeMessagesTest extends InteractiveTestCase {
+        @Override
+        View inflate(ViewGroup parent) {
+            return createAutoItem(parent, R.string.attention_some_are_filtered_messages);
         }
 
         @Override
         void setUp() {
             mNm.setInterruptionFilter(NotificationManager.INTERRUPTION_FILTER_PRIORITY);
             NotificationManager.Policy policy = mNm.getNotificationPolicy();
-            policy = new NotificationManager.Policy(policy.priorityCategories
-                    | NotificationManager.Policy.PRIORITY_CATEGORY_MESSAGES,
+            policy = new NotificationManager.Policy(
+                    NotificationManager.Policy.PRIORITY_CATEGORY_MESSAGES,
                     policy.priorityCallSenders,
                     NotificationManager.Policy.PRIORITY_SENDERS_STARRED);
             mNm.setNotificationPolicy(policy);
@@ -338,7 +478,7 @@
                 try {
                     String tag = payload.getString(JSON_TAG);
                     boolean zen = payload.getBoolean(JSON_MATCHES_ZEN_FILTER);
-                    Log.e(TAG, tag + (zen ? "" : " not") + " intercepted");
+                    Log.e(TAG, tag + (!zen ? "" : " not") + " intercepted");
                     if (found.contains(tag)) {
                         // multiple entries for same notification!
                         pass = false;
@@ -357,13 +497,144 @@
                     Log.e(TAG, "failed to unpack data from mocklistener", e);
                 }
             }
-            pass &= found.size() == 3;
+            pass &= found.size() >= 3;
             status = pass ? PASS : FAIL;
         }
 
         @Override
         void tearDown() {
-            mNm.setInterruptionFilter(NotificationManager.INTERRUPTION_FILTER_ALL);
+            mNm.cancelAll();
+            deleteChannels();
+            MockListener.getInstance().resetData();
+        }
+    }
+
+    protected class PriorityInterceptsAlarmsTest extends InteractiveTestCase {
+        @Override
+        View inflate(ViewGroup parent) {
+            return createAutoItem(parent, R.string.attention_some_are_filtered_alarms);
+        }
+
+        @Override
+        void setUp() {
+            mNm.setInterruptionFilter(NotificationManager.INTERRUPTION_FILTER_PRIORITY);
+            NotificationManager.Policy policy = mNm.getNotificationPolicy();
+            policy = new NotificationManager.Policy(
+                    NotificationManager.Policy.PRIORITY_CATEGORY_ALARMS,
+                    policy.priorityCallSenders,
+                    policy.priorityMessageSenders);
+            mNm.setNotificationPolicy(policy);
+            createChannels();
+            // Event to Alice, Alarm to Bob, Reminder to Charlie:
+            sendEventAlarmReminderNotifications(SEND_ALL);
+            status = READY;
+        }
+
+        @Override
+        void test() {
+            List<JSONObject> result = new ArrayList<>(MockListener.getInstance().getPosted());
+
+            Set<String> found = new HashSet<String>();
+            if (result.size() == 0) {
+                status = FAIL;
+                return;
+            }
+            boolean pass = true;
+            for (JSONObject payload : result) {
+                try {
+                    String tag = payload.getString(JSON_TAG);
+                    boolean zenIntercepted = !payload.getBoolean(JSON_MATCHES_ZEN_FILTER);
+                    Log.e(TAG, tag + (zenIntercepted ? "" : " not") + " intercepted");
+                    if (found.contains(tag)) {
+                        // multiple entries for same notification!
+                        pass = false;
+                    } else if (ALICE.equals(tag)) {
+                        found.add(ALICE);
+                        pass &= zenIntercepted; // Alice's event notif should be intercepted
+                    } else if (BOB.equals(tag)) {
+                        found.add(BOB);
+                        pass &= !zenIntercepted;   // Bob's alarm notif should not be intercepted
+                    } else if (CHARLIE.equals(tag)) {
+                        found.add(CHARLIE);
+                        pass &= zenIntercepted; // Charlie's reminder notif should be intercepted
+                    }
+                } catch (JSONException e) {
+                    pass = false;
+                    Log.e(TAG, "failed to unpack data from mocklistener", e);
+                }
+            }
+            pass &= found.size() >= 3;
+            status = pass ? PASS : FAIL;
+        }
+
+        @Override
+        void tearDown() {
+            mNm.cancelAll();
+            deleteChannels();
+            MockListener.getInstance().resetData();
+        }
+    }
+
+    protected class PriorityInterceptsMediaSystemOtherTest extends InteractiveTestCase {
+        @Override
+        View inflate(ViewGroup parent) {
+            return createAutoItem(parent, R.string.attention_some_are_filtered_media_system_other);
+        }
+
+        @Override
+        void setUp() {
+            mNm.setInterruptionFilter(NotificationManager.INTERRUPTION_FILTER_PRIORITY);
+            NotificationManager.Policy policy = mNm.getNotificationPolicy();
+            policy = new NotificationManager.Policy(
+                    NotificationManager.Policy.PRIORITY_CATEGORY_MEDIA_SYSTEM_OTHER,
+                    policy.priorityCallSenders,
+                    policy.priorityMessageSenders);
+            mNm.setNotificationPolicy(policy);
+            createChannels();
+            // Alarm to Alice, Other (Game) to Bob, Media to Charlie:
+            sendAlarmOtherMediaNotifications(SEND_ALL);
+            status = READY;
+        }
+
+        @Override
+        void test() {
+            List<JSONObject> result = new ArrayList<>(MockListener.getInstance().getPosted());
+
+            Set<String> found = new HashSet<String>();
+            if (result.size() == 0) {
+                status = FAIL;
+                return;
+            }
+            boolean pass = true;
+            for (JSONObject payload : result) {
+                try {
+                    String tag = payload.getString(JSON_TAG);
+                    boolean zenIntercepted = !payload.getBoolean(JSON_MATCHES_ZEN_FILTER);
+                    Log.e(TAG, tag + (zenIntercepted ? "" : " not") + " intercepted");
+                    if (found.contains(tag)) {
+                        // multiple entries for same notification!
+                        pass = false;
+                    } else if (ALICE.equals(tag)) {
+                        found.add(ALICE);
+                        pass &= zenIntercepted;
+                    } else if (BOB.equals(tag)) {
+                        found.add(BOB);
+                        pass &= !zenIntercepted;
+                    } else if (CHARLIE.equals(tag)) {
+                        found.add(CHARLIE);
+                        pass &= !zenIntercepted;
+                    }
+                } catch (JSONException e) {
+                    pass = false;
+                    Log.e(TAG, "failed to unpack data from mocklistener", e);
+                }
+            }
+            pass &= found.size() >= 3;
+            status = pass ? PASS : FAIL;
+        }
+
+        @Override
+        void tearDown() {
             mNm.cancelAll();
             deleteChannels();
             MockListener.getInstance().resetData();
@@ -379,6 +650,7 @@
 
         @Override
         void setUp() {
+            mNm.setInterruptionFilter(NotificationManager.INTERRUPTION_FILTER_ALL);
             createChannels();
             sendNotifications(MODE_NONE, false, false);
             status = READY;
@@ -415,6 +687,7 @@
 
         @Override
         void setUp() {
+            mNm.setInterruptionFilter(NotificationManager.INTERRUPTION_FILTER_ALL);
             createChannels();
             sendNotifications(MODE_NONE, true, false);
             status = READY;
@@ -453,6 +726,7 @@
 
         @Override
         void setUp() {
+            mNm.setInterruptionFilter(NotificationManager.INTERRUPTION_FILTER_ALL);
             delayTime = 15000;
             createChannels();
             // send B & C noisy with contact affinity
@@ -727,6 +1001,83 @@
         }
     }
 
+    private void sendEventAlarmReminderNotifications(int which) {
+        long when = System.currentTimeMillis() - 4000000L;
+        final String channelId = NOTIFICATION_CHANNEL_ID;
+
+        // Event notification to Alice
+        if ((which & SEND_A) != 0) {
+            Notification.Builder alice = new Notification.Builder(mContext, channelId)
+                    .setContentTitle(ALICE)
+                    .setContentText(ALICE)
+                    .setSmallIcon(R.drawable.ic_stat_alice)
+                    .setCategory(Notification.CATEGORY_EVENT)
+                    .setWhen(when);
+            mNm.notify(ALICE, NOTIFICATION_ID + 1, alice.build());
+        }
+
+        // Alarm notification to Bob
+        if ((which & SEND_B) != 0) {
+            Notification.Builder bob = new Notification.Builder(mContext, channelId)
+                    .setContentTitle(BOB)
+                    .setContentText(BOB)
+                    .setSmallIcon(R.drawable.ic_stat_bob)
+                    .setCategory(Notification.CATEGORY_ALARM)
+                    .setWhen(when);
+            mNm.notify(BOB, NOTIFICATION_ID + 2, bob.build());
+        }
+
+        // Reminder notification to Charlie
+        if ((which & SEND_C) != 0) {
+            Notification.Builder charlie =
+                    new Notification.Builder(mContext, channelId)
+                            .setContentTitle(CHARLIE)
+                            .setContentText(CHARLIE)
+                            .setSmallIcon(R.drawable.ic_stat_charlie)
+                            .setCategory(Notification.CATEGORY_REMINDER)
+                            .setWhen(when);
+            mNm.notify(CHARLIE, NOTIFICATION_ID + 3, charlie.build());
+        }
+    }
+
+    private void sendAlarmOtherMediaNotifications(int which) {
+        long when = System.currentTimeMillis() - 4000000L;
+        final String channelId = NOTIFICATION_CHANNEL_ID;
+
+        // Alarm notification to Alice
+        if ((which & SEND_A) != 0) {
+            Notification.Builder alice = new Notification.Builder(mContext, channelId)
+                    .setContentTitle(ALICE)
+                    .setContentText(ALICE)
+                    .setSmallIcon(R.drawable.ic_stat_alice)
+                    .setCategory(Notification.CATEGORY_ALARM)
+                    .setWhen(when);
+            mNm.notify(ALICE, NOTIFICATION_ID + 1, alice.build());
+        }
+
+        // "Other" notification to Bob
+        if ((which & SEND_B) != 0) {
+            Notification.Builder bob = new Notification.Builder(mContext,
+                    NOTIFICATION_CHANNEL_ID_GAME)
+                    .setContentTitle(BOB)
+                    .setContentText(BOB)
+                    .setSmallIcon(R.drawable.ic_stat_bob)
+                    .setWhen(when);
+            mNm.notify(BOB, NOTIFICATION_ID + 2, bob.build());
+        }
+
+        // Media notification to Charlie
+        if ((which & SEND_C) != 0) {
+            Notification.Builder charlie =
+                    new Notification.Builder(mContext, NOTIFICATION_CHANNEL_ID_MEDIA)
+                            .setContentTitle(CHARLIE)
+                            .setContentText(CHARLIE)
+                            .setSmallIcon(R.drawable.ic_stat_charlie)
+                            .setWhen(when);
+            mNm.notify(CHARLIE, NOTIFICATION_ID + 3, charlie.build());
+        }
+    }
+
     private void addPerson(int mode, Notification.Builder note,
             Uri uri, String phone, String email) {
         if (mode == MODE_URI && uri != null) {
diff --git a/apps/CtsVerifier/src/com/android/cts/verifier/notifications/BlockChangeReceiver.java b/apps/CtsVerifier/src/com/android/cts/verifier/notifications/BlockChangeReceiver.java
new file mode 100644
index 0000000..f432ddb
--- /dev/null
+++ b/apps/CtsVerifier/src/com/android/cts/verifier/notifications/BlockChangeReceiver.java
@@ -0,0 +1,27 @@
+package com.android.cts.verifier.notifications;
+
+import static android.app.NotificationManager.ACTION_NOTIFICATION_CHANNEL_BLOCK_STATE_CHANGED;
+import static android.app.NotificationManager.ACTION_NOTIFICATION_CHANNEL_GROUP_BLOCK_STATE_CHANGED;
+
+import android.app.NotificationManager;
+import android.content.BroadcastReceiver;
+import android.content.Context;
+import android.content.Intent;
+import android.content.SharedPreferences;
+
+public class BlockChangeReceiver extends BroadcastReceiver {
+
+    @Override
+    public void onReceive(Context context, Intent intent) {
+        SharedPreferences prefs = context.getSharedPreferences(
+                NotificationListenerVerifierActivity.PREFS, Context.MODE_PRIVATE);
+        if (ACTION_NOTIFICATION_CHANNEL_GROUP_BLOCK_STATE_CHANGED.equals(intent.getAction())
+                || ACTION_NOTIFICATION_CHANNEL_BLOCK_STATE_CHANGED.equals(intent.getAction())) {
+            SharedPreferences.Editor editor = prefs.edit();
+            editor.putBoolean(
+                    intent.getStringExtra(NotificationManager.EXTRA_BLOCK_STATE_CHANGED_ID),
+                    intent.getBooleanExtra(NotificationManager.EXTRA_BLOCKED_STATE, false));
+            editor.commit();
+        }
+    }
+}
diff --git a/apps/CtsVerifier/src/com/android/cts/verifier/notifications/ConditionProviderVerifierActivity.java b/apps/CtsVerifier/src/com/android/cts/verifier/notifications/ConditionProviderVerifierActivity.java
index f3bf7e5..285855f 100644
--- a/apps/CtsVerifier/src/com/android/cts/verifier/notifications/ConditionProviderVerifierActivity.java
+++ b/apps/CtsVerifier/src/com/android/cts/verifier/notifications/ConditionProviderVerifierActivity.java
@@ -18,15 +18,18 @@
 
 import com.android.cts.verifier.R;
 
+import android.app.ActivityManager;
 import android.app.AutomaticZenRule;
 import android.app.NotificationManager;
 import android.content.ComponentName;
+import android.content.Context;
 import android.content.Intent;
 import android.net.Uri;
 import android.os.Parcelable;
 import android.provider.Settings;
 import android.provider.Settings.Secure;
 import android.text.TextUtils;
+import android.util.Log;
 import android.view.View;
 import android.view.ViewGroup;
 
@@ -56,17 +59,25 @@
     @Override
     protected List<InteractiveTestCase> createTestItems() {
         List<InteractiveTestCase> tests = new ArrayList<>(9);
-        tests.add(new IsEnabledTest());
-        tests.add(new ServiceStartedTest());
-        tests.add(new CreateAutomaticZenRuleTest());
-        tests.add(new UpdateAutomaticZenRuleTest());
-        tests.add(new GetAutomaticZenRuleTest());
-        tests.add(new GetAutomaticZenRulesTest());
-        tests.add(new SubscribeAutomaticZenRuleTest());
-        tests.add(new DeleteAutomaticZenRuleTest());
-        tests.add(new UnsubscribeAutomaticZenRuleTest());
-        tests.add(new IsDisabledTest());
-        tests.add(new ServiceStoppedTest());
+        ActivityManager am = (ActivityManager) getSystemService(Context.ACTIVITY_SERVICE);
+        if (am.isLowRamDevice()) {
+            tests.add(new CannotBeEnabledTest());
+            tests.add(new ServiceStoppedTest());
+        } else {
+            tests.add(new IsEnabledTest());
+            tests.add(new ServiceStartedTest());
+            tests.add(new CreateAutomaticZenRuleTest());
+            tests.add(new UpdateAutomaticZenRuleTest());
+            tests.add(new GetAutomaticZenRuleTest());
+            tests.add(new GetAutomaticZenRulesTest());
+            tests.add(new SubscribeAutomaticZenRuleTest());
+            tests.add(new DeleteAutomaticZenRuleTest());
+            tests.add(new UnsubscribeAutomaticZenRuleTest());
+            tests.add(new RequestUnbindTest());
+            tests.add(new RequestBindTest());
+            tests.add(new IsDisabledTest());
+            tests.add(new ServiceStoppedTest());
+        }
         return tests;
     }
 
@@ -101,6 +112,50 @@
             // wait for the service to start
             delay();
         }
+
+        @Override
+        protected Intent getIntent() {
+            return new Intent(Settings.ACTION_NOTIFICATION_POLICY_ACCESS_SETTINGS);
+        }
+    }
+
+    protected class CannotBeEnabledTest extends InteractiveTestCase {
+        @Override
+        View inflate(ViewGroup parent) {
+            return createNlsSettingsItem(parent, R.string.cp_cannot_enable_service);
+        }
+
+        @Override
+        boolean autoStart() {
+            return true;
+        }
+
+        @Override
+        void test() {
+            mNm.cancelAll();
+            Intent settings = new Intent(Settings.ACTION_NOTIFICATION_POLICY_ACCESS_SETTINGS);
+            if (settings.resolveActivity(mPackageManager) == null) {
+                logFail("no settings activity");
+                status = FAIL;
+            } else {
+                if (mNm.isNotificationPolicyAccessGranted()) {
+                    status = FAIL;
+                } else {
+                    status = PASS;
+                }
+                next();
+            }
+        }
+
+        void tearDown() {
+            // wait for the service to start
+            delay();
+        }
+
+        @Override
+        protected Intent getIntent() {
+            return new Intent(Settings.ACTION_NOTIFICATION_POLICY_ACCESS_SETTINGS);
+        }
     }
 
     protected class ServiceStartedTest extends InteractiveTestCase {
@@ -135,6 +190,75 @@
         }
     }
 
+    private class RequestUnbindTest extends InteractiveTestCase {
+        int mRetries = 5;
+
+        @Override
+        View inflate(ViewGroup parent) {
+            return createAutoItem(parent, R.string.nls_snooze);
+
+        }
+
+        @Override
+        void setUp() {
+            status = READY;
+        }
+
+        @Override
+        void test() {
+            if (status == READY) {
+                MockConditionProvider.getInstance().requestUnbind();
+                status = RETEST;
+            } else {
+                if (MockConditionProvider.getInstance() == null ||
+                        !MockConditionProvider.getInstance().isConnected()) {
+                    status = PASS;
+                } else {
+                    if (--mRetries > 0) {
+                        status = RETEST;
+                    } else {
+                        logFail();
+                        status = FAIL;
+                    }
+                }
+                next();
+            }
+        }
+    }
+
+    private class RequestBindTest extends InteractiveTestCase {
+        int mRetries = 5;
+
+        @Override
+        View inflate(ViewGroup parent) {
+            return createAutoItem(parent, R.string.nls_unsnooze);
+
+        }
+
+        @Override
+        void test() {
+            if (status == READY) {
+                MockConditionProvider.requestRebind(MockConditionProvider.COMPONENT_NAME);
+                status = RETEST;
+            } else {
+                if (MockConditionProvider.getInstance().isConnected()
+                        && MockConditionProvider.getInstance().isBound()) {
+                    status = PASS;
+                    next();
+                } else {
+                    if (--mRetries > 0) {
+                        status = RETEST;
+                        next();
+                    } else {
+                        logFail();
+                        status = FAIL;
+                    }
+                }
+            }
+        }
+    }
+
+
     private class CreateAutomaticZenRuleTest extends InteractiveTestCase {
         private String id = null;
 
@@ -516,6 +640,11 @@
             MockConditionProvider.resetData(mContext);
             delay();
         }
+
+        @Override
+        protected Intent getIntent() {
+            return new Intent(Settings.ACTION_NOTIFICATION_POLICY_ACCESS_SETTINGS);
+        }
     }
 
     private class ServiceStoppedTest extends InteractiveTestCase {
@@ -570,21 +699,4 @@
     protected View createSettingsItem(ViewGroup parent, int messageId) {
         return createUserItem(parent, R.string.cp_start_settings, messageId);
     }
-
-    public void launchSettings() {
-        startActivity(new Intent(Settings.ACTION_NOTIFICATION_POLICY_ACCESS_SETTINGS));
-    }
-
-    public void actionPressed(View v) {
-        Object tag = v.getTag();
-        if (tag instanceof Integer) {
-            int id = ((Integer) tag).intValue();
-            if (id == R.string.cp_start_settings) {
-                launchSettings();
-            } else if (id == R.string.attention_ready) {
-                mCurrentTest.status = READY;
-                next();
-            }
-        }
-    }
 }
diff --git a/apps/CtsVerifier/src/com/android/cts/verifier/notifications/InteractiveVerifierActivity.java b/apps/CtsVerifier/src/com/android/cts/verifier/notifications/InteractiveVerifierActivity.java
index 7d5de8c..0ffd77d 100644
--- a/apps/CtsVerifier/src/com/android/cts/verifier/notifications/InteractiveVerifierActivity.java
+++ b/apps/CtsVerifier/src/com/android/cts/verifier/notifications/InteractiveVerifierActivity.java
@@ -16,6 +16,8 @@
 
 package com.android.cts.verifier.notifications;
 
+import static android.provider.Settings.ACTION_NOTIFICATION_LISTENER_SETTINGS;
+
 import android.app.NotificationManager;
 import android.app.PendingIntent;
 import android.app.Service;
@@ -65,8 +67,6 @@
 
     // TODO remove these once b/10023397 is fixed
     public static final String ENABLED_NOTIFICATION_LISTENERS = "enabled_notification_listeners";
-    public static final String NOTIFICATION_LISTENER_SETTINGS =
-            "android.settings.ACTION_NOTIFICATION_LISTENER_SETTINGS";
 
     protected InteractiveTestCase mCurrentTest;
     protected PackageManager mPackageManager;
@@ -133,6 +133,12 @@
             Log.e(TAG, "failed " + this.getClass().getSimpleName() +
                     ((message == null) ? "" : ": " + message), e);
         }
+
+        // If this test contains a button that launches another activity, override this
+        // method to provide the intent to launch.
+        protected Intent getIntent() {
+            return null;
+        }
     }
 
     abstract int getTitleResource();
@@ -345,16 +351,12 @@
 
     // UI callbacks
 
-    public void launchSettings() {
-        startActivity(new Intent(NOTIFICATION_LISTENER_SETTINGS));
-    }
-
     public void actionPressed(View v) {
         Object tag = v.getTag();
         if (tag instanceof Integer) {
             int id = ((Integer) tag).intValue();
-            if (id == R.string.nls_start_settings) {
-                launchSettings();
+            if (mCurrentTest != null && mCurrentTest.getIntent() != null) {
+                startActivity(mCurrentTest.getIntent());
             } else if (id == R.string.attention_ready) {
                 if (mCurrentTest != null) {
                     mCurrentTest.status = READY;
@@ -452,7 +454,7 @@
         @Override
         void test() {
             mNm.cancelAll();
-            Intent settings = new Intent(NOTIFICATION_LISTENER_SETTINGS);
+            Intent settings = new Intent(ACTION_NOTIFICATION_LISTENER_SETTINGS);
             if (settings.resolveActivity(mPackageManager) == null) {
                 logFail("no settings activity");
                 status = FAIL;
@@ -468,10 +470,57 @@
             }
         }
 
+        @Override
         void tearDown() {
             // wait for the service to start
             delay();
         }
+
+        @Override
+        protected Intent getIntent() {
+            return new Intent(ACTION_NOTIFICATION_LISTENER_SETTINGS);
+        }
+    }
+
+    protected class CannotBeEnabledTest extends InteractiveTestCase {
+        @Override
+        View inflate(ViewGroup parent) {
+            return createNlsSettingsItem(parent, R.string.nls_cannot_enable_service);
+        }
+
+        @Override
+        boolean autoStart() {
+            return true;
+        }
+
+        @Override
+        void test() {
+            mNm.cancelAll();
+            Intent settings = new Intent(ACTION_NOTIFICATION_LISTENER_SETTINGS);
+            if (settings.resolveActivity(mPackageManager) == null) {
+                logFail("no settings activity");
+                status = FAIL;
+            } else {
+                String listeners = Secure.getString(getContentResolver(),
+                        ENABLED_NOTIFICATION_LISTENERS);
+                if (listeners != null && listeners.contains(LISTENER_PATH)) {
+                    status = FAIL;
+                } else {
+                    status = PASS;
+                }
+                next();
+            }
+        }
+
+        void tearDown() {
+            // wait for the service to start
+            delay();
+        }
+
+        @Override
+        protected Intent getIntent() {
+            return new Intent(ACTION_NOTIFICATION_LISTENER_SETTINGS);
+        }
     }
 
     protected class ServiceStartedTest extends InteractiveTestCase {
@@ -482,7 +531,7 @@
 
         @Override
         void test() {
-            if (MockListener.getInstance() != null) {
+            if (MockListener.getInstance() != null && MockListener.getInstance().isConnected) {
                 status = PASS;
                 next();
             } else {
diff --git a/apps/CtsVerifier/src/com/android/cts/verifier/notifications/MockConditionProvider.java b/apps/CtsVerifier/src/com/android/cts/verifier/notifications/MockConditionProvider.java
index 489797b..f32460f 100644
--- a/apps/CtsVerifier/src/com/android/cts/verifier/notifications/MockConditionProvider.java
+++ b/apps/CtsVerifier/src/com/android/cts/verifier/notifications/MockConditionProvider.java
@@ -19,6 +19,7 @@
 import android.app.Activity;
 import android.app.AutomaticZenRule;
 import android.content.BroadcastReceiver;
+import android.content.ComponentName;
 import android.content.Context;
 import android.content.Intent;
 import android.content.IntentFilter;
@@ -35,7 +36,8 @@
 public class MockConditionProvider extends ConditionProviderService {
     static final String TAG = "MockConditionProvider";
 
-    static final String PACKAGE_NAME = "com.android.cts.verifier.notifications";
+    public static final ComponentName COMPONENT_NAME =
+            new ComponentName("com.android.cts.verifier", MockConditionProvider.class.getName());
     static final String PATH = "mock_cp";
     static final String QUERY = "query_item";
 
@@ -56,6 +58,7 @@
     private ArrayList<Uri> mSubscriptions = new ArrayList<>();
     private boolean mConnected = false;
     private BroadcastReceiver mReceiver;
+    private static MockConditionProvider sConditionProviderInstance = null;
 
     @Override
     public void onCreate() {
@@ -103,6 +106,20 @@
         unregisterReceiver(mReceiver);
         mReceiver = null;
         Log.d(TAG, "destroyed");
+        sConditionProviderInstance = null;
+    }
+
+    public void requestUnbindService() {
+        sConditionProviderInstance = null;
+        super.requestUnbind();
+    }
+
+    public boolean isConnected() {
+        return mConnected;
+    }
+
+    public static MockConditionProvider getInstance() {
+        return sConditionProviderInstance;
     }
 
     public void resetData() {
@@ -141,6 +158,7 @@
     public void onConnected() {
         Log.d(TAG, "connected");
         mConnected = true;
+        sConditionProviderInstance = this;
     }
 
     @Override
diff --git a/apps/CtsVerifier/src/com/android/cts/verifier/notifications/MockListener.java b/apps/CtsVerifier/src/com/android/cts/verifier/notifications/MockListener.java
index 8b5acf8..5dfee12 100644
--- a/apps/CtsVerifier/src/com/android/cts/verifier/notifications/MockListener.java
+++ b/apps/CtsVerifier/src/com/android/cts/verifier/notifications/MockListener.java
@@ -18,6 +18,7 @@
 import android.app.Notification;
 import android.content.ComponentName;
 import android.service.notification.NotificationListenerService;
+import android.service.notification.NotificationStats;
 import android.service.notification.StatusBarNotification;
 import android.util.ArrayMap;
 import android.util.Log;
@@ -47,6 +48,7 @@
     public static final String JSON_AMBIENT = "ambient";
     public static final String JSON_MATCHES_ZEN_FILTER = "matches_zen_filter";
     public static final String JSON_REASON = "reason";
+    public static final String JSON_STATS = "stats";
 
     ArrayList<String> mPosted = new ArrayList<String>();
     ArrayMap<String, JSONObject> mNotifications = new ArrayMap<>();
@@ -174,13 +176,14 @@
 
     @Override
     public void onNotificationRemoved(StatusBarNotification sbn, RankingMap rankingMap,
-            int reason) {
+            NotificationStats stats, int reason) {
         Log.d(TAG, "removed: " + sbn.getTag() + " for reason " + reason);
         mRemoved.add(sbn.getTag());
         JSONObject removed = new JSONObject();
         try {
             removed.put(JSON_TAG, sbn.getTag());
             removed.put(JSON_REASON, reason);
+            removed.put(JSON_STATS, stats != null);
         } catch (JSONException e) {
             Log.e(TAG, "failed to pack up notification payload", e);
         }
@@ -189,5 +192,4 @@
         mRemovedReason.put(sbn.getTag(), removed);
         onNotificationRankingUpdate(rankingMap);
     }
-
 }
diff --git a/apps/CtsVerifier/src/com/android/cts/verifier/notifications/NotificationListenerVerifierActivity.java b/apps/CtsVerifier/src/com/android/cts/verifier/notifications/NotificationListenerVerifierActivity.java
index 062b8a9..90b1f7d 100644
--- a/apps/CtsVerifier/src/com/android/cts/verifier/notifications/NotificationListenerVerifierActivity.java
+++ b/apps/CtsVerifier/src/com/android/cts/verifier/notifications/NotificationListenerVerifierActivity.java
@@ -16,20 +16,30 @@
 
 package com.android.cts.verifier.notifications;
 
+import static android.app.NotificationManager.IMPORTANCE_LOW;
+import static android.app.NotificationManager.IMPORTANCE_NONE;
+import static android.provider.Settings.ACTION_NOTIFICATION_LISTENER_SETTINGS;
+import static android.provider.Settings.EXTRA_APP_PACKAGE;
+import static android.provider.Settings.EXTRA_CHANNEL_GROUP_ID;
+import static android.provider.Settings.EXTRA_CHANNEL_ID;
+
 import android.annotation.SuppressLint;
-import android.app.Activity;
+import android.app.ActivityManager;
 import android.app.Notification;
 import android.app.NotificationChannel;
-import android.app.NotificationManager;
-import android.graphics.Bitmap;
-import android.graphics.BitmapFactory;
+import android.app.NotificationChannelGroup;
+import android.content.Context;
+import android.content.Intent;
+import android.content.SharedPreferences;
 import android.os.Bundle;
+import android.provider.Settings;
 import android.provider.Settings.Secure;
 import android.service.notification.StatusBarNotification;
 import android.support.v4.app.NotificationCompat;
 import android.util.Log;
 import android.view.View;
 import android.view.ViewGroup;
+import android.widget.Button;
 
 import com.android.cts.verifier.R;
 
@@ -45,11 +55,13 @@
 import static com.android.cts.verifier.notifications.MockListener.*;
 
 import static junit.framework.Assert.assertNotNull;
+import static junit.framework.Assert.assertNull;
 
 public class NotificationListenerVerifierActivity extends InteractiveVerifierActivity
         implements Runnable {
     private static final String TAG = "NoListenerVerifier";
     private static final String NOTIFICATION_CHANNEL_ID = TAG;
+    protected static final String PREFS = "listener_prefs";
 
     private String mTag1;
     private String mTag2;
@@ -82,30 +94,40 @@
     @Override
     protected List<InteractiveTestCase> createTestItems() {
         List<InteractiveTestCase> tests = new ArrayList<>(17);
-        tests.add(new IsEnabledTest());
-        tests.add(new ServiceStartedTest());
-        tests.add(new NotificationReceivedTest());
-        tests.add(new DataIntactTest());
-        tests.add(new DismissOneTest());
-        tests.add(new DismissOneWithReasonTest());
-        tests.add(new DismissAllTest());
-        tests.add(new SnoozeNotificationForTimeTest());
-        tests.add(new SnoozeNotificationForTimeCancelTest());
-        tests.add(new GetSnoozedNotificationTest());
-        tests.add(new EnableHintsTest());
-        tests.add(new RequestUnbindTest());
-        tests.add(new RequestBindTest());
-        tests.add(new MessageBundleTest());
-        tests.add(new EnableHintsTest());
-        tests.add(new IsDisabledTest());
-        tests.add(new ServiceStoppedTest());
-        tests.add(new NotificationNotReceivedTest());
+        ActivityManager am = (ActivityManager) getSystemService(Context.ACTIVITY_SERVICE);
+        if (am.isLowRamDevice()) {
+            tests.add(new CannotBeEnabledTest());
+            tests.add(new ServiceStoppedTest());
+            tests.add(new NotificationNotReceivedTest());
+        } else {
+            tests.add(new IsEnabledTest());
+            tests.add(new ServiceStartedTest());
+            tests.add(new NotificationReceivedTest());
+            tests.add(new DataIntactTest());
+            tests.add(new DismissOneTest());
+            tests.add(new DismissOneWithReasonTest());
+            tests.add(new DismissOneWithStatsTest());
+            tests.add(new DismissAllTest());
+            tests.add(new SnoozeNotificationForTimeTest());
+            tests.add(new SnoozeNotificationForTimeCancelTest());
+            tests.add(new GetSnoozedNotificationTest());
+            tests.add(new EnableHintsTest());
+            tests.add(new ReceiveChannelBlockNoticeTest());
+            tests.add(new ReceiveGroupBlockNoticeTest());
+            tests.add(new RequestUnbindTest());
+            tests.add(new RequestBindTest());
+            tests.add(new MessageBundleTest());
+            tests.add(new EnableHintsTest());
+            tests.add(new IsDisabledTest());
+            tests.add(new ServiceStoppedTest());
+            tests.add(new NotificationNotReceivedTest());
+        }
         return tests;
     }
 
     private void createChannel() {
         NotificationChannel channel = new NotificationChannel(NOTIFICATION_CHANNEL_ID,
-                NOTIFICATION_CHANNEL_ID, NotificationManager.IMPORTANCE_LOW);
+                NOTIFICATION_CHANNEL_ID, IMPORTANCE_LOW);
         mNm.createNotificationChannel(channel);
     }
 
@@ -206,6 +228,161 @@
         }
     }
 
+    /**
+     * Creates a notification channel. Sends the user to settings to block the channel. Waits
+     * to receive the broadcast that the channel was blocked, and confirms that the broadcast
+     * contains the correct extras.
+     */
+    protected class ReceiveChannelBlockNoticeTest extends InteractiveTestCase {
+        private String mChannelId;
+        private int mRetries = 2;
+        private View mView;
+        @Override
+        View inflate(ViewGroup parent) {
+            mView = createNlsSettingsItem(parent, R.string.nls_block_channel);
+            Button button = mView.findViewById(R.id.nls_action_button);
+            button.setEnabled(false);
+            return mView;
+        }
+
+        @Override
+        void setUp() {
+            mChannelId = UUID.randomUUID().toString();
+            NotificationChannel channel = new NotificationChannel(
+                    mChannelId, "ReceiveChannelBlockNoticeTest", IMPORTANCE_LOW);
+            mNm.createNotificationChannel(channel);
+            status = READY;
+            Button button = mView.findViewById(R.id.nls_action_button);
+            button.setEnabled(true);
+        }
+
+        @Override
+        boolean autoStart() {
+            return true;
+        }
+
+        @Override
+        void test() {
+            NotificationChannel channel = mNm.getNotificationChannel(mChannelId);
+            SharedPreferences prefs = mContext.getSharedPreferences(
+                    NotificationListenerVerifierActivity.PREFS, Context.MODE_PRIVATE);
+
+            if (channel.getImportance() == IMPORTANCE_NONE) {
+                if (prefs.contains(mChannelId) && prefs.getBoolean(mChannelId, false)) {
+                    status = PASS;
+                } else {
+                    if (mRetries > 0) {
+                        mRetries--;
+                        status = RETEST;
+                    } else {
+                        status = FAIL;
+                    }
+                }
+            } else {
+                // user hasn't jumped to settings to block the channel yet
+                status = WAIT_FOR_USER;
+            }
+
+            next();
+        }
+
+        void tearDown() {
+            MockListener.getInstance().resetData();
+            mNm.deleteNotificationChannel(mChannelId);
+            SharedPreferences prefs = mContext.getSharedPreferences(
+                    NotificationListenerVerifierActivity.PREFS, Context.MODE_PRIVATE);
+            SharedPreferences.Editor editor = prefs.edit();
+            editor.remove(mChannelId);
+        }
+
+        @Override
+        protected Intent getIntent() {
+         return new Intent(Settings.ACTION_CHANNEL_NOTIFICATION_SETTINGS)
+                 .putExtra(EXTRA_APP_PACKAGE, mContext.getPackageName())
+                 .putExtra(EXTRA_CHANNEL_ID, mChannelId);
+        }
+    }
+
+    /**
+     * Creates a notification channel group. Sends the user to settings to block the group. Waits
+     * to receive the broadcast that the group was blocked, and confirms that the broadcast contains
+     * the correct extras.
+     */
+    protected class ReceiveGroupBlockNoticeTest extends InteractiveTestCase {
+        private String mGroupId;
+        private int mRetries = 2;
+        private View mView;
+        @Override
+        View inflate(ViewGroup parent) {
+            mView = createNlsSettingsItem(parent, R.string.nls_block_group);
+            Button button = mView.findViewById(R.id.nls_action_button);
+            button.setEnabled(false);
+            return mView;
+        }
+
+        @Override
+        void setUp() {
+            mGroupId = UUID.randomUUID().toString();
+            NotificationChannelGroup group
+                    = new NotificationChannelGroup(mGroupId, "ReceiveChannelGroupBlockNoticeTest");
+            mNm.createNotificationChannelGroup(group);
+            NotificationChannel channel = new NotificationChannel(
+                    mGroupId, "ReceiveChannelBlockNoticeTest", IMPORTANCE_LOW);
+            channel.setGroup(mGroupId);
+            mNm.createNotificationChannel(channel);
+            status = READY;
+            Button button = mView.findViewById(R.id.nls_action_button);
+            button.setEnabled(true);
+        }
+
+        @Override
+        boolean autoStart() {
+            return true;
+        }
+
+        @Override
+        void test() {
+            NotificationChannelGroup group = mNm.getNotificationChannelGroup(mGroupId);
+            SharedPreferences prefs = mContext.getSharedPreferences(
+                    NotificationListenerVerifierActivity.PREFS, Context.MODE_PRIVATE);
+
+            if (group.isBlocked()) {
+                if (prefs.contains(mGroupId) && prefs.getBoolean(mGroupId, false)) {
+                    status = PASS;
+                } else {
+                    if (mRetries > 0) {
+                        mRetries--;
+                        status = RETEST;
+                    } else {
+                        status = FAIL;
+                    }
+                }
+            } else {
+                // user hasn't jumped to settings to block the channel yet
+                status = WAIT_FOR_USER;
+            }
+
+            next();
+        }
+
+        void tearDown() {
+            MockListener.getInstance().resetData();
+            mNm.deleteNotificationChannelGroup(mGroupId);
+            SharedPreferences prefs = mContext.getSharedPreferences(
+                    NotificationListenerVerifierActivity.PREFS, Context.MODE_PRIVATE);
+            SharedPreferences.Editor editor = prefs.edit();
+            editor.remove(mGroupId);
+        }
+
+        @Override
+        protected Intent getIntent() {
+            return new Intent(Settings.ACTION_CHANNEL_GROUP_NOTIFICATION_SETTINGS)
+                    .addFlags(Intent.FLAG_ACTIVITY_CLEAR_TASK)
+                    .putExtra(EXTRA_APP_PACKAGE, mContext.getPackageName())
+                    .putExtra(EXTRA_CHANNEL_GROUP_ID, mGroupId);
+        }
+    }
+
     private class DataIntactTest extends InteractiveTestCase {
         @Override
         View inflate(ViewGroup parent) {
@@ -265,9 +442,6 @@
                                 "data integrity test: notification ID (%d, %d)");
                         pass &= checkEquals(mWhen3, payload.getLong(JSON_WHEN),
                                 "data integrity test: notification when (%d, %d)");
-                    } else {
-                        pass = false;
-                        logFail("unexpected notification tag: " + tag);
                     }
                 } catch (JSONException e) {
                     pass = false;
@@ -275,7 +449,7 @@
                 }
             }
 
-            pass &= found.size() == 3;
+            pass &= found.size() >= 3;
             status = pass ? PASS : FAIL;
         }
 
@@ -389,6 +563,61 @@
         }
     }
 
+    private class DismissOneWithStatsTest extends InteractiveTestCase {
+        int mRetries = 3;
+
+        @Override
+        View inflate(ViewGroup parent) {
+            return createAutoItem(parent, R.string.nls_clear_one_stats);
+        }
+
+        @Override
+        void setUp() {
+            createChannel();
+            sendNotifications();
+            status = READY;
+        }
+
+        @Override
+        void test() {
+            if (status == READY) {
+                MockListener.getInstance().cancelNotification(
+                        MockListener.getInstance().getKeyForTag(mTag1));
+                status = RETEST;
+            } else {
+                List<JSONObject> result =
+                        new ArrayList<>(MockListener.getInstance().mRemovedReason.values());
+                boolean pass = true;
+                for (JSONObject payload : result) {
+                    try {
+                        pass &= (payload.getBoolean(JSON_STATS) == false);
+                    } catch (JSONException e) {
+                        e.printStackTrace();
+                        pass = false;
+                    }
+                }
+                if (pass) {
+                    status = PASS;
+                } else {
+                    if (--mRetries > 0) {
+                        sleep(100);
+                        status = RETEST;
+                    } else {
+                        logFail("Notification listener got populated stats object.");
+                        status = FAIL;
+                    }
+                }
+            }
+        }
+
+        @Override
+        void tearDown() {
+            mNm.cancelAll();
+            deleteChannel();
+            MockListener.getInstance().resetData();
+        }
+    }
+
     private class DismissAllTest extends InteractiveTestCase {
         @Override
         View inflate(ViewGroup parent) {
@@ -458,6 +687,7 @@
     }
 
     private class ServiceStoppedTest extends InteractiveTestCase {
+        int mRetries = 3;
         @Override
         View inflate(ViewGroup parent) {
             return createAutoItem(parent, R.string.nls_service_stopped);
@@ -465,12 +695,23 @@
 
         @Override
         void test() {
-            if (mNm.getEffectsSuppressor() == null) {
+            if (mNm.getEffectsSuppressor() == null && (MockListener.getInstance() == null
+                    || !MockListener.getInstance().isConnected)) {
                 status = PASS;
             } else {
-                status = FAIL;
+                if (--mRetries > 0) {
+                    sleep(100);
+                    status = RETEST;
+                } else {
+                    status = FAIL;
+                }
             }
         }
+
+        @Override
+        protected Intent getIntent() {
+            return new Intent(ACTION_NOTIFICATION_LISTENER_SETTINGS);
+        }
     }
 
     private class NotificationNotReceivedTest extends InteractiveTestCase {
@@ -489,12 +730,16 @@
 
         @Override
         void test() {
-            List<String> result = new ArrayList<>(MockListener.getInstance().mPosted);
-            if (result.size() == 0) {
+            if (MockListener.getInstance() == null) {
                 status = PASS;
             } else {
-                logFail();
-                status = FAIL;
+                List<String> result = new ArrayList<>(MockListener.getInstance().mPosted);
+                if (result.size() == 0) {
+                    status = PASS;
+                } else {
+                    logFail();
+                    status = FAIL;
+                }
             }
             next();
         }
@@ -503,7 +748,9 @@
         void tearDown() {
             mNm.cancelAll();
             deleteChannel();
-            MockListener.getInstance().resetData();
+            if (MockListener.getInstance() != null) {
+                MockListener.getInstance().resetData();
+            }
         }
     }
 
diff --git a/apps/PermissionApp/AndroidManifest.xml b/apps/PermissionApp/AndroidManifest.xml
index 82e3617..f880933 100644
--- a/apps/PermissionApp/AndroidManifest.xml
+++ b/apps/PermissionApp/AndroidManifest.xml
@@ -21,6 +21,7 @@
     <uses-sdk android:minSdkVersion="23"/>
 
     <uses-permission android:name="android.permission.READ_CONTACTS"/>
+    <uses-permission android:name="android.permission.INTERACT_ACROSS_USERS"/>
 
     <application android:label="CtsPermissionApp"
             android:icon="@drawable/ic_permissionapp">
diff --git a/build/device_info_package.mk b/build/device_info_package.mk
index 5c290b0..7922f28 100644
--- a/build/device_info_package.mk
+++ b/build/device_info_package.mk
@@ -19,7 +19,9 @@
 DEVICE_INFO_PACKAGE := com.android.compatibility.common.deviceinfo
 DEVICE_INFO_INSTRUMENT := android.support.test.runner.AndroidJUnitRunner
 DEVICE_INFO_USES_LIBRARY := android.test.runner
-DEVICE_INFO_PERMISSIONS += android.permission.WRITE_EXTERNAL_STORAGE
+DEVICE_INFO_PERMISSIONS += \
+  android.permission.READ_PHONE_STATE \
+  android.permission.WRITE_EXTERNAL_STORAGE
 DEVICE_INFO_ACTIVITIES += \
   $(DEVICE_INFO_PACKAGE).ConfigurationDeviceInfo \
   $(DEVICE_INFO_PACKAGE).CpuDeviceInfo \
@@ -42,7 +44,7 @@
 endif
 
 ifeq ($(DEVICE_INFO_TARGET_SDK),)
-DEVICE_INFO_TARGET_SDK := 8
+DEVICE_INFO_TARGET_SDK := 17
 endif
 
 # Add the base device info
diff --git a/common/device-side/device-info/Android.mk b/common/device-side/device-info/Android.mk
index bf6e122..4ab1ac6 100644
--- a/common/device-side/device-info/Android.mk
+++ b/common/device-side/device-info/Android.mk
@@ -23,8 +23,9 @@
 LOCAL_STATIC_JAVA_LIBRARIES := \
     compatibility-device-util \
     android-support-test \
-    junit \
-    legacy-android-test
+    junit
+
+LOCAL_JAVA_LIBRARIES := android.test.base
 
 LOCAL_MODULE := compatibility-device-info
 
diff --git a/common/device-side/device-info/src/com/android/compatibility/common/deviceinfo/GenericDeviceInfo.java b/common/device-side/device-info/src/com/android/compatibility/common/deviceinfo/GenericDeviceInfo.java
index cb4324c..c81f8fe 100644
--- a/common/device-side/device-info/src/com/android/compatibility/common/deviceinfo/GenericDeviceInfo.java
+++ b/common/device-side/device-info/src/com/android/compatibility/common/deviceinfo/GenericDeviceInfo.java
@@ -73,7 +73,7 @@
         store.addResult(BUILD_FINGERPRINT, Build.FINGERPRINT);
         store.addResult(BUILD_ABI, Build.CPU_ABI);
         store.addResult(BUILD_ABI2, Build.CPU_ABI2);
-        store.addResult(BUILD_SERIAL, Build.SERIAL);
+        store.addResult(BUILD_SERIAL, Build.getSerial());
         store.addResult(BUILD_VERSION_RELEASE, Build.VERSION.RELEASE);
         store.addResult(BUILD_VERSION_SDK, Build.VERSION.SDK);
         store.addResult(BUILD_REFERENCE_FINGERPRINT,
diff --git a/common/device-side/device-info/tests/Android.mk b/common/device-side/device-info/tests/Android.mk
index d40614c..db4f250 100644
--- a/common/device-side/device-info/tests/Android.mk
+++ b/common/device-side/device-info/tests/Android.mk
@@ -18,9 +18,9 @@
 
 LOCAL_SRC_FILES := $(call all-java-files-under, src)
 
-LOCAL_STATIC_JAVA_LIBRARIES := compatibility-device-info junit legacy-android-test
+LOCAL_STATIC_JAVA_LIBRARIES := compatibility-device-info junit
 
-LOCAL_JAVA_LIBRARIES := android.test.runner
+LOCAL_JAVA_LIBRARIES := android.test.runner android.test.base
 
 LOCAL_MODULE_TAGS := optional
 
diff --git a/common/device-side/nativetesthelper/Android.mk b/common/device-side/nativetesthelper/Android.mk
new file mode 100644
index 0000000..19f584d
--- /dev/null
+++ b/common/device-side/nativetesthelper/Android.mk
@@ -0,0 +1,25 @@
+# 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_SRC_FILES := $(call all-java-files-under, src)
+LOCAL_STATIC_JAVA_LIBRARIES := compatibility-common-util-devicesidelib
+LOCAL_MODULE := nativetesthelper
+
+include $(BUILD_STATIC_JAVA_LIBRARY)
+
+include $(call all-makefiles-under,$(LOCAL_PATH))
diff --git a/common/device-side/nativetesthelper/jni/Android.mk b/common/device-side/nativetesthelper/jni/Android.mk
new file mode 100644
index 0000000..f970e8c
--- /dev/null
+++ b/common/device-side/nativetesthelper/jni/Android.mk
@@ -0,0 +1,36 @@
+# 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.
+
+#
+# This is the shared library included by the JNI test app.
+#
+
+LOCAL_PATH:= $(call my-dir)
+
+include $(CLEAR_VARS)
+
+LOCAL_MODULE := libnativetesthelper_jni
+
+LOCAL_SRC_FILES := \
+        gtest_wrapper.cpp
+
+LOCAL_SHARED_LIBRARIES := libnativehelper_compat_libc++
+LOCAL_WHOLE_STATIC_LIBRARIES := libgtest_ndk_c++
+LOCAL_EXPORT_STATIC_LIBRARY_HEADERS := libgtest_ndk_c++
+LOCAL_SDK_VERSION := current
+LOCAL_NDK_STL_VARIANT := c++_static
+LOCAL_CFLAGS := -Wall -Werror
+LOCAL_MULTILIB := both
+
+include $(BUILD_STATIC_LIBRARY)
diff --git a/common/device-side/nativetesthelper/jni/gtest_wrapper.cpp b/common/device-side/nativetesthelper/jni/gtest_wrapper.cpp
new file mode 100644
index 0000000..1f91b3a
--- /dev/null
+++ b/common/device-side/nativetesthelper/jni/gtest_wrapper.cpp
@@ -0,0 +1,185 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <jni.h>
+#include <nativehelper/ScopedLocalRef.h>
+#include <gtest/gtest.h>
+
+static struct {
+    jclass clazz;
+
+    /** static methods **/
+    jmethodID createTestDescription;
+
+    /** methods **/
+    jmethodID addChild;
+} gDescription;
+
+static struct {
+    jclass clazz;
+
+    jmethodID fireTestStarted;
+    jmethodID fireTestIgnored;
+    jmethodID fireTestFailure;
+    jmethodID fireTestFinished;
+
+} gRunNotifier;
+
+static struct {
+    jclass clazz;
+    jmethodID ctor;
+} gAssertionFailure;
+
+static struct {
+    jclass clazz;
+    jmethodID ctor;
+} gFailure;
+
+jobject gEmptyAnnotationsArray;
+
+static jobject createTestDescription(JNIEnv* env, const char* className, const char* testName) {
+    ScopedLocalRef<jstring> jClassName(env, env->NewStringUTF(className));
+    ScopedLocalRef<jstring> jTestName(env, env->NewStringUTF(testName));
+    return env->CallStaticObjectMethod(gDescription.clazz, gDescription.createTestDescription,
+            jClassName.get(), jTestName.get(), gEmptyAnnotationsArray);
+}
+
+static void addChild(JNIEnv* env, jobject description, jobject childDescription) {
+    env->CallVoidMethod(description, gDescription.addChild, childDescription);
+}
+
+
+class JUnitNotifyingListener : public ::testing::EmptyTestEventListener {
+public:
+
+    JUnitNotifyingListener(JNIEnv* env, jobject runNotifier)
+            : mEnv(env)
+            , mRunNotifier(runNotifier)
+            , mCurrentTestDescription{env, nullptr}
+    {}
+    virtual ~JUnitNotifyingListener() {}
+
+    virtual void OnTestStart(const testing::TestInfo &testInfo) override {
+        mCurrentTestDescription.reset(
+                createTestDescription(mEnv, testInfo.test_case_name(), testInfo.name()));
+        notify(gRunNotifier.fireTestStarted);
+    }
+
+    virtual void OnTestPartResult(const testing::TestPartResult &testPartResult) override {
+        if (!testPartResult.passed()) {
+            char message[1024];
+            snprintf(message, 1024, "%s:%d\n%s", testPartResult.file_name(), testPartResult.line_number(),
+                    testPartResult.message());
+            ScopedLocalRef<jstring> jmessage(mEnv, mEnv->NewStringUTF(message));
+            ScopedLocalRef<jobject> jthrowable(mEnv, mEnv->NewObject(gAssertionFailure.clazz,
+                    gAssertionFailure.ctor, jmessage.get()));
+            ScopedLocalRef<jobject> jfailure(mEnv, mEnv->NewObject(gFailure.clazz,
+                    gFailure.ctor, mCurrentTestDescription.get(), jthrowable.get()));
+            mEnv->CallVoidMethod(mRunNotifier, gRunNotifier.fireTestFailure, jfailure.get());
+        }
+    }
+
+    virtual void OnTestEnd(const testing::TestInfo&) override {
+        notify(gRunNotifier.fireTestFinished);
+        mCurrentTestDescription.reset();
+    }
+
+    virtual void OnTestProgramEnd(const testing::UnitTest& unitTest) override {
+        // Invoke the notifiers for all the disabled tests
+        for (int testCaseIndex = 0; testCaseIndex < unitTest.total_test_case_count(); testCaseIndex++) {
+            auto testCase = unitTest.GetTestCase(testCaseIndex);
+            for (int testIndex = 0; testIndex < testCase->total_test_count(); testIndex++) {
+                auto testInfo = testCase->GetTestInfo(testIndex);
+                if (!testInfo->should_run()) {
+                    mCurrentTestDescription.reset(
+                            createTestDescription(mEnv, testCase->name(), testInfo->name()));
+                    notify(gRunNotifier.fireTestIgnored);
+                    mCurrentTestDescription.reset();
+                }
+            }
+        }
+    }
+
+private:
+    void notify(jmethodID method) {
+        mEnv->CallVoidMethod(mRunNotifier, method, mCurrentTestDescription.get());
+    }
+
+    JNIEnv* mEnv;
+    jobject mRunNotifier;
+    ScopedLocalRef<jobject> mCurrentTestDescription;
+};
+
+extern "C"
+JNIEXPORT void JNICALL
+Java_com_android_gtestrunner_GtestRunner_nInitialize(JNIEnv *env, jclass, jobject suite) {
+    // Initialize gtest, removing the default result printer
+    int argc = 1;
+    const char* argv[] = { "gtest_wrapper" };
+    ::testing::InitGoogleTest(&argc, (char**) argv);
+
+    auto& listeners = ::testing::UnitTest::GetInstance()->listeners();
+    delete listeners.Release(listeners.default_result_printer());
+
+    gDescription.clazz = (jclass) env->NewGlobalRef(env->FindClass("org/junit/runner/Description"));
+    gDescription.createTestDescription = env->GetStaticMethodID(gDescription.clazz, "createTestDescription",
+            "(Ljava/lang/String;Ljava/lang/String;[Ljava/lang/annotation/Annotation;)Lorg/junit/runner/Description;");
+    gDescription.addChild = env->GetMethodID(gDescription.clazz, "addChild",
+            "(Lorg/junit/runner/Description;)V");
+
+    jclass annotations = env->FindClass("java/lang/annotation/Annotation");
+    gEmptyAnnotationsArray = env->NewGlobalRef(env->NewObjectArray(0, annotations, nullptr));
+
+    gAssertionFailure.clazz = (jclass) env->NewGlobalRef(env->FindClass("java/lang/AssertionError"));
+    gAssertionFailure.ctor = env->GetMethodID(gAssertionFailure.clazz, "<init>", "(Ljava/lang/Object;)V");
+
+    gFailure.clazz = (jclass) env->NewGlobalRef(env->FindClass("org/junit/runner/notification/Failure"));
+    gFailure.ctor = env->GetMethodID(gFailure.clazz, "<init>",
+            "(Lorg/junit/runner/Description;Ljava/lang/Throwable;)V");
+
+    gRunNotifier.clazz = (jclass) env->NewGlobalRef(
+            env->FindClass("org/junit/runner/notification/RunNotifier"));
+    gRunNotifier.fireTestStarted = env->GetMethodID(gRunNotifier.clazz, "fireTestStarted",
+            "(Lorg/junit/runner/Description;)V");
+    gRunNotifier.fireTestIgnored = env->GetMethodID(gRunNotifier.clazz, "fireTestIgnored",
+            "(Lorg/junit/runner/Description;)V");
+    gRunNotifier.fireTestFinished = env->GetMethodID(gRunNotifier.clazz, "fireTestFinished",
+            "(Lorg/junit/runner/Description;)V");
+    gRunNotifier.fireTestFailure = env->GetMethodID(gRunNotifier.clazz, "fireTestFailure",
+            "(Lorg/junit/runner/notification/Failure;)V");
+
+    auto unitTest = ::testing::UnitTest::GetInstance();
+    for (int testCaseIndex = 0; testCaseIndex < unitTest->total_test_case_count(); testCaseIndex++) {
+        auto testCase = unitTest->GetTestCase(testCaseIndex);
+        for (int testIndex = 0; testIndex < testCase->total_test_count(); testIndex++) {
+            auto testInfo = testCase->GetTestInfo(testIndex);
+            ScopedLocalRef<jobject> testDescription(env,
+                    createTestDescription(env, testCase->name(), testInfo->name()));
+            addChild(env, suite, testDescription.get());
+        }
+    }
+}
+
+extern "C"
+JNIEXPORT jboolean JNICALL
+Java_com_android_gtestrunner_GtestRunner_nRun(JNIEnv *env, jclass, jobject notifier) {
+    auto& listeners = ::testing::UnitTest::GetInstance()->listeners();
+    JUnitNotifyingListener junitListener{env, notifier};
+    listeners.Append(&junitListener);
+    int success = RUN_ALL_TESTS();
+    listeners.Release(&junitListener);
+    return success == 0;
+}
diff --git a/common/device-side/nativetesthelper/src/com/android/gtestrunner/GtestRunner.java b/common/device-side/nativetesthelper/src/com/android/gtestrunner/GtestRunner.java
new file mode 100644
index 0000000..222a1a0
--- /dev/null
+++ b/common/device-side/nativetesthelper/src/com/android/gtestrunner/GtestRunner.java
@@ -0,0 +1,59 @@
+/*
+ * 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.gtestrunner;
+
+import org.junit.runner.Description;
+import org.junit.runner.Runner;
+import org.junit.runner.notification.RunNotifier;
+
+public class GtestRunner extends Runner {
+    private static boolean sOnceFlag = false;
+
+    private Class mTargetClass;
+    private Description mDescription;
+
+    public GtestRunner(Class testClass) {
+        synchronized (GtestRunner.class) {
+            if (sOnceFlag) {
+                throw new IllegalStateException("Error multiple GtestRunners defined");
+            }
+            sOnceFlag = true;
+        }
+
+        mTargetClass = testClass;
+        TargetLibrary library = (TargetLibrary) testClass.getAnnotation(TargetLibrary.class);
+        if (library == null) {
+            throw new IllegalStateException("Missing required @TargetLibrary annotation");
+        }
+        System.loadLibrary(library.value());
+        mDescription = Description.createSuiteDescription(testClass);
+        nInitialize(mDescription);
+    }
+
+    @Override
+    public Description getDescription() {
+        return mDescription;
+    }
+
+    @Override
+    public void run(RunNotifier notifier) {
+        nRun(notifier);
+    }
+
+    private static native void nInitialize(Description description);
+    private static native void nRun(RunNotifier notifier);
+}
diff --git a/common/device-side/nativetesthelper/src/com/android/gtestrunner/TargetLibrary.java b/common/device-side/nativetesthelper/src/com/android/gtestrunner/TargetLibrary.java
new file mode 100644
index 0000000..23bc53d
--- /dev/null
+++ b/common/device-side/nativetesthelper/src/com/android/gtestrunner/TargetLibrary.java
@@ -0,0 +1,30 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.gtestrunner;
+
+import java.lang.annotation.ElementType;
+import java.lang.annotation.Inherited;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.lang.annotation.Target;
+
+@Retention(RetentionPolicy.RUNTIME)
+@Target(ElementType.TYPE)
+@Inherited
+public @interface TargetLibrary {
+    String value();
+}
diff --git a/common/device-side/test-app/AndroidManifest.xml b/common/device-side/test-app/AndroidManifest.xml
index 9c857f0..883b439 100755
--- a/common/device-side/test-app/AndroidManifest.xml
+++ b/common/device-side/test-app/AndroidManifest.xml
@@ -17,7 +17,10 @@
 
 <manifest xmlns:android="http://schemas.android.com/apk/res/android"
     package="com.android.compatibility.common">
+
     <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
+    <uses-permission android:name="android.permission.READ_PHONE_STATE" />
+
     <application>
         <uses-library android:name="android.test.runner" />
         <activity android:name="com.android.compatibility.common.deviceinfo.TestDeviceInfo" />
diff --git a/common/device-side/util/Android.mk b/common/device-side/util/Android.mk
index 26a37ec..fbf0ee4 100644
--- a/common/device-side/util/Android.mk
+++ b/common/device-side/util/Android.mk
@@ -23,10 +23,9 @@
     compatibility-common-util-devicesidelib \
     android-support-test \
     ub-uiautomator \
-    mockito-target-minus-junit4 \
-    legacy-android-test
+    mockito-target-minus-junit4
 
-LOCAL_JAVA_LIBRARIES := android.test.runner
+LOCAL_JAVA_LIBRARIES := android.test.runner android.test.base.stubs
 
 LOCAL_MODULE_TAGS := optional
 
diff --git a/common/host-side/manifest-generator/tests/src/com/android/compatibility/common/generator/ManifestGeneratorTest.java b/common/host-side/manifest-generator/tests/src/com/android/compatibility/common/generator/ManifestGeneratorTest.java
index aed5926..b68b34c 100644
--- a/common/host-side/manifest-generator/tests/src/com/android/compatibility/common/generator/ManifestGeneratorTest.java
+++ b/common/host-side/manifest-generator/tests/src/com/android/compatibility/common/generator/ManifestGeneratorTest.java
@@ -30,11 +30,11 @@
     private static final String PACKAGE = "test.package";
     private static final String INSTRUMENT = "test.package.TestInstrument";
     private static final String MIN_SDK = "8";
-    private static final String TARGET_SDK = "9";
+    private static final String TARGET_SDK = "17";
     private static final String MANIFEST = "<?xml version='1.0' encoding='UTF-8' standalone='yes' ?>\r\n"
         + "<manifest xmlns:android=\"http://schemas.android.com/apk/res/android\" "
         + "package=\"test.package\">\r\n"
-        + "  <uses-sdk android:minSdkVersion=\"8\" android:targetSdkVersion=\"9\" />\r\n"
+        + "  <uses-sdk android:minSdkVersion=\"8\" android:targetSdkVersion=\"17\" />\r\n"
         + "%s"
         + "  <application>\r\n"
         + "%s"
diff --git a/common/host-side/tradefed/src/com/android/compatibility/common/tradefed/build/CompatibilityBuildHelper.java b/common/host-side/tradefed/src/com/android/compatibility/common/tradefed/build/CompatibilityBuildHelper.java
index 50d1c3a..48ed864 100644
--- a/common/host-side/tradefed/src/com/android/compatibility/common/tradefed/build/CompatibilityBuildHelper.java
+++ b/common/host-side/tradefed/src/com/android/compatibility/common/tradefed/build/CompatibilityBuildHelper.java
@@ -16,6 +16,7 @@
 package com.android.compatibility.common.tradefed.build;
 
 import com.android.tradefed.build.IBuildInfo;
+import com.android.tradefed.build.IDeviceBuildInfo;
 import com.android.tradefed.build.IFolderBuildInfo;
 import com.android.tradefed.build.VersionedFile;
 import com.android.tradefed.util.FileUtil;
@@ -232,18 +233,6 @@
     }
 
     /**
-     * @return a {@link File} representing the directory to store screenshots taken while testing.
-     * @throws FileNotFoundException if the directory structure is not valid.
-     */
-    public File getScreenshotsDir() throws FileNotFoundException {
-        File screenshotsDir = new File(getResultDir(), "screenshots");
-        if (!screenshotsDir.exists()) {
-            screenshotsDir.mkdirs();
-        }
-        return screenshotsDir;
-    }
-
-    /**
      * @return a {@link File} representing the test modules directory.
      * @throws FileNotFoundException if the directory structure is not valid.
      */
@@ -264,6 +253,12 @@
         }
 
         if (testsDir == null) {
+            if (mBuildInfo instanceof IDeviceBuildInfo) {
+                testsDir = ((IDeviceBuildInfo) mBuildInfo).getTestsDir();
+            }
+        }
+
+        if (testsDir == null) {
             String altTestsDir = System.getenv().get(ALT_HOST_TESTCASE_DIR);
             if (altTestsDir != null) {
                 testsDir = new File(altTestsDir);
diff --git a/common/host-side/tradefed/src/com/android/compatibility/common/tradefed/build/CompatibilityBuildProvider.java b/common/host-side/tradefed/src/com/android/compatibility/common/tradefed/build/CompatibilityBuildProvider.java
index 3654d78..ad5e60f 100644
--- a/common/host-side/tradefed/src/com/android/compatibility/common/tradefed/build/CompatibilityBuildProvider.java
+++ b/common/host-side/tradefed/src/com/android/compatibility/common/tradefed/build/CompatibilityBuildProvider.java
@@ -16,7 +16,6 @@
 package com.android.compatibility.common.tradefed.build;
 
 import com.android.annotations.VisibleForTesting;
-import com.android.tradefed.build.BuildInfo;
 import com.android.tradefed.build.BuildRetrievalError;
 import com.android.tradefed.build.DeviceBuildInfo;
 import com.android.tradefed.build.IBuildInfo;
@@ -29,6 +28,8 @@
 import com.android.tradefed.config.OptionClass;
 import com.android.tradefed.device.DeviceNotAvailableException;
 import com.android.tradefed.device.ITestDevice;
+import com.android.tradefed.invoker.IInvocationContext;
+import com.android.tradefed.testtype.IInvocationContextReceiver;
 import com.android.tradefed.testtype.suite.TestSuiteInfo;
 import com.android.tradefed.util.FileUtil;
 
@@ -42,7 +43,7 @@
  * A simple {@link IBuildProvider} that uses a pre-existing Compatibility install.
  */
 @OptionClass(alias="compatibility-build-provider")
-public class CompatibilityBuildProvider implements IDeviceBuildProvider {
+public class CompatibilityBuildProvider implements IDeviceBuildProvider, IInvocationContextReceiver {
 
     private static final Pattern RELEASE_BUILD = Pattern.compile("^[A-Z]{3}\\d{2}[A-Z]{0,1}$");
     private static final String ROOT_DIR = "ROOT_DIR";
@@ -75,9 +76,6 @@
     @Option(name="use-device-build-info", description="Bootstrap build info from device")
     private boolean mUseDeviceBuildInfo = false;
 
-    @Option(name="test-tag", description="test tag name to supply.")
-    private String mTestTag = "cts";
-
     @Option(name = "dynamic-config-url",
             description = "Specify the url for override config")
     private String mURL = "https://androidpartner.googleapis.com/v1/dynamicconfig/"
@@ -88,6 +86,8 @@
             importance = Importance.ALWAYS)
     private String mSuitePlan;
 
+    private String mTestTag;
+
     /**
      * Util method to inject build attributes into supplied {@link IBuildInfo}
      * @param buildInfo
@@ -102,6 +102,14 @@
      * {@inheritDoc}
      */
     @Override
+    public void setInvocationContext(IInvocationContext invocationContext) {
+        mTestTag = invocationContext.getTestTag();
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    @Override
     public IBuildInfo getBuild() {
         // Create a blank BuildInfo which will get populated later.
         String version = null;
@@ -113,7 +121,7 @@
                 version = IBuildInfo.UNKNOWN_BUILD_ID;
             }
         }
-        IBuildInfo ctsBuild = new BuildInfo(version, mTestTag);
+        IBuildInfo ctsBuild = new DeviceBuildInfo(version, mTestTag);
         if (mBranch  != null) {
             ctsBuild.setBuildBranch(mBranch);
         }
@@ -208,7 +216,7 @@
         info.addBuildAttribute(ROOT_DIR, rootDir.getAbsolutePath());
         // For DeviceBuildInfo we populate the testsDir folder of the build info.
         if (info instanceof IDeviceBuildInfo) {
-            File testDir =  new File(rootDir, String.format("android-%s/testcases/",
+            File testDir = new File(rootDir, String.format("android-%s/testcases/",
                     getSuiteInfoName().toLowerCase()));
             ((IDeviceBuildInfo) info).setTestsDir(testDir, "0");
         }
diff --git a/common/host-side/tradefed/src/com/android/compatibility/common/tradefed/command/CompatibilityConsole.java b/common/host-side/tradefed/src/com/android/compatibility/common/tradefed/command/CompatibilityConsole.java
index 32e6197..fe29c7f 100644
--- a/common/host-side/tradefed/src/com/android/compatibility/common/tradefed/command/CompatibilityConsole.java
+++ b/common/host-side/tradefed/src/com/android/compatibility/common/tradefed/command/CompatibilityConsole.java
@@ -195,7 +195,12 @@
                 "\t\t\t--screenshot-on-failure: Capture a screenshot when a test fails"
                 + LINE_SEPARATOR +
                 "\t\t\t--shard-count <shards>: Shards a run into the given number of independent " +
-                "chunks, to run on multiple devices in parallel." + LINE_SEPARATOR;
+                "chunks, to run on multiple devices in parallel." + LINE_SEPARATOR +
+                "\t ----- In order to retry a previous run -----" + LINE_SEPARATOR +
+                "\tretry --retry <session id to retry> [--retry-type <FAILED | NOT_EXECUTED>]"
+                + LINE_SEPARATOR +
+                "\t\tWithout --retry-type, retry will run both FAIL and NOT_EXECUTED tests"
+                + LINE_SEPARATOR;
         commandHelp.put(RUN_PATTERN, combinedRunHelp);
 
         commandHelp.put(ADD_PATTERN, String.format(
diff --git a/common/host-side/tradefed/src/com/android/compatibility/common/tradefed/result/ResultReporter.java b/common/host-side/tradefed/src/com/android/compatibility/common/tradefed/result/ResultReporter.java
index 574a2a1..11a7466 100644
--- a/common/host-side/tradefed/src/com/android/compatibility/common/tradefed/result/ResultReporter.java
+++ b/common/host-side/tradefed/src/com/android/compatibility/common/tradefed/result/ResultReporter.java
@@ -16,7 +16,8 @@
 package com.android.compatibility.common.tradefed.result;
 
 import com.android.compatibility.common.tradefed.build.CompatibilityBuildHelper;
-import com.android.compatibility.common.tradefed.testtype.CompatibilityTest;
+import com.android.compatibility.common.tradefed.testtype.retry.RetryFactoryTest;
+import com.android.compatibility.common.tradefed.testtype.suite.CompatibilityTestSuite;
 import com.android.compatibility.common.tradefed.util.RetryType;
 import com.android.compatibility.common.util.ChecksumReporter;
 import com.android.compatibility.common.util.DeviceInfo;
@@ -39,6 +40,7 @@
 import com.android.tradefed.config.OptionCopier;
 import com.android.tradefed.invoker.IInvocationContext;
 import com.android.tradefed.log.LogUtil.CLog;
+import com.android.tradefed.result.FileInputStreamSource;
 import com.android.tradefed.result.ILogSaver;
 import com.android.tradefed.result.ILogSaverListener;
 import com.android.tradefed.result.IShardableListener;
@@ -49,6 +51,7 @@
 import com.android.tradefed.result.LogFile;
 import com.android.tradefed.result.LogFileSaver;
 import com.android.tradefed.result.TestSummary;
+import com.android.tradefed.result.suite.SuiteResultReporter;
 import com.android.tradefed.util.FileUtil;
 import com.android.tradefed.util.StreamUtil;
 import com.android.tradefed.util.TimeUtil;
@@ -63,6 +66,8 @@
 import java.io.FileNotFoundException;
 import java.io.IOException;
 import java.io.InputStream;
+import java.nio.file.Files;
+import java.nio.file.Path;
 import java.util.Arrays;
 import java.util.Collections;
 import java.util.HashMap;
@@ -80,10 +85,12 @@
 public class ResultReporter implements ILogSaverListener, ITestInvocationListener,
        ITestSummaryListener, IShardableListener {
 
+    public static final String INCLUDE_HTML_IN_ZIP = "html-in-zip";
     private static final String UNKNOWN_DEVICE = "unknown_device";
     private static final String RESULT_KEY = "COMPATIBILITY_TEST_RESULT";
     private static final String CTS_PREFIX = "cts:";
     private static final String BUILD_INFO = CTS_PREFIX + "build_";
+    private static final String LATEST_LINK_NAME = "latest";
 
     public static final String BUILD_BRAND = "build_brand";
     public static final String BUILD_DEVICE = "build_device";
@@ -100,14 +107,14 @@
             ResultHandler.FAILURE_REPORT_NAME,
             "diffs");
 
-    @Option(name = CompatibilityTest.RETRY_OPTION,
+    @Option(name = RetryFactoryTest.RETRY_OPTION,
             shortName = 'r',
             description = "retry a previous session.",
             importance = Importance.IF_UNSET)
     private Integer mRetrySessionId = null;
 
-    @Option(name = CompatibilityTest.RETRY_TYPE_OPTION,
-            description = "used with " + CompatibilityTest.RETRY_OPTION
+    @Option(name = RetryFactoryTest.RETRY_TYPE_OPTION,
+            description = "used with " + RetryFactoryTest.RETRY_OPTION
             + ", retry tests of a certain status. Possible values include \"failed\", "
             + "\"not_executed\", and \"custom\".",
             importance = Importance.IF_UNSET)
@@ -128,6 +135,10 @@
     @Option(name = "compress-logs", description = "Whether logs will be saved with compression")
     private boolean mCompressLogs = true;
 
+    @Option(name = INCLUDE_HTML_IN_ZIP,
+            description = "Whether failure summary report is included in the zip fie.")
+    private boolean mIncludeHtml = false;
+
     private CompatibilityBuildHelper mBuildHelper;
     private File mResultDir = null;
     private File mLogDir = null;
@@ -449,10 +460,24 @@
      */
     @Override
     public void putSummary(List<TestSummary> summaries) {
-        // This is safe to be invoked on either the master or a shard ResultReporter,
-        // but the value added to the report will be that of the master ResultReporter.
-        if (summaries.size() > 0) {
-            mReferenceUrl = summaries.get(0).getSummary().getString();
+        for (TestSummary summary : summaries) {
+            // If one summary is from SuiteResultReporter, log it as an extra file.
+            if (SuiteResultReporter.SUITE_REPORTER_SOURCE.equals(summary.getSource())) {
+                File summaryFile = null;
+                try {
+                    summaryFile = FileUtil.createTempFile("summary", ".txt");
+                    FileUtil.writeToFile(summary.getSummary().getString(), summaryFile);
+                    try (InputStreamSource stream = new FileInputStreamSource(summaryFile)) {
+                        testLog("summary", LogDataType.TEXT, stream);
+                    }
+                } catch (IOException e) {
+                    CLog.e(e);
+                } finally {
+                    FileUtil.deleteFile(summaryFile);
+                }
+            } else if (mReferenceUrl == null && summary.getSummary().getString() != null) {
+                mReferenceUrl = summary.getSummary().getString();
+            }
         }
     }
 
@@ -521,10 +546,17 @@
                 copyRetryFiles(ResultHandler.getResultDirectory(
                         mBuildHelper.getResultsDir(), mRetrySessionId), mResultDir);
             }
+            File failureReport = null;
+            if (mIncludeHtml) {
+                // Create the html report before the zip file.
+                failureReport = ResultHandler.createFailureReport(resultFile);
+            }
             File zippedResults = zipResults(mResultDir);
-            // Create failure report after zip file so extra data is not uploaded
-            File failureReport = ResultHandler.createFailureReport(resultFile);
-            if (failureReport.exists()) {
+            if (!mIncludeHtml) {
+                // Create failure report after zip file so extra data is not uploaded
+                failureReport = ResultHandler.createFailureReport(resultFile);
+            }
+            if (failureReport != null && failureReport.exists()) {
                 info("Test Result: %s", failureReport.getCanonicalPath());
             } else {
                 info("Test Result: %s", resultFile.getCanonicalPath());
@@ -532,6 +564,16 @@
             info("Test Logs: %s", mLogDir.getCanonicalPath());
             debug("Full Result: %s", zippedResults.getCanonicalPath());
 
+            Path latestLink = createLatestLinkDirectory(mResultDir.toPath());
+            if (latestLink != null) {
+                info("Latest results link: " + latestLink.toAbsolutePath());
+            }
+
+            latestLink = createLatestLinkDirectory(mLogDir.toPath());
+            if (latestLink != null) {
+                info("Latest logs link: " + latestLink.toAbsolutePath());
+            }
+
             saveLog(resultFile, zippedResults);
 
             uploadResult(resultFile);
@@ -548,6 +590,30 @@
                 moduleProgress);
     }
 
+    private Path createLatestLinkDirectory(Path directory) {
+        Path link = null;
+
+        Path parent = directory.getParent();
+
+        if (parent != null) {
+            link = parent.resolve(LATEST_LINK_NAME);
+            try {
+                // if latest already exists, we have to remove it before creating
+                Files.deleteIfExists(link);
+                Files.createSymbolicLink(link, directory);
+            } catch (IOException ioe) {
+                CLog.e("Exception while attempting to create 'latest' link to: [%s]",
+                    directory);
+                CLog.e(ioe);
+                return null;
+            } catch (UnsupportedOperationException uoe) {
+                CLog.e("Failed to create 'latest' symbolic link - unsupported operation");
+                return null;
+            }
+        }
+        return link;
+    }
+
     /**
      * {@inheritDoc}
      */
@@ -572,6 +638,7 @@
             // Handle device info file case
             testLogDeviceInfo(name, stream);
         } else {
+            // Handle default case
             try {
                 File logFile = null;
                 if (mCompressLogs) {
@@ -803,11 +870,11 @@
         }
         return !(RetryType.FAILED.equals(mRetryType)
                 || RetryType.CUSTOM.equals(mRetryType)
-                || args.contains(CompatibilityTest.INCLUDE_FILTER_OPTION)
-                || args.contains(CompatibilityTest.EXCLUDE_FILTER_OPTION)
-                || args.contains(CompatibilityTest.SUBPLAN_OPTION)
+                || args.contains(CompatibilityTestSuite.INCLUDE_FILTER_OPTION)
+                || args.contains(CompatibilityTestSuite.EXCLUDE_FILTER_OPTION)
+                || args.contains(CompatibilityTestSuite.SUBPLAN_OPTION)
                 || args.matches(String.format(".* (-%s|--%s) .*",
-                CompatibilityTest.TEST_OPTION_SHORT_NAME, CompatibilityTest.TEST_OPTION)));
+                CompatibilityTestSuite.TEST_OPTION_SHORT_NAME, CompatibilityTestSuite.TEST_OPTION)));
     }
 
     /**
diff --git a/common/host-side/tradefed/src/com/android/compatibility/common/tradefed/result/SubPlanHelper.java b/common/host-side/tradefed/src/com/android/compatibility/common/tradefed/result/SubPlanHelper.java
index 9c87a75..87f2436 100644
--- a/common/host-side/tradefed/src/com/android/compatibility/common/tradefed/result/SubPlanHelper.java
+++ b/common/host-side/tradefed/src/com/android/compatibility/common/tradefed/result/SubPlanHelper.java
@@ -16,9 +16,9 @@
 package com.android.compatibility.common.tradefed.result;
 
 import com.android.compatibility.common.tradefed.build.CompatibilityBuildHelper;
-import com.android.compatibility.common.tradefed.testtype.CompatibilityTest;
 import com.android.compatibility.common.tradefed.testtype.ISubPlan;
 import com.android.compatibility.common.tradefed.testtype.SubPlan;
+import com.android.compatibility.common.tradefed.testtype.suite.CompatibilityTestSuite;
 import com.android.compatibility.common.tradefed.util.OptionHelper;
 import com.android.compatibility.common.util.ICaseResult;
 import com.android.compatibility.common.util.IInvocationResult;
@@ -33,8 +33,8 @@
 import com.android.tradefed.config.Option;
 import com.android.tradefed.config.Option.Importance;
 import com.android.tradefed.log.LogUtil.CLog;
-import com.android.tradefed.util.xml.AbstractXmlParser.ParseException;
 import com.android.tradefed.util.StreamUtil;
+import com.android.tradefed.util.xml.AbstractXmlParser.ParseException;
 
 import com.google.common.annotations.VisibleForTesting;
 
@@ -43,13 +43,13 @@
 import java.io.FileInputStream;
 import java.io.FileNotFoundException;
 import java.io.FileOutputStream;
-import java.io.InputStream;
 import java.io.IOException;
+import java.io.InputStream;
 import java.util.Arrays;
 import java.util.Collection;
 import java.util.Collections;
-import java.util.HashSet;
 import java.util.HashMap;
+import java.util.HashSet;
 import java.util.Map;
 import java.util.Set;
 
@@ -90,32 +90,32 @@
             importance=Importance.IF_UNSET)
     private Set<String> mResultTypes = new HashSet<String>();
 
-    @Option(name = CompatibilityTest.INCLUDE_FILTER_OPTION,
+    @Option(name = CompatibilityTestSuite.INCLUDE_FILTER_OPTION,
             description = "the include module filters to apply.",
             importance = Importance.NEVER)
     private Set<String> mIncludeFilters = new HashSet<String>();
 
-    @Option(name = CompatibilityTest.EXCLUDE_FILTER_OPTION,
+    @Option(name = CompatibilityTestSuite.EXCLUDE_FILTER_OPTION,
             description = "the exclude module filters to apply.",
             importance = Importance.NEVER)
     private Set<String> mExcludeFilters = new HashSet<String>();
 
-    @Option(name = CompatibilityTest.MODULE_OPTION, shortName = 'm',
+    @Option(name = CompatibilityTestSuite.MODULE_OPTION, shortName = 'm',
             description = "the test module to run.",
             importance = Importance.NEVER)
     private String mModuleName = null;
 
-    @Option(name = CompatibilityTest.TEST_OPTION, shortName = 't',
+    @Option(name = CompatibilityTestSuite.TEST_OPTION, shortName = 't',
             description = "the test to run.",
             importance = Importance.NEVER)
     private String mTestName = null;
 
-    @Option(name = CompatibilityTest.ABI_OPTION, shortName = 'a',
+    @Option(name = CompatibilityTestSuite.ABI_OPTION, shortName = 'a',
             description = "the abi to test.",
             importance = Importance.NEVER)
     private String mAbiName = null;
 
-    @Option(name = CompatibilityTest.SUBPLAN_OPTION,
+    @Option(name = CompatibilityTestSuite.SUBPLAN_OPTION,
             description = "the subplan used in the previous session",
             importance = Importance.NEVER)
     private String mLastSubPlan;
@@ -159,7 +159,7 @@
             throw new RuntimeException(
                     String.format("Unable to find or parse subplan %s", name), e);
         } finally {
-            StreamUtil.closeStream(subPlanInputStream);
+            StreamUtil.close(subPlanInputStream);
         }
     }
 
@@ -207,7 +207,7 @@
      * Create a subplan derived from a result.
      * <p/>
      * {@link Option} values must be set before this is called.
-     * @param build
+     * @param buildHelper
      * @return subplan
      * @throws ConfigurationException
      */
diff --git a/common/host-side/tradefed/src/com/android/compatibility/common/tradefed/testtype/BusinessLogicHostTestBase.java b/common/host-side/tradefed/src/com/android/compatibility/common/tradefed/testtype/BusinessLogicHostTestBase.java
index 7ebd717..749be97 100644
--- a/common/host-side/tradefed/src/com/android/compatibility/common/tradefed/testtype/BusinessLogicHostTestBase.java
+++ b/common/host-side/tradefed/src/com/android/compatibility/common/tradefed/testtype/BusinessLogicHostTestBase.java
@@ -29,13 +29,14 @@
 import com.android.compatibility.common.util.BusinessLogicFactory;
 import com.android.compatibility.common.util.BusinessLogicHostExecutor;
 import com.android.tradefed.log.LogUtil.CLog;
+import com.android.tradefed.testtype.junit4.BaseHostJUnit4Test;
 
 import java.io.File;
 
 /**
  * Host-side base class for tests leveraging the Business Logic service.
  */
-public class BusinessLogicHostTestBase extends CompatibilityHostTestBase {
+public class BusinessLogicHostTestBase extends BaseHostJUnit4Test {
 
     /* String marking the beginning of the parameter in a test name */
     private static final String PARAM_START = "[";
@@ -51,7 +52,7 @@
         // Business logic must be retrieved in this @Before method, since the build info contains
         // the location of the business logic file and cannot be referenced from a static context
         if (mBusinessLogic == null) {
-            CompatibilityBuildHelper helper = new CompatibilityBuildHelper(mBuild);
+            CompatibilityBuildHelper helper = new CompatibilityBuildHelper(getBuild());
             File businessLogicFile = helper.getBusinessLogicHostFile();
             if (businessLogicFile != null && businessLogicFile.canRead()) {
                 mBusinessLogic = BusinessLogicFactory.createFromFile(businessLogicFile);
@@ -71,7 +72,7 @@
         if (mBusinessLogic.hasLogicFor(testName)) {
             CLog.i("Applying business logic for test case: ", testName);
             BusinessLogicExecutor executor = new BusinessLogicHostExecutor(getDevice(),
-                    mBuild, this);
+                    getBuild(), this);
             mBusinessLogic.applyLogicFor(testName, executor);
         }
     }
diff --git a/common/host-side/tradefed/src/com/android/compatibility/common/tradefed/testtype/CompatibilityHostTestBase.java b/common/host-side/tradefed/src/com/android/compatibility/common/tradefed/testtype/CompatibilityHostTestBase.java
deleted file mode 100644
index 1222a1f..0000000
--- a/common/host-side/tradefed/src/com/android/compatibility/common/tradefed/testtype/CompatibilityHostTestBase.java
+++ /dev/null
@@ -1,249 +0,0 @@
-/*
- * Copyright (C) 2016 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.compatibility.common.tradefed.testtype;
-
-import static org.junit.Assert.assertNull;
-import static org.junit.Assert.assertTrue;
-
-import com.android.annotations.Nullable;
-import com.android.annotations.VisibleForTesting;
-import com.android.compatibility.common.tradefed.build.CompatibilityBuildHelper;
-import com.android.ddmlib.Log.LogLevel;
-import com.android.ddmlib.testrunner.RemoteAndroidTestRunner;
-import com.android.ddmlib.testrunner.TestIdentifier;
-import com.android.ddmlib.testrunner.TestResult;
-import com.android.ddmlib.testrunner.TestResult.TestStatus;
-import com.android.ddmlib.testrunner.TestRunResult;
-import com.android.tradefed.build.IBuildInfo;
-import com.android.tradefed.device.DeviceNotAvailableException;
-import com.android.tradefed.device.ITestDevice;
-import com.android.tradefed.log.LogUtil.CLog;
-import com.android.tradefed.result.CollectingTestListener;
-import com.android.tradefed.testtype.IAbi;
-import com.android.tradefed.testtype.IAbiReceiver;
-import com.android.tradefed.testtype.IBuildReceiver;
-import com.android.tradefed.testtype.IDeviceTest;
-import com.android.tradefed.util.AbiUtils;
-
-import org.junit.Assume;
-import org.junit.Before;
-import org.junit.internal.AssumptionViolatedException;
-
-import java.io.File;
-import java.io.FileNotFoundException;
-import java.util.ArrayList;
-import java.util.Arrays;
-import java.util.List;
-import java.util.Map;
-
-/**
- * Compatibility host test base class for JUnit4 tests. Enables host-side tests written in JUnit4
- * to access build and ABI information, as well as a reference to the testing device. The class
- * includes methods to install and uninstall test packages, as well as methods to run device-side
- * tests and retrieve their results.
- */
-public class CompatibilityHostTestBase implements IAbiReceiver, IBuildReceiver, IDeviceTest {
-
-    protected static final String AJUR = "android.support.test.runner.AndroidJUnitRunner";
-
-    /** The build will be used. */
-    protected IBuildInfo mBuild;
-
-    /** The ABI to use. */
-    protected IAbi mAbi;
-
-    /** A reference to the device under test. */
-    protected ITestDevice mDevice;
-
-    /** The test runner used for test apps */
-    private String mRunner;
-
-    @Override
-    public void setAbi(IAbi abi) {
-        mAbi = abi;
-    }
-
-    @Override
-    public IAbi getAbi() {
-        return mAbi;
-    }
-
-    @Override
-    public void setBuild(IBuildInfo buildInfo) {
-        // Get the build, this is used to access the APK.
-        mBuild = buildInfo;
-    }
-
-    /**
-     * {@inheritDoc}
-     */
-    @Override
-    public ITestDevice getDevice() {
-        return mDevice;
-    }
-
-    /**
-     * {@inheritDoc}
-     */
-    @Override
-    public void setDevice(ITestDevice device) {
-        mDevice = device;
-    }
-
-    @Before
-    public void baseSetUp() throws Exception {
-        mRunner = AJUR;
-    }
-
-    /**
-     * Set the runner name
-     * @param runner of the device test runner
-     */
-    protected void setRunner(String runner) {
-        mRunner = runner;
-    }
-
-    /**
-     * Get the runner name
-     * @return name of the device test runner
-     */
-    protected String getRunner() {
-        return mRunner;
-    }
-
-    /**
-     * Installs a package on the device
-     * @param fileName the name of the file to install
-     * @param options optional extra arguments to pass. See 'adb shell pm install --help' for
-     * available options
-     * @throws FileNotFoundException if file with filename cannot be found
-     * @throws DeviceNotAvailableException
-     */
-    protected void installPackage(String fileName, String... options)
-            throws FileNotFoundException, DeviceNotAvailableException {
-
-        final List<String> optList = new ArrayList<>(Arrays.asList(options));
-        optList.add(AbiUtils.createAbiFlag(mAbi.getName()));
-        options = optList.toArray(new String[optList.size()]);
-
-        CompatibilityBuildHelper buildHelper = new CompatibilityBuildHelper(mBuild);
-        File testFile = buildHelper.getTestFile(fileName);
-        // Install the APK on the device.
-        String installResult = mDevice.installPackage(testFile, true, options);
-
-        assertNull(String.format("Failed to install %s, Reason: %s", fileName, installResult),
-                installResult);
-    }
-
-    /**
-     * Uninstalls a package on the device
-     * @param pkgName the Android package to uninstall
-     * @return a {@link String} with an error code, or <code>null</code> if success
-     * @throws DeviceNotAvailableException
-     */
-    protected String uninstallPackage(String pkgName) throws DeviceNotAvailableException {
-        return mDevice.uninstallPackage(pkgName);
-    }
-
-    /**
-     * Checks if a package of a given name is installed on the device
-     * @param pkg the name of the package
-     * @return true if the package is found on the device
-     * @throws DeviceNotAvailableException
-     */
-    protected boolean isPackageInstalled(String pkg) throws DeviceNotAvailableException {
-        for (String installedPackage : mDevice.getInstalledPackageNames()) {
-            if (pkg.equals(installedPackage)) {
-                return true;
-            }
-        }
-        return false;
-    }
-
-    private void printTestResult(TestRunResult runResult) {
-        for (Map.Entry<TestIdentifier, TestResult> testEntry :
-                     runResult.getTestResults().entrySet()) {
-            TestResult testResult = testEntry.getValue();
-            TestStatus testStatus = testResult.getStatus();
-            CLog.logAndDisplay(LogLevel.INFO,
-                    "Test " + testEntry.getKey() + ": " + testStatus);
-            if (testStatus != TestStatus.PASSED && testStatus != TestStatus.ASSUMPTION_FAILURE) {
-                CLog.logAndDisplay(LogLevel.WARN, testResult.getStackTrace());
-            }
-        }
-    }
-
-    /**
-     * Runs tests of a given package on the device and reports success.
-     * @param pkgName the name of the package containing tests
-     * @param testClassName the class from which tests should be collected. Tests are collected
-     * from all test classes in the package if null
-     * @return true if at least once test runs and there are no failures
-     * @throws AssertionError if device fails to run instrumentation tests
-     * @throws AssumptionViolatedException if each device test fails an assumption
-     * @throws DeviceNotAvailableException
-     */
-    protected boolean runDeviceTests(String pkgName, @Nullable String testClassName)
-            throws DeviceNotAvailableException {
-        return runDeviceTests(pkgName, testClassName, null /*testMethodName*/);
-    }
-
-    /**
-     * Runs tests of a given package on the device and reports success.
-     * @param pkgName the name of the package containing tests
-     * @param testClassName the class from which tests should be collected. Tests are collected
-     * from all test classes in the package if null
-     * @param testMethodName the test method to run. All tests from the class or package are run
-     * if null
-     * @return true if at least once test runs and there are no failures
-     * @throws AssertionError if device fails to run instrumentation tests
-     * @throws AssumptionViolatedException if each device test fails an assumption
-     * @throws DeviceNotAvailableException
-     */
-    protected boolean runDeviceTests(String pkgName, @Nullable String testClassName,
-            @Nullable String testMethodName)
-            throws DeviceNotAvailableException {
-        TestRunResult runResult = doRunTests(pkgName, testClassName, testMethodName);
-        printTestResult(runResult);
-        // assume not all tests have skipped (and rethrow AssumptionViolatedException if so)
-        Assume.assumeTrue(runResult.getNumTests() != runResult.getNumTestsInState(
-                TestStatus.ASSUMPTION_FAILURE));
-        return !runResult.hasFailedTests() && runResult.getNumTests() > 0;
-    }
-
-    /** Helper method to run tests and return the listener that collected the results. */
-    private TestRunResult doRunTests(
-        String pkgName, String testClassName,
-        String testMethodName) throws DeviceNotAvailableException {
-        RemoteAndroidTestRunner testRunner = new RemoteAndroidTestRunner(
-            pkgName, mRunner, mDevice.getIDevice());
-        if (testClassName != null && testMethodName != null) {
-            testRunner.setMethodName(testClassName, testMethodName);
-        } else if (testClassName != null) {
-            testRunner.setClassName(testClassName);
-        }
-
-        CollectingTestListener listener = createCollectingListener();
-        assertTrue(mDevice.runInstrumentationTests(testRunner, listener));
-        return listener.getCurrentRunResults();
-    }
-
-    @VisibleForTesting
-    protected CollectingTestListener createCollectingListener() {
-        return new CollectingTestListener();
-    }
-}
diff --git a/common/host-side/tradefed/src/com/android/compatibility/common/tradefed/testtype/CompatibilityTest.java b/common/host-side/tradefed/src/com/android/compatibility/common/tradefed/testtype/CompatibilityTest.java
index 07bfe83..a8c016c 100644
--- a/common/host-side/tradefed/src/com/android/compatibility/common/tradefed/testtype/CompatibilityTest.java
+++ b/common/host-side/tradefed/src/com/android/compatibility/common/tradefed/testtype/CompatibilityTest.java
@@ -20,6 +20,7 @@
 import com.android.compatibility.common.tradefed.result.InvocationFailureHandler;
 import com.android.compatibility.common.tradefed.result.SubPlanHelper;
 import com.android.compatibility.common.tradefed.targetprep.NetworkConnectivityChecker;
+import com.android.compatibility.common.tradefed.testtype.suite.CompatibilityTestSuite;
 import com.android.compatibility.common.tradefed.util.RetryFilterHelper;
 import com.android.compatibility.common.tradefed.util.RetryType;
 import com.android.compatibility.common.tradefed.util.UniqueModuleCountUtil;
@@ -79,8 +80,10 @@
 import java.util.concurrent.TimeUnit;
 
 /**
- * A Test for running Compatibility Suites
+ * A Test for running Compatibility Suites.
+ * @deprecated use {@link CompatibilityTestSuite} instead.
  */
+@Deprecated
 @OptionClass(alias = "compatibility")
 public class CompatibilityTest implements IDeviceTest, IShardableTest, IBuildReceiver,
         IStrictShardableTest, ISystemStatusCheckerReceiver, ITestCollector,
@@ -473,7 +476,16 @@
                 moduleContext.addInvocationAttribute(IModuleDef.MODULE_ABI,
                         module.getAbi().getName());
                 mInvocationContext.setModuleInvocationContext(moduleContext);
+                // Populate the module context with devices and builds
+                for (String deviceName : mInvocationContext.getDeviceConfigNames()) {
+                    moduleContext.addAllocatedDevice(
+                            deviceName, mInvocationContext.getDevice(deviceName));
+                    moduleContext.addDeviceBuildInfo(
+                            deviceName, mInvocationContext.getBuildInfo(deviceName));
+                }
+                module.setInvocationContext(moduleContext);
                 try {
+                    listener.testModuleStarted(moduleContext);
                     module.run(listener);
                 } catch (DeviceUnresponsiveException due) {
                     // being able to catch a DeviceUnresponsiveException here implies that recovery
@@ -490,6 +502,7 @@
                     // clear out module invocation context since we are now done with module
                     // execution
                     mInvocationContext.setModuleInvocationContext(null);
+                    listener.testModuleEnded();
                 }
                 long duration = System.currentTimeMillis() - start;
                 long expected = module.getRuntimeHint();
diff --git a/common/host-side/tradefed/src/com/android/compatibility/common/tradefed/testtype/IModuleDef.java b/common/host-side/tradefed/src/com/android/compatibility/common/tradefed/testtype/IModuleDef.java
index 7a2c44d..51b33a1 100644
--- a/common/host-side/tradefed/src/com/android/compatibility/common/tradefed/testtype/IModuleDef.java
+++ b/common/host-side/tradefed/src/com/android/compatibility/common/tradefed/testtype/IModuleDef.java
@@ -21,6 +21,7 @@
 import com.android.tradefed.testtype.IAbi;
 import com.android.tradefed.testtype.IBuildReceiver;
 import com.android.tradefed.testtype.IDeviceTest;
+import com.android.tradefed.testtype.IInvocationContextReceiver;
 import com.android.tradefed.testtype.IRemoteTest;
 import com.android.tradefed.testtype.IRuntimeHintProvider;
 import com.android.tradefed.testtype.ITestCollector;
@@ -33,7 +34,7 @@
  * Container for Compatibility test info.
  */
 public interface IModuleDef extends Comparable<IModuleDef>, IBuildReceiver, IDeviceTest,
-        IRemoteTest, IRuntimeHintProvider, ITestCollector {
+        IRemoteTest, IRuntimeHintProvider, ITestCollector, IInvocationContextReceiver {
 
     /** key names used for saving module info into {@link IInvocationContext} */
     // This currently references ModuleDefinition so that there's only once source for String
diff --git a/common/host-side/tradefed/src/com/android/compatibility/common/tradefed/testtype/JarHostTest.java b/common/host-side/tradefed/src/com/android/compatibility/common/tradefed/testtype/JarHostTest.java
index 5ef3a93..4104202 100644
--- a/common/host-side/tradefed/src/com/android/compatibility/common/tradefed/testtype/JarHostTest.java
+++ b/common/host-side/tradefed/src/com/android/compatibility/common/tradefed/testtype/JarHostTest.java
@@ -36,8 +36,8 @@
 import java.lang.reflect.Modifier;
 import java.net.URL;
 import java.net.URLClassLoader;
-import java.util.Collections;
 import java.util.Enumeration;
+import java.util.HashMap;
 import java.util.HashSet;
 import java.util.List;
 import java.util.Map;
@@ -136,11 +136,25 @@
      */
     @Override
     public void run(ITestInvocationListener listener) throws DeviceNotAvailableException {
-        int numTests = countTestCases();
+        int numTests = 0;
+        RuntimeException bufferedException = null;
+        try {
+            numTests = countTestCases();
+        } catch (RuntimeException e) {
+            bufferedException = e;
+        }
         long startTime = System.currentTimeMillis();
         listener.testRunStarted(getClass().getName(), numTests);
-        super.run(new HostTestListener(listener));
-        listener.testRunEnded(System.currentTimeMillis() - startTime, Collections.emptyMap());
+        HostTestListener hostListener = new HostTestListener(listener);
+        try {
+            if (bufferedException != null) {
+                throw bufferedException;
+            }
+            super.run(hostListener);
+        } finally {
+            listener.testRunEnded(System.currentTimeMillis() - startTime,
+                    hostListener.getMetrics());
+        }
     }
 
     /**
@@ -150,6 +164,8 @@
      */
     public class HostTestListener extends ResultForwarder {
 
+        private Map<String, String> mCollectedMetrics = new HashMap<>();
+
         public HostTestListener(ITestInvocationListener listener) {
             super(listener);
         }
@@ -168,6 +184,14 @@
         @Override
         public void testRunEnded(long elapsedTime, Map<String, String> metrics) {
             CLog.d("HostTestListener.testRunEnded(%d, %s)", elapsedTime, metrics.toString());
+            mCollectedMetrics.putAll(metrics);
+        }
+
+        /**
+         * Returns all the metrics reported by the tests
+         */
+        Map<String, String> getMetrics() {
+            return mCollectedMetrics;
         }
     }
 }
diff --git a/common/host-side/tradefed/src/com/android/compatibility/common/tradefed/testtype/ModuleDef.java b/common/host-side/tradefed/src/com/android/compatibility/common/tradefed/testtype/ModuleDef.java
index f51be93..ffb7ef7 100644
--- a/common/host-side/tradefed/src/com/android/compatibility/common/tradefed/testtype/ModuleDef.java
+++ b/common/host-side/tradefed/src/com/android/compatibility/common/tradefed/testtype/ModuleDef.java
@@ -26,6 +26,7 @@
 import com.android.tradefed.config.OptionSetter;
 import com.android.tradefed.device.DeviceNotAvailableException;
 import com.android.tradefed.device.ITestDevice;
+import com.android.tradefed.invoker.IInvocationContext;
 import com.android.tradefed.log.LogUtil.CLog;
 import com.android.tradefed.result.ITestInvocationListener;
 import com.android.tradefed.result.ResultForwarder;
@@ -37,6 +38,7 @@
 import com.android.tradefed.testtype.IAbiReceiver;
 import com.android.tradefed.testtype.IBuildReceiver;
 import com.android.tradefed.testtype.IDeviceTest;
+import com.android.tradefed.testtype.IInvocationContextReceiver;
 import com.android.tradefed.testtype.IRemoteTest;
 import com.android.tradefed.testtype.IRuntimeHintProvider;
 import com.android.tradefed.testtype.ITestCollector;
@@ -68,6 +70,7 @@
     private ITestDevice mDevice;
     private Set<String> mPreparerWhitelist = new HashSet<>();
     private ConfigurationDescriptor mConfigurationDescriptor;
+    private IInvocationContext mContext;
 
     public ModuleDef(String name, IAbi abi, IRemoteTest test,
             List<ITargetPreparer> preparers, ConfigurationDescriptor configurationDescriptor) {
@@ -302,6 +305,9 @@
         if (mTest instanceof IDeviceTest) {
             ((IDeviceTest) mTest).setDevice(mDevice);
         }
+        if (mTest instanceof IInvocationContextReceiver) {
+            ((IInvocationContextReceiver) mTest).setInvocationContext(mContext);
+        }
     }
 
     /**
@@ -409,4 +415,16 @@
     public ConfigurationDescriptor getConfigurationDescriptor() {
         return mConfigurationDescriptor;
     }
+
+    /**
+     * @return the {@link IInvocationContext} for the module
+     */
+    protected IInvocationContext getInvocationContext() {
+        return mContext;
+    }
+
+    @Override
+    public void setInvocationContext(IInvocationContext invocationContext) {
+        mContext = invocationContext;
+    }
 }
diff --git a/common/host-side/tradefed/src/com/android/compatibility/common/tradefed/testtype/retry/RetryFactoryTest.java b/common/host-side/tradefed/src/com/android/compatibility/common/tradefed/testtype/retry/RetryFactoryTest.java
index 40bf633..a95ebfb 100644
--- a/common/host-side/tradefed/src/com/android/compatibility/common/tradefed/testtype/retry/RetryFactoryTest.java
+++ b/common/host-side/tradefed/src/com/android/compatibility/common/tradefed/testtype/retry/RetryFactoryTest.java
@@ -16,7 +16,6 @@
 package com.android.compatibility.common.tradefed.testtype.retry;
 
 import com.android.compatibility.common.tradefed.build.CompatibilityBuildHelper;
-import com.android.compatibility.common.tradefed.testtype.CompatibilityTest;
 import com.android.compatibility.common.tradefed.testtype.suite.CompatibilityTestSuite;
 import com.android.compatibility.common.tradefed.util.RetryFilterHelper;
 import com.android.compatibility.common.tradefed.util.RetryType;
@@ -41,13 +40,14 @@
 
 import com.google.common.annotations.VisibleForTesting;
 
+import java.util.ArrayList;
 import java.util.Collection;
 import java.util.HashSet;
 import java.util.List;
 import java.util.Set;
 
 /**
- * Runner that creates a {@link CompatibilityTest} to re-run some previous results.
+ * Runner that creates a {@link CompatibilityTestSuite} to re-run some previous results.
  * Only the 'cts' plan is supported.
  * TODO: explore other new way to build the retry (instead of relying on one massive pair of
  * include/exclude filters)
@@ -57,50 +57,58 @@
         ISystemStatusCheckerReceiver, IInvocationContextReceiver, IShardableTest {
 
     /**
-     * Mirror the {@link CompatibilityTest} options in order to create it.
+     * Mirror the {@link CompatibilityTestSuite} options in order to create it.
      */
     public static final String RETRY_OPTION = "retry";
+    public static final String RETRY_TYPE_OPTION = "retry-type";
+
     @Option(name = RETRY_OPTION,
             shortName = 'r',
             description = "retry a previous session's failed and not executed tests.",
             mandatory = true)
     private Integer mRetrySessionId = null;
 
-    @Option(name = CompatibilityTest.SUBPLAN_OPTION,
+    @Option(name = CompatibilityTestSuite.SUBPLAN_OPTION,
             description = "the subplan to run",
             importance = Importance.IF_UNSET)
     protected String mSubPlan;
 
-    @Option(name = CompatibilityTest.INCLUDE_FILTER_OPTION,
+    @Option(name = CompatibilityTestSuite.INCLUDE_FILTER_OPTION,
             description = "the include module filters to apply.",
             importance = Importance.ALWAYS)
     protected Set<String> mIncludeFilters = new HashSet<>();
 
-    @Option(name = CompatibilityTest.EXCLUDE_FILTER_OPTION,
+    @Option(name = CompatibilityTestSuite.EXCLUDE_FILTER_OPTION,
             description = "the exclude module filters to apply.",
             importance = Importance.ALWAYS)
     protected Set<String> mExcludeFilters = new HashSet<>();
 
-    @Option(name = CompatibilityTest.ABI_OPTION,
+    @Option(name = CompatibilityTestSuite.ABI_OPTION,
             shortName = 'a',
             description = "the abi to test.",
             importance = Importance.IF_UNSET)
     protected String mAbiName = null;
 
-    @Option(name = CompatibilityTest.MODULE_OPTION,
+    @Option(name = CompatibilityTestSuite.MODULE_OPTION,
             shortName = 'm',
             description = "the test module to run.",
             importance = Importance.IF_UNSET)
     protected String mModuleName = null;
 
-    @Option(name = CompatibilityTest.TEST_OPTION,
-            shortName = CompatibilityTest.TEST_OPTION_SHORT_NAME,
+    @Option(name = CompatibilityTestSuite.TEST_OPTION,
+            shortName = CompatibilityTestSuite.TEST_OPTION_SHORT_NAME,
             description = "the test run.",
             importance = Importance.IF_UNSET)
     protected String mTestName = null;
 
-    @Option(name = CompatibilityTest.RETRY_TYPE_OPTION,
-            description = "used with " + CompatibilityTest.RETRY_OPTION + ", retry tests"
+    @Option(name = CompatibilityTestSuite.TEST_ARG_OPTION,
+            description = "the arguments to pass to a test. The expected format is "
+                    + "\"<test-class>:<arg-name>:[<arg-key>:=]<arg-value>\"",
+            importance = Importance.ALWAYS)
+    private List<String> mTestArgs = new ArrayList<>();
+
+    @Option(name = RETRY_TYPE_OPTION,
+            description = "used with " + RETRY_OPTION + ", retry tests"
             + " of a certain status. Possible values include \"failed\" and \"not_executed\".",
             importance = Importance.IF_UNSET)
     protected RetryType mRetryType = null;
@@ -176,11 +184,9 @@
 
         try {
             OptionSetter setter = new OptionSetter(test);
-            setter.setOptionValue("compatibility:test-arg",
-                    "com.android.tradefed.testtype.AndroidJUnitTest:rerun-from-file:true");
-            setter.setOptionValue("compatibility:test-arg",
-                    "com.android.tradefed.testtype.AndroidJUnitTest:fallback-to-serial-rerun:"
-                    + "false");
+            for (String testArg : mTestArgs) {
+                setter.setOptionValue("compatibility:test-arg", testArg);
+            }
         } catch (ConfigurationException e) {
             throw new RuntimeException(e);
         }
@@ -191,13 +197,17 @@
         test.setBuild(mBuildInfo);
         test.setSystemStatusChecker(mStatusCheckers);
         test.setInvocationContext(mContext);
+        // reset the retry id - Ensure that retry of retry does not throw
+        test.resetRetryId();
         // clean the helper
         helper.tearDown();
         return test;
     }
 
-    @VisibleForTesting
-    RetryFilterHelper createFilterHelper(CompatibilityBuildHelper buildHelper) {
+    /**
+     * @return a {@link RetryFilterHelper} created from the attributes of this object.
+     */
+    protected RetryFilterHelper createFilterHelper(CompatibilityBuildHelper buildHelper) {
         return new RetryFilterHelper(buildHelper, mRetrySessionId, mSubPlan, mIncludeFilters,
                 mExcludeFilters, mAbiName, mModuleName, mTestName, mRetryType);
     }
@@ -206,4 +216,11 @@
     CompatibilityTestSuite createTest() {
         return new CompatibilityTestSuite();
     }
+
+    /**
+     * @return the ID of the session to be retried.
+     */
+    protected Integer getRetrySessionId() {
+        return mRetrySessionId;
+    }
 }
diff --git a/common/host-side/tradefed/src/com/android/compatibility/common/tradefed/testtype/suite/CompatibilityTestSuite.java b/common/host-side/tradefed/src/com/android/compatibility/common/tradefed/testtype/suite/CompatibilityTestSuite.java
index bb74f23..80758fd 100644
--- a/common/host-side/tradefed/src/com/android/compatibility/common/tradefed/testtype/suite/CompatibilityTestSuite.java
+++ b/common/host-side/tradefed/src/com/android/compatibility/common/tradefed/testtype/suite/CompatibilityTestSuite.java
@@ -16,9 +16,9 @@
 package com.android.compatibility.common.tradefed.testtype.suite;
 
 import com.android.compatibility.common.tradefed.build.CompatibilityBuildHelper;
-import com.android.compatibility.common.tradefed.testtype.CompatibilityTest;
 import com.android.compatibility.common.tradefed.testtype.ISubPlan;
 import com.android.compatibility.common.tradefed.testtype.SubPlan;
+import com.android.compatibility.common.tradefed.testtype.retry.RetryFactoryTest;
 import com.android.compatibility.common.util.TestFilter;
 import com.android.tradefed.build.IBuildInfo;
 import com.android.tradefed.config.IConfiguration;
@@ -26,16 +26,9 @@
 import com.android.tradefed.config.Option.Importance;
 import com.android.tradefed.config.OptionClass;
 import com.android.tradefed.device.DeviceNotAvailableException;
-import com.android.tradefed.device.ITestDevice;
-import com.android.tradefed.log.LogUtil.CLog;
-import com.android.tradefed.testtype.Abi;
 import com.android.tradefed.testtype.IAbi;
 import com.android.tradefed.testtype.suite.ITestSuite;
-import com.android.tradefed.testtype.suite.TestSuiteInfo;
-import com.android.tradefed.util.AbiFormatter;
-import com.android.tradefed.util.AbiUtils;
 import com.android.tradefed.util.ArrayUtil;
-import com.android.tradefed.util.MultiMap;
 import com.android.tradefed.util.xml.AbstractXmlParser.ParseException;
 
 import java.io.File;
@@ -43,10 +36,8 @@
 import java.io.FileNotFoundException;
 import java.io.InputStream;
 import java.util.ArrayList;
-import java.util.Arrays;
 import java.util.HashSet;
 import java.util.LinkedHashMap;
-import java.util.LinkedHashSet;
 import java.util.List;
 import java.util.Set;
 
@@ -56,20 +47,17 @@
 @OptionClass(alias = "compatibility")
 public class CompatibilityTestSuite extends ITestSuite {
 
-    private static final String INCLUDE_FILTER_OPTION = "include-filter";
-    private static final String EXCLUDE_FILTER_OPTION = "exclude-filter";
-    private static final String SUBPLAN_OPTION = "subplan";
-    private static final String MODULE_OPTION = "module";
-    private static final String TEST_OPTION = "test";
+    public static final String INCLUDE_FILTER_OPTION = "include-filter";
+    public static final String EXCLUDE_FILTER_OPTION = "exclude-filter";
+    public static final String SUBPLAN_OPTION = "subplan";
+    public static final String MODULE_OPTION = "module";
+    public static final String TEST_ARG_OPTION = "test-arg";
+    public static final String TEST_OPTION = "test";
+    public static final char TEST_OPTION_SHORT_NAME = 't';
     private static final String MODULE_ARG_OPTION = "module-arg";
-    private static final String TEST_ARG_OPTION = "test-arg";
-    private static final String ABI_OPTION = "abi";
-    private static final String SKIP_HOST_ARCH_CHECK = "skip-host-arch-check";
-    private static final String PRIMARY_ABI_RUN = "primary-abi-only";
-    private static final String PRODUCT_CPU_ABI_KEY = "ro.product.cpu.abi";
 
     // TODO: remove this option when CompatibilityTest goes away
-    @Option(name = CompatibilityTest.RETRY_OPTION,
+    @Option(name = RetryFactoryTest.RETRY_OPTION,
             shortName = 'r',
             description = "Copy of --retry from CompatibilityTest to prevent using it.")
     private Integer mRetrySessionId = null;
@@ -96,7 +84,7 @@
     private String mModuleName = null;
 
     @Option(name = TEST_OPTION,
-            shortName = 't',
+            shortName = TEST_OPTION_SHORT_NAME,
             description = "the test to run.",
             importance = Importance.IF_UNSET)
     private String mTestName = null;
@@ -113,38 +101,6 @@
             importance = Importance.ALWAYS)
     private List<String> mTestArgs = new ArrayList<>();
 
-    @Option(name = ABI_OPTION,
-            shortName = 'a',
-            description = "the abi to test.",
-            importance = Importance.IF_UNSET)
-    private String mAbiName = null;
-
-    @Option(name = SKIP_HOST_ARCH_CHECK,
-            description = "Whether host architecture check should be skipped")
-    private boolean mSkipHostArchCheck = false;
-
-    @Option(name = PRIMARY_ABI_RUN,
-            description = "Whether to run tests with only the device primary abi. "
-                    + "This override the --abi option.")
-    private boolean mPrimaryAbiRun = false;
-
-    @Option(name = "module-metadata-include-filter",
-            description = "Include modules for execution based on matching of metadata fields: "
-                    + "for any of the specified filter name and value, if a module has a metadata "
-                    + "field with the same name and value, it will be included. When both module "
-                    + "inclusion and exclusion rules are applied, inclusion rules will be "
-                    + "evaluated first. Using this together with test filter inclusion rules may "
-                    + "result in no tests to execute if the rules don't overlap.")
-    private MultiMap<String, String> mModuleMetadataIncludeFilter = new MultiMap<>();
-
-    @Option(name = "module-metadata-exclude-filter",
-            description = "Exclude modules for execution based on matching of metadata fields: "
-                    + "for any of the specified filter name and value, if a module has a metadata "
-                    + "field with the same name and value, it will be excluded. When both module "
-                    + "inclusion and exclusion rules are applied, inclusion rules will be "
-                    + "evaluated first.")
-    private MultiMap<String, String> mModuleMetadataExcludeFilter = new MultiMap<>();
-
     private ModuleRepoSuite mModuleRepo = new ModuleRepoSuite();
     private CompatibilityBuildHelper mBuildHelper;
 
@@ -163,8 +119,7 @@
             // Initialize the repository, {@link CompatibilityBuildHelper#getTestsDir} can
             // throw a {@link FileNotFoundException}
             return mModuleRepo.loadConfigs(mBuildHelper.getTestsDir(),
-                    abis, mTestArgs, mModuleArgs, mIncludeFilters,
-                    mExcludeFilters, mModuleMetadataIncludeFilter, mModuleMetadataExcludeFilter);
+                    abis, mTestArgs, mModuleArgs, mIncludeFilters, mExcludeFilters);
         } catch (DeviceNotAvailableException | FileNotFoundException e) {
             throw new RuntimeException(e);
         }
@@ -180,64 +135,6 @@
     }
 
     /**
-     * Gets the set of ABIs supported by both Compatibility and the device under test
-     *
-     * @return The set of ABIs to run the tests on
-     * @throws DeviceNotAvailableException
-     */
-    Set<IAbi> getAbis(ITestDevice device) throws DeviceNotAvailableException {
-        Set<IAbi> abis = new LinkedHashSet<>();
-        Set<String> archAbis = getAbisForBuildTargetArch();
-        if (mPrimaryAbiRun) {
-            if (mAbiName == null) {
-                // Get the primary from the device and make it the --abi to run.
-                mAbiName = device.getProperty(PRODUCT_CPU_ABI_KEY).trim();
-            } else {
-                CLog.d("Option --%s supersedes the option --%s, using abi: %s", ABI_OPTION,
-                        PRIMARY_ABI_RUN, mAbiName);
-            }
-        }
-        if (mAbiName != null) {
-            // A particular abi was requested, it still needs to be supported by the build.
-            if ((!mSkipHostArchCheck && !archAbis.contains(mAbiName)) ||
-                    !AbiUtils.isAbiSupportedByCompatibility(mAbiName)) {
-                throw new IllegalArgumentException(String.format("Your CTS hasn't been built with "
-                        + "abi '%s' support, this CTS currently supports '%s'.",
-                        mAbiName, archAbis));
-            } else {
-                abis.add(new Abi(mAbiName, AbiUtils.getBitness(mAbiName)));
-                return abis;
-            }
-        } else {
-            // Run on all abi in common between the device and CTS.
-            List<String> deviceAbis = Arrays.asList(AbiFormatter.getSupportedAbis(device, ""));
-            for (String abi : deviceAbis) {
-                if ((mSkipHostArchCheck || archAbis.contains(abi)) &&
-                        AbiUtils.isAbiSupportedByCompatibility(abi)) {
-                    abis.add(new Abi(abi, AbiUtils.getBitness(abi)));
-                } else {
-                    CLog.d("abi '%s' is supported by device but not by this CTS build (%s), tests "
-                            + "will not run against it.", abi, archAbis);
-                }
-            }
-            if (abis.isEmpty()) {
-                throw new IllegalArgumentException(String.format("None of the abi supported by this"
-                       + " CTS build ('%s') are supported by the device ('%s').",
-                       archAbis, deviceAbis));
-            }
-            return abis;
-        }
-    }
-
-    /**
-     * Return the abis supported by the Host build target architecture.
-     * Exposed for testing.
-     */
-    protected Set<String> getAbisForBuildTargetArch() {
-        return AbiUtils.getAbisForArch(TestSuiteInfo.getInstance().getTargetArch());
-    }
-
-    /**
      * Sets the include/exclude filters up based on if a module name was given or whether this is a
      * retry run.
      */
@@ -273,7 +170,8 @@
                 String moduleName = modules.get(0);
                 checkFilters(mIncludeFilters, moduleName);
                 checkFilters(mExcludeFilters, moduleName);
-                mIncludeFilters.add(new TestFilter(mAbiName, moduleName, mTestName).toString());
+                mIncludeFilters.add(new TestFilter(getRequestedAbi(), moduleName, mTestName)
+                        .toString());
             }
         } else if (mTestName != null) {
             throw new IllegalArgumentException(
@@ -306,4 +204,11 @@
     public void setExcludeFilter(Set<String> excludeFilters) {
         mExcludeFilters.addAll(excludeFilters);
     }
+
+    /**
+     * Allow to reset the requested session id for retry.
+     */
+    public void resetRetryId() {
+        mRetrySessionId = null;
+    }
 }
diff --git a/common/host-side/tradefed/src/com/android/compatibility/common/tradefed/testtype/suite/ModuleRepoSuite.java b/common/host-side/tradefed/src/com/android/compatibility/common/tradefed/testtype/suite/ModuleRepoSuite.java
index a889330..9693ce0 100644
--- a/common/host-side/tradefed/src/com/android/compatibility/common/tradefed/testtype/suite/ModuleRepoSuite.java
+++ b/common/host-side/tradefed/src/com/android/compatibility/common/tradefed/testtype/suite/ModuleRepoSuite.java
@@ -30,7 +30,6 @@
 import com.android.tradefed.testtype.ITestFilterReceiver;
 import com.android.tradefed.util.AbiUtils;
 import com.android.tradefed.util.FileUtil;
-import com.android.tradefed.util.MultiMap;
 import com.android.tradefed.util.StreamUtil;
 
 import java.io.File;
@@ -42,7 +41,6 @@
 import java.util.Collection;
 import java.util.Collections;
 import java.util.HashMap;
-import java.util.HashSet;
 import java.util.LinkedHashMap;
 import java.util.List;
 import java.util.Map;
@@ -67,9 +65,7 @@
      */
     public LinkedHashMap<String, IConfiguration> loadConfigs(File testsDir, Set<IAbi> abis,
             List<String> testArgs, List<String> moduleArgs,
-            Set<String> includeFilters, Set<String> excludeFilters,
-            MultiMap<String, String> metadataIncludeFilters,
-            MultiMap<String, String> metadataExcludeFilters) {
+            Set<String> includeFilters, Set<String> excludeFilters) {
         CLog.d("Initializing ModuleRepo\nTests Dir:%s\nABIs:%s\n" +
                 "Test Args:%s\nModule Args:%s\nIncludes:%s\nExcludes:%s",
                 testsDir.getAbsolutePath(), abis, testArgs, moduleArgs,
@@ -108,12 +104,6 @@
                         continue;
                     }
                     IConfiguration config = mConfigFactory.createConfigurationFromArgs(pathArg);
-                    if (!filterByConfigMetadata(config,
-                            metadataIncludeFilters, metadataExcludeFilters)) {
-                        // if the module config did not pass the metadata filters, it's excluded
-                        // from execution
-                        continue;
-                    }
                     Map<String, List<String>> args = new HashMap<>();
                     if (mModuleArgs.containsKey(name)) {
                         args.putAll(mModuleArgs.get(name));
@@ -176,47 +166,6 @@
         }
     }
 
-    @VisibleForTesting
-    protected boolean filterByConfigMetadata(IConfiguration config,
-            MultiMap<String, String> include, MultiMap<String, String> exclude) {
-        MultiMap<String, String> metadata = config.getConfigurationDescription().getAllMetaData();
-        boolean shouldInclude = false;
-        for (String key : include.keySet()) {
-            Set<String> filters = new HashSet<>(include.get(key));
-            if (metadata.containsKey(key)) {
-                filters.retainAll(metadata.get(key));
-                if (!filters.isEmpty()) {
-                    // inclusion filter is not empty and there's at least one matching inclusion
-                    // rule so there's no need to match other inclusion rules
-                    shouldInclude = true;
-                    break;
-                }
-            }
-        }
-        if (!include.isEmpty() && !shouldInclude) {
-            // if inclusion filter is not empty and we didn't find a match, the module will not be
-            // included
-            return false;
-        }
-        // Now evaluate exclusion rules, this ordering also means that exclusion rules may override
-        // inclusion rules: a config already matched for inclusion may still be excluded if matching
-        // rules exist
-        for (String key : exclude.keySet()) {
-            Set<String> filters = new HashSet<>(exclude.get(key));
-            if (metadata.containsKey(key)) {
-                filters.retainAll(metadata.get(key));
-                if (!filters.isEmpty()) {
-                    // we found at least one matching exclusion rules, so we are excluding this
-                    // this module
-                    return false;
-                }
-            }
-        }
-        // we've matched at least one inclusion rule (if there's any) AND we didn't match any of the
-        // exclusion rules (if there's any)
-        return true;
-    }
-
     /**
      * @return the {@link List} of modules whose name contains the given pattern.
      */
diff --git a/common/host-side/tradefed/tests/src/com/android/compatibility/common/tradefed/UnitTests.java b/common/host-side/tradefed/tests/src/com/android/compatibility/common/tradefed/UnitTests.java
index 5aaa542..960d788 100644
--- a/common/host-side/tradefed/tests/src/com/android/compatibility/common/tradefed/UnitTests.java
+++ b/common/host-side/tradefed/tests/src/com/android/compatibility/common/tradefed/UnitTests.java
@@ -33,14 +33,12 @@
 import com.android.compatibility.common.tradefed.targetprep.MediaPreparerTest;
 import com.android.compatibility.common.tradefed.targetprep.PropertyCheckTest;
 import com.android.compatibility.common.tradefed.targetprep.SettingsPreparerTest;
-import com.android.compatibility.common.tradefed.testtype.CompatibilityHostTestBaseTest;
 import com.android.compatibility.common.tradefed.testtype.CompatibilityTestTest;
 import com.android.compatibility.common.tradefed.testtype.JarHostTestTest;
 import com.android.compatibility.common.tradefed.testtype.ModuleDefTest;
 import com.android.compatibility.common.tradefed.testtype.ModuleRepoTest;
 import com.android.compatibility.common.tradefed.testtype.SubPlanTest;
 import com.android.compatibility.common.tradefed.testtype.retry.RetryFactoryTestTest;
-import com.android.compatibility.common.tradefed.testtype.suite.CompatibilityTestSuiteTest;
 import com.android.compatibility.common.tradefed.testtype.suite.ModuleRepoSuiteTest;
 import com.android.compatibility.common.tradefed.util.CollectorUtilTest;
 import com.android.compatibility.common.tradefed.util.DynamicConfigFileReaderTest;
@@ -90,7 +88,6 @@
     SettingsPreparerTest.class,
 
     // testtype
-    CompatibilityHostTestBaseTest.class,
     CompatibilityTestTest.class,
     JarHostTestTest.class,
     ModuleDefTest.class,
@@ -101,7 +98,6 @@
     RetryFactoryTestTest.class,
 
     // testype.suite
-    CompatibilityTestSuiteTest.class,
     ModuleRepoSuiteTest.class,
 
     // util
diff --git a/common/host-side/tradefed/tests/src/com/android/compatibility/common/tradefed/build/CompatibilityBuildHelperTest.java b/common/host-side/tradefed/tests/src/com/android/compatibility/common/tradefed/build/CompatibilityBuildHelperTest.java
index 829dcc0..1a3c723 100644
--- a/common/host-side/tradefed/tests/src/com/android/compatibility/common/tradefed/build/CompatibilityBuildHelperTest.java
+++ b/common/host-side/tradefed/tests/src/com/android/compatibility/common/tradefed/build/CompatibilityBuildHelperTest.java
@@ -178,6 +178,7 @@
      * references and not absolute path. When sharding, path are invalidated but Files are copied.
      */
     public void testAddDynamicFiles() throws Exception {
+        createDirStructure();
         File tmpDynamicFile = FileUtil.createTempFile("cts-test-file", ".dynamic");
         FileUtil.writeToFile("test string", tmpDynamicFile);
         try {
diff --git a/common/host-side/tradefed/tests/src/com/android/compatibility/common/tradefed/build/CompatibilityBuildProviderTest.java b/common/host-side/tradefed/tests/src/com/android/compatibility/common/tradefed/build/CompatibilityBuildProviderTest.java
index 50c5da3..09e16a6 100644
--- a/common/host-side/tradefed/tests/src/com/android/compatibility/common/tradefed/build/CompatibilityBuildProviderTest.java
+++ b/common/host-side/tradefed/tests/src/com/android/compatibility/common/tradefed/build/CompatibilityBuildProviderTest.java
@@ -15,7 +15,6 @@
  */
 package com.android.compatibility.common.tradefed.build;
 
-import static org.junit.Assert.assertFalse;
 import static org.junit.Assert.assertNotNull;
 import static org.junit.Assert.assertTrue;
 
@@ -69,7 +68,10 @@
         EasyMock.replay(mMockDevice);
         IBuildInfo info = mProvider.getBuild(mMockDevice);
         EasyMock.verify(mMockDevice);
-        assertFalse(info instanceof IDeviceBuildInfo);
+        // Still creates a device build for us.
+        assertTrue(info instanceof IDeviceBuildInfo);
+        // tests dir should be populated
+        assertNotNull(((IDeviceBuildInfo)info).getTestsDir());
     }
 
     /**
diff --git a/common/host-side/tradefed/tests/src/com/android/compatibility/common/tradefed/presubmit/CtsConfigLoadingTest.java b/common/host-side/tradefed/tests/src/com/android/compatibility/common/tradefed/presubmit/CtsConfigLoadingTest.java
index bcc82ac9..644ae22 100644
--- a/common/host-side/tradefed/tests/src/com/android/compatibility/common/tradefed/presubmit/CtsConfigLoadingTest.java
+++ b/common/host-side/tradefed/tests/src/com/android/compatibility/common/tradefed/presubmit/CtsConfigLoadingTest.java
@@ -32,6 +32,8 @@
 
 import org.junit.Assert;
 import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.JUnit4;
 
 import java.io.File;
 import java.io.FilenameFilter;
@@ -43,6 +45,7 @@
 /**
  * Test that configuration in CTS can load and have expected properties.
  */
+@RunWith(JUnit4.class)
 public class CtsConfigLoadingTest {
 
     private static final String METADATA_COMPONENT = "component";
@@ -69,6 +72,7 @@
             "neuralnetworks",
             "renderscript",
             "security",
+            "statsd",
             "systems",
             "sysui",
             "telecom",
@@ -79,6 +83,22 @@
     ));
 
     /**
+     * List of the officially supported runners in CTS, they meet all the interfaces criteria as
+     * well as support sharding very well. Any new addition should go through a review.
+     */
+    private static final Set<String> SUPPORTED_CTS_TEST_TYPE = new HashSet<>(Arrays.asList(
+            // Cts runners
+            "com.android.compatibility.common.tradefed.testtype.JarHostTest",
+            "com.android.compatibility.testtype.DalvikTest",
+            "com.android.compatibility.testtype.LibcoreTest",
+            "com.drawelements.deqp.runner.DeqpTestRunner",
+            // Tradefed runners
+            "com.android.tradefed.testtype.AndroidJUnitTest",
+            "com.android.tradefed.testtype.HostTest",
+            "com.android.tradefed.testtype.GTest"
+    ));
+
+    /**
      * Test that configuration shipped in Tradefed can be parsed.
      * -> Exclude deprecated ApkInstaller.
      * -> Check if host-side tests are non empty.
@@ -121,6 +141,12 @@
             }
             // We can ensure that Host side tests are not empty.
             for (IRemoteTest test : c.getTests()) {
+                // Check that all the tests runners are well supported.
+                if (!SUPPORTED_CTS_TEST_TYPE.contains(test.getClass().getCanonicalName())) {
+                    throw new ConfigurationException(
+                            String.format("testtype %s is not officially supported by CTS.",
+                                    test.getClass().getCanonicalName()));
+                }
                 if (test instanceof HostTest) {
                     HostTest hostTest = (HostTest) test;
                     // We inject a made up folder so that it can find the tests.
@@ -148,6 +174,13 @@
             Assert.assertTrue(String.format("Module config contains unknown \"component\" metadata "
                     + "field \"%s\", supported ones are: %s\nconfig: %s",
                     cmp, KNOWN_COMPONENTS, config), KNOWN_COMPONENTS.contains(cmp));
+
+            // Ensure each CTS module is tagged with <option name="test-suite-tag" value="cts" />
+            Assert.assertTrue(String.format(
+                    "Module config %s does not contains "
+                    + "'<option name=\"test-suite-tag\" value=\"cts\" />'", config.getName()),
+                    cd.getSuiteTags().contains("cts"));
+
             // Check not-shardable: JarHostTest cannot create empty shards so it should never need
             // to be not-shardable.
             if (cd.isNotShardable()) {
diff --git a/common/host-side/tradefed/tests/src/com/android/compatibility/common/tradefed/testtype/CompatibilityHostTestBaseTest.java b/common/host-side/tradefed/tests/src/com/android/compatibility/common/tradefed/testtype/CompatibilityHostTestBaseTest.java
deleted file mode 100644
index 66d05fc..0000000
--- a/common/host-side/tradefed/tests/src/com/android/compatibility/common/tradefed/testtype/CompatibilityHostTestBaseTest.java
+++ /dev/null
@@ -1,101 +0,0 @@
-/*
- * Copyright (C) 2016 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.compatibility.common.tradefed.testtype;
-
-import com.android.ddmlib.IDevice;
-import com.android.ddmlib.testrunner.RemoteAndroidTestRunner;
-import com.android.ddmlib.testrunner.TestIdentifier;
-import com.android.ddmlib.testrunner.TestRunResult;
-import com.android.tradefed.build.IBuildInfo;
-import com.android.tradefed.device.ITestDevice;
-import com.android.tradefed.result.CollectingTestListener;
-import com.android.tradefed.result.ITestInvocationListener;
-import com.android.tradefed.result.JUnit4ResultForwarder;
-import com.android.tradefed.testtype.DeviceJUnit4ClassRunner;
-import com.android.tradefed.testtype.IAbi;
-import com.android.tradefed.testtype.IAbiReceiver;
-import com.android.tradefed.testtype.IBuildReceiver;
-import com.android.tradefed.testtype.IDeviceTest;
-
-import junit.framework.TestCase;
-
-import org.easymock.EasyMock;
-import org.junit.Test;
-import org.junit.runner.JUnitCore;
-import org.junit.runner.Request;
-import org.junit.runner.RunWith;
-import org.junit.runner.Runner;
-
-import java.util.Collections;
-
-/**
- * Tests for the CompatibilityHostTestBase class.
- */
-public class CompatibilityHostTestBaseTest extends TestCase {
-
-    private static final String DEVICE_TEST_PKG = "com.android.foo";
-
-    @RunWith(DeviceJUnit4ClassRunner.class)
-    public static class MockTest extends CompatibilityHostTestBase {
-
-        @Test
-        public void testRunDeviceTests() throws Exception {
-            runDeviceTests(DEVICE_TEST_PKG, null, null);
-        }
-
-        @Override
-        protected CollectingTestListener createCollectingListener() {
-            return new CollectingTestListener() {
-                @Override
-                public TestRunResult getCurrentRunResults() {
-                    TestRunResult result = new TestRunResult();
-                    TestIdentifier t1 = new TestIdentifier("class1", "test1");
-                    result.testStarted(t1);
-                    result.testEnded(t1, Collections.emptyMap());
-                    return result;
-                }
-            };
-        }
-
-    }
-
-    public void testRunMockDeviceTests() throws Exception {
-        final TestIdentifier testRunDeviceTests =
-                new TestIdentifier(MockTest.class.getName(), "testRunDeviceTests");
-
-        ITestInvocationListener listener = EasyMock.createStrictMock(ITestInvocationListener.class);
-        ITestDevice device = EasyMock.createMock(ITestDevice.class);
-
-        listener.testStarted(testRunDeviceTests);
-        EasyMock.expect(device.getIDevice()).andReturn(EasyMock.createMock(IDevice.class)).once();
-        EasyMock.expect(device.runInstrumentationTests((RemoteAndroidTestRunner)
-                EasyMock.anyObject(), (CollectingTestListener) EasyMock.anyObject())).andReturn(
-                true).once();
-        listener.testEnded(testRunDeviceTests, Collections.emptyMap());
-        EasyMock.replay(listener, device);
-
-        JUnitCore runnerCore = new JUnitCore();
-        runnerCore.addListener(new JUnit4ResultForwarder(listener));
-        Runner checkRunner = Request.aClass(MockTest.class).getRunner();
-        ((IDeviceTest) checkRunner).setDevice(device);
-        ((IBuildReceiver) checkRunner).setBuild(EasyMock.createMock(IBuildInfo.class));
-        ((IAbiReceiver) checkRunner).setAbi(EasyMock.createMock(IAbi.class));
-        runnerCore.run(checkRunner);
-        EasyMock.verify(listener, device);
-    }
-
-}
diff --git a/common/host-side/tradefed/tests/src/com/android/compatibility/common/tradefed/testtype/JarHostTestTest.java b/common/host-side/tradefed/tests/src/com/android/compatibility/common/tradefed/testtype/JarHostTestTest.java
index a06bca0..064cee4 100644
--- a/common/host-side/tradefed/tests/src/com/android/compatibility/common/tradefed/testtype/JarHostTestTest.java
+++ b/common/host-side/tradefed/tests/src/com/android/compatibility/common/tradefed/testtype/JarHostTestTest.java
@@ -15,19 +15,28 @@
  */
 package com.android.compatibility.common.tradefed.testtype;
 
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertTrue;
+import static org.junit.Assert.fail;
+
 import com.android.compatibility.common.tradefed.build.CompatibilityBuildHelper;
+import com.android.ddmlib.testrunner.TestIdentifier;
 import com.android.tradefed.build.BuildInfo;
 import com.android.tradefed.build.IBuildInfo;
 import com.android.tradefed.config.OptionSetter;
 import com.android.tradefed.device.ITestDevice;
 import com.android.tradefed.log.LogUtil.CLog;
+import com.android.tradefed.result.ITestInvocationListener;
+import com.android.tradefed.testtype.DeviceJUnit4ClassRunner.TestMetrics;
 import com.android.tradefed.testtype.HostTest;
 import com.android.tradefed.testtype.IRemoteTest;
 import com.android.tradefed.util.FileUtil;
 
-import junit.framework.TestCase;
-
 import org.easymock.EasyMock;
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Rule;
+import org.junit.Test;
 import org.junit.runner.RunWith;
 import org.junit.runners.JUnit4;
 
@@ -39,17 +48,21 @@
 import java.net.URL;
 import java.net.URLClassLoader;
 import java.util.Arrays;
+import java.util.HashMap;
 import java.util.List;
+import java.util.Map;
 
 /**
  * Unit tests for {@link JarHostTest}.
  */
-public class JarHostTestTest extends TestCase {
+@RunWith(JUnit4.class)
+public class JarHostTestTest {
 
     private static final String TEST_JAR1 = "/testtype/testJar1.jar";
     private static final String TEST_JAR2 = "/testtype/testJar2.jar";
     private JarHostTest mTest;
     private File mTestDir = null;
+    private ITestInvocationListener mListener;
 
     /**
      * More testable version of {@link JarHostTest}
@@ -74,23 +87,16 @@
         }
     }
 
-    /**
-     * {@inheritDoc}
-     */
-    @Override
-    protected void setUp() throws Exception {
-        super.setUp();
+    @Before
+    public void setUp() throws Exception {
         mTest = new JarHostTest();
         mTestDir = FileUtil.createTempDir("jarhostest");
+        mListener = EasyMock.createMock(ITestInvocationListener.class);
     }
 
-    /**
-     * {@inheritDoc}
-     */
-    @Override
-    protected void tearDown() throws Exception {
+    @After
+    public void tearDown() throws Exception {
         FileUtil.recursiveDelete(mTestDir);
-        super.tearDown();
     }
 
     /**
@@ -123,13 +129,18 @@
     @RunWith(JUnit4.class)
     public static class Junit4TestClass2  {
         public Junit4TestClass2() {}
+        @Rule public TestMetrics metrics = new TestMetrics();
+
         @org.junit.Test
-        public void testPass2() {}
+        public void testPass2() {
+            metrics.addTestMetric("key", "value");
+        }
     }
 
     /**
      * Test that {@link JarHostTest#split()} inherited from {@link HostTest} is still good.
      */
+    @Test
     public void testSplit_withoutJar() throws Exception {
         OptionSetter setter = new OptionSetter(mTest);
         setter.setOptionValue("class", "com.android.compatibility.common.tradefed.testtype."
@@ -146,6 +157,7 @@
     /**
      * Test that {@link JarHostTest#split()} can split classes coming from a jar.
      */
+    @Test
     public void testSplit_withJar() throws Exception {
         File testJar = getJarResource(TEST_JAR1, mTestDir);
         mTest = new JarHostTestable(mTestDir);
@@ -165,6 +177,7 @@
     /**
      * Test that {@link JarHostTest#getTestShard(int, int)} can split classes coming from a jar.
      */
+    @Test
     public void testGetTestShard_withJar() throws Exception {
         File testJar = getJarResource(TEST_JAR2, mTestDir);
         mTest = new JarHostTestLoader(mTestDir, testJar);
@@ -259,4 +272,45 @@
             return child;
         }
     }
+
+    /**
+     * If a jar file is not found, the countTest will fail but we still want to report a
+     * testRunStart and End pair for results.
+     */
+    @Test
+    public void testCountTestFails() throws Exception {
+        OptionSetter setter = new OptionSetter(mTest);
+        setter.setOptionValue("jar", "thisjardoesnotexistatall.jar");
+        mListener.testRunStarted(EasyMock.anyObject(), EasyMock.eq(0));
+        mListener.testRunEnded(EasyMock.anyLong(), EasyMock.anyObject());
+        EasyMock.replay(mListener);
+        try {
+            mTest.run(mListener);
+            fail("Should have thrown an exception.");
+        } catch(RuntimeException expected) {
+            // expected
+        }
+        EasyMock.verify(mListener);
+    }
+
+    /**
+     * Test that metrics from tests in JarHost are reported and accounted for.
+     */
+    @Test
+    public void testJarHostMetrics() throws Exception {
+        OptionSetter setter = new OptionSetter(mTest);
+        setter.setOptionValue("class", "com.android.compatibility.common.tradefed.testtype."
+                + "JarHostTestTest$Junit4TestClass2");
+        mListener.testRunStarted(EasyMock.anyObject(), EasyMock.eq(1));
+        TestIdentifier tid = new TestIdentifier("com.android.compatibility.common.tradefed."
+                + "testtype.JarHostTestTest$Junit4TestClass2", "testPass2");
+        mListener.testStarted(EasyMock.eq(tid), EasyMock.anyLong());
+        Map<String, String> metrics = new HashMap<>();
+        metrics.put("key", "value");
+        mListener.testEnded(EasyMock.eq(tid), EasyMock.anyLong(), EasyMock.eq(metrics));
+        mListener.testRunEnded(EasyMock.anyLong(), EasyMock.anyObject());
+        EasyMock.replay(mListener);
+        mTest.run(mListener);
+        EasyMock.verify(mListener);
+    }
 }
diff --git a/common/host-side/tradefed/tests/src/com/android/compatibility/common/tradefed/testtype/retry/RetryFactoryTestTest.java b/common/host-side/tradefed/tests/src/com/android/compatibility/common/tradefed/testtype/retry/RetryFactoryTestTest.java
index d67619a..682088c 100644
--- a/common/host-side/tradefed/tests/src/com/android/compatibility/common/tradefed/testtype/retry/RetryFactoryTestTest.java
+++ b/common/host-side/tradefed/tests/src/com/android/compatibility/common/tradefed/testtype/retry/RetryFactoryTestTest.java
@@ -15,10 +15,9 @@
  */
 package com.android.compatibility.common.tradefed.testtype.retry;
 
-import static org.junit.Assert.*;
+import static org.junit.Assert.assertEquals;
 
 import com.android.compatibility.common.tradefed.build.CompatibilityBuildHelper;
-import com.android.compatibility.common.tradefed.testtype.CompatibilityTest;
 import com.android.compatibility.common.tradefed.testtype.suite.CompatibilityTestSuite;
 import com.android.compatibility.common.tradefed.util.RetryFilterHelper;
 import com.android.tradefed.config.IConfiguration;
@@ -52,7 +51,7 @@
     private RetryFilterHelper mSpyFilter;
 
     /**
-     * A {@link CompatibilityTest} that does not run anything.
+     * A {@link CompatibilityTestSuite} that does not run anything.
      */
     @OptionClass(alias = "compatibility")
     public static class VoidCompatibilityTest extends CompatibilityTestSuite {
@@ -90,7 +89,7 @@
         };
         mFactory = new RetryFactoryTest() {
             @Override
-            RetryFilterHelper createFilterHelper(CompatibilityBuildHelper buildHelper) {
+            protected RetryFilterHelper createFilterHelper(CompatibilityBuildHelper buildHelper) {
                 return mSpyFilter;
             }
             @Override
@@ -108,6 +107,7 @@
     public void testRetry_receiveOption() throws Exception {
         OptionSetter setter = new OptionSetter(mFactory);
         setter.setOptionValue("retry", "10599");
+        setter.setOptionValue("test-arg", "abcd");
         EasyMock.replay(mMockListener);
         mFactory.run(mMockListener);
         EasyMock.verify(mMockListener);
diff --git a/common/host-side/tradefed/tests/src/com/android/compatibility/common/tradefed/testtype/suite/CompatibilityTestSuiteTest.java b/common/host-side/tradefed/tests/src/com/android/compatibility/common/tradefed/testtype/suite/CompatibilityTestSuiteTest.java
deleted file mode 100644
index 86569ef..0000000
--- a/common/host-side/tradefed/tests/src/com/android/compatibility/common/tradefed/testtype/suite/CompatibilityTestSuiteTest.java
+++ /dev/null
@@ -1,179 +0,0 @@
-/*
- * Copyright (C) 2017 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package com.android.compatibility.common.tradefed.testtype.suite;
-
-import static org.junit.Assert.assertEquals;
-import static org.junit.Assert.assertTrue;
-import static org.junit.Assert.fail;
-
-import com.android.compatibility.common.tradefed.testtype.CompatibilityTest;
-import com.android.tradefed.config.OptionSetter;
-import com.android.tradefed.device.DeviceNotAvailableException;
-import com.android.tradefed.device.ITestDevice;
-import com.android.tradefed.testtype.IAbi;
-import com.android.tradefed.util.AbiUtils;
-
-import org.easymock.EasyMock;
-import org.junit.Before;
-import org.junit.Test;
-
-import java.util.HashSet;
-import java.util.Set;
-
-/**
- * Unit tests for {@link CompatibilityTestSuite}.
- */
-public class CompatibilityTestSuiteTest {
-
-    private static final String FAKE_HOST_ARCH = "arm";
-    private CompatibilityTestSuite mTest;
-    private ITestDevice mMockDevice;
-
-    @Before
-    public void setUp() throws Exception {
-        mTest = new CompatibilityTestSuite() {
-            @Override
-            protected Set<String> getAbisForBuildTargetArch() {
-                return AbiUtils.getAbisForArch(FAKE_HOST_ARCH);
-            }
-        };
-        mMockDevice = EasyMock.createMock(ITestDevice.class);
-        mTest.setDevice(mMockDevice);
-    }
-
-    /**
-     * Test that {@link CompatibilityTestSuite#getAbis(ITestDevice)} is returning a proper
-     * intersection of CTS supported architectures and Device supported architectures.
-     */
-    @Test
-    public void testGetAbis() throws DeviceNotAvailableException {
-        EasyMock.expect(mMockDevice.getProperty(EasyMock.eq("ro.product.cpu.abilist")))
-                .andReturn("arm64-v8a,armeabi-v7a,armeabi");
-        Set<String> expectedAbis = new HashSet<>();
-        expectedAbis.add("arm64-v8a");
-        expectedAbis.add("armeabi-v7a");
-        EasyMock.replay(mMockDevice);
-        Set<IAbi> res = mTest.getAbis(mMockDevice);
-        assertEquals(2, res.size());
-        for (IAbi abi : res) {
-            assertTrue(expectedAbis.contains(abi.getName()));
-        }
-        EasyMock.verify(mMockDevice);
-    }
-
-    /**
-     * Test that {@link CompatibilityTestSuite#getAbis(ITestDevice)} is throwing an exception when
-     * none of the CTS build supported abi match the device abi.
-     */
-    @Test
-    public void testGetAbis_notSupported() throws DeviceNotAvailableException {
-        EasyMock.expect(mMockDevice.getProperty(EasyMock.eq("ro.product.cpu.abilist")))
-                .andReturn("armeabi");
-        EasyMock.replay(mMockDevice);
-        try {
-            mTest.getAbis(mMockDevice);
-            fail("Should have thrown an exception");
-        } catch (IllegalArgumentException e) {
-            assertEquals("None of the abi supported by this CTS build ('[armeabi-v7a, arm64-v8a]')"
-                    + " are supported by the device ('[armeabi]').", e.getMessage());
-        }
-        EasyMock.verify(mMockDevice);
-    }
-
-    /**
-     * Test that {@link CompatibilityTestSuite#getAbis(ITestDevice)} is returning only the device
-     * primary abi.
-     */
-    @Test
-    public void testGetAbis_primaryAbiOnly() throws Exception {
-        OptionSetter setter = new OptionSetter(mTest);
-        setter.setOptionValue(CompatibilityTest.PRIMARY_ABI_RUN, "true");
-        EasyMock.expect(mMockDevice.getProperty(EasyMock.eq("ro.product.cpu.abi")))
-                .andReturn("arm64-v8a");
-        Set<String> expectedAbis = new HashSet<>();
-        expectedAbis.add("arm64-v8a");
-        EasyMock.replay(mMockDevice);
-        Set<IAbi> res = mTest.getAbis(mMockDevice);
-        assertEquals(1, res.size());
-        for (IAbi abi : res) {
-            assertTrue(expectedAbis.contains(abi.getName()));
-        }
-        EasyMock.verify(mMockDevice);
-    }
-
-    /**
-     * Test that {@link CompatibilityTestSuite#getAbis(ITestDevice)} is throwing an exception if
-     * the primary abi is not supported.
-     */
-    @Test
-    public void testGetAbis_primaryAbiOnly_NotSupported() throws Exception {
-        OptionSetter setter = new OptionSetter(mTest);
-        setter.setOptionValue(CompatibilityTest.PRIMARY_ABI_RUN, "true");
-        EasyMock.expect(mMockDevice.getProperty(EasyMock.eq("ro.product.cpu.abi")))
-                .andReturn("armeabi");
-        EasyMock.replay(mMockDevice);
-        try {
-            mTest.getAbis(mMockDevice);
-            fail("Should have thrown an exception");
-        } catch (IllegalArgumentException e) {
-            assertEquals("Your CTS hasn't been built with abi 'armeabi' support, "
-                    + "this CTS currently supports '[armeabi-v7a, arm64-v8a]'.", e.getMessage());
-        }
-        EasyMock.verify(mMockDevice);
-    }
-
-    /**
-     * Test that {@link CompatibilityTestSuite#getAbis(ITestDevice)} is returning the list of abi
-     * supported by Compatibility and the device, and not the particular CTS build.
-     */
-    @Test
-    public void testGetAbis_skipCtsArchCheck() throws Exception {
-        OptionSetter setter = new OptionSetter(mTest);
-        setter.setOptionValue(CompatibilityTest.SKIP_HOST_ARCH_CHECK, "true");
-        EasyMock.expect(mMockDevice.getProperty(EasyMock.eq("ro.product.cpu.abilist")))
-                .andReturn("x86_64,x86,armeabi");
-        Set<String> expectedAbis = new HashSet<>();
-        expectedAbis.add("x86_64");
-        expectedAbis.add("x86");
-        EasyMock.replay(mMockDevice);
-        Set<IAbi> res = mTest.getAbis(mMockDevice);
-        assertEquals(2, res.size());
-        for (IAbi abi : res) {
-            assertTrue(expectedAbis.contains(abi.getName()));
-        }
-        EasyMock.verify(mMockDevice);
-    }
-
-    /**
-     * Test {@link CompatibilityTestSuite#getAbis(ITestDevice)} when we skip the Cts side
-     * architecture check and want to run x86 abi.
-     */
-    @Test
-    public void testGetAbis_skipCtsArchCheck_abiSpecified() throws Exception {
-        OptionSetter setter = new OptionSetter(mTest);
-        setter.setOptionValue(CompatibilityTest.SKIP_HOST_ARCH_CHECK, "true");
-        setter.setOptionValue(CompatibilityTest.ABI_OPTION, "x86");
-        Set<String> expectedAbis = new HashSet<>();
-        expectedAbis.add("x86");
-        EasyMock.replay(mMockDevice);
-        Set<IAbi> res = mTest.getAbis(mMockDevice);
-        assertEquals(1, res.size());
-        for (IAbi abi : res) {
-            assertTrue(expectedAbis.contains(abi.getName()));
-        }
-        EasyMock.verify(mMockDevice);
-    }
-}
diff --git a/common/host-side/tradefed/tests/src/com/android/compatibility/common/tradefed/testtype/suite/ModuleRepoSuiteTest.java b/common/host-side/tradefed/tests/src/com/android/compatibility/common/tradefed/testtype/suite/ModuleRepoSuiteTest.java
index c67afc0..31db704 100644
--- a/common/host-side/tradefed/tests/src/com/android/compatibility/common/tradefed/testtype/suite/ModuleRepoSuiteTest.java
+++ b/common/host-side/tradefed/tests/src/com/android/compatibility/common/tradefed/testtype/suite/ModuleRepoSuiteTest.java
@@ -16,17 +16,13 @@
 package com.android.compatibility.common.tradefed.testtype.suite;
 
 import static org.junit.Assert.assertEquals;
-import static org.junit.Assert.assertFalse;
-import static org.junit.Assert.assertTrue;
 
 import com.android.tradefed.config.Configuration;
-import com.android.tradefed.config.ConfigurationDescriptor;
 import com.android.tradefed.config.IConfiguration;
 import com.android.tradefed.config.Option;
 import com.android.tradefed.device.DeviceNotAvailableException;
 import com.android.tradefed.result.ITestInvocationListener;
 import com.android.tradefed.testtype.IRemoteTest;
-import com.android.tradefed.util.MultiMap;
 
 import org.junit.Before;
 import org.junit.Test;
@@ -44,8 +40,6 @@
 @RunWith(JUnit4.class)
 public class ModuleRepoSuiteTest {
 
-    private static final MultiMap<String, String> METADATA_INCLUDES = new MultiMap<>();
-    private static final MultiMap<String, String> METADATA_EXCLUDES = new MultiMap<>();
     private ModuleRepoSuite mRepo;
 
     @Before
@@ -53,261 +47,6 @@
         mRepo = new ModuleRepoSuite();
     }
 
-    /**
-     * When there are no metadata based filters specified, config should be included.
-     */
-    @Test
-    public void testMetadataFilter_emptyFilters() throws Exception {
-        IConfiguration config = new Configuration("foo", "bar");
-        assertTrue("config not included when metadata filters are empty",
-                mRepo.filterByConfigMetadata(config, METADATA_INCLUDES, METADATA_EXCLUDES));
-    }
-
-    /**
-     * When inclusion filter is specified, config matching the filter is included.
-     */
-    @Test
-    public void testMetadataFilter_matchInclude() throws Exception {
-        IConfiguration config = new Configuration("foo", "bar");
-        ConfigurationDescriptor desc = config.getConfigurationDescription();
-        MultiMap<String, String> metadata = new MultiMap<>();
-        metadata.put("component", "foo");
-        desc.setMetaData(metadata);
-        MultiMap<String, String> includeFilter = new MultiMap<>();
-        includeFilter.put("component", "foo");
-        assertTrue("config not included with matching inclusion filter",
-                mRepo.filterByConfigMetadata(config, includeFilter, METADATA_EXCLUDES));
-    }
-
-    /**
-     * When inclusion filter is specified, config not matching the filter is excluded
-     */
-    @Test
-    public void testMetadataFilter_noMatchInclude_mismatchValue() throws Exception {
-        IConfiguration config = new Configuration("foo", "bar");
-        ConfigurationDescriptor desc = config.getConfigurationDescription();
-        MultiMap<String, String> metadata = new MultiMap<>();
-        metadata.put("component", "foo");
-        desc.setMetaData(metadata);
-        MultiMap<String, String> includeFilter = new MultiMap<>();
-        includeFilter.put("component", "bar");
-        assertFalse("config not excluded with mismatching inclusion filter",
-                mRepo.filterByConfigMetadata(config, includeFilter, METADATA_EXCLUDES));
-    }
-
-    /**
-     * When inclusion filter is specified, config not matching the filter is excluded.
-     */
-    @Test
-    public void testMetadataFilter_noMatchInclude_mismatchKey() throws Exception {
-        IConfiguration config = new Configuration("foo", "bar");
-        ConfigurationDescriptor desc = config.getConfigurationDescription();
-        MultiMap<String, String> metadata = new MultiMap<>();
-        metadata.put("component", "foo");
-        desc.setMetaData(metadata);
-        MultiMap<String, String> includeFilter = new MultiMap<>();
-        includeFilter.put("group", "bar");
-        assertFalse("config not excluded with mismatching inclusion filter",
-                mRepo.filterByConfigMetadata(config, includeFilter, METADATA_EXCLUDES));
-    }
-
-    /**
-     * When exclusion filter is specified, config matching the filter is excluded.
-     */
-    @Test
-    public void testMetadataFilter_matchExclude() throws Exception {
-        IConfiguration config = new Configuration("foo", "bar");
-        ConfigurationDescriptor desc = config.getConfigurationDescription();
-        MultiMap<String, String> metadata = new MultiMap<>();
-        metadata.put("component", "foo");
-        desc.setMetaData(metadata);
-        MultiMap<String, String> excludeFilter = new MultiMap<>();
-        excludeFilter.put("component", "foo");
-        assertFalse("config not excluded with matching exclusion filter",
-                mRepo.filterByConfigMetadata(config, METADATA_INCLUDES, excludeFilter));
-    }
-
-    /**
-     * When exclusion filter is specified, config not matching the filter is included.
-     */
-    @Test
-    public void testMetadataFilter_noMatchExclude_mismatchKey() throws Exception {
-        IConfiguration config = new Configuration("foo", "bar");
-        ConfigurationDescriptor desc = config.getConfigurationDescription();
-        MultiMap<String, String> metadata = new MultiMap<>();
-        metadata.put("component", "foo");
-        desc.setMetaData(metadata);
-        MultiMap<String, String> excludeFilter = new MultiMap<>();
-        excludeFilter.put("component", "bar");
-        assertTrue("config not included with mismatching exclusion filter",
-                mRepo.filterByConfigMetadata(config, METADATA_INCLUDES, excludeFilter));
-    }
-
-    /**
-     * When exclusion filter is specified, config not matching the filter is included.
-     */
-    @Test
-    public void testMetadataFilter_noMatchExclude_mismatchValue() throws Exception {
-        IConfiguration config = new Configuration("foo", "bar");
-        ConfigurationDescriptor desc = config.getConfigurationDescription();
-        MultiMap<String, String> metadata = new MultiMap<>();
-        metadata.put("component", "foo");
-        desc.setMetaData(metadata);
-        MultiMap<String, String> excludeFilter = new MultiMap<>();
-        excludeFilter.put("group", "bar");
-        assertTrue("config not included with mismatching exclusion filter",
-                mRepo.filterByConfigMetadata(config, METADATA_INCLUDES, excludeFilter));
-    }
-
-    /**
-     * When inclusion filter is specified, config with one of the metadata field matching the filter
-     * is included.
-     */
-    @Test
-    public void testMetadataFilter_matchInclude_multipleMetadataField() throws Exception {
-        IConfiguration config = new Configuration("foo", "bar");
-        ConfigurationDescriptor desc = config.getConfigurationDescription();
-        MultiMap<String, String> metadata = new MultiMap<>();
-        metadata.put("component", "foo");
-        metadata.put("component", "bar");
-        desc.setMetaData(metadata);
-        MultiMap<String, String> includeFilter = new MultiMap<>();
-        includeFilter.put("component", "foo");
-        assertTrue("config not included with matching inclusion filter",
-                mRepo.filterByConfigMetadata(config, includeFilter, METADATA_EXCLUDES));
-    }
-
-    /**
-     * When exclusion filter is specified, config with one of the metadata field matching the filter
-     * is excluded.
-     */
-    @Test
-    public void testMetadataFilter_matchExclude_multipleMetadataField() throws Exception {
-        IConfiguration config = new Configuration("foo", "bar");
-        ConfigurationDescriptor desc = config.getConfigurationDescription();
-        MultiMap<String, String> metadata = new MultiMap<>();
-        metadata.put("component", "foo");
-        metadata.put("component", "bar");
-        desc.setMetaData(metadata);
-        MultiMap<String, String> excludeFilter = new MultiMap<>();
-        excludeFilter.put("component", "foo");
-        assertFalse("config not excluded with matching exclusion filter",
-                mRepo.filterByConfigMetadata(config, METADATA_INCLUDES, excludeFilter));
-    }
-
-    /**
-     * When inclusion filters are specified, config with metadata field matching one of the filter
-     * is included.
-     */
-    @Test
-    public void testMetadataFilter_matchInclude_multipleFilters() throws Exception {
-        IConfiguration config = new Configuration("foo", "bar");
-        ConfigurationDescriptor desc = config.getConfigurationDescription();
-        MultiMap<String, String> metadata = new MultiMap<>();
-        metadata.put("component", "foo");
-        desc.setMetaData(metadata);
-        MultiMap<String, String> includeFilter = new MultiMap<>();
-        includeFilter.put("component", "foo");
-        includeFilter.put("component", "bar");
-        assertTrue("config not included with matching inclusion filter",
-                mRepo.filterByConfigMetadata(config, includeFilter, METADATA_EXCLUDES));
-    }
-
-    /**
-     * When exclusion filters are specified, config with metadata field matching one of the filter
-     * is excluded.
-     */
-    @Test
-    public void testMetadataFilter_matchExclude_multipleFilters() throws Exception {
-        IConfiguration config = new Configuration("foo", "bar");
-        ConfigurationDescriptor desc = config.getConfigurationDescription();
-        MultiMap<String, String> metadata = new MultiMap<>();
-        metadata.put("component", "foo");
-        desc.setMetaData(metadata);
-        MultiMap<String, String> excludeFilter = new MultiMap<>();
-        excludeFilter.put("component", "foo");
-        excludeFilter.put("component", "bar");
-        assertFalse("config not excluded with matching exclusion filter",
-                mRepo.filterByConfigMetadata(config, METADATA_INCLUDES, excludeFilter));
-    }
-
-    /**
-     * When inclusion filters are specified, config with metadata field matching one of the filter
-     * is included.
-     */
-    @Test
-    public void testMetadataFilter_matchInclude_multipleMetadataAndFilters() throws Exception {
-        IConfiguration config = new Configuration("foo", "bar");
-        ConfigurationDescriptor desc = config.getConfigurationDescription();
-        MultiMap<String, String> metadata = new MultiMap<>();
-        metadata.put("component", "foo1");
-        metadata.put("group", "bar1");
-        desc.setMetaData(metadata);
-        MultiMap<String, String> includeFilter = new MultiMap<>();
-        includeFilter.put("component", "foo1");
-        includeFilter.put("group", "bar2");
-        assertTrue("config not included with matching inclusion filter",
-                mRepo.filterByConfigMetadata(config, includeFilter, METADATA_EXCLUDES));
-    }
-
-    /**
-     * When exclusion filters are specified, config with metadata field matching one of the filter
-     * is excluded.
-     */
-    @Test
-    public void testMetadataFilter_matchExclude_multipleMetadataAndFilters() throws Exception {
-        IConfiguration config = new Configuration("foo", "bar");
-        ConfigurationDescriptor desc = config.getConfigurationDescription();
-        MultiMap<String, String> metadata = new MultiMap<>();
-        metadata.put("component", "foo1");
-        metadata.put("group", "bar1");
-        desc.setMetaData(metadata);
-        MultiMap<String, String> excludeFilter = new MultiMap<>();
-        excludeFilter.put("component", "foo1");
-        excludeFilter.put("group", "bar2");
-        assertFalse("config not excluded with matching exclusion filter",
-                mRepo.filterByConfigMetadata(config, METADATA_INCLUDES, excludeFilter));
-    }
-
-    /**
-     * When inclusion and exclusion filters are both specified, config can pass through the filters
-     * as expected.
-     */
-    @Test
-    public void testMetadataFilter_includeAndExclude() throws Exception {
-        IConfiguration config = new Configuration("foo", "bar");
-        ConfigurationDescriptor desc = config.getConfigurationDescription();
-        MultiMap<String, String> metadata = new MultiMap<>();
-        metadata.put("component", "foo");
-        metadata.put("group", "bar1");
-        desc.setMetaData(metadata);
-        MultiMap<String, String> includeFilter = new MultiMap<>();
-        includeFilter.put("component", "foo");
-        MultiMap<String, String> excludeFilter = new MultiMap<>();
-        excludeFilter.put("group", "bar2");
-        assertTrue("config not included with matching inclusion and mismatching exclusion filters",
-                mRepo.filterByConfigMetadata(config, includeFilter, excludeFilter));
-    }
-
-    /**
-     * When inclusion and exclusion filters are both specified, config be excluded as specified
-     */
-    @Test
-    public void testMetadataFilter_includeThenExclude() throws Exception {
-        IConfiguration config = new Configuration("foo", "bar");
-        ConfigurationDescriptor desc = config.getConfigurationDescription();
-        MultiMap<String, String> metadata = new MultiMap<>();
-        metadata.put("component", "foo");
-        metadata.put("group", "bar");
-        desc.setMetaData(metadata);
-        MultiMap<String, String> includeFilter = new MultiMap<>();
-        includeFilter.put("component", "foo");
-        MultiMap<String, String> excludeFilter = new MultiMap<>();
-        excludeFilter.put("group", "bar");
-        assertFalse("config not excluded with matching inclusion and exclusion filters",
-                mRepo.filterByConfigMetadata(config, includeFilter, excludeFilter));
-    }
-
     public static class TestInject implements IRemoteTest {
         @Option(name = "simple-string")
         public String test = null;
diff --git a/common/host-side/util/src/com/android/compatibility/common/util/DeviceInfo.java b/common/host-side/util/src/com/android/compatibility/common/util/DeviceInfo.java
index 5d303e5..369189d 100644
--- a/common/host-side/util/src/com/android/compatibility/common/util/DeviceInfo.java
+++ b/common/host-side/util/src/com/android/compatibility/common/util/DeviceInfo.java
@@ -18,13 +18,12 @@
 import static org.junit.Assert.fail;
 
 import com.android.compatibility.common.util.HostInfoStore;
-import com.android.tradefed.device.ITestDevice;
 import com.android.tradefed.log.LogUtil.CLog;
 import com.android.tradefed.result.FileInputStreamSource;
 import com.android.tradefed.result.LogDataType;
 import com.android.tradefed.testtype.DeviceJUnit4ClassRunner;
 import com.android.tradefed.testtype.DeviceJUnit4ClassRunner.TestLogData;
-import com.android.tradefed.testtype.IDeviceTest;
+import com.android.tradefed.testtype.junit4.BaseHostJUnit4Test;
 import com.android.tradefed.util.FileUtil;
 import com.android.tradefed.util.StreamUtil;
 
@@ -38,37 +37,18 @@
  * Collect device information from host and write to a JSON file.
  */
 @RunWith(DeviceJUnit4ClassRunner.class)
-public abstract class DeviceInfo implements IDeviceTest {
+public abstract class DeviceInfo extends BaseHostJUnit4Test {
 
     // Default name of the directory for storing device info files within the result directory
     public static final String RESULT_DIR_NAME = "device-info-files";
 
     public static final String FILE_SUFFIX = ".deviceinfo.json";
 
-    /** A reference to the device under test. */
-    protected ITestDevice mDevice;
-
     private HostInfoStore mStore;
 
     @Rule
     public TestLogData mLogger = new TestLogData();
 
-    /**
-     * {@inheritDoc}
-     */
-    @Override
-    public ITestDevice getDevice() {
-        return mDevice;
-    }
-
-    /**
-     * {@inheritDoc}
-     */
-    @Override
-    public void setDevice(ITestDevice device) {
-        mDevice = device;
-    }
-
     @Test
     public void testCollectDeviceInfo() throws Exception {
         String deviceInfoName = getClass().getSimpleName() + FILE_SUFFIX;
diff --git a/common/util/src/com/android/compatibility/common/util/LogcatInspector.java b/common/util/src/com/android/compatibility/common/util/LogcatInspector.java
new file mode 100644
index 0000000..ed82307
--- /dev/null
+++ b/common/util/src/com/android/compatibility/common/util/LogcatInspector.java
@@ -0,0 +1,130 @@
+package com.android.compatibility.common.util;
+
+import static junit.framework.TestCase.fail;
+
+import com.google.common.base.Joiner;
+import com.google.common.io.Closeables;
+
+import java.io.BufferedReader;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.InputStreamReader;
+import java.util.UUID;
+import java.util.concurrent.TimeUnit;
+
+/**
+ * Inherit this class and implement {@link #executeShellCommand(String)} to be able to assert that
+ * logcat contains what you want.
+ */
+public abstract class LogcatInspector {
+    private static final int SMALL_LOGCAT_DELAY = 1000;
+
+    /**
+     * Should execute adb shell {@param command} and return an {@link InputStream} with the result.
+     */
+    protected abstract InputStream executeShellCommand(String command) throws IOException;
+
+    /**
+     * Logs an unique string using tag {@param tag} and wait until it appears to continue execution.
+     *
+     * @return a unique separator string.
+     * @throws IOException if error while executing command.
+     */
+    public String mark(String tag) throws IOException {
+        String uniqueString = ":::" + UUID.randomUUID().toString();
+        executeShellCommand("log -t " + tag + " " + uniqueString);
+        // This is to guarantee that we only return after the string has been logged, otherwise
+        // in practice the case where calling Log.?(<message1>) right after clearAndMark() resulted
+        // in <message1> appearing before the unique identifier. It's not guaranteed per the docs
+        // that log command will have written when returning, so better be safe. 5s should be fine.
+        assertLogcatContainsInOrder(tag + ":* *:S", 5, uniqueString);
+        return uniqueString;
+    }
+
+    /**
+     * Wait for up to {@param maxTimeoutInSeconds} for the given {@param logcatStrings} strings to
+     * appear in logcat in the given order. By passing the separator returned by {@link
+     * #mark(String)} as the first string you can ensure that only logs emitted after that
+     * call to mark() are found. Repeated strings are not supported.
+     *
+     * @throws AssertionError if the strings are not found in the given time.
+     * @throws IOException if error while reading.
+     */
+    public void assertLogcatContainsInOrder(
+            String filterSpec, int maxTimeoutInSeconds, String... logcatStrings)
+            throws AssertionError, IOException {
+        try {
+            int nextStringIndex =
+                    numberOfLogcatStringsFound(filterSpec, maxTimeoutInSeconds, logcatStrings);
+            if (nextStringIndex < logcatStrings.length) {
+                fail(
+                        "Couldn't find "
+                                + logcatStrings[nextStringIndex]
+                                + (nextStringIndex > 0
+                                        ? " after " + logcatStrings[nextStringIndex - 1]
+                                        : "")
+                                + " within "
+                                + maxTimeoutInSeconds
+                                + " seconds ");
+            }
+        } catch (InterruptedException e) {
+            fail("Thread interrupted unexpectedly: " + e.getMessage());
+        }
+    }
+
+    /**
+     * Wait for up to {@param timeInSeconds}, if all the strings {@param logcatStrings} are found in
+     * order then the assertion fails, otherwise it succeeds.
+     *
+     * @throws AssertionError if all the strings are found in order in the given time.
+     * @throws IOException if error while reading.
+     */
+    public void assertLogcatDoesNotContainInOrder(int timeInSeconds, String... logcatStrings)
+            throws IOException {
+        try {
+            int stringsFound = numberOfLogcatStringsFound("", timeInSeconds, logcatStrings);
+            if (stringsFound == logcatStrings.length) {
+                fail("Found " + Joiner.on(", ").join(logcatStrings) + " that weren't expected");
+            }
+        } catch (InterruptedException e) {
+            fail("Thread interrupted unexpectedly: " + e.getMessage());
+        }
+    }
+
+    private int numberOfLogcatStringsFound(
+            String filterSpec, int timeInSeconds, String... logcatStrings)
+            throws InterruptedException, IOException {
+        long timeout = System.currentTimeMillis() + TimeUnit.SECONDS.toMillis(timeInSeconds);
+        int stringIndex = 0;
+        while (timeout >= System.currentTimeMillis()) {
+            InputStream logcatStream = executeShellCommand("logcat -v brief -d " + filterSpec);
+            BufferedReader logcat = new BufferedReader(new InputStreamReader(logcatStream));
+            String line;
+            stringIndex = 0;
+            while ((line = logcat.readLine()) != null) {
+                if (line.contains(logcatStrings[stringIndex])) {
+                    stringIndex++;
+                    if (stringIndex >= logcatStrings.length) {
+                        drainAndClose(logcat);
+                        return stringIndex;
+                    }
+                }
+            }
+            Closeables.closeQuietly(logcat);
+            // In case the key has not been found, wait for the log to update before
+            // performing the next search.
+            Thread.sleep(SMALL_LOGCAT_DELAY);
+        }
+        return stringIndex;
+    }
+
+    private static void drainAndClose(BufferedReader reader) {
+        try {
+            while (reader.read() >= 0) {
+                // do nothing.
+            }
+        } catch (IOException ignored) {
+        }
+        Closeables.closeQuietly(reader);
+    }
+}
diff --git a/common/util/src/com/android/compatibility/common/util/ResultHandler.java b/common/util/src/com/android/compatibility/common/util/ResultHandler.java
index 4308947..c394b8d 100644
--- a/common/util/src/com/android/compatibility/common/util/ResultHandler.java
+++ b/common/util/src/com/android/compatibility/common/util/ResultHandler.java
@@ -25,7 +25,6 @@
 import org.xmlpull.v1.XmlSerializer;
 
 import java.io.File;
-import java.io.FileNotFoundException;
 import java.io.FileOutputStream;
 import java.io.FileReader;
 import java.io.IOException;
@@ -44,6 +43,7 @@
 import java.util.Locale;
 import java.util.Map.Entry;
 import java.util.Set;
+
 import javax.xml.transform.Transformer;
 import javax.xml.transform.TransformerException;
 import javax.xml.transform.TransformerFactory;
@@ -115,17 +115,20 @@
     private static final String SUMMARY_TAG = "Summary";
     private static final String TEST_TAG = "Test";
 
+    private static final String LATEST_RESULT_DIR = "latest";
 
     /**
      * Returns IInvocationResults that can be queried for general reporting information, but that
      * do not store underlying module data. Useful for summarizing invocation history.
      * @param resultsDir
-     * @param useChecksum
      */
     public static List<IInvocationResult> getLightResults(File resultsDir) {
         List<IInvocationResult> results = new ArrayList<>();
         List<File> files = getResultDirectories(resultsDir);
         for (File resultDir : files) {
+            if (LATEST_RESULT_DIR.equals(resultDir.getName())) {
+                continue;
+            }
             IInvocationResult result = getResultFromDir(resultDir, false);
             if (result != null) {
                 results.add(new LightInvocationResult(result));
@@ -491,8 +494,7 @@
     /**
      * Find the IInvocationResult for the given sessionId.
      */
-    public static IInvocationResult findResult(File resultsDir, Integer sessionId)
-            throws FileNotFoundException {
+    public static IInvocationResult findResult(File resultsDir, Integer sessionId) {
         return findResult(resultsDir, sessionId, true);
     }
 
@@ -500,7 +502,7 @@
      * Find the IInvocationResult for the given sessionId.
      */
     private static IInvocationResult findResult(
-            File resultsDir, Integer sessionId, Boolean useChecksum) throws FileNotFoundException {
+            File resultsDir, Integer sessionId, Boolean useChecksum) {
         if (sessionId < 0) {
             throw new IllegalArgumentException(
                 String.format("Invalid session id [%d] ", sessionId));
@@ -532,7 +534,7 @@
     /**
      * Get a list of child directories that contain test invocation results
      * @param resultsDir the root test result directory
-     * @return
+     * @return the list of {@link File} results directory.
      */
     public static List<File> getResultDirectories(File resultsDir) {
         List<File> directoryList = new ArrayList<>();
diff --git a/error_prone_rules_tests.mk b/error_prone_rules_tests.mk
index d17828d..7c729ec 100644
--- a/error_prone_rules_tests.mk
+++ b/error_prone_rules_tests.mk
@@ -17,9 +17,13 @@
 # Goal is to eventually merge with error_prone_rules.mk
 LOCAL_ERROR_PRONE_FLAGS:= -Xep:ArrayToString:ERROR \
                           -Xep:CollectionIncompatibleType:ERROR \
+                          -Xep:EqualsIncompatibleType:ERROR \
                           -Xep:EqualsNaN:ERROR \
                           -Xep:FormatString:ERROR \
+                          -Xep:IdentityBinaryExpression:ERROR \
                           -Xep:JUnit3TestNotRun:ERROR \
+                          -Xep:JUnitAmbiguousTestClass:ERROR \
+                          -Xep:MissingFail:ERROR \
                           -Xep:SizeGreaterThanOrEqualsZero:ERROR \
                           -Xep:TryFailThrowable:ERROR
 
diff --git a/hostsidetests/aadb/AndroidTest.xml b/hostsidetests/aadb/AndroidTest.xml
index 2224df2..a98ee5a 100644
--- a/hostsidetests/aadb/AndroidTest.xml
+++ b/hostsidetests/aadb/AndroidTest.xml
@@ -14,6 +14,7 @@
      limitations under the License.
 -->
 <configuration description="Config for the CTS aadb host tests">
+    <option name="test-suite-tag" value="cts" />
     <option name="config-descriptor:metadata" key="component" value="misc" />
     <test class="com.android.compatibility.common.tradefed.testtype.JarHostTest">
         <option name="jar" value="CtsAadbHostTestCases.jar" />
diff --git a/hostsidetests/aadb/src/android/aadb/cts/TestDeviceFuncTest.java b/hostsidetests/aadb/src/android/aadb/cts/TestDeviceFuncTest.java
index a570232..6719923 100644
--- a/hostsidetests/aadb/src/android/aadb/cts/TestDeviceFuncTest.java
+++ b/hostsidetests/aadb/src/android/aadb/cts/TestDeviceFuncTest.java
@@ -57,16 +57,14 @@
      * Simple testcase to ensure that the grabbing a bugreport from a real TestDevice works.
      */
     public void testBugreport() throws Exception {
-        InputStreamSource bugreport = mTestDevice.getBugreport();
         InputStream bugreportData = null;
-        try {
+        try (InputStreamSource bugreport = mTestDevice.getBugreport()) {
             bugreportData = bugreport.createInputStream();
             String data = StreamUtil.getStringFromStream(bugreportData);
             assertTrue(String.format("Expected at least %d characters; only saw %d",
                     mMinBugreportBytes, data.length()), data.length() >= mMinBugreportBytes);
             // TODO: check the captured report more extensively, perhaps using loganalysis
         } finally {
-            StreamUtil.cancel(bugreport);
             StreamUtil.close(bugreportData);
         }
     }
@@ -366,10 +364,9 @@
         while (!passed) {
             // sleep a small amount of time to ensure last log message makes it into capture
             RunUtil.getDefault().sleep(10);
-            InputStreamSource source = getDevice().getLogcat(100 * 1024);
-            assertNotNull(source);
             File tmpTxtFile = FileUtil.createTempFile("logcat", ".txt");
-            try {
+            try (InputStreamSource source = getDevice().getLogcat(100 * 1024)) {
+                assertNotNull(source);
                 FileUtil.writeToFile(source.createInputStream(), tmpTxtFile);
                 CLog.i("Created file at %s", tmpTxtFile.getAbsolutePath());
                 // ensure last log message is present in log
@@ -379,7 +376,6 @@
                 }
             } finally {
                 FileUtil.deleteFile(tmpTxtFile);
-                source.cancel();
             }
             retry++;
             if ((retry > 100) && !passed) {
diff --git a/hostsidetests/aadb/src/android/aadb/cts/TestDeviceStressTest.java b/hostsidetests/aadb/src/android/aadb/cts/TestDeviceStressTest.java
index 3f2ffa8..75ffc85 100644
--- a/hostsidetests/aadb/src/android/aadb/cts/TestDeviceStressTest.java
+++ b/hostsidetests/aadb/src/android/aadb/cts/TestDeviceStressTest.java
@@ -33,8 +33,8 @@
  */
 public class TestDeviceStressTest extends DeviceTestCase {
 
-    private static final int TEST_FILE_COUNT= 200;
-    private int mIterations = 25;
+    private static final int TEST_FILE_COUNT= 100;
+    private int mIterations = 20;
     private ITestDevice mTestDevice;
 
     @Override
diff --git a/hostsidetests/abioverride/AndroidTest.xml b/hostsidetests/abioverride/AndroidTest.xml
index 18b0063..99f731c 100644
--- a/hostsidetests/abioverride/AndroidTest.xml
+++ b/hostsidetests/abioverride/AndroidTest.xml
@@ -14,6 +14,7 @@
      limitations under the License.
 -->
 <configuration description="Config for CTS AbiOverride host test cases">
+    <option name="test-suite-tag" value="cts" />
     <option name="config-descriptor:metadata" key="component" value="webview" />
     <target_preparer class="com.android.tradefed.targetprep.suite.SuiteApkInstaller">
         <option name="cleanup-apks" value="true" />
diff --git a/hostsidetests/abioverride/app/Android.mk b/hostsidetests/abioverride/app/Android.mk
index a246f72..9dd8d1f 100755
--- a/hostsidetests/abioverride/app/Android.mk
+++ b/hostsidetests/abioverride/app/Android.mk
@@ -27,7 +27,10 @@
 
 LOCAL_MULTILIB := both
 
-LOCAL_STATIC_JAVA_LIBRARIES := compatibility-device-util ctstestrunner
+LOCAL_STATIC_JAVA_LIBRARIES := \
+    compatibility-device-util \
+    ctstestrunner \
+
 
 LOCAL_JNI_SHARED_LIBRARIES := libctsabioverride
 
diff --git a/hostsidetests/abioverride/app/AndroidManifest.xml b/hostsidetests/abioverride/app/AndroidManifest.xml
index c42d3a8..6135732 100755
--- a/hostsidetests/abioverride/app/AndroidManifest.xml
+++ b/hostsidetests/abioverride/app/AndroidManifest.xml
@@ -19,6 +19,8 @@
     package="android.abioverride.app">
 
     <application android:use32bitAbi="true" android:multiArch="true">
+        <uses-library android:name="android.test.runner" />
+
         <activity android:name=".AbiOverrideActivity" >
             <intent-filter>
                 <action android:name="android.intent.action.MAIN" />
diff --git a/hostsidetests/appsecurity/AndroidTest.xml b/hostsidetests/appsecurity/AndroidTest.xml
index 6a9b557..2109a47 100644
--- a/hostsidetests/appsecurity/AndroidTest.xml
+++ b/hostsidetests/appsecurity/AndroidTest.xml
@@ -14,6 +14,7 @@
      limitations under the License.
 -->
 <configuration description="Config for the CTS App Security host tests">
+    <option name="test-suite-tag" value="cts" />
     <option name="config-descriptor:metadata" key="component" value="framework" />
     <target_preparer class="android.appsecurity.cts.AppSecurityPreparer" />
     <test class="com.android.compatibility.common.tradefed.testtype.JarHostTest" >
diff --git a/hostsidetests/appsecurity/res/pkgsigverify/v1-only-with-tampered-classes-dex.apk b/hostsidetests/appsecurity/res/pkgsigverify/v1-only-with-tampered-classes-dex.apk
new file mode 100644
index 0000000..07c356e
--- /dev/null
+++ b/hostsidetests/appsecurity/res/pkgsigverify/v1-only-with-tampered-classes-dex.apk
Binary files differ
diff --git a/hostsidetests/appsecurity/src/android/appsecurity/cts/AdoptableHostTest.java b/hostsidetests/appsecurity/src/android/appsecurity/cts/AdoptableHostTest.java
index afd7245..aa7e4a0 100644
--- a/hostsidetests/appsecurity/src/android/appsecurity/cts/AdoptableHostTest.java
+++ b/hostsidetests/appsecurity/src/android/appsecurity/cts/AdoptableHostTest.java
@@ -23,13 +23,17 @@
 import static android.appsecurity.cts.SplitTests.CLASS;
 import static android.appsecurity.cts.SplitTests.PKG;
 
-import com.android.tradefed.build.IBuildInfo;
+import static org.junit.Assert.fail;
+
 import com.android.tradefed.device.CollectingOutputReceiver;
-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 com.android.tradefed.testtype.DeviceJUnit4ClassRunner;
+import com.android.tradefed.testtype.junit4.BaseHostJUnit4Test;
+
+import org.junit.After;
+import org.junit.Assert;
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
 
 import java.util.Arrays;
 import java.util.concurrent.TimeUnit;
@@ -37,45 +41,57 @@
 /**
  * Set of tests that verify behavior of adopted storage media, if supported.
  */
-public class AdoptableHostTest extends DeviceTestCase implements IAbiReceiver, IBuildReceiver {
-    private IAbi mAbi;
-    private IBuildInfo mCtsBuild;
+@RunWith(DeviceJUnit4ClassRunner.class)
+public class AdoptableHostTest extends BaseHostJUnit4Test {
 
-    @Override
-    public void setAbi(IAbi abi) {
-        mAbi = abi;
-    }
+    public static final String FEATURE_ADOPTABLE_STORAGE = "feature:android.software.adoptable_storage";
 
-    @Override
-    public void setBuild(IBuildInfo buildInfo) {
-        mCtsBuild = buildInfo;
-    }
-
-    @Override
-    protected void setUp() throws Exception {
-        super.setUp();
-
-        Utils.prepareSingleUser(getDevice());
-        assertNotNull(mAbi);
-        assertNotNull(mCtsBuild);
+    @Before
+    public void setUp() throws Exception {
+        // Start all possible users to make sure their storage is unlocked
+        Utils.prepareMultipleUsers(getDevice(), Integer.MAX_VALUE);
 
         getDevice().uninstallPackage(PKG);
+
+        // Enable a virtual disk to give us the best shot at being able to pass
+        // the various tests below. This helps verify devices that may not
+        // currently have an SD card inserted.
+        if (isSupportedDevice()) {
+            getDevice().executeShellCommand("sm set-virtual-disk true");
+        }
     }
 
-    @Override
-    protected void tearDown() throws Exception {
-        super.tearDown();
-
+    @After
+    public void tearDown() throws Exception {
         getDevice().uninstallPackage(PKG);
+
+        if (isSupportedDevice()) {
+            getDevice().executeShellCommand("sm set-virtual-disk false");
+        }
     }
 
+    /**
+     * Ensure that we have consistency between the feature flag and what we
+     * sniffed from the underlying fstab.
+     */
+    @Test
+    public void testFeatureConsistent() throws Exception {
+        final boolean hasFeature = hasFeature();
+        final boolean hasFstab = hasFstab();
+        if (hasFeature != hasFstab) {
+            fail("Inconsistent adoptable storage status; feature claims " + hasFeature
+                    + " but fstab claims " + hasFstab);
+        }
+    }
+
+    @Test
     public void testApps() throws Exception {
-        if (!hasAdoptable()) return;
+        if (!isSupportedDevice()) return;
         final String diskId = getAdoptionDisk();
         try {
-            final String abi = mAbi.getName();
+            final String abi = getAbi().getName();
             final String apk = ABI_TO_APK.get(abi);
-            assertNotNull("Failed to find APK for ABI " + abi, apk);
+            Assert.assertNotNull("Failed to find APK for ABI " + abi, apk);
 
             // Install simple app on internal
             new InstallMultiple().useNaturalAbi().addApk(APK).addApk(apk).run();
@@ -119,8 +135,9 @@
         }
     }
 
+    @Test
     public void testPrimaryStorage() throws Exception {
-        if (!hasAdoptable()) return;
+        if (!isSupportedDevice()) return;
         final String diskId = getAdoptionDisk();
         try {
             final String originalVol = getDevice()
@@ -218,8 +235,9 @@
      * Verify that we can install both new and inherited packages directly on
      * adopted volumes.
      */
+    @Test
     public void testPackageInstaller() throws Exception {
-        if (!hasAdoptable()) return;
+        if (!isSupportedDevice()) return;
         final String diskId = getAdoptionDisk();
         try {
             assertEmpty(getDevice().executeShellCommand("sm partition " + diskId + " private"));
@@ -246,8 +264,9 @@
      * Verify behavior when changes occur while adopted device is ejected and
      * returned at a later time.
      */
+    @Test
     public void testEjected() throws Exception {
-        if (!hasAdoptable()) return;
+        if (!isSupportedDevice()) return;
         final String diskId = getAdoptionDisk();
         try {
             assertEmpty(getDevice().executeShellCommand("sm partition " + diskId + " private"));
@@ -291,7 +310,15 @@
         }
     }
 
-    private boolean hasAdoptable() throws Exception {
+    private boolean isSupportedDevice() throws Exception {
+        return hasFeature() || hasFstab();
+    }
+
+    private boolean hasFeature() throws Exception {
+        return getDevice().hasFeature(FEATURE_ADOPTABLE_STORAGE);
+    }
+
+    private boolean hasFstab() throws Exception {
         return Boolean.parseBoolean(getDevice().executeShellCommand("sm has-adoptable").trim());
     }
 
@@ -335,11 +362,6 @@
         getDevice().executeShellCommand("sm forget all");
     }
 
-    private void runDeviceTests(String packageName, String testClassName, String testMethodName)
-            throws DeviceNotAvailableException {
-        Utils.runDeviceTests(getDevice(), packageName, testClassName, testMethodName);
-    }
-
     private static void assertSuccess(String str) {
         if (str == null || !str.startsWith("Success")) {
             throw new AssertionError("Expected success string but found " + str);
@@ -367,7 +389,7 @@
 
     private class InstallMultiple extends BaseInstallMultiple<InstallMultiple> {
         public InstallMultiple() {
-            super(getDevice(), mCtsBuild, mAbi);
+            super(getDevice(), getBuild(), getAbi());
         }
     }
 }
diff --git a/hostsidetests/appsecurity/src/android/appsecurity/cts/AppSecurityPreparer.java b/hostsidetests/appsecurity/src/android/appsecurity/cts/AppSecurityPreparer.java
index 90b89af..797ffcb 100644
--- a/hostsidetests/appsecurity/src/android/appsecurity/cts/AppSecurityPreparer.java
+++ b/hostsidetests/appsecurity/src/android/appsecurity/cts/AppSecurityPreparer.java
@@ -29,7 +29,6 @@
 import com.android.tradefed.targetprep.ITargetCleaner;
 import com.android.tradefed.targetprep.ITargetPreparer;
 import com.android.tradefed.targetprep.TargetSetupError;
-import com.android.tradefed.util.StreamUtil;
 
 /**
  * Creates secondary and tertiary users for use during a test suite.
@@ -56,11 +55,8 @@
                         "Created secondary user " + device.createUser("CTS_" + System.nanoTime()));
             }
         } catch (IllegalStateException e) {
-            InputStreamSource logcat = device.getLogcatDump();
-            try {
+            try (InputStreamSource logcat = device.getLogcatDump()) {
                 mLogger.testLog("AppSecurityPrep_failed_create_user", LogDataType.LOGCAT, logcat);
-            } finally {
-                StreamUtil.cancel(logcat);
             }
             throw new TargetSetupError("Failed to create user.", e, device.getDeviceDescriptor());
         }
diff --git a/hostsidetests/appsecurity/src/android/appsecurity/cts/AppSecurityTests.java b/hostsidetests/appsecurity/src/android/appsecurity/cts/AppSecurityTests.java
index 00bac12..02e62d3 100644
--- a/hostsidetests/appsecurity/src/android/appsecurity/cts/AppSecurityTests.java
+++ b/hostsidetests/appsecurity/src/android/appsecurity/cts/AppSecurityTests.java
@@ -16,32 +16,31 @@
 
 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 com.android.ddmlib.Log;
-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 com.android.tradefed.testtype.DeviceJUnit4ClassRunner;
+import com.android.tradefed.testtype.junit4.BaseHostJUnit4Test;
 import com.android.tradefed.util.AbiUtils;
-import com.android.tradefed.util.RunUtil;
 
-import java.io.BufferedReader;
-import java.io.EOFException;
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
 import java.io.File;
 import java.io.FileNotFoundException;
-import java.io.IOException;
-import java.io.InputStream;
-import java.io.InputStreamReader;
-import java.util.ArrayList;
-import java.util.List;
 
 /**
  * Set of tests that verify various security checks involving multiple apps are
  * properly enforced.
  */
-public class AppSecurityTests extends DeviceTestCase implements IAbiReceiver, IBuildReceiver {
+@RunWith(DeviceJUnit4ClassRunner.class)
+public class AppSecurityTests extends BaseHostJUnit4Test {
 
     // testSharedUidDifferentCerts constants
     private static final String SHARED_UI_APK = "CtsSharedUidInstall.apk";
@@ -86,39 +85,22 @@
 
     private static final String LOG_TAG = "AppSecurityTests";
 
-    private IAbi mAbi;
-    private IBuildInfo mCtsBuild;
-
-    @Override
-    public void setAbi(IAbi abi) {
-        mAbi = abi;
-    }
-
-    /**
-     * {@inheritDoc}
-     */
-    @Override
-    public void setBuild(IBuildInfo buildInfo) {
-        mCtsBuild = buildInfo;
-    }
-
     private File getTestAppFile(String fileName) throws FileNotFoundException {
-        CompatibilityBuildHelper buildHelper = new CompatibilityBuildHelper(mCtsBuild);
+        CompatibilityBuildHelper buildHelper = new CompatibilityBuildHelper(getBuild());
         return buildHelper.getTestFile(fileName);
     }
 
-    @Override
-    protected void setUp() throws Exception {
-        super.setUp();
-
+    @Before
+    public void setUp() throws Exception {
         Utils.prepareSingleUser(getDevice());
-        assertNotNull(mCtsBuild);
+        assertNotNull(getBuild());
     }
 
     /**
      * Test that an app that declares the same shared uid as an existing app, cannot be installed
      * if it is signed with a different certificate.
      */
+    @Test
     public void testSharedUidDifferentCerts() throws Exception {
         Log.i(LOG_TAG, "installing apks with shared uid, but different certs");
         try {
@@ -126,7 +108,7 @@
             getDevice().uninstallPackage(SHARED_UI_PKG);
             getDevice().uninstallPackage(SHARED_UI_DIFF_CERT_PKG);
 
-            String[] options = {AbiUtils.createAbiFlag(mAbi.getName())};
+            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),
@@ -147,13 +129,14 @@
      * Test that an app update cannot be installed over an existing app if it has a different
      * certificate.
      */
+    @Test
     public void testAppUpgradeDifferentCerts() 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);
 
-            String[] options = {AbiUtils.createAbiFlag(mAbi.getName())};
+            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),
@@ -172,6 +155,7 @@
     /**
      * Test that an app cannot access another app's private data.
      */
+    @Test
     public void testAppFailAccessPrivateData() throws Exception {
         Log.i(LOG_TAG, "installing app that attempts to access another app's private data");
         try {
@@ -179,7 +163,7 @@
             getDevice().uninstallPackage(APP_WITH_DATA_PKG);
             getDevice().uninstallPackage(APP_ACCESS_DATA_PKG);
 
-            String[] options = {AbiUtils.createAbiFlag(mAbi.getName())};
+            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),
@@ -202,13 +186,14 @@
     /**
      * Test that uninstall of an app removes its private data.
      */
+    @Test
     public void testUninstallRemovesData() 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(mAbi.getName())};
+            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),
@@ -233,6 +218,7 @@
     /**
      * Test that an app cannot instrument another app that is signed with different certificate.
      */
+    @Test
     public void testInstrumentationDiffCert() throws Exception {
         Log.i(LOG_TAG, "installing app that attempts to instrument another app");
         try {
@@ -240,7 +226,7 @@
             getDevice().uninstallPackage(TARGET_INSTRUMENT_PKG);
             getDevice().uninstallPackage(INSTRUMENT_DIFF_CERT_PKG);
 
-            String[] options = {AbiUtils.createAbiFlag(mAbi.getName())};
+            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",
@@ -266,6 +252,7 @@
      * Test that an app cannot use a signature-enforced permission if it is signed with a different
      * certificate than the app that declared the permission.
      */
+    @Test
     public void testPermissionDiffCert() throws Exception {
         Log.i(LOG_TAG, "installing app that attempts to use permission of another app");
         try {
@@ -274,7 +261,7 @@
             getDevice().uninstallPackage(DECLARE_PERMISSION_COMPAT_PKG);
             getDevice().uninstallPackage(PERMISSION_DIFF_CERT_PKG);
 
-            String[] options = {AbiUtils.createAbiFlag(mAbi.getName())};
+            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",
@@ -302,18 +289,14 @@
     /**
      * 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");
-        assertEquals("Error text", "Error: APK content must be streamed\n", output);
+        assertTrue("Error text", output.contains("Error"));
     }
 
     private void runDeviceTests(String packageName) throws DeviceNotAvailableException {
-        Utils.runDeviceTests(getDevice(), packageName);
-    }
-
-    private void runDeviceTests(String packageName, String testClassName, String testMethodName)
-            throws DeviceNotAvailableException {
-        Utils.runDeviceTests(getDevice(), packageName, testClassName, testMethodName);
+        runDeviceTests(packageName, null);
     }
 }
diff --git a/hostsidetests/appsecurity/src/android/appsecurity/cts/BaseAppSecurityTest.java b/hostsidetests/appsecurity/src/android/appsecurity/cts/BaseAppSecurityTest.java
index 920e0a5..5e2a97b 100644
--- a/hostsidetests/appsecurity/src/android/appsecurity/cts/BaseAppSecurityTest.java
+++ b/hostsidetests/appsecurity/src/android/appsecurity/cts/BaseAppSecurityTest.java
@@ -17,18 +17,18 @@
 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.IBuildReceiver;
+import com.android.tradefed.testtype.junit4.BaseHostJUnit4Test;
+
+import org.junit.Assert;
+import org.junit.Before;
 
 import java.util.ArrayList;
 
 /**
  * Base class.
  */
-public class BaseAppSecurityTest extends DeviceTestCase implements IBuildReceiver {
-    protected IBuildInfo mBuildInfo;
+abstract class BaseAppSecurityTest extends BaseHostJUnit4Test {
 
     /** Whether multi-user is supported. */
     protected boolean mSupportsMultiUser;
@@ -37,15 +37,9 @@
     /** Users we shouldn't delete in the tests */
     private ArrayList<Integer> mFixedUsers;
 
-    @Override
-    public void setBuild(IBuildInfo buildInfo) {
-        mBuildInfo = buildInfo;
-    }
-
-    @Override
-    protected void setUp() throws Exception {
-        super.setUp();
-        assertNotNull(mBuildInfo); // ensure build has been set before test is run.
+    @Before
+    public void setUp() throws Exception {
+        Assert.assertNotNull(getBuild()); // ensure build has been set before test is run.
 
         mSupportsMultiUser = getDevice().getMaxNumberOfUsersSupported() > 1;
         mIsSplitSystemUser = checkIfSplitSystemUser();
@@ -70,8 +64,8 @@
         if (userId < 0) {
             userId = mPrimaryUserId;
         }
-        CompatibilityBuildHelper buildHelper = new CompatibilityBuildHelper(mBuildInfo);
-        assertNull(getDevice().installPackageForUser(
+        CompatibilityBuildHelper buildHelper = new CompatibilityBuildHelper(getBuild());
+        Assert.assertNull(getDevice().installPackageForUser(
                 buildHelper.getTestFile(apk), true, false, userId, "-t"));
     }
 
diff --git a/hostsidetests/appsecurity/src/android/appsecurity/cts/ClassloaderSplitsTest.java b/hostsidetests/appsecurity/src/android/appsecurity/cts/ClassloaderSplitsTest.java
index e291771..f4f6d9e 100644
--- a/hostsidetests/appsecurity/src/android/appsecurity/cts/ClassloaderSplitsTest.java
+++ b/hostsidetests/appsecurity/src/android/appsecurity/cts/ClassloaderSplitsTest.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.DeviceJUnit4ClassRunner;
 import com.android.tradefed.testtype.IBuildReceiver;
+import com.android.tradefed.testtype.junit4.BaseHostJUnit4Test;
 
-public class ClassloaderSplitsTest 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 ClassloaderSplitsTest extends BaseHostJUnit4Test implements IBuildReceiver {
     private static final String PKG = "com.android.cts.classloadersplitapp";
     private static final String TEST_CLASS = PKG + ".SplitAppTest";
 
@@ -39,54 +45,48 @@
     private static final String APK_FEATURE_A = "CtsClassloaderSplitAppFeatureA.apk";
     private static final String APK_FEATURE_B = "CtsClassloaderSplitAppFeatureB.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);
     }
 
+    @Test
     public void testBaseClassLoader() throws Exception {
         new InstallMultiple().addApk(APK_BASE).run();
-        Utils.runDeviceTests(getDevice(), PKG, TEST_CLASS, "testBaseClassLoader");
+        runDeviceTests(getDevice(), PKG, TEST_CLASS, "testBaseClassLoader");
     }
 
+    @Test
     public void testFeatureAClassLoader() throws Exception {
         new InstallMultiple().addApk(APK_BASE).addApk(APK_FEATURE_A).run();
-        Utils.runDeviceTests(getDevice(), PKG, TEST_CLASS, "testBaseClassLoader");
-        Utils.runDeviceTests(getDevice(), PKG, TEST_CLASS, "testFeatureAClassLoader");
+        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();
-        Utils.runDeviceTests(getDevice(), PKG, TEST_CLASS, "testBaseClassLoader");
-        Utils.runDeviceTests(getDevice(), PKG, TEST_CLASS, "testFeatureAClassLoader");
-        Utils.runDeviceTests(getDevice(), PKG, TEST_CLASS, "testFeatureBClassLoader");
+        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();
-        Utils.runDeviceTests(getDevice(), PKG, TEST_CLASS, "testBaseClassLoader");
-        Utils.runDeviceTests(getDevice(), PKG, TEST_CLASS, "testAllReceivers");
-    }
-
-    @Override
-    public void setBuild(IBuildInfo buildInfo) {
-        mBuildInfo = buildInfo;
+        runDeviceTests(getDevice(), PKG, TEST_CLASS, "testBaseClassLoader");
+        runDeviceTests(getDevice(), PKG, TEST_CLASS, "testAllReceivers");
     }
 
     private class InstallMultiple extends BaseInstallMultiple<InstallMultiple> {
         public InstallMultiple() {
-            super(getDevice(), mBuildInfo, null);
+            super(getDevice(), getBuild(), null);
         }
     }
 }
diff --git a/hostsidetests/appsecurity/src/android/appsecurity/cts/DirectBootHostTest.java b/hostsidetests/appsecurity/src/android/appsecurity/cts/DirectBootHostTest.java
index 2174fa0..5408115 100644
--- a/hostsidetests/appsecurity/src/android/appsecurity/cts/DirectBootHostTest.java
+++ b/hostsidetests/appsecurity/src/android/appsecurity/cts/DirectBootHostTest.java
@@ -16,15 +16,20 @@
 
 package android.appsecurity.cts;
 
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertTrue;
+
 import com.android.ddmlib.AdbCommandRejectedException;
 import com.android.ddmlib.CollectingOutputReceiver;
 import com.android.ddmlib.Log;
-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 com.android.tradefed.testtype.DeviceJUnit4ClassRunner;
+import com.android.tradefed.testtype.junit4.BaseHostJUnit4Test;
+
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
 
 /**
  * Set of tests that verify behavior of direct boot, if supported.
@@ -32,7 +37,8 @@
  * Note that these tests drive PIN setup manually instead of relying on device
  * administrators, which are not supported by all devices.
  */
-public class DirectBootHostTest extends DeviceTestCase implements IAbiReceiver, IBuildReceiver {
+@RunWith(DeviceJUnit4ClassRunner.class)
+public class DirectBootHostTest extends BaseHostJUnit4Test {
     private static final String TAG = "DirectBootHostTest";
 
     private static final String PKG = "com.android.cts.encryptionapp";
@@ -47,43 +53,25 @@
     private static final String MODE_EMULATED = "emulated";
     private static final String MODE_NONE = "none";
 
-    private static final String FEATURE_DEVICE_ADMIN = "feature:android.software.device_admin\n";
-    private static final String FEATURE_AUTOMOTIVE = "feature:android.hardware.type.automotive\n";
+    private static final String FEATURE_DEVICE_ADMIN = "feature:android.software.device_admin";
+    private static final String FEATURE_AUTOMOTIVE = "feature:android.hardware.type.automotive";
 
     private static final long SHUTDOWN_TIME_MS = 30 * 1000;
 
-    private String mFeatureList = null;
-
     private int[] mUsers;
-    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 {
         mUsers = Utils.prepareSingleUser(getDevice());
-        assertNotNull(mAbi);
-        assertNotNull(mCtsBuild);
+        assertNotNull(getAbi());
+        assertNotNull(getBuild());
 
         getDevice().uninstallPackage(PKG);
         getDevice().uninstallPackage(OTHER_PKG);
     }
 
-    @Override
-    protected void tearDown() throws Exception {
-        super.tearDown();
-
+    @After
+    public void tearDown() throws Exception {
         getDevice().uninstallPackage(PKG);
         getDevice().uninstallPackage(OTHER_PKG);
     }
@@ -91,6 +79,7 @@
     /**
      * Automotive devices MUST support native FBE.
      */
+    @Test
     public void testAutomotiveNativeFbe() throws Exception {
         if (!isSupportedDevice()) {
             Log.v(TAG, "Device not supported; skipping test");
@@ -107,6 +96,7 @@
     /**
      * If device has native FBE, verify lifecycle.
      */
+    @Test
     public void testDirectBootNative() throws Exception {
         if (!isSupportedDevice()) {
             Log.v(TAG, "Device not supported; skipping test");
@@ -122,6 +112,7 @@
     /**
      * If device doesn't have native FBE, enable emulation and verify lifecycle.
      */
+    @Test
     public void testDirectBootEmulated() throws Exception {
         if (!isSupportedDevice()) {
             Log.v(TAG, "Device not supported; skipping test");
@@ -137,6 +128,7 @@
     /**
      * If device doesn't have native FBE, verify normal lifecycle.
      */
+    @Test
     public void testDirectBootNone() throws Exception {
         if (!isSupportedDevice()) {
             Log.v(TAG, "Device not supported; skipping test");
@@ -213,7 +205,7 @@
             int... users) throws DeviceNotAvailableException {
         for (int user : users) {
             Log.d(TAG, "runDeviceTests " + testMethodName + " u" + user);
-            Utils.runDeviceTests(getDevice(), packageName, testClassName, testMethodName, user);
+            runDeviceTests(getDevice(), packageName, testClassName, testMethodName, user, null);
         }
     }
 
@@ -236,20 +228,12 @@
         return "1".equals(output);
     }
 
-    private boolean hasSystemFeature(final String feature) throws Exception {
-        if (mFeatureList == null) {
-            mFeatureList = getDevice().executeShellCommand("pm list features");
-        }
-
-        return mFeatureList.contains(feature);
-    }
-
     private boolean isSupportedDevice() throws Exception {
-        return hasSystemFeature(FEATURE_DEVICE_ADMIN);
+        return getDevice().hasFeature(FEATURE_DEVICE_ADMIN);
     }
 
     private boolean isAutomotiveDevice() throws Exception {
-        return hasSystemFeature(FEATURE_AUTOMOTIVE);
+        return getDevice().hasFeature(FEATURE_AUTOMOTIVE);
     }
 
     private void waitForBootCompleted() throws Exception {
@@ -269,7 +253,7 @@
 
     private class InstallMultiple extends BaseInstallMultiple<InstallMultiple> {
         public InstallMultiple() {
-            super(getDevice(), mCtsBuild, mAbi);
+            super(getDevice(), getBuild(), getAbi());
         }
     }
 }
diff --git a/hostsidetests/appsecurity/src/android/appsecurity/cts/EphemeralTest.java b/hostsidetests/appsecurity/src/android/appsecurity/cts/EphemeralTest.java
index 914f4ef..5564125 100644
--- a/hostsidetests/appsecurity/src/android/appsecurity/cts/EphemeralTest.java
+++ b/hostsidetests/appsecurity/src/android/appsecurity/cts/EphemeralTest.java
@@ -219,6 +219,7 @@
         runDeviceTests(EPHEMERAL_1_PKG, TEST_CLASS, "testInstallPermissionGranted");
     }
 
+    /** Test for android.permission.INSTANT_APP_FOREGROUND_SERVICE */
     public void testStartForegrondService() throws Exception {
         // Make sure the test package does not have INSTANT_APP_FOREGROUND_SERVICE
         getDevice().executeShellCommand("cmd package revoke " + EPHEMERAL_1_PKG
@@ -226,6 +227,41 @@
         runDeviceTests(EPHEMERAL_1_PKG, TEST_CLASS, "testStartForegroundService");
     }
 
+    /** Test for android.permission.RECORD_AUDIO */
+    public void testRecordAudioPermission() throws Exception {
+        runDeviceTests(EPHEMERAL_1_PKG, TEST_CLASS, "testRecordAudioPermission");
+    }
+
+    /** Test for android.permission.CAMERA */
+    public void testCameraPermission() throws Exception {
+        runDeviceTests(EPHEMERAL_1_PKG, TEST_CLASS, "testCameraPermission");
+    }
+
+    /** Test for android.permission.READ_PHONE_NUMBERS */
+    public void testReadPhoneNumbersPermission() throws Exception {
+        runDeviceTests(EPHEMERAL_1_PKG, TEST_CLASS, "testReadPhoneNumbersPermission");
+    }
+
+    /** Test for android.permission.ACCESS_COARSE_LOCATION */
+    public void testAccessCoarseLocationPermission() throws Throwable {
+        runDeviceTests(EPHEMERAL_1_PKG, TEST_CLASS, "testAccessCoarseLocationPermission");
+    }
+
+    /** Test for android.permission.NETWORK */
+    public void testInternetPermission() throws Throwable {
+        runDeviceTests(EPHEMERAL_1_PKG, TEST_CLASS, "testInternetPermission");
+    }
+
+    /** Test for android.permission.VIBRATE */
+    public void testVibratePermission() throws Throwable {
+        runDeviceTests(EPHEMERAL_1_PKG, TEST_CLASS, "testVibratePermission");
+    }
+
+    /** Test for android.permission.WAKE_LOCK */
+    public void testWakeLockPermission() throws Throwable {
+        runDeviceTests(EPHEMERAL_1_PKG, TEST_CLASS, "testWakeLockPermission");
+    }
+
     private static final HashMap<String, String> makeArgs(
             String action, String category, String mimeType) {
         if (action == null || action.length() == 0) {
diff --git a/hostsidetests/appsecurity/src/android/appsecurity/cts/ExternalStorageHostTest.java b/hostsidetests/appsecurity/src/android/appsecurity/cts/ExternalStorageHostTest.java
index 87d8bd6..8cf385d 100644
--- a/hostsidetests/appsecurity/src/android/appsecurity/cts/ExternalStorageHostTest.java
+++ b/hostsidetests/appsecurity/src/android/appsecurity/cts/ExternalStorageHostTest.java
@@ -16,24 +16,32 @@
 
 package android.appsecurity.cts;
 
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertNull;
+import static org.junit.Assert.assertTrue;
+
 import com.android.compatibility.common.tradefed.build.CompatibilityBuildHelper;
 import com.android.ddmlib.Log;
-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 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;
+import java.util.ArrayList;
+import java.util.List;
 
 /**
  * Set of tests that verify behavior of external storage devices.
  */
-public class ExternalStorageHostTest extends DeviceTestCase
-        implements IAbiReceiver, IBuildReceiver {
+@RunWith(DeviceJUnit4ClassRunner.class)
+public class ExternalStorageHostTest extends BaseHostJUnit4Test {
     private static final String TAG = "ExternalStorageHostTest";
 
     private static final String COMMON_CLASS =
@@ -53,47 +61,29 @@
     private static final String MULTIUSER_CLASS = ".MultiUserStorageTest";
 
     private int[] mUsers;
-    private IAbi mAbi;
-    private IBuildInfo mCtsBuild;
-
-    @Override
-    public void setAbi(IAbi abi) {
-        mAbi = abi;
-    }
-
-    @Override
-    public void setBuild(IBuildInfo buildInfo) {
-        mCtsBuild = buildInfo;
-    }
 
     private File getTestAppFile(String fileName) throws FileNotFoundException {
-        CompatibilityBuildHelper buildHelper = new CompatibilityBuildHelper(mCtsBuild);
+        CompatibilityBuildHelper buildHelper = new CompatibilityBuildHelper(getBuild());
         return buildHelper.getTestFile(fileName);
     }
 
-    @Override
-    protected void setUp() throws Exception {
-        super.setUp();
-
+    @Before
+    public void setUp() throws Exception {
         mUsers = Utils.prepareMultipleUsers(getDevice());
-        assertNotNull(mAbi);
-        assertNotNull(mCtsBuild);
-    }
-
-    @Override
-    protected void tearDown() throws Exception {
-        super.tearDown();
+        assertNotNull(getAbi());
+        assertNotNull(getBuild());
     }
 
     /**
      * Verify that app with no external storage permissions works correctly.
      */
+    @Test
     public void testExternalStorageNone() throws Exception {
         try {
             wipePrimaryExternalStorage();
 
             getDevice().uninstallPackage(NONE_PKG);
-            String[] options = {AbiUtils.createAbiFlag(mAbi.getName())};
+            String[] options = {AbiUtils.createAbiFlag(getAbi().getName())};
             assertNull(getDevice().installPackage(getTestAppFile(NONE_APK), false, options));
 
             for (int user : mUsers) {
@@ -110,12 +100,13 @@
      * {@link android.Manifest.permission#READ_EXTERNAL_STORAGE} works
      * correctly.
      */
+    @Test
     public void testExternalStorageRead() throws Exception {
         try {
             wipePrimaryExternalStorage();
 
             getDevice().uninstallPackage(READ_PKG);
-            String[] options = {AbiUtils.createAbiFlag(mAbi.getName())};
+            String[] options = {AbiUtils.createAbiFlag(getAbi().getName())};
             assertNull(getDevice().installPackage(getTestAppFile(READ_APK), false, options));
 
             for (int user : mUsers) {
@@ -132,12 +123,13 @@
      * {@link android.Manifest.permission#WRITE_EXTERNAL_STORAGE} works
      * correctly.
      */
+    @Test
     public void testExternalStorageWrite() throws Exception {
         try {
             wipePrimaryExternalStorage();
 
             getDevice().uninstallPackage(WRITE_PKG);
-            String[] options = {AbiUtils.createAbiFlag(mAbi.getName())};
+            String[] options = {AbiUtils.createAbiFlag(getAbi().getName())};
             assertNull(getDevice().installPackage(getTestAppFile(WRITE_APK), false, options));
 
             for (int user : mUsers) {
@@ -153,6 +145,7 @@
      * Verify that app with WRITE_EXTERNAL can leave gifts in external storage
      * directories belonging to other apps, and those apps can read.
      */
+    @Test
     public void testExternalStorageGifts() throws Exception {
         try {
             wipePrimaryExternalStorage();
@@ -160,7 +153,7 @@
             getDevice().uninstallPackage(NONE_PKG);
             getDevice().uninstallPackage(READ_PKG);
             getDevice().uninstallPackage(WRITE_PKG);
-            final String[] options = {AbiUtils.createAbiFlag(mAbi.getName())};
+            final String[] options = {AbiUtils.createAbiFlag(getAbi().getName())};
 
             // We purposefully delay the installation of the reading apps to
             // verify that the daemon correctly invalidates any caches.
@@ -186,6 +179,7 @@
      * Test multi-user emulated storage environment, ensuring that each user has
      * isolated storage.
      */
+    @Test
     public void testMultiUserStorageIsolated() throws Exception {
         try {
             if (mUsers.length == 1) {
@@ -198,7 +192,7 @@
 
             // Install our test app
             getDevice().uninstallPackage(MULTIUSER_PKG);
-            String[] options = {AbiUtils.createAbiFlag(mAbi.getName())};
+            String[] options = {AbiUtils.createAbiFlag(getAbi().getName())};
             final String installResult = getDevice()
                     .installPackage(getTestAppFile(MULTIUSER_APK), false, options);
             assertNull("Failed to install: " + installResult, installResult);
@@ -231,6 +225,7 @@
      * Test that apps with read permissions see the appropriate permissions
      * when apps with r/w permission levels move around their files.
      */
+    @Test
     public void testMultiViewMoveConsistency() throws Exception {
         try {
             wipePrimaryExternalStorage();
@@ -238,7 +233,7 @@
             getDevice().uninstallPackage(NONE_PKG);
             getDevice().uninstallPackage(READ_PKG);
             getDevice().uninstallPackage(WRITE_PKG);
-            final String[] options = {AbiUtils.createAbiFlag(mAbi.getName())};
+            final String[] options = {AbiUtils.createAbiFlag(getAbi().getName())};
 
             assertNull(getDevice().installPackage(getTestAppFile(WRITE_APK), false, options));
             assertNull(getDevice().installPackage(getTestAppFile(READ_APK), false, options));
@@ -271,13 +266,14 @@
     }
 
     /** Verify that app without READ_EXTERNAL can play default URIs in external storage. */
+    @Test
     public void testExternalStorageReadDefaultUris() throws Exception {
         try {
             wipePrimaryExternalStorage();
 
             getDevice().uninstallPackage(NONE_PKG);
             getDevice().uninstallPackage(WRITE_PKG);
-            final String[] options = {AbiUtils.createAbiFlag(mAbi.getName())};
+            final String[] options = {AbiUtils.createAbiFlag(getAbi().getName())};
 
             assertNull(getDevice().installPackage(getTestAppFile(WRITE_APK), false, options));
             assertNull(getDevice().installPackage(getTestAppFile(NONE_APK), false, options));
@@ -301,6 +297,45 @@
         }
     }
 
+    /**
+     * For security reasons, the shell user cannot access the shared storage of
+     * secondary users. Instead, developers should use the {@code content} shell
+     * tool to read/write files in those locations.
+     */
+    @Test
+    public void testSecondaryUsersInaccessible() throws Exception {
+        List<String> mounts = new ArrayList<>();
+        for (String line : getDevice().executeShellCommand("cat /proc/mount").split("\n")) {
+            String[] split = line.split(" ");
+            if (split[1].startsWith("/storage/") || split[1].startsWith("/mnt/")) {
+                mounts.add(split[1]);
+            }
+        }
+
+        for (int user : mUsers) {
+            String probe = "/sdcard/../" + user;
+            if (user == Utils.USER_SYSTEM) {
+                // Primary user should always be visible. Skip checking raw
+                // mount points, since we'd get false-positives for physical
+                // devices that aren't multi-user aware.
+                assertTrue(probe, access(probe));
+            } else {
+                // Secondary user should never be visible.
+                assertFalse(probe, access(probe));
+                for (String mount : mounts) {
+                    probe = mount + "/" + user;
+                    assertFalse(probe, access(probe));
+                }
+            }
+        }
+    }
+
+    private boolean access(String path) throws DeviceNotAvailableException {
+        final long nonce = System.nanoTime();
+        return getDevice().executeShellCommand("ls -la " + path + " && echo " + nonce)
+                .contains(Long.toString(nonce));
+    }
+
     private void enableWriteSettings(String packageName, int userId)
             throws DeviceNotAvailableException {
         StringBuilder cmd = new StringBuilder();
@@ -324,11 +359,11 @@
 
     private void runDeviceTests(String packageName, String testClassName, int userId)
             throws DeviceNotAvailableException {
-        Utils.runDeviceTests(getDevice(), packageName, testClassName, userId);
+        runDeviceTests(getDevice(), packageName, testClassName, null, userId, null);
     }
 
     private void runDeviceTests(String packageName, String testClassName, String testMethodName,
             int userId) throws DeviceNotAvailableException {
-        Utils.runDeviceTests(getDevice(), packageName, testClassName, testMethodName, userId);
+        runDeviceTests(getDevice(), packageName, testClassName, testMethodName, userId, null);
     }
 }
diff --git a/hostsidetests/appsecurity/src/android/appsecurity/cts/MajorVersionTest.java b/hostsidetests/appsecurity/src/android/appsecurity/cts/MajorVersionTest.java
new file mode 100644
index 0000000..15c3d3c
--- /dev/null
+++ b/hostsidetests/appsecurity/src/android/appsecurity/cts/MajorVersionTest.java
@@ -0,0 +1,133 @@
+/*
+ * 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.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;
+
+/**
+ * Test that install of apps using major version codes is being handled properly.
+ */
+public class MajorVersionTest extends DeviceTestCase implements IAbiReceiver, IBuildReceiver {
+    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();
+
+        Utils.prepareSingleUser(getDevice());
+        assertNotNull(mAbi);
+        assertNotNull(mBuildHelper);
+
+        getDevice().uninstallPackage(PKG);
+    }
+
+    @Override
+    protected void tearDown() throws Exception {
+        super.tearDown();
+
+        getDevice().uninstallPackage(PKG);
+    }
+
+    public void testInstallMinorVersion() throws Exception {
+        assertNull(getDevice().installPackage(
+                mBuildHelper.getTestFile(APK_000000000000ffff), false, false));
+        assertTrue(getDevice().getInstalledPackageNames().contains(PKG));
+        runVersionDeviceTests("testCheckVersion");
+        getDevice().uninstallPackage(PKG);
+    }
+
+    public void testInstallMajorVersion() throws Exception {
+        assertNull(getDevice().installPackage(
+                mBuildHelper.getTestFile(APK_000000ff00000000), false, false));
+        assertTrue(getDevice().getInstalledPackageNames().contains(PKG));
+        runVersionDeviceTests("testCheckVersion");
+        getDevice().uninstallPackage(PKG);
+    }
+
+    public void testInstallUpdateAcrossMinorMajorVersion() throws Exception {
+        assertNull(getDevice().installPackage(
+                mBuildHelper.getTestFile(APK_000000000000ffff), false, false));
+        assertTrue(getDevice().getInstalledPackageNames().contains(PKG));
+        runVersionDeviceTests("testCheckVersion");
+        assertNull(getDevice().installPackage(
+                mBuildHelper.getTestFile(APK_00000000ffffffff), true, false));
+        assertTrue(getDevice().getInstalledPackageNames().contains(PKG));
+        runVersionDeviceTests("testCheckVersion");
+        assertNull(getDevice().installPackage(
+                mBuildHelper.getTestFile(APK_000000ff00000000), true, false));
+        assertTrue(getDevice().getInstalledPackageNames().contains(PKG));
+        runVersionDeviceTests("testCheckVersion");
+        assertNull(getDevice().installPackage(
+                mBuildHelper.getTestFile(APK_000000ffffffffff), true, false));
+        assertTrue(getDevice().getInstalledPackageNames().contains(PKG));
+        runVersionDeviceTests("testCheckVersion");
+        getDevice().uninstallPackage(PKG);
+    }
+
+    public void testInstallDowngradeAcrossMajorMinorVersion() throws Exception {
+        assertNull(getDevice().installPackage(
+                mBuildHelper.getTestFile(APK_000000ffffffffff), false, false));
+        assertTrue(getDevice().getInstalledPackageNames().contains(PKG));
+        runVersionDeviceTests("testCheckVersion");
+        assertEquals("INSTALL_FAILED_VERSION_DOWNGRADE", getDevice().installPackage(
+                mBuildHelper.getTestFile(APK_00000000ffffffff), true, false));
+        assertTrue(getDevice().getInstalledPackageNames().contains(PKG));
+        runVersionDeviceTests("testCheckVersion");
+        assertEquals("INSTALL_FAILED_VERSION_DOWNGRADE", getDevice().installPackage(
+                mBuildHelper.getTestFile(APK_000000ff00000000), true, false));
+        assertTrue(getDevice().getInstalledPackageNames().contains(PKG));
+        runVersionDeviceTests("testCheckVersion");
+        assertEquals("INSTALL_FAILED_VERSION_DOWNGRADE", getDevice().installPackage(
+                mBuildHelper.getTestFile(APK_000000000000ffff), true, false));
+        assertTrue(getDevice().getInstalledPackageNames().contains(PKG));
+        runVersionDeviceTests("testCheckVersion");
+        getDevice().uninstallPackage(PKG);
+    }
+
+    private void runVersionDeviceTests(String testMethodName)
+            throws DeviceNotAvailableException {
+        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/PackageVisibilityTest.java b/hostsidetests/appsecurity/src/android/appsecurity/cts/PackageVisibilityTest.java
index 033f257..5bb70d1 100644
--- a/hostsidetests/appsecurity/src/android/appsecurity/cts/PackageVisibilityTest.java
+++ b/hostsidetests/appsecurity/src/android/appsecurity/cts/PackageVisibilityTest.java
@@ -16,11 +16,21 @@
 
 package android.appsecurity.cts;
 
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertTrue;
+
 import com.android.tradefed.device.DeviceNotAvailableException;
+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 visibility of packages installed in one user, in a different user.
  */
+@RunWith(DeviceJUnit4ClassRunner.class)
 public class PackageVisibilityTest extends BaseAppSecurityTest {
 
     private static final String TINY_APK = "CtsPkgInstallTinyApp.apk";
@@ -35,8 +45,8 @@
     private int[] mUsers;
     private String mOldVerifierValue;
 
-    public void setUp() throws Exception {
-        super.setUp();
+    @Before
+    public void setUpPackage() throws Exception {
 
         mUsers = Utils.prepareMultipleUsers(getDevice());
         mOldVerifierValue =
@@ -47,14 +57,15 @@
         installTestAppForUser(TEST_APK, mPrimaryUserId);
     }
 
+    @After
     public void tearDown() throws Exception {
         getDevice().uninstallPackage(TEST_PKG);
         getDevice().uninstallPackage(TINY_PKG);
         getDevice().executeShellCommand("settings put global package_verifier_enable "
                 + mOldVerifierValue);
-        super.tearDown();
     }
 
+    @Test
     public void testUninstalledPackageVisibility() throws Exception {
         if (!mSupportsMultiUser) {
             return;
diff --git a/hostsidetests/appsecurity/src/android/appsecurity/cts/PermissionsHostTest.java b/hostsidetests/appsecurity/src/android/appsecurity/cts/PermissionsHostTest.java
index caabeb5..28c44fc 100644
--- a/hostsidetests/appsecurity/src/android/appsecurity/cts/PermissionsHostTest.java
+++ b/hostsidetests/appsecurity/src/android/appsecurity/cts/PermissionsHostTest.java
@@ -46,6 +46,10 @@
     private static final String APK_ESCLATE_TO_RUNTIME_PERMISSIONS =
             "CtsEscalateToRuntimePermissions.apk";
 
+    private static final String APK_ACCESS_SERIAL_LEGACY = "CtsAccessSerialLegacy.apk";
+    private static final String APK_ACCESS_SERIAL_MODERN = "CtsAccessSerialModern.apk";
+    private static final String ACCESS_SERIAL_PKG = "android.os.cts";
+
     private static final String SCREEN_OFF_TIMEOUT_NS = "system";
     private static final String SCREEN_OFF_TIMEOUT_KEY = "screen_off_timeout";
     private String mScreenTimeoutBeforeTest;
@@ -74,6 +78,7 @@
         getDevice().uninstallPackage(USES_PERMISSION_PKG);
         getDevice().uninstallPackage(ESCALATE_PERMISSION_PKG);
         getDevice().uninstallPackage(PERMISSION_POLICY_25_PKG);
+        getDevice().uninstallPackage(ACCESS_SERIAL_PKG);
 
         // Set screen timeout to 30 min to not timeout while waiting for UI to change
         mScreenTimeoutBeforeTest = getDevice().getSetting(SCREEN_OFF_TIMEOUT_NS,
@@ -95,6 +100,7 @@
         getDevice().uninstallPackage(USES_PERMISSION_PKG);
         getDevice().uninstallPackage(ESCALATE_PERMISSION_PKG);
         getDevice().uninstallPackage(PERMISSION_POLICY_25_PKG);
+        getDevice().uninstallPackage(ACCESS_SERIAL_PKG);
     }
 
     public void testFail() throws Exception {
@@ -347,6 +353,30 @@
                 "testNoProtectionFlagsAddedToNonSignatureProtectionPermissions");
     }
 
+    public void testLegacyAppAccessSerial() throws Exception {
+        assertNull(getDevice().installPackage(mBuildHelper.getTestFile(
+                APK_PERMISSION_POLICY_25), false, false));
+        runDeviceTests(PERMISSION_POLICY_25_PKG,
+                "com.android.cts.permission.policy.PermissionPolicyTest25",
+                "testNoProtectionFlagsAddedToNonSignatureProtectionPermissions");
+    }
+
+    public void testSerialAccessPolicy() throws Exception {
+        // Verify legacy app behavior
+        assertNull(getDevice().installPackage(mBuildHelper.getTestFile(
+                APK_ACCESS_SERIAL_LEGACY), false, false));
+        runDeviceTests(ACCESS_SERIAL_PKG,
+                "android.os.cts.AccessSerialLegacyTest",
+                "testAccessSerialNoPermissionNeeded");
+
+        // Verify modern app behavior
+        assertNull(getDevice().installPackage(mBuildHelper.getTestFile(
+                APK_ACCESS_SERIAL_MODERN), true, false));
+        runDeviceTests(ACCESS_SERIAL_PKG,
+                "android.os.cts.AccessSerialModernTest",
+                "testAccessSerialPermissionNeeded");
+    }
+
     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/PkgInstallSignatureVerificationTest.java b/hostsidetests/appsecurity/src/android/appsecurity/cts/PkgInstallSignatureVerificationTest.java
index 589d3b9..d64cfd4 100644
--- a/hostsidetests/appsecurity/src/android/appsecurity/cts/PkgInstallSignatureVerificationTest.java
+++ b/hostsidetests/appsecurity/src/android/appsecurity/cts/PkgInstallSignatureVerificationTest.java
@@ -234,6 +234,16 @@
                 );
     }
 
+    public void testInstallV1SignatureOnlyDoesNotVerify() throws Exception {
+        // APK signed with v1 scheme only, but not all digests match those recorded in
+        // META-INF/MANIFEST.MF.
+        String error = "META-INF/MANIFEST.MF has invalid digest";
+
+        // Bitflip in classes.dex of otherwise good file.
+        assertInstallFailsWithError(
+                "v1-only-with-tampered-classes-dex.apk", error);
+    }
+
     public void testInstallV2SignatureDoesNotVerify() throws Exception {
         // APK signed with v2 scheme only, but the signature over signed-data does not verify.
         String error = "signature did not verify";
@@ -469,9 +479,10 @@
     }
 
     public void testInstallEphemeralRequiresV2Signature() throws Exception {
-        String expectedNoSigError = "No APK Signature Scheme v2 signature in ephemeral package";
-        assertInstallEphemeralFailsWithError("unsigned-ephemeral.apk", expectedNoSigError);
-        assertInstallEphemeralFailsWithError("v1-only-ephemeral.apk", expectedNoSigError);
+        assertInstallEphemeralFailsWithError("unsigned-ephemeral.apk",
+                "Failed to collect certificates");
+        assertInstallEphemeralFailsWithError("v1-only-ephemeral.apk",
+                "No APK Signature Scheme v2 signature");
         assertInstallEphemeralSucceeds("v2-only-ephemeral.apk");
         assertInstallEphemeralSucceeds("v1-v2-ephemeral.apk"); // signed with both schemes
     }
diff --git a/hostsidetests/appsecurity/src/android/appsecurity/cts/SplitTests.java b/hostsidetests/appsecurity/src/android/appsecurity/cts/SplitTests.java
index 9474ba8..acec1fb 100644
--- a/hostsidetests/appsecurity/src/android/appsecurity/cts/SplitTests.java
+++ b/hostsidetests/appsecurity/src/android/appsecurity/cts/SplitTests.java
@@ -34,7 +34,7 @@
     static final String APK_NO_RESTART_FEATURE = "CtsNoRestartFeature.apk";
 
     static final String PKG = "com.android.cts.splitapp";
-    static final String CLASS = ".SplitAppTest";
+    static final String CLASS = PKG + ".SplitAppTest";
 
     static final String APK = "CtsSplitApp.apk";
 
diff --git a/hostsidetests/appsecurity/src/android/appsecurity/cts/StorageHostTest.java b/hostsidetests/appsecurity/src/android/appsecurity/cts/StorageHostTest.java
index fa0120d..b31f91a 100644
--- a/hostsidetests/appsecurity/src/android/appsecurity/cts/StorageHostTest.java
+++ b/hostsidetests/appsecurity/src/android/appsecurity/cts/StorageHostTest.java
@@ -16,9 +16,13 @@
 
 package android.appsecurity.cts;
 
-import com.android.compatibility.common.tradefed.testtype.CompatibilityHostTestBase;
+import com.android.ddmlib.testrunner.TestIdentifier;
+import com.android.ddmlib.testrunner.TestResult;
+import com.android.ddmlib.testrunner.TestResult.TestStatus;
+import com.android.ddmlib.testrunner.TestRunResult;
 import com.android.tradefed.device.DeviceNotAvailableException;
 import com.android.tradefed.testtype.DeviceJUnit4ClassRunner;
+import com.android.tradefed.testtype.junit4.BaseHostJUnit4Test;
 
 import junit.framework.AssertionFailedError;
 
@@ -27,13 +31,13 @@
 import org.junit.Test;
 import org.junit.runner.RunWith;
 
-import java.util.concurrent.TimeUnit;
+import java.util.Map;
 
 /**
  * Tests that exercise various storage APIs.
  */
 @RunWith(DeviceJUnit4ClassRunner.class)
-public class StorageHostTest extends CompatibilityHostTestBase {
+public class StorageHostTest extends BaseHostJUnit4Test {
     private static final String PKG_STATS = "com.android.cts.storagestatsapp";
     private static final String PKG_A = "com.android.cts.storageapp_a";
     private static final String PKG_B = "com.android.cts.storageapp_b";
@@ -69,8 +73,8 @@
     }
 
     @Test
-    public void testVerifyQuota() throws Exception {
-        Utils.runDeviceTests(getDevice(), PKG_STATS, CLASS_STATS, "testVerifyQuota");
+    public void testVerify() throws Exception {
+        Utils.runDeviceTests(getDevice(), PKG_STATS, CLASS_STATS, "testVerify");
     }
 
     @Test
@@ -236,7 +240,22 @@
 
     public void runDeviceTests(String packageName, String testClassName, String testMethodName,
             int userId) throws DeviceNotAvailableException {
-        Utils.runDeviceTests(getDevice(), packageName, testClassName, testMethodName, userId, null,
-                20L, TimeUnit.MINUTES);
+        if (!runDeviceTests(getDevice(), packageName, testClassName, testMethodName, userId, 20 * 60 * 1000L)) {
+            TestRunResult res = getLastDeviceRunResults();
+            if (res != null) {
+                StringBuilder errorBuilder = new StringBuilder("on-device tests failed:\n");
+                for (Map.Entry<TestIdentifier, TestResult> resultEntry :
+                    res.getTestResults().entrySet()) {
+                    if (!resultEntry.getValue().getStatus().equals(TestStatus.PASSED)) {
+                        errorBuilder.append(resultEntry.getKey().toString());
+                        errorBuilder.append(":\n");
+                        errorBuilder.append(resultEntry.getValue().getStackTrace());
+                    }
+                }
+                throw new AssertionError(errorBuilder.toString());
+            } else {
+                throw new AssertionFailedError("Error when running device tests.");
+            }
+        }
     }
 }
diff --git a/hostsidetests/appsecurity/src/android/appsecurity/cts/Utils.java b/hostsidetests/appsecurity/src/android/appsecurity/cts/Utils.java
index 67ee091..8bad048 100644
--- a/hostsidetests/appsecurity/src/android/appsecurity/cts/Utils.java
+++ b/hostsidetests/appsecurity/src/android/appsecurity/cts/Utils.java
@@ -32,26 +32,6 @@
 public class Utils {
     public static final int USER_SYSTEM = 0;
 
-    public static void runDeviceTests(ITestDevice device, String packageName)
-            throws DeviceNotAvailableException {
-        runDeviceTests(device, packageName, null, null, USER_SYSTEM, null);
-    }
-
-    public static void runDeviceTests(ITestDevice device, String packageName, int userId)
-            throws DeviceNotAvailableException {
-        runDeviceTests(device, packageName, null, null, userId, null);
-    }
-
-    public static void runDeviceTests(ITestDevice device, String packageName, String testClassName)
-            throws DeviceNotAvailableException {
-        runDeviceTests(device, packageName, testClassName, null, USER_SYSTEM, null);
-    }
-
-    public static void runDeviceTests(ITestDevice device, String packageName, String testClassName,
-            int userId) throws DeviceNotAvailableException {
-        runDeviceTests(device, packageName, testClassName, null, userId, null);
-    }
-
     public static void runDeviceTests(ITestDevice device, String packageName, String testClassName,
             String testMethodName) throws DeviceNotAvailableException {
         runDeviceTests(device, packageName, testClassName, testMethodName, USER_SYSTEM, null);
diff --git a/hostsidetests/appsecurity/test-apps/AccessSerialLegacy/Android.mk b/hostsidetests/appsecurity/test-apps/AccessSerialLegacy/Android.mk
new file mode 100644
index 0000000..e4b7962
--- /dev/null
+++ b/hostsidetests/appsecurity/test-apps/AccessSerialLegacy/Android.mk
@@ -0,0 +1,35 @@
+#
+# Copyright (C) 2017 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#      http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+#
+
+LOCAL_PATH := $(call my-dir)
+
+include $(CLEAR_VARS)
+
+LOCAL_MODULE_TAGS := tests
+LOCAL_STATIC_JAVA_LIBRARIES := \
+    android-support-test \
+
+LOCAL_SRC_FILES := $(call all-java-files-under, src)
+
+LOCAL_PACKAGE_NAME := CtsAccessSerialLegacy
+
+LOCAL_COMPATIBILITY_SUITE := cts vts general-tests
+
+LOCAL_PROGUARD_ENABLED := disabled
+
+LOCAL_DEX_PREOPT := false
+
+include $(BUILD_CTS_SUPPORT_PACKAGE)
diff --git a/hostsidetests/appsecurity/test-apps/AccessSerialLegacy/AndroidManifest.xml b/hostsidetests/appsecurity/test-apps/AccessSerialLegacy/AndroidManifest.xml
new file mode 100644
index 0000000..6976c56
--- /dev/null
+++ b/hostsidetests/appsecurity/test-apps/AccessSerialLegacy/AndroidManifest.xml
@@ -0,0 +1,28 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+  * Copyright (C) 2017 The Android Open Source Project
+  *
+  * Licensed under the Apache License, Version 2.0 (the "License");
+  * you may not use this file except in compliance with the License.
+  * You may obtain a copy of the License at
+  *
+  *      http://www.apache.org/licenses/LICENSE-2.0
+  *
+  * Unless required by applicable law or agreed to in writing, software
+  * distributed under the License is distributed on an "AS IS" BASIS,
+  * WITHOUT 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.os.cts">
+    <uses-sdk android:minSdkVersion="27" android:targetSdkVersion="27" />
+
+    <application/>
+
+    <instrumentation
+            android:name="android.support.test.runner.AndroidJUnitRunner"
+            android:targetPackage="android.os.cts" />
+
+</manifest>
diff --git a/hostsidetests/appsecurity/test-apps/AccessSerialLegacy/src/android/os/cts/AccessSerialLegacyTest.java b/hostsidetests/appsecurity/test-apps/AccessSerialLegacy/src/android/os/cts/AccessSerialLegacyTest.java
new file mode 100644
index 0000000..6dd97c3
--- /dev/null
+++ b/hostsidetests/appsecurity/test-apps/AccessSerialLegacy/src/android/os/cts/AccessSerialLegacyTest.java
@@ -0,0 +1,45 @@
+/*
+ * 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.os.cts;
+
+import static junit.framework.Assert.assertTrue;
+import static junit.framework.Assert.fail;
+
+import android.os.Build;
+import org.junit.Test;
+
+/**
+ * Test that legacy apps can access the device serial without the phone permission.
+ */
+public class AccessSerialLegacyTest {
+    @Test
+    public void testAccessSerialNoPermissionNeeded() throws Exception {
+        // Build.SERIAL should provide the device serial for legacy apps.
+        // We don't know the serial but know that it should not be the dummy
+        // value returned to unauthorized callers, so make sure not that value
+        assertTrue("Build.SERIAL must be visible to legacy apps",
+                !Build.UNKNOWN.equals(Build.SERIAL));
+
+        // We don't have the READ_PHONE_STATE permission, so this should throw
+        try {
+            Build.getSerial();
+            fail("getSerial() must be gated on the READ_PHONE_STATE permission");
+        } catch (SecurityException e) {
+            /* expected */
+        }
+    }
+}
diff --git a/hostsidetests/appsecurity/test-apps/AccessSerialModern/Android.mk b/hostsidetests/appsecurity/test-apps/AccessSerialModern/Android.mk
new file mode 100644
index 0000000..20d51b6
--- /dev/null
+++ b/hostsidetests/appsecurity/test-apps/AccessSerialModern/Android.mk
@@ -0,0 +1,36 @@
+#
+# 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 := \
+    compatibility-device-util \
+    android-support-test \
+
+LOCAL_SRC_FILES := $(call all-java-files-under, src)
+
+LOCAL_PACKAGE_NAME := CtsAccessSerialModern
+
+LOCAL_COMPATIBILITY_SUITE := cts vts general-tests
+
+LOCAL_PROGUARD_ENABLED := disabled
+
+LOCAL_DEX_PREOPT := false
+
+include $(BUILD_CTS_SUPPORT_PACKAGE)
diff --git a/hostsidetests/appsecurity/test-apps/AccessSerialModern/AndroidManifest.xml b/hostsidetests/appsecurity/test-apps/AccessSerialModern/AndroidManifest.xml
new file mode 100644
index 0000000..2eb5777
--- /dev/null
+++ b/hostsidetests/appsecurity/test-apps/AccessSerialModern/AndroidManifest.xml
@@ -0,0 +1,32 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+  * Copyright (C) 2017 The Android Open Source Project
+  *
+  * Licensed under the Apache License, Version 2.0 (the "License");
+  * you may not use this file except in compliance with the License.
+  * You may obtain a copy of the License at
+  *
+  *      http://www.apache.org/licenses/LICENSE-2.0
+  *
+  * Unless required by applicable law or agreed to in writing, software
+  * distributed under the License is distributed on an "AS IS" BASIS,
+  * WITHOUT 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.os.cts">
+    <uses-sdk android:minSdkVersion="27" android:targetSdkVersion="28" />
+
+    <uses-permission android:name="android.permission.READ_PHONE_STATE" />
+
+    <application>
+        <uses-library android:name="android.test.runner" />
+    </application>
+
+    <instrumentation
+            android:name="android.support.test.runner.AndroidJUnitRunner"
+            android:targetPackage="android.os.cts" />
+
+</manifest>
diff --git a/hostsidetests/appsecurity/test-apps/AccessSerialModern/src/android/os/cts/AccessSerialModernTest.java b/hostsidetests/appsecurity/test-apps/AccessSerialModern/src/android/os/cts/AccessSerialModernTest.java
new file mode 100644
index 0000000..df8ed97
--- /dev/null
+++ b/hostsidetests/appsecurity/test-apps/AccessSerialModern/src/android/os/cts/AccessSerialModernTest.java
@@ -0,0 +1,71 @@
+/*
+ * 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.os.cts;
+
+import static junit.framework.Assert.assertTrue;
+import static junit.framework.Assert.fail;
+
+import android.Manifest;
+import android.os.Build;
+import android.support.test.InstrumentationRegistry;
+import com.android.compatibility.common.util.SystemUtil;
+import org.junit.Test;
+
+import java.io.IOException;
+
+/**
+ * Test that legacy apps can access the device serial without the phone permission.
+ */
+public class AccessSerialModernTest {
+    @Test
+    public void testAccessSerialPermissionNeeded() throws Exception {
+        // Build.SERIAL should not provide the device serial for modern apps.
+        // We don't know the serial but know that it should be the dummy
+        // value returned to unauthorized callers, so make sure that value
+        assertTrue("Build.SERIAL must not work for modern apps",
+                Build.UNKNOWN.equals(Build.SERIAL));
+
+        // We don't have the read phone state permission, so this should throw
+        try {
+            Build.getSerial();
+            fail("getSerial() must be gated on the READ_PHONE_STATE permission");
+        } catch (SecurityException e) {
+            /* expected */
+        }
+
+        // Now grant ourselves READ_PHONE_STATE
+        grantReadPhoneStatePermission();
+
+        // Build.SERIAL should not provide the device serial for modern apps.
+        assertTrue("Build.SERIAL must not work for modern apps",
+                Build.UNKNOWN.equals(Build.SERIAL));
+
+        // We have the READ_PHONE_STATE permission, so this should not throw
+        try {
+            assertTrue("Build.getSerial() must work for apps holding READ_PHONE_STATE",
+                    !Build.UNKNOWN.equals(Build.getSerial()));
+        } catch (SecurityException e) {
+            fail("getSerial() must be gated on the READ_PHONE_STATE permission");
+        }
+    }
+
+    private void grantReadPhoneStatePermission() throws IOException {
+        SystemUtil.runShellCommand(InstrumentationRegistry.getInstrumentation(),
+                "pm grant " + InstrumentationRegistry.getContext().getPackageName()
+                + " " + Manifest.permission.READ_PHONE_STATE);
+    }
+}
diff --git a/hostsidetests/appsecurity/test-apps/AppAccessData/Android.mk b/hostsidetests/appsecurity/test-apps/AppAccessData/Android.mk
index 2e6911d..615492c 100644
--- a/hostsidetests/appsecurity/test-apps/AppAccessData/Android.mk
+++ b/hostsidetests/appsecurity/test-apps/AppAccessData/Android.mk
@@ -21,7 +21,9 @@
 LOCAL_SRC_FILES := $(call all-java-files-under, src)
 
 LOCAL_SDK_VERSION := current
-LOCAL_STATIC_JAVA_LIBRARIES := android-support-test legacy-android-test
+LOCAL_STATIC_JAVA_LIBRARIES := android-support-test
+
+LOCAL_JAVA_LIBRARIES := android.test.base.stubs
 
 LOCAL_PACKAGE_NAME := CtsAppAccessData
 
diff --git a/hostsidetests/appsecurity/test-apps/AppAccessData/AndroidManifest.xml b/hostsidetests/appsecurity/test-apps/AppAccessData/AndroidManifest.xml
index ffc104e..6a846fc 100644
--- a/hostsidetests/appsecurity/test-apps/AppAccessData/AndroidManifest.xml
+++ b/hostsidetests/appsecurity/test-apps/AppAccessData/AndroidManifest.xml
@@ -22,6 +22,8 @@
     created by com.android.cts.appwithdata.
     -->
 
+    <uses-permission android:name="android.permission.INTERNET"/>
+
     <application>
         <uses-library android:name="android.test.runner" />
     </application>
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 0867968..8c5b768 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,17 +16,25 @@
 
 package com.android.cts.appaccessdata;
 
+import android.content.pm.ApplicationInfo;
+import android.content.pm.PackageManager;
+import android.content.pm.PackageManager.NameNotFoundException;
+import android.net.TrafficStats;
+import android.net.Uri;
+import android.os.Bundle;
+import android.os.ParcelFileDescriptor;
+import android.os.SystemClock;
+import android.test.AndroidTestCase;
+
 import java.io.BufferedReader;
 import java.io.File;
 import java.io.FileInputStream;
 import java.io.FileNotFoundException;
 import java.io.FileReader;
 import java.io.IOException;
-
-import android.content.pm.ApplicationInfo;
-import android.content.pm.PackageManager;
-import android.content.pm.PackageManager.NameNotFoundException;
-import android.test.AndroidTestCase;
+import java.net.InetSocketAddress;
+import java.net.Socket;
+import java.net.SocketAddress;
 
 /**
  * Test that another app's private data cannot be accessed, while its public data can.
@@ -51,6 +59,8 @@
      */
     private static final String PUBLIC_FILE_NAME = "public_file.txt";
 
+    private static final Uri PRIVATE_TARGET = Uri.parse("content://com.android.cts.appwithdata/");
+
     /**
      * Tests that another app's private data cannot be accessed. It includes file
      * and detailed traffic stats.
@@ -68,7 +78,6 @@
         } catch (FileNotFoundException | SecurityException e) {
             // expected
         }
-        accessPrivateTrafficStats();
     }
 
     private ApplicationInfo getApplicationInfo(String packageName) {
@@ -97,7 +106,7 @@
         }
     }
 
-    private void accessPrivateTrafficStats() throws IOException {
+    public void testAccessPrivateTrafficStats() throws IOException {
         int otherAppUid = -1;
         try {
             otherAppUid = getContext()
@@ -121,4 +130,46 @@
             fail("Was not able to access qtaguid/stats: " + e);
         }
     }
+
+    public void testTrafficStatsStatsUidSelf() throws Exception {
+        final int uid = android.os.Process.myUid();
+        final long rxb = TrafficStats.getUidRxBytes(uid);
+        final long rxp = TrafficStats.getUidRxPackets(uid);
+        final long txb = TrafficStats.getUidTxBytes(uid);
+        final long txp = TrafficStats.getUidTxPackets(uid);
+
+        // Start remote server
+        final int port = getContext().getContentResolver().call(PRIVATE_TARGET, "start", null, null)
+                .getInt("port");
+
+        // Try talking to them, but shift blame
+        try {
+            final Socket socket = new Socket();
+            socket.setTcpNoDelay(true);
+
+            Bundle extras = new Bundle();
+            extras.putParcelable("fd", ParcelFileDescriptor.fromSocket(socket));
+            getContext().getContentResolver().call(PRIVATE_TARGET, "tag", null, extras);
+
+            socket.connect(new InetSocketAddress("localhost", port));
+
+            socket.getOutputStream().write(42);
+            socket.getOutputStream().flush();
+            final int val = socket.getInputStream().read();
+            assertEquals(42, val);
+            socket.close();
+        } catch (IOException e) {
+            throw new RuntimeException(e);
+        } finally {
+            getContext().getContentResolver().call(PRIVATE_TARGET, "stop", null, null);
+        }
+
+        SystemClock.sleep(1000);
+
+        // Since we shifted blame, our stats shouldn't have changed
+        assertEquals(rxb, TrafficStats.getUidRxBytes(uid));
+        assertEquals(rxp, TrafficStats.getUidRxPackets(uid));
+        assertEquals(txb, TrafficStats.getUidTxBytes(uid));
+        assertEquals(txp, TrafficStats.getUidTxPackets(uid));
+    }
 }
diff --git a/hostsidetests/appsecurity/test-apps/AppWithData/Android.mk b/hostsidetests/appsecurity/test-apps/AppWithData/Android.mk
index 3d11a83..c719665 100644
--- a/hostsidetests/appsecurity/test-apps/AppWithData/Android.mk
+++ b/hostsidetests/appsecurity/test-apps/AppWithData/Android.mk
@@ -21,7 +21,9 @@
 LOCAL_SRC_FILES := $(call all-java-files-under, src)
 
 LOCAL_SDK_VERSION := current
-LOCAL_STATIC_JAVA_LIBRARIES := android-support-test legacy-android-test
+LOCAL_STATIC_JAVA_LIBRARIES := android-support-test
+
+LOCAL_JAVA_LIBRARIES := android.test.base.stubs
 
 LOCAL_PACKAGE_NAME := CtsAppWithData
 
diff --git a/hostsidetests/appsecurity/test-apps/AppWithData/AndroidManifest.xml b/hostsidetests/appsecurity/test-apps/AppWithData/AndroidManifest.xml
index 598ebe7..2accec1 100644
--- a/hostsidetests/appsecurity/test-apps/AppWithData/AndroidManifest.xml
+++ b/hostsidetests/appsecurity/test-apps/AppWithData/AndroidManifest.xml
@@ -23,8 +23,14 @@
     -->
 
     <uses-permission android:name="android.permission.INTERNET" />
+
     <application>
         <uses-library android:name="android.test.runner" />
+
+        <provider
+            android:name="com.android.cts.appwithdata.MyProvider"
+            android:authorities="com.android.cts.appwithdata"
+            android:exported="true" />
     </application>
 
     <instrumentation android:name="android.support.test.runner.AndroidJUnitRunner"
diff --git a/hostsidetests/appsecurity/test-apps/AppWithData/src/com/android/cts/appwithdata/MyProvider.java b/hostsidetests/appsecurity/test-apps/AppWithData/src/com/android/cts/appwithdata/MyProvider.java
new file mode 100644
index 0000000..135c27c
--- /dev/null
+++ b/hostsidetests/appsecurity/test-apps/AppWithData/src/com/android/cts/appwithdata/MyProvider.java
@@ -0,0 +1,139 @@
+/*
+ * 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.appwithdata;
+
+import android.content.ContentProvider;
+import android.content.ContentValues;
+import android.database.Cursor;
+import android.net.TrafficStats;
+import android.net.Uri;
+import android.os.Bundle;
+import android.os.ParcelFileDescriptor;
+import android.util.Log;
+
+import java.io.IOException;
+import java.net.ServerSocket;
+import java.net.Socket;
+
+public class MyProvider extends ContentProvider {
+    private static final String TAG = "CTS";
+
+    @Override
+    public boolean onCreate() {
+        return true;
+    }
+
+    @Override
+    public Cursor query(Uri uri, String[] projection, String selection, String[] selectionArgs,
+            String sortOrder) {
+        throw new UnsupportedOperationException();
+    }
+
+    @Override
+    public String getType(Uri uri) {
+        throw new UnsupportedOperationException();
+    }
+
+    @Override
+    public Uri insert(Uri uri, ContentValues values) {
+        throw new UnsupportedOperationException();
+    }
+
+    @Override
+    public int delete(Uri uri, String selection, String[] selectionArgs) {
+        throw new UnsupportedOperationException();
+    }
+
+    @Override
+    public int update(Uri uri, ContentValues values, String selection, String[] selectionArgs) {
+        throw new UnsupportedOperationException();
+    }
+
+    @Override
+    public Bundle call(String method, String arg, Bundle extras) {
+        Log.v(TAG, "call() " + method);
+        switch (method) {
+            case "start": {
+                server = new EchoServer();
+                server.start();
+                extras = new Bundle();
+                extras.putInt("port", server.server.getLocalPort());
+                return extras;
+            }
+            case "stop": {
+                server.halt();
+                return null;
+            }
+            case "tag": {
+                Log.v(TAG, "My UID is " + android.os.Process.myUid());
+                TrafficStats.setThreadStatsUidSelf();
+                try {
+                    final ParcelFileDescriptor pfd = extras.getParcelable("fd");
+                    TrafficStats.tagFileDescriptor(pfd.getFileDescriptor());
+                } catch (IOException e) {
+                    throw new RuntimeException(e);
+                } finally {
+                    TrafficStats.clearThreadStatsUid();
+                }
+                return null;
+            }
+            default: {
+                throw new UnsupportedOperationException();
+            }
+        }
+    }
+
+    private EchoServer server;
+
+    private static class EchoServer extends Thread {
+        final ServerSocket server;
+        volatile boolean halted = false;
+
+        public EchoServer() {
+            try {
+                server = new ServerSocket(0);
+            } catch (IOException e) {
+                throw new RuntimeException(e);
+            }
+        }
+
+        public void halt() {
+            halted = true;
+            try {
+                server.close();
+            } catch (IOException e) {
+                throw new RuntimeException(e);
+            }
+        }
+
+        @Override
+        public void run() {
+            while (!halted) {
+                try {
+                    final Socket socket = server.accept();
+                    socket.setTcpNoDelay(true);
+                    final int val = socket.getInputStream().read();
+                    socket.getOutputStream().write(val);
+                    socket.getOutputStream().flush();
+                    socket.close();
+                } catch (IOException e) {
+                    Log.w(TAG, e);
+                }
+            }
+        }
+    }
+}
diff --git a/hostsidetests/appsecurity/test-apps/DocumentClient/Android.mk b/hostsidetests/appsecurity/test-apps/DocumentClient/Android.mk
index 0a55834..ad50a57 100644
--- a/hostsidetests/appsecurity/test-apps/DocumentClient/Android.mk
+++ b/hostsidetests/appsecurity/test-apps/DocumentClient/Android.mk
@@ -22,6 +22,8 @@
 LOCAL_SDK_VERSION := current
 LOCAL_STATIC_JAVA_LIBRARIES := android-support-test compatibility-device-util ctstestrunner ub-uiautomator
 
+LOCAL_JAVA_LIBRARIES := android.test.runner.stubs android.test.base.stubs
+
 LOCAL_SRC_FILES := $(call all-java-files-under, src)
 
 LOCAL_PACKAGE_NAME := CtsDocumentClient
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 68e554d..7038685 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
@@ -60,6 +60,10 @@
     public void setUp() throws Exception {
         super.setUp();
 
+        // Wake up the device and dismiss the keyguard before the test starts.
+        executeShellCommand("input keyevent KEYCODE_WAKEUP");
+        executeShellCommand("wm dismiss-keyguard");
+
         Configurator.getInstance().setToolType(MotionEvent.TOOL_TYPE_FINGER);
 
         // Disable IME's to avoid virtual keyboards from showing up. Occasionally IME draws some UI
@@ -140,6 +144,14 @@
         return true;
     }
 
+    protected boolean supportedHardwareForScopedDirectoryAccess() {
+        final PackageManager pm = getInstrumentation().getContext().getPackageManager();
+        if (pm.hasSystemFeature("android.hardware.type.watch")) {
+            return false;
+        }
+        return true;
+    }
+
     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 dec8769..7277148 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
@@ -77,7 +77,7 @@
     }
 
     public void testInvalidPath() throws Exception {
-        if (!supportedHardware()) return;
+        if (!supportedHardwareForScopedDirectoryAccess()) return;
 
         for (StorageVolume volume : getVolumes()) {
             openExternalDirectoryInvalidPath(volume, "");
@@ -89,7 +89,7 @@
     }
 
     public void testUserRejects() throws Exception {
-        if (!supportedHardware()) return;
+        if (!supportedHardwareForScopedDirectoryAccess()) return;
 
         for (StorageVolume volume : getVolumes()) {
             // Tests user clicking DENY button, for all valid directories.
@@ -114,7 +114,7 @@
     }
 
     public void testUserAccepts() throws Exception {
-        if (!supportedHardware()) return;
+        if (!supportedHardwareForScopedDirectoryAccess()) return;
 
         for (StorageVolume volume : getVolumes()) {
             userAcceptsTest(volume, DIRECTORY_PICTURES);
@@ -125,7 +125,7 @@
     }
 
     public void testUserAcceptsNewDirectory() throws Exception {
-        if (!supportedHardware()) return;
+        if (!supportedHardwareForScopedDirectoryAccess()) return;
 
         // TODO: figure out a better way to remove the directory.
         final String command = "rm -rf /sdcard/" + DIRECTORY_PICTURES;
@@ -137,7 +137,7 @@
     }
 
     public void testNotAskedAgain() throws Exception {
-        if (!supportedHardware()) return;
+        if (!supportedHardwareForScopedDirectoryAccess()) return;
 
         for (StorageVolume volume : getVolumes()) {
             final String volumeDesc = volume.getDescription(getInstrumentation().getContext());
@@ -157,7 +157,7 @@
     }
 
     public void testNotAskedAgainOnRoot() throws Exception {
-        if (!supportedHardware()) return;
+        if (!supportedHardwareForScopedDirectoryAccess()) return;
 
         for (StorageVolume volume : getVolumes()) {
             if (volume.isPrimary()) continue;
@@ -185,7 +185,7 @@
     }
 
     public void testDeniesOnceButAllowsAskingAgain() throws Exception {
-        if (!supportedHardware())return;
+        if (!supportedHardwareForScopedDirectoryAccess())return;
 
         final String[] dirs = { DIRECTORY_DCIM, DIRECTORY_ROOT };
         for (StorageVolume volume : getVolumes()) {
@@ -210,7 +210,7 @@
     }
 
     public void testDeniesOnceForAll() throws Exception {
-        if (!supportedHardware()) return;
+        if (!supportedHardwareForScopedDirectoryAccess()) return;
 
         final String[] dirs = {DIRECTORY_PICTURES, DIRECTORY_ROOT};
         for (StorageVolume volume : getVolumes()) {
@@ -246,13 +246,13 @@
     }
 
     public void testRemovePackageStep1UserDenies() throws Exception {
-        if (!supportedHardware()) return;
+        if (!supportedHardwareForScopedDirectoryAccess()) return;
 
         deniesOnceForAllTest(getPrimaryVolume(), DIRECTORY_NOTIFICATIONS);
     }
 
     public void testRemovePackageStep2UserAcceptsDoNotClear() throws Exception {
-        if (!supportedHardware()) return;
+        if (!supportedHardwareForScopedDirectoryAccess()) return;
 
         userAcceptsTest(getPrimaryVolume(), DIRECTORY_NOTIFICATIONS);
     }
diff --git a/hostsidetests/appsecurity/test-apps/DocumentProvider/Android.mk b/hostsidetests/appsecurity/test-apps/DocumentProvider/Android.mk
index 7609e33..42aa074 100644
--- a/hostsidetests/appsecurity/test-apps/DocumentProvider/Android.mk
+++ b/hostsidetests/appsecurity/test-apps/DocumentProvider/Android.mk
@@ -20,7 +20,12 @@
 
 LOCAL_MODULE_TAGS := tests
 LOCAL_SDK_VERSION := current
-LOCAL_STATIC_JAVA_LIBRARIES := android-support-test compatibility-device-util ctstestrunner ub-uiautomator
+LOCAL_STATIC_JAVA_LIBRARIES := \
+    android-support-test \
+    compatibility-device-util \
+    ctstestrunner \
+    ub-uiautomator \
+
 
 LOCAL_SRC_FILES := $(call all-java-files-under, src)
 
diff --git a/hostsidetests/appsecurity/test-apps/DocumentProvider/AndroidManifest.xml b/hostsidetests/appsecurity/test-apps/DocumentProvider/AndroidManifest.xml
index 894eff1..6e140d7 100644
--- a/hostsidetests/appsecurity/test-apps/DocumentProvider/AndroidManifest.xml
+++ b/hostsidetests/appsecurity/test-apps/DocumentProvider/AndroidManifest.xml
@@ -17,6 +17,8 @@
 <manifest xmlns:android="http://schemas.android.com/apk/res/android"
           package="com.android.cts.documentprovider">
     <application>
+        <uses-library android:name="android.test.runner" />
+
         <provider android:name=".MyDocumentsProvider"
                 android:authorities="com.android.cts.documentprovider"
                 android:exported="true"
diff --git a/hostsidetests/appsecurity/test-apps/EncryptionApp/Android.mk b/hostsidetests/appsecurity/test-apps/EncryptionApp/Android.mk
index a4a9436..9734112 100644
--- a/hostsidetests/appsecurity/test-apps/EncryptionApp/Android.mk
+++ b/hostsidetests/appsecurity/test-apps/EncryptionApp/Android.mk
@@ -22,6 +22,8 @@
 LOCAL_SDK_VERSION := current
 LOCAL_STATIC_JAVA_LIBRARIES := android-support-test compatibility-device-util ctstestrunner ub-uiautomator
 
+LOCAL_JAVA_LIBRARIES := android.test.base.stubs
+
 LOCAL_SRC_FILES := $(call all-java-files-under, src)
 
 LOCAL_PACKAGE_NAME := CtsEncryptionApp
diff --git a/hostsidetests/appsecurity/test-apps/EphemeralTestApp/EphemeralApp1/Android.mk b/hostsidetests/appsecurity/test-apps/EphemeralTestApp/EphemeralApp1/Android.mk
index aaeb8c0..f011c80 100644
--- a/hostsidetests/appsecurity/test-apps/EphemeralTestApp/EphemeralApp1/Android.mk
+++ b/hostsidetests/appsecurity/test-apps/EphemeralTestApp/EphemeralApp1/Android.mk
@@ -17,12 +17,11 @@
 LOCAL_PATH := $(call my-dir)
 include $(CLEAR_VARS)
 
-LOCAL_JAVA_LIBRARIES := android.test.runner
+LOCAL_JAVA_LIBRARIES := android.test.runner android.test.base
 LOCAL_MODULE_TAGS := tests
 LOCAL_STATIC_JAVA_LIBRARIES := \
     cts-aia-util \
     android-support-test \
-    legacy-android-test \
 	ctsdeviceutillegacy \
 	ctstestrunner
 
diff --git a/hostsidetests/appsecurity/test-apps/EphemeralTestApp/EphemeralApp1/AndroidManifest.xml b/hostsidetests/appsecurity/test-apps/EphemeralTestApp/EphemeralApp1/AndroidManifest.xml
index 6028ae5..8f39344 100644
--- a/hostsidetests/appsecurity/test-apps/EphemeralTestApp/EphemeralApp1/AndroidManifest.xml
+++ b/hostsidetests/appsecurity/test-apps/EphemeralTestApp/EphemeralApp1/AndroidManifest.xml
@@ -21,8 +21,15 @@
         android:minSdkVersion="25" />
 
     <uses-permission android:name="com.android.alarm.permission.SET_ALARM" />
-    <uses-permission android:name="android.permission.INTERNET" />
+    <uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION"/>
+    <uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
+    <uses-permission android:name="android.permission.CAMERA" />
     <uses-permission android:name="android.permission.INSTANT_APP_FOREGROUND_SERVICE" />
+    <uses-permission android:name="android.permission.INTERNET" />
+    <uses-permission android:name="android.permission.RECORD_AUDIO" />
+    <uses-permission android:name="android.permission.READ_PHONE_NUMBERS" />
+    <uses-permission android:name="android.permission.VIBRATE" />
+    <uses-permission android:name="android.permission.WAKE_LOCK" />
 
     <application
         android:label="@string/app_name">
diff --git a/hostsidetests/appsecurity/test-apps/EphemeralTestApp/EphemeralApp1/src/com/android/cts/ephemeralapp1/ClientTest.java b/hostsidetests/appsecurity/test-apps/EphemeralTestApp/EphemeralApp1/src/com/android/cts/ephemeralapp1/ClientTest.java
index 656be27..0db23d4 100644
--- a/hostsidetests/appsecurity/test-apps/EphemeralTestApp/EphemeralApp1/src/com/android/cts/ephemeralapp1/ClientTest.java
+++ b/hostsidetests/appsecurity/test-apps/EphemeralTestApp/EphemeralApp1/src/com/android/cts/ephemeralapp1/ClientTest.java
@@ -16,17 +16,17 @@
 
 package com.android.cts.ephemeralapp1;
 
+import static android.media.AudioFormat.CHANNEL_IN_MONO;
+import static android.media.AudioFormat.ENCODING_PCM_16BIT;
+import static android.media.MediaRecorder.AudioSource.MIC;
 import static org.hamcrest.CoreMatchers.is;
-import static org.hamcrest.CoreMatchers.nullValue;
 import static org.hamcrest.CoreMatchers.notNullValue;
-import static org.junit.Assert.assertFalse;
+import static org.hamcrest.CoreMatchers.nullValue;
 import static org.junit.Assert.assertSame;
 import static org.junit.Assert.assertThat;
-import static org.junit.Assert.assertTrue;
 import static org.junit.Assert.fail;
 
 import android.Manifest;
-import android.annotation.Nullable;
 import android.app.NotificationChannel;
 import android.app.NotificationManager;
 import android.content.ActivityNotFoundException;
@@ -41,14 +41,29 @@
 import android.content.pm.ProviderInfo;
 import android.content.pm.ResolveInfo;
 import android.database.Cursor;
+import android.hardware.camera2.CameraDevice;
+import android.hardware.camera2.CameraManager;
+import android.media.AudioRecord;
+import android.net.ConnectivityManager;
 import android.net.Uri;
 import android.os.Build;
 import android.os.Bundle;
+import android.os.Handler;
+import android.os.HandlerThread;
 import android.os.IBinder;
+import android.os.Looper;
+import android.os.PowerManager;
+import android.os.PowerManager.WakeLock;
+import android.os.VibrationEffect;
+import android.os.Vibrator;
 import android.support.test.InstrumentationRegistry;
 import android.support.test.runner.AndroidJUnit4;
+import android.telephony.CellLocation;
+import android.telephony.PhoneStateListener;
+import android.telephony.TelephonyManager;
 
 import com.android.compatibility.common.util.SystemUtil;
+import com.android.compatibility.common.util.TestThread;
 import com.android.cts.util.TestResult;
 
 import org.junit.After;
@@ -85,6 +100,7 @@
             "com.android.cts.ephemeraltest.EXTRA_ACTIVITY_RESULT";
 
     private BroadcastReceiver mReceiver;
+    private PhoneStateListener mPhoneStateListener;
     private final SynchronousQueue<TestResult> mResultQueue = new SynchronousQueue<>();
 
     @Before
@@ -994,6 +1010,137 @@
         latch2.await(5, TimeUnit.SECONDS);
     }
 
+    @Test
+    public void testRecordAudioPermission() throws Throwable {
+        final AudioRecord record =
+                new AudioRecord(MIC, 8000, CHANNEL_IN_MONO, ENCODING_PCM_16BIT, 4096);
+        try {
+            assertThat("audio record not initialized",
+                    record.getState(), is(AudioRecord.STATE_INITIALIZED));
+        } finally {
+            record.release();
+        }
+    }
+
+    @Test
+    public void testReadPhoneNumbersPermission() throws Throwable {
+        final Context context = InstrumentationRegistry.getContext();
+        if (!context.getPackageManager().hasSystemFeature(PackageManager.FEATURE_TELEPHONY)) {
+            return;
+        }
+
+        try {
+            final TelephonyManager telephonyManager =
+                    (TelephonyManager) context.getSystemService(Context.TELEPHONY_SERVICE);
+            final String nmbr = telephonyManager.getLine1Number();
+        } catch (SecurityException e) {
+            fail("Permission not granted");
+        }
+    }
+
+    @Test
+    public void testAccessCoarseLocationPermission() throws Throwable {
+        final Context context = InstrumentationRegistry.getContext();
+        final ConnectivityManager mCm =
+                (ConnectivityManager) context.getSystemService(Context.CONNECTIVITY_SERVICE);
+        if (mCm.getNetworkInfo(ConnectivityManager.TYPE_MOBILE) == null) {
+            return;
+        }
+
+        final TelephonyManager mTelephonyManager =
+                (TelephonyManager) context.getSystemService(Context.TELEPHONY_SERVICE);
+        // getCellLocation should never return null, but that is allowed if the cell network type
+        // is LTE (since there is no LteCellLocation class)
+        if (mTelephonyManager.getNetworkType() != TelephonyManager.NETWORK_TYPE_LTE) {
+            assertThat(mTelephonyManager.getCellLocation(), is(notNullValue()));
+        }
+
+        final CountDownLatch latch = new CountDownLatch(1);
+        final TestThread t = new TestThread(() -> {
+            Looper.prepare();
+            mPhoneStateListener = new PhoneStateListener() {
+                @Override
+                public void onCellLocationChanged(CellLocation location) {
+                    latch.countDown();
+                }
+            };
+            mTelephonyManager.listen(mPhoneStateListener, PhoneStateListener.LISTEN_CELL_LOCATION);
+            Looper.loop();
+        });
+        t.start();
+
+        try {
+            CellLocation.requestLocationUpdate();
+            assertThat(latch.await(1000, TimeUnit.MILLISECONDS), is(true));
+        } finally {
+            mTelephonyManager.listen(mPhoneStateListener, PhoneStateListener.LISTEN_NONE);
+        }
+        t.checkException();
+    }
+
+    @Test
+    public void testCameraPermission() throws Throwable {
+        final Context context = InstrumentationRegistry.getContext();
+        final CameraManager manager =
+                (CameraManager) context.getSystemService(Context.CAMERA_SERVICE);
+        final String[] cameraIds = manager.getCameraIdList();
+        if (cameraIds.length == 0) {
+            return;
+        }
+        final CountDownLatch latch = new CountDownLatch(1);
+        final HandlerThread backgroundThread = new HandlerThread("camera_bg");
+        backgroundThread.start();
+        final CameraDevice.StateCallback callback = new CameraDevice.StateCallback() {
+            @Override
+            public void onOpened(CameraDevice camera) {
+                latch.countDown();
+                camera.close();
+            }
+            @Override
+            public void onDisconnected(CameraDevice camera) {
+                camera.close();
+            }
+            @Override
+            public void onError(CameraDevice camera, int error) {
+                camera.close();
+            }
+        };
+        manager.openCamera(cameraIds[0], callback, new Handler(backgroundThread.getLooper()));
+        assertThat(latch.await(1000, TimeUnit.MILLISECONDS), is(true));
+    }
+
+    @Test
+    public void testInternetPermission() throws Throwable {
+        final ConnectivityManager manager = (ConnectivityManager) InstrumentationRegistry.getContext()
+                .getSystemService(Context.CONNECTIVITY_SERVICE);
+        manager.reportNetworkConnectivity(null, false);
+    }
+
+    @Test
+    public void testVibratePermission() throws Throwable {
+        final Vibrator vibrator = (Vibrator) InstrumentationRegistry.getContext()
+                .getSystemService(Context.VIBRATOR_SERVICE);
+        final VibrationEffect effect =
+                VibrationEffect.createOneShot(100, VibrationEffect.DEFAULT_AMPLITUDE);
+        vibrator.vibrate(effect);
+    }
+
+    @Test
+    public void testWakeLockPermission() throws Throwable {
+        WakeLock wakeLock = null;
+        try {
+            final PowerManager powerManager = (PowerManager) InstrumentationRegistry.getContext()
+                    .getSystemService(Context.POWER_SERVICE);
+            wakeLock = powerManager.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, "test");
+            wakeLock.acquire();
+        }
+        finally {
+            if (wakeLock != null &&  wakeLock.isHeld()) {
+                wakeLock.release();
+            }
+        }
+    }
+
     private TestResult getResult() {
         final TestResult result;
         try {
diff --git a/hostsidetests/appsecurity/test-apps/EphemeralTestApp/ImplicitlyExposedApp/Android.mk b/hostsidetests/appsecurity/test-apps/EphemeralTestApp/ImplicitlyExposedApp/Android.mk
index 31a45b0..6309704 100644
--- a/hostsidetests/appsecurity/test-apps/EphemeralTestApp/ImplicitlyExposedApp/Android.mk
+++ b/hostsidetests/appsecurity/test-apps/EphemeralTestApp/ImplicitlyExposedApp/Android.mk
@@ -20,8 +20,7 @@
 LOCAL_MODULE_TAGS := tests
 LOCAL_STATIC_JAVA_LIBRARIES := \
     cts-aia-util \
-    android-support-test \
-    legacy-android-test
+    android-support-test
 
 # tag this module as a cts test artifact
 LOCAL_COMPATIBILITY_SUITE := cts vts general-tests
diff --git a/hostsidetests/appsecurity/test-apps/EphemeralTestApp/NormalApp/Android.mk b/hostsidetests/appsecurity/test-apps/EphemeralTestApp/NormalApp/Android.mk
index 43deb82..d9fa238 100644
--- a/hostsidetests/appsecurity/test-apps/EphemeralTestApp/NormalApp/Android.mk
+++ b/hostsidetests/appsecurity/test-apps/EphemeralTestApp/NormalApp/Android.mk
@@ -20,8 +20,9 @@
 LOCAL_MODULE_TAGS := tests
 LOCAL_STATIC_JAVA_LIBRARIES := \
     cts-aia-util \
-    android-support-test \
-    legacy-android-test
+    android-support-test
+
+LOCAL_JAVA_LIBRARIES := android.test.base
 
 # tag this module as a cts test artifact
 LOCAL_COMPATIBILITY_SUITE := cts vts general-tests
diff --git a/hostsidetests/appsecurity/test-apps/EphemeralTestApp/UserApp/Android.mk b/hostsidetests/appsecurity/test-apps/EphemeralTestApp/UserApp/Android.mk
index 1206e56..35c089f 100644
--- a/hostsidetests/appsecurity/test-apps/EphemeralTestApp/UserApp/Android.mk
+++ b/hostsidetests/appsecurity/test-apps/EphemeralTestApp/UserApp/Android.mk
@@ -20,8 +20,7 @@
 LOCAL_MODULE_TAGS := tests
 LOCAL_STATIC_JAVA_LIBRARIES := \
     cts-aia-util \
-    android-support-test \
-    legacy-android-test
+    android-support-test
 
 # tag this module as a cts test artifact
 LOCAL_COMPATIBILITY_SUITE := cts vts general-tests
diff --git a/hostsidetests/appsecurity/test-apps/EphemeralTestApp/UserAppTest/Android.mk b/hostsidetests/appsecurity/test-apps/EphemeralTestApp/UserAppTest/Android.mk
index f446140..cfa0b55 100644
--- a/hostsidetests/appsecurity/test-apps/EphemeralTestApp/UserAppTest/Android.mk
+++ b/hostsidetests/appsecurity/test-apps/EphemeralTestApp/UserAppTest/Android.mk
@@ -19,8 +19,7 @@
 
 LOCAL_MODULE_TAGS := tests
 LOCAL_STATIC_JAVA_LIBRARIES := \
-    android-support-test \
-    legacy-android-test
+    android-support-test
 
 # tag this module as a cts test artifact
 LOCAL_COMPATIBILITY_SUITE := cts vts general-tests
diff --git a/hostsidetests/appsecurity/test-apps/ExternalStorageApp/Android.mk b/hostsidetests/appsecurity/test-apps/ExternalStorageApp/Android.mk
index 47d468e..2fcbc20 100644
--- a/hostsidetests/appsecurity/test-apps/ExternalStorageApp/Android.mk
+++ b/hostsidetests/appsecurity/test-apps/ExternalStorageApp/Android.mk
@@ -18,7 +18,9 @@
 
 LOCAL_MODULE_TAGS := tests
 LOCAL_SDK_VERSION := current
-LOCAL_STATIC_JAVA_LIBRARIES := android-support-test legacy-android-test
+LOCAL_STATIC_JAVA_LIBRARIES := android-support-test
+
+LOCAL_JAVA_LIBRARIES := android.test.base.stubs
 
 LOCAL_SRC_FILES := $(call all-java-files-under, src)
 LOCAL_PACKAGE_NAME := CtsExternalStorageApp
diff --git a/hostsidetests/appsecurity/test-apps/InstrumentationAppDiffCert/Android.mk b/hostsidetests/appsecurity/test-apps/InstrumentationAppDiffCert/Android.mk
index a48abbf..b99597b 100644
--- a/hostsidetests/appsecurity/test-apps/InstrumentationAppDiffCert/Android.mk
+++ b/hostsidetests/appsecurity/test-apps/InstrumentationAppDiffCert/Android.mk
@@ -21,7 +21,9 @@
 LOCAL_SRC_FILES := $(call all-java-files-under, src)
 
 LOCAL_SDK_VERSION := current
-LOCAL_STATIC_JAVA_LIBRARIES := android-support-test legacy-android-test
+LOCAL_STATIC_JAVA_LIBRARIES := android-support-test
+
+LOCAL_JAVA_LIBRARIES := android.test.base.stubs
 
 LOCAL_PACKAGE_NAME := CtsInstrumentationAppDiffCert
 
diff --git a/hostsidetests/appsecurity/test-apps/IsolatedSplitApp/Android.mk b/hostsidetests/appsecurity/test-apps/IsolatedSplitApp/Android.mk
index 0834642..3231710 100644
--- a/hostsidetests/appsecurity/test-apps/IsolatedSplitApp/Android.mk
+++ b/hostsidetests/appsecurity/test-apps/IsolatedSplitApp/Android.mk
@@ -18,20 +18,25 @@
 
 include $(CLEAR_VARS)
 
+LOCAL_PACKAGE_NAME := CtsIsolatedSplitApp
 LOCAL_USE_AAPT2 := true
 LOCAL_MODULE_TAGS := tests
-LOCAL_SDK_VERSION := current
 LOCAL_COMPATIBILITY_SUITE := cts vts general-tests
-LOCAL_EXPORT_PACKAGE_RESOURCES := true
-LOCAL_STATIC_JAVA_LIBRARIES := ctstestrunner android-support-test
+LOCAL_SDK_VERSION := current
 
+# Feature splits are dependent on this base, so it must be exported.
+LOCAL_EXPORT_PACKAGE_RESOURCES := true
+
+# Make sure our test locale polish is not stripped.
+LOCAL_AAPT_INCLUDE_ALL_RESOURCES := true
+
+LOCAL_STATIC_JAVA_LIBRARIES := ctstestrunner android-support-test
 LOCAL_SRC_FILES := $(call all-java-files-under, src)
 
-LOCAL_PACKAGE_NAME := CtsIsolatedSplitApp
+# Generate a locale split.
 LOCAL_PACKAGE_SPLITS := pl
 
-# Tag this module as a cts test artifact
-
 include $(BUILD_CTS_SUPPORT_PACKAGE)
 
+# Build the other splits.
 include $(call first-makefiles-under,$(LOCAL_PATH))
diff --git a/hostsidetests/appsecurity/test-apps/IsolatedSplitApp/feature_a/Android.mk b/hostsidetests/appsecurity/test-apps/IsolatedSplitApp/feature_a/Android.mk
index 48b4e3b..dd76592 100644
--- a/hostsidetests/appsecurity/test-apps/IsolatedSplitApp/feature_a/Android.mk
+++ b/hostsidetests/appsecurity/test-apps/IsolatedSplitApp/feature_a/Android.mk
@@ -17,20 +17,32 @@
 LOCAL_PATH:= $(call my-dir)
 include $(CLEAR_VARS)
 
+LOCAL_PACKAGE_NAME := CtsIsolatedSplitAppFeatureA
 LOCAL_USE_AAPT2 := true
 LOCAL_MODULE_TAGS := tests
 LOCAL_COMPATIBILITY_SUITE := cts vts general-tests
+
+# Feature splits are dependent on this split, so it must be exported.
 LOCAL_EXPORT_PACKAGE_RESOURCES := true
-LOCAL_PACKAGE_NAME := CtsIsolatedSplitAppFeatureA
+
+# Make sure our test locale polish is not stripped.
+LOCAL_AAPT_INCLUDE_ALL_RESOURCES := true
 
 LOCAL_SRC_FILES := $(call all-subdir-java-files)
 
+# Generate a locale split.
 LOCAL_PACKAGE_SPLITS := pl
 
+# Code and resource dependency on the base.
 LOCAL_APK_LIBRARIES := CtsIsolatedSplitApp
 LOCAL_RES_LIBRARIES := $(LOCAL_APK_LIBRARIES)
 
-LOCAL_AAPT_FLAGS += --custom-package com.android.cts.isolatedsplitapp.feature_a
+# Although feature splits use unique resource package names, they must all
+# have the same manifest package name to be considered one app.
+LOCAL_AAPT_FLAGS += --rename-manifest-package com.android.cts.isolatedsplitapp
+
+# Assign a unique package ID to this feature split. Since these are isolated splits,
+# it must only be unique across a dependency chain.
 LOCAL_AAPT_FLAGS += --package-id 0x80
 
 include $(BUILD_CTS_SUPPORT_PACKAGE)
diff --git a/hostsidetests/appsecurity/test-apps/IsolatedSplitApp/feature_a/AndroidManifest.xml b/hostsidetests/appsecurity/test-apps/IsolatedSplitApp/feature_a/AndroidManifest.xml
index d3aed1d..958b8d0 100644
--- a/hostsidetests/appsecurity/test-apps/IsolatedSplitApp/feature_a/AndroidManifest.xml
+++ b/hostsidetests/appsecurity/test-apps/IsolatedSplitApp/feature_a/AndroidManifest.xml
@@ -15,17 +15,17 @@
 -->
 
 <manifest xmlns:android="http://schemas.android.com/apk/res/android"
-        package="com.android.cts.isolatedsplitapp"
+        package="com.android.cts.isolatedsplitapp.feature_a"
         featureSplit="feature_a">
 
     <application>
-        <activity android:name=".feature_a.FeatureAActivity">
+        <activity android:name=".FeatureAActivity">
             <intent-filter>
                 <action android:name="android.intent.action.MAIN" />
                 <category android:name="android.intent.category.LAUNCHER" />
             </intent-filter>
         </activity>
-        <receiver android:name=".feature_a.FeatureAReceiver">
+        <receiver android:name=".FeatureAReceiver">
             <intent-filter>
                 <action android:name="com.android.cts.isolatedsplitapp.ACTION" />
             </intent-filter>
diff --git a/hostsidetests/appsecurity/test-apps/IsolatedSplitApp/feature_b/Android.mk b/hostsidetests/appsecurity/test-apps/IsolatedSplitApp/feature_b/Android.mk
index 64b5fc3..240fc2c 100644
--- a/hostsidetests/appsecurity/test-apps/IsolatedSplitApp/feature_b/Android.mk
+++ b/hostsidetests/appsecurity/test-apps/IsolatedSplitApp/feature_b/Android.mk
@@ -17,19 +17,29 @@
 LOCAL_PATH:= $(call my-dir)
 include $(CLEAR_VARS)
 
+LOCAL_PACKAGE_NAME := CtsIsolatedSplitAppFeatureB
 LOCAL_USE_AAPT2 := true
 LOCAL_MODULE_TAGS := tests
 LOCAL_COMPATIBILITY_SUITE := cts vts general-tests
-LOCAL_PACKAGE_NAME := CtsIsolatedSplitAppFeatureB
+
+# Make sure our test locale polish is not stripped.
+LOCAL_AAPT_INCLUDE_ALL_RESOURCES := true
 
 LOCAL_SRC_FILES := $(call all-subdir-java-files)
 
+# Generate a locale split.
 LOCAL_PACKAGE_SPLITS := pl
 
+# Code and resource dependency on the base and feature A.
 LOCAL_APK_LIBRARIES := CtsIsolatedSplitApp CtsIsolatedSplitAppFeatureA
 LOCAL_RES_LIBRARIES := $(LOCAL_APK_LIBRARIES)
 
-LOCAL_AAPT_FLAGS := --custom-package com.android.cts.isolatedsplitapp.feature_b
+# Although feature splits use unique resource package names, they must all
+# have the same manifest package name to be considered one app.
+LOCAL_AAPT_FLAGS := --rename-manifest-package com.android.cts.isolatedsplitapp
+
+# Assign a unique package ID to this feature split. Since these are isolated splits,
+# it must only be unique across a dependency chain.
 LOCAL_AAPT_FLAGS += --package-id 0x81
 
 include $(BUILD_CTS_SUPPORT_PACKAGE)
diff --git a/hostsidetests/appsecurity/test-apps/IsolatedSplitApp/feature_b/AndroidManifest.xml b/hostsidetests/appsecurity/test-apps/IsolatedSplitApp/feature_b/AndroidManifest.xml
index 00c2d6c..d89a1f2 100644
--- a/hostsidetests/appsecurity/test-apps/IsolatedSplitApp/feature_b/AndroidManifest.xml
+++ b/hostsidetests/appsecurity/test-apps/IsolatedSplitApp/feature_b/AndroidManifest.xml
@@ -15,19 +15,19 @@
 -->
 
 <manifest xmlns:android="http://schemas.android.com/apk/res/android"
-        package="com.android.cts.isolatedsplitapp"
+        package="com.android.cts.isolatedsplitapp.feature_b"
         featureSplit="feature_b">
 
     <uses-split android:name="feature_a" />
 
     <application>
-        <activity android:name=".feature_b.FeatureBActivity">
+        <activity android:name=".FeatureBActivity">
             <intent-filter>
                 <action android:name="android.intent.action.MAIN" />
                 <category android:name="android.intent.category.LAUNCHER" />
             </intent-filter>
         </activity>
-        <receiver android:name=".feature_b.FeatureBReceiver">
+        <receiver android:name=".FeatureBReceiver">
             <intent-filter>
                 <action android:name="com.android.cts.isolatedsplitapp.ACTION" />
             </intent-filter>
diff --git a/hostsidetests/appsecurity/test-apps/IsolatedSplitApp/feature_c/Android.mk b/hostsidetests/appsecurity/test-apps/IsolatedSplitApp/feature_c/Android.mk
index f21d1d0..35b3252 100644
--- a/hostsidetests/appsecurity/test-apps/IsolatedSplitApp/feature_c/Android.mk
+++ b/hostsidetests/appsecurity/test-apps/IsolatedSplitApp/feature_c/Android.mk
@@ -17,19 +17,29 @@
 LOCAL_PATH:= $(call my-dir)
 include $(CLEAR_VARS)
 
+LOCAL_PACKAGE_NAME := CtsIsolatedSplitAppFeatureC
 LOCAL_USE_AAPT2 := true
 LOCAL_MODULE_TAGS := tests
 LOCAL_COMPATIBILITY_SUITE := cts vts general-tests
-LOCAL_PACKAGE_NAME := CtsIsolatedSplitAppFeatureC
+
+# Make sure our test locale polish is not stripped.
+LOCAL_AAPT_INCLUDE_ALL_RESOURCES := true
 
 LOCAL_SRC_FILES := $(call all-subdir-java-files)
 
+# Generate a locale split.
 LOCAL_PACKAGE_SPLITS := pl
 
+# Code and resource dependency on the base.
 LOCAL_APK_LIBRARIES := CtsIsolatedSplitApp
 LOCAL_RES_LIBRARIES := $(LOCAL_APK_LIBRARIES)
 
-LOCAL_AAPT_FLAGS := --custom-package com.android.cts.isolatedsplitapp.feature_c
-LOCAL_AAPT_FLAGS += --package-id 0x82
+# Although feature splits use unique resource package names, they must all
+# have the same manifest package name to be considered one app.
+LOCAL_AAPT_FLAGS := --rename-manifest-package com.android.cts.isolatedsplitapp
+
+# Use the same package ID as feature A, since this is an isolated split and
+# will not be loaded together with feature A.
+LOCAL_AAPT_FLAGS += --package-id 0x80
 
 include $(BUILD_CTS_SUPPORT_PACKAGE)
diff --git a/hostsidetests/appsecurity/test-apps/IsolatedSplitApp/feature_c/AndroidManifest.xml b/hostsidetests/appsecurity/test-apps/IsolatedSplitApp/feature_c/AndroidManifest.xml
index ac3a57f..64b087c 100644
--- a/hostsidetests/appsecurity/test-apps/IsolatedSplitApp/feature_c/AndroidManifest.xml
+++ b/hostsidetests/appsecurity/test-apps/IsolatedSplitApp/feature_c/AndroidManifest.xml
@@ -15,17 +15,17 @@
 -->
 
 <manifest xmlns:android="http://schemas.android.com/apk/res/android"
-        package="com.android.cts.isolatedsplitapp"
+        package="com.android.cts.isolatedsplitapp.feature_c"
         featureSplit="feature_c">
 
     <application>
-        <activity android:name=".feature_c.FeatureCActivity">
+        <activity android:name=".FeatureCActivity">
             <intent-filter>
                 <action android:name="android.intent.action.MAIN" />
                 <category android:name="android.intent.category.LAUNCHER" />
             </intent-filter>
         </activity>
-        <receiver android:name=".feature_c.FeatureCReceiver">
+        <receiver android:name=".FeatureCReceiver">
             <intent-filter>
                 <action android:name="com.android.cts.isolatedsplitapp.ACTION" />
             </intent-filter>
diff --git a/hostsidetests/appsecurity/test-apps/IsolatedSplitApp/src/com/android/cts/isolatedsplitapp/SplitAppTest.java b/hostsidetests/appsecurity/test-apps/IsolatedSplitApp/src/com/android/cts/isolatedsplitapp/SplitAppTest.java
index 2f6af13..b85e21b 100644
--- a/hostsidetests/appsecurity/test-apps/IsolatedSplitApp/src/com/android/cts/isolatedsplitapp/SplitAppTest.java
+++ b/hostsidetests/appsecurity/test-apps/IsolatedSplitApp/src/com/android/cts/isolatedsplitapp/SplitAppTest.java
@@ -45,15 +45,20 @@
 @RunWith(AndroidJUnit4.class)
 public class SplitAppTest {
     private static final String PACKAGE = "com.android.cts.isolatedsplitapp";
+
     private static final ComponentName FEATURE_A_ACTIVITY =
             ComponentName.createRelative(PACKAGE, ".feature_a.FeatureAActivity");
     private static final ComponentName FEATURE_B_ACTIVITY =
             ComponentName.createRelative(PACKAGE, ".feature_b.FeatureBActivity");
     private static final ComponentName FEATURE_C_ACTIVITY =
             ComponentName.createRelative(PACKAGE, ".feature_c.FeatureCActivity");
-    private static final String FEATURE_A_STRING = PACKAGE + ":string/feature_a_string";
-    private static final String FEATURE_B_STRING = PACKAGE + ":string/feature_b_string";
-    private static final String FEATURE_C_STRING = PACKAGE + ":string/feature_c_string";
+
+    private static final String FEATURE_A_STRING =
+            "com.android.cts.isolatedsplitapp.feature_a:string/feature_a_string";
+    private static final String FEATURE_B_STRING =
+            "com.android.cts.isolatedsplitapp.feature_b:string/feature_b_string";
+    private static final String FEATURE_C_STRING =
+            "com.android.cts.isolatedsplitapp.feature_c:string/feature_c_string";
 
     private static final Configuration PL = new Configuration();
     static {
diff --git a/hostsidetests/appsecurity/test-apps/MajorVersionApp/Android.mk b/hostsidetests/appsecurity/test-apps/MajorVersionApp/Android.mk
new file mode 100644
index 0000000..6047ca5
--- /dev/null
+++ b/hostsidetests/appsecurity/test-apps/MajorVersionApp/Android.mk
@@ -0,0 +1,21 @@
+#
+# 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)
+
+include $(call first-makefiles-under,$(LOCAL_PATH))
diff --git a/hostsidetests/appsecurity/test-apps/MajorVersionApp/Version000000000000ffff/Android.mk b/hostsidetests/appsecurity/test-apps/MajorVersionApp/Version000000000000ffff/Android.mk
new file mode 100644
index 0000000..db83bb0
--- /dev/null
+++ b/hostsidetests/appsecurity/test-apps/MajorVersionApp/Version000000000000ffff/Android.mk
@@ -0,0 +1,37 @@
+# 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_SRC_FILES := $(call all-java-files-under, ../src-common) $(call all-java-files-under, src)
+
+LOCAL_STATIC_JAVA_LIBRARIES := android-support-test
+
+LOCAL_SDK_VERSION := current
+
+LOCAL_PACKAGE_NAME := CtsMajorVersion000000000000ffff
+
+# tag this module as a cts test artifact
+LOCAL_COMPATIBILITY_SUITE := cts vts general-tests
+
+# sign this app with a different cert than CtsSimpleAppInstallDiffCert
+LOCAL_CERTIFICATE := cts/hostsidetests/appsecurity/certs/cts-testkey1
+
+LOCAL_DEX_PREOPT := false
+
+include $(BUILD_CTS_SUPPORT_PACKAGE)
diff --git a/hostsidetests/appsecurity/test-apps/MajorVersionApp/Version000000000000ffff/AndroidManifest.xml b/hostsidetests/appsecurity/test-apps/MajorVersionApp/Version000000000000ffff/AndroidManifest.xml
new file mode 100644
index 0000000..2a63cd7
--- /dev/null
+++ b/hostsidetests/appsecurity/test-apps/MajorVersionApp/Version000000000000ffff/AndroidManifest.xml
@@ -0,0 +1,26 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- 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.
+-->
+<manifest xmlns:android="http://schemas.android.com/apk/res/android"
+        package="com.android.cts.majorversion"
+        android:versionCodeMajor="0x00000000" android:versionCode="0x0000ffff">
+
+    <application/>
+
+    <instrumentation
+        android:name="android.support.test.runner.AndroidJUnitRunner"
+        android:targetPackage="com.android.cts.majorversion" />
+
+</manifest>
diff --git a/hostsidetests/appsecurity/test-apps/MajorVersionApp/Version000000000000ffff/src/com/android/cts/majorversion/VersionConstants.java b/hostsidetests/appsecurity/test-apps/MajorVersionApp/Version000000000000ffff/src/com/android/cts/majorversion/VersionConstants.java
new file mode 100644
index 0000000..4fe9fed
--- /dev/null
+++ b/hostsidetests/appsecurity/test-apps/MajorVersionApp/Version000000000000ffff/src/com/android/cts/majorversion/VersionConstants.java
@@ -0,0 +1,21 @@
+/*
+ * 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.majorversion;
+
+public class VersionConstants {
+    public static final long PACKAGE_VERSION = 0x000000000000ffffL;
+}
diff --git a/hostsidetests/appsecurity/test-apps/MajorVersionApp/Version00000000ffffffff/Android.mk b/hostsidetests/appsecurity/test-apps/MajorVersionApp/Version00000000ffffffff/Android.mk
new file mode 100644
index 0000000..dd32f59
--- /dev/null
+++ b/hostsidetests/appsecurity/test-apps/MajorVersionApp/Version00000000ffffffff/Android.mk
@@ -0,0 +1,37 @@
+# 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_SRC_FILES := $(call all-java-files-under, ../src-common) $(call all-java-files-under, src)
+
+LOCAL_STATIC_JAVA_LIBRARIES := android-support-test
+
+LOCAL_SDK_VERSION := current
+
+LOCAL_PACKAGE_NAME := CtsMajorVersion00000000ffffffff
+
+# tag this module as a cts test artifact
+LOCAL_COMPATIBILITY_SUITE := cts vts general-tests
+
+# sign this app with a different cert than CtsSimpleAppInstallDiffCert
+LOCAL_CERTIFICATE := cts/hostsidetests/appsecurity/certs/cts-testkey1
+
+LOCAL_DEX_PREOPT := false
+
+include $(BUILD_CTS_SUPPORT_PACKAGE)
diff --git a/hostsidetests/appsecurity/test-apps/MajorVersionApp/Version00000000ffffffff/AndroidManifest.xml b/hostsidetests/appsecurity/test-apps/MajorVersionApp/Version00000000ffffffff/AndroidManifest.xml
new file mode 100644
index 0000000..934deec
--- /dev/null
+++ b/hostsidetests/appsecurity/test-apps/MajorVersionApp/Version00000000ffffffff/AndroidManifest.xml
@@ -0,0 +1,26 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- 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.
+-->
+<manifest xmlns:android="http://schemas.android.com/apk/res/android"
+        package="com.android.cts.majorversion"
+        android:versionCodeMajor="0x00000000" android:versionCode="0xffffffff">
+
+    <application/>
+
+    <instrumentation
+        android:name="android.support.test.runner.AndroidJUnitRunner"
+        android:targetPackage="com.android.cts.majorversion" />
+
+</manifest>
diff --git a/hostsidetests/appsecurity/test-apps/MajorVersionApp/Version00000000ffffffff/src/com/android/cts/majorversion/VersionConstants.java b/hostsidetests/appsecurity/test-apps/MajorVersionApp/Version00000000ffffffff/src/com/android/cts/majorversion/VersionConstants.java
new file mode 100644
index 0000000..4c9a62b
--- /dev/null
+++ b/hostsidetests/appsecurity/test-apps/MajorVersionApp/Version00000000ffffffff/src/com/android/cts/majorversion/VersionConstants.java
@@ -0,0 +1,21 @@
+/*
+ * 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.majorversion;
+
+public class VersionConstants {
+    public static final long PACKAGE_VERSION = 0x00000000ffffffffL;
+}
diff --git a/hostsidetests/appsecurity/test-apps/MajorVersionApp/Version000000ff00000000/Android.mk b/hostsidetests/appsecurity/test-apps/MajorVersionApp/Version000000ff00000000/Android.mk
new file mode 100644
index 0000000..d534b7b
--- /dev/null
+++ b/hostsidetests/appsecurity/test-apps/MajorVersionApp/Version000000ff00000000/Android.mk
@@ -0,0 +1,37 @@
+# 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_SRC_FILES := $(call all-java-files-under, ../src-common) $(call all-java-files-under, src)
+
+LOCAL_STATIC_JAVA_LIBRARIES := android-support-test
+
+LOCAL_SDK_VERSION := current
+
+LOCAL_PACKAGE_NAME := CtsMajorVersion000000ff00000000
+
+# tag this module as a cts test artifact
+LOCAL_COMPATIBILITY_SUITE := cts vts general-tests
+
+# sign this app with a different cert than CtsSimpleAppInstallDiffCert
+LOCAL_CERTIFICATE := cts/hostsidetests/appsecurity/certs/cts-testkey1
+
+LOCAL_DEX_PREOPT := false
+
+include $(BUILD_CTS_SUPPORT_PACKAGE)
diff --git a/hostsidetests/appsecurity/test-apps/MajorVersionApp/Version000000ff00000000/AndroidManifest.xml b/hostsidetests/appsecurity/test-apps/MajorVersionApp/Version000000ff00000000/AndroidManifest.xml
new file mode 100644
index 0000000..c7b9dd0
--- /dev/null
+++ b/hostsidetests/appsecurity/test-apps/MajorVersionApp/Version000000ff00000000/AndroidManifest.xml
@@ -0,0 +1,26 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- 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.
+-->
+<manifest xmlns:android="http://schemas.android.com/apk/res/android"
+        package="com.android.cts.majorversion"
+        android:versionCodeMajor="0x000000ff" android:versionCode="0x00000000">
+
+    <application/>
+
+    <instrumentation
+        android:name="android.support.test.runner.AndroidJUnitRunner"
+        android:targetPackage="com.android.cts.majorversion" />
+
+</manifest>
diff --git a/hostsidetests/appsecurity/test-apps/MajorVersionApp/Version000000ff00000000/src/com/android/cts/majorversion/VersionConstants.java b/hostsidetests/appsecurity/test-apps/MajorVersionApp/Version000000ff00000000/src/com/android/cts/majorversion/VersionConstants.java
new file mode 100644
index 0000000..6a10139
--- /dev/null
+++ b/hostsidetests/appsecurity/test-apps/MajorVersionApp/Version000000ff00000000/src/com/android/cts/majorversion/VersionConstants.java
@@ -0,0 +1,21 @@
+/*
+ * 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.majorversion;
+
+public class VersionConstants {
+    public static final long PACKAGE_VERSION = 0x000000ff00000000L;
+}
diff --git a/hostsidetests/appsecurity/test-apps/MajorVersionApp/Version000000ffffffffff/Android.mk b/hostsidetests/appsecurity/test-apps/MajorVersionApp/Version000000ffffffffff/Android.mk
new file mode 100644
index 0000000..72c9914
--- /dev/null
+++ b/hostsidetests/appsecurity/test-apps/MajorVersionApp/Version000000ffffffffff/Android.mk
@@ -0,0 +1,37 @@
+# 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_SRC_FILES := $(call all-java-files-under, ../src-common) $(call all-java-files-under, src)
+
+LOCAL_STATIC_JAVA_LIBRARIES := android-support-test
+
+LOCAL_SDK_VERSION := current
+
+LOCAL_PACKAGE_NAME := CtsMajorVersion000000ffffffffff
+
+# tag this module as a cts test artifact
+LOCAL_COMPATIBILITY_SUITE := cts vts general-tests
+
+# sign this app with a different cert than CtsSimpleAppInstallDiffCert
+LOCAL_CERTIFICATE := cts/hostsidetests/appsecurity/certs/cts-testkey1
+
+LOCAL_DEX_PREOPT := false
+
+include $(BUILD_CTS_SUPPORT_PACKAGE)
diff --git a/hostsidetests/appsecurity/test-apps/MajorVersionApp/Version000000ffffffffff/AndroidManifest.xml b/hostsidetests/appsecurity/test-apps/MajorVersionApp/Version000000ffffffffff/AndroidManifest.xml
new file mode 100644
index 0000000..91d4e39
--- /dev/null
+++ b/hostsidetests/appsecurity/test-apps/MajorVersionApp/Version000000ffffffffff/AndroidManifest.xml
@@ -0,0 +1,26 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- 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.
+-->
+<manifest xmlns:android="http://schemas.android.com/apk/res/android"
+        package="com.android.cts.majorversion"
+        android:versionCodeMajor="0x000000ff" android:versionCode="0xffffffff">
+
+    <application/>
+
+    <instrumentation
+        android:name="android.support.test.runner.AndroidJUnitRunner"
+        android:targetPackage="com.android.cts.majorversion" />
+
+</manifest>
diff --git a/hostsidetests/appsecurity/test-apps/MajorVersionApp/Version000000ffffffffff/src/com/android/cts/majorversion/VersionConstants.java b/hostsidetests/appsecurity/test-apps/MajorVersionApp/Version000000ffffffffff/src/com/android/cts/majorversion/VersionConstants.java
new file mode 100644
index 0000000..dead996
--- /dev/null
+++ b/hostsidetests/appsecurity/test-apps/MajorVersionApp/Version000000ffffffffff/src/com/android/cts/majorversion/VersionConstants.java
@@ -0,0 +1,21 @@
+/*
+ * 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.majorversion;
+
+public class VersionConstants {
+    public static final long PACKAGE_VERSION = 0x000000ffffffffffL;
+}
diff --git a/hostsidetests/appsecurity/test-apps/MajorVersionApp/src-common/com/android/cts/majorversion/VersionTest.java b/hostsidetests/appsecurity/test-apps/MajorVersionApp/src-common/com/android/cts/majorversion/VersionTest.java
new file mode 100644
index 0000000..e69e14b
--- /dev/null
+++ b/hostsidetests/appsecurity/test-apps/MajorVersionApp/src-common/com/android/cts/majorversion/VersionTest.java
@@ -0,0 +1,42 @@
+/*
+ * 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.majorversion;
+
+import android.content.pm.PackageInfo;
+import android.content.pm.PackageManager;
+import android.support.test.InstrumentationRegistry;
+import android.support.test.runner.AndroidJUnit4;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertTrue;
+import static org.junit.Assert.fail;
+
+@RunWith(AndroidJUnit4.class)
+public class VersionTest {
+    @Test
+    public void testCheckVersion() throws Exception {
+        PackageManager pm = InstrumentationRegistry.getContext().getPackageManager();
+        PackageInfo pi = pm.getPackageInfo(InstrumentationRegistry.getContext().getPackageName(),
+                0);
+        assertEquals("com.android.cts.majorversion",
+                InstrumentationRegistry.getContext().getPackageName());
+        assertEquals(VersionConstants.PACKAGE_VERSION, pi.getLongVersionCode());
+    }
+}
diff --git a/hostsidetests/appsecurity/test-apps/MultiUserStorageApp/Android.mk b/hostsidetests/appsecurity/test-apps/MultiUserStorageApp/Android.mk
index 0744834..e08ac6f 100644
--- a/hostsidetests/appsecurity/test-apps/MultiUserStorageApp/Android.mk
+++ b/hostsidetests/appsecurity/test-apps/MultiUserStorageApp/Android.mk
@@ -18,7 +18,9 @@
 
 LOCAL_MODULE_TAGS := tests
 LOCAL_SDK_VERSION := current
-LOCAL_STATIC_JAVA_LIBRARIES := android-support-test legacy-android-test
+LOCAL_STATIC_JAVA_LIBRARIES := android-support-test
+
+LOCAL_JAVA_LIBRARIES := android.test.base.stubs
 
 LOCAL_SRC_FILES := $(call all-java-files-under, src) \
     ../ExternalStorageApp/src/com/android/cts/externalstorageapp/CommonExternalStorageTest.java
diff --git a/hostsidetests/appsecurity/test-apps/PermissionPolicy25/Android.mk b/hostsidetests/appsecurity/test-apps/PermissionPolicy25/Android.mk
index 9206b6f..0d53ac8 100644
--- a/hostsidetests/appsecurity/test-apps/PermissionPolicy25/Android.mk
+++ b/hostsidetests/appsecurity/test-apps/PermissionPolicy25/Android.mk
@@ -25,8 +25,6 @@
     compatibility-device-util \
     ctstestrunner \
 
-LOCAL_JAVA_LIBRARIES := legacy-android-test
-
 LOCAL_SRC_FILES := $(call all-java-files-under, src)
 
 LOCAL_PACKAGE_NAME := CtsPermissionPolicyTest25
diff --git a/hostsidetests/appsecurity/test-apps/PrivilegedUpdateApp/Android.mk b/hostsidetests/appsecurity/test-apps/PrivilegedUpdateApp/Android.mk
index e86fae9..5a584c1 100644
--- a/hostsidetests/appsecurity/test-apps/PrivilegedUpdateApp/Android.mk
+++ b/hostsidetests/appsecurity/test-apps/PrivilegedUpdateApp/Android.mk
@@ -24,6 +24,7 @@
 LOCAL_MODULE_TAGS := tests
 LOCAL_SDK_VERSION := current
 LOCAL_STATIC_JAVA_LIBRARIES := android-support-test compatibility-device-util ctstestrunner
+LOCAL_JAVA_LIBRARIES := android.test.base.stubs
 # Tag this module as a cts test artifact
 LOCAL_COMPATIBILITY_SUITE := cts vts general-tests
 LOCAL_PROGUARD_ENABLED := disabled
diff --git a/hostsidetests/appsecurity/test-apps/ReadExternalStorageApp/Android.mk b/hostsidetests/appsecurity/test-apps/ReadExternalStorageApp/Android.mk
index 4f828ad..51699dd 100644
--- a/hostsidetests/appsecurity/test-apps/ReadExternalStorageApp/Android.mk
+++ b/hostsidetests/appsecurity/test-apps/ReadExternalStorageApp/Android.mk
@@ -18,7 +18,9 @@
 
 LOCAL_MODULE_TAGS := tests
 LOCAL_SDK_VERSION := current
-LOCAL_STATIC_JAVA_LIBRARIES := android-support-test legacy-android-test
+LOCAL_STATIC_JAVA_LIBRARIES := android-support-test
+
+LOCAL_JAVA_LIBRARIES := android.test.runner.stubs android.test.base.stubs
 
 LOCAL_SRC_FILES := $(call all-java-files-under, src) \
     ../ExternalStorageApp/src/com/android/cts/externalstorageapp/CommonExternalStorageTest.java
diff --git a/hostsidetests/appsecurity/test-apps/SplitApp/Android.mk b/hostsidetests/appsecurity/test-apps/SplitApp/Android.mk
index 9341949..7c642b1 100644
--- a/hostsidetests/appsecurity/test-apps/SplitApp/Android.mk
+++ b/hostsidetests/appsecurity/test-apps/SplitApp/Android.mk
@@ -20,7 +20,9 @@
 
 LOCAL_MODULE_TAGS := tests
 LOCAL_SDK_VERSION := current
-LOCAL_STATIC_JAVA_LIBRARIES := android-support-test legacy-android-test
+LOCAL_STATIC_JAVA_LIBRARIES := android-support-test
+
+LOCAL_JAVA_LIBRARIES := android.test.runner.stubs android.test.base.stubs
 
 LOCAL_SRC_FILES := $(call all-java-files-under, src)
 
@@ -48,7 +50,9 @@
 
 LOCAL_MODULE_TAGS := tests
 LOCAL_SDK_VERSION := current
-LOCAL_STATIC_JAVA_LIBRARIES := android-support-test legacy-android-test
+LOCAL_STATIC_JAVA_LIBRARIES := android-support-test
+
+LOCAL_JAVA_LIBRARIES := android.test.runner.stubs android.test.base.stubs
 
 LOCAL_SRC_FILES := $(call all-java-files-under, src)
 
@@ -75,7 +79,9 @@
 
 LOCAL_MODULE_TAGS := tests
 LOCAL_SDK_VERSION := current
-LOCAL_STATIC_JAVA_LIBRARIES := android-support-test legacy-android-test
+LOCAL_STATIC_JAVA_LIBRARIES := android-support-test
+
+LOCAL_JAVA_LIBRARIES := android.test.runner.stubs android.test.base.stubs
 
 LOCAL_SRC_FILES := $(call all-java-files-under, src)
 
@@ -101,7 +107,9 @@
 
 LOCAL_MODULE_TAGS := tests
 LOCAL_SDK_VERSION := current
-LOCAL_STATIC_JAVA_LIBRARIES := android-support-test legacy-android-test
+LOCAL_STATIC_JAVA_LIBRARIES := android-support-test
+
+LOCAL_JAVA_LIBRARIES := android.test.runner.stubs android.test.base.stubs
 
 LOCAL_SRC_FILES := $(call all-java-files-under, src)
 
diff --git a/hostsidetests/appsecurity/test-apps/StorageApp/src/com/android/cts/storageapp/StorageTest.java b/hostsidetests/appsecurity/test-apps/StorageApp/src/com/android/cts/storageapp/StorageTest.java
index 9d0dec6..23e4c9f 100644
--- a/hostsidetests/appsecurity/test-apps/StorageApp/src/com/android/cts/storageapp/StorageTest.java
+++ b/hostsidetests/appsecurity/test-apps/StorageApp/src/com/android/cts/storageapp/StorageTest.java
@@ -16,6 +16,8 @@
 
 package com.android.cts.storageapp;
 
+import static android.os.storage.StorageManager.UUID_DEFAULT;
+
 import static com.android.cts.storageapp.Utils.CACHE_ALL;
 import static com.android.cts.storageapp.Utils.CACHE_EXT;
 import static com.android.cts.storageapp.Utils.CACHE_INT;
@@ -26,7 +28,6 @@
 import static com.android.cts.storageapp.Utils.assertMostlyEquals;
 import static com.android.cts.storageapp.Utils.getSizeManual;
 import static com.android.cts.storageapp.Utils.makeUniqueFile;
-import static com.android.cts.storageapp.Utils.shouldHaveQuota;
 import static com.android.cts.storageapp.Utils.useSpace;
 
 import android.app.usage.StorageStats;
@@ -39,7 +40,6 @@
 import android.os.ParcelFileDescriptor;
 import android.os.UserHandle;
 import android.os.storage.StorageManager;
-import android.system.Os;
 import android.test.InstrumentationTestCase;
 
 import java.io.File;
@@ -59,17 +59,10 @@
     }
 
     public void testFullDisk() throws Exception {
-        if (shouldHaveQuota(Os.uname())) {
+        final StorageStatsManager stats = getContext()
+                .getSystemService(StorageStatsManager.class);
+        if (stats.isReservedSupported(UUID_DEFAULT)) {
             final File dataDir = getContext().getDataDir();
-
-            // Pre-flight to see if we have enough disk space to test with
-            final long total = dataDir.getTotalSpace();
-            final long free = dataDir.getFreeSpace();
-            final long required = ((total * 9) / 10) + MB_IN_BYTES;
-            if (free < required) {
-                fail("Skipping full disk test; only found " + free + " free out of " + total);
-            }
-
             Hoarder.doBlocks(dataDir, true);
         } else {
             fail("Skipping full disk test due to missing quota support");
diff --git a/hostsidetests/appsecurity/test-apps/StorageApp/src/com/android/cts/storageapp/Utils.java b/hostsidetests/appsecurity/test-apps/StorageApp/src/com/android/cts/storageapp/Utils.java
index 6908ad8..08dc7ad 100644
--- a/hostsidetests/appsecurity/test-apps/StorageApp/src/com/android/cts/storageapp/Utils.java
+++ b/hostsidetests/appsecurity/test-apps/StorageApp/src/com/android/cts/storageapp/Utils.java
@@ -169,30 +169,6 @@
         return success;
     }
 
-    public static boolean shouldHaveQuota(StructUtsname uname) throws Exception {
-        try (BufferedReader br = new BufferedReader(new FileReader("/proc/mounts"))) {
-            String line;
-            while ((line = br.readLine()) != null) {
-                final String[] fields = line.split(" ");
-                final String target = fields[1];
-                final String format = fields[2];
-
-                if (target.equals("/data") && !format.equals("ext4")) {
-                    Log.d(TAG, "Assuming no quota support because /data is " + format);
-                    return false;
-                }
-            }
-        }
-
-        final Matcher matcher = Pattern.compile("(\\d+)\\.(\\d+)").matcher(uname.release);
-        if (!matcher.find()) {
-            throw new IllegalStateException("Failed to parse version: " + uname.release);
-        }
-        final int major = Integer.parseInt(matcher.group(1));
-        final int minor = Integer.parseInt(matcher.group(2));
-        return (major > 3 || (major == 3 && minor >= 18));
-    }
-
     public static void logCommand(String... cmd) throws Exception {
         final Process proc = new ProcessBuilder(cmd).redirectErrorStream(true).start();
 
diff --git a/hostsidetests/appsecurity/test-apps/StorageAppA/Android.mk b/hostsidetests/appsecurity/test-apps/StorageAppA/Android.mk
index f3d7d35..ebbc892 100644
--- a/hostsidetests/appsecurity/test-apps/StorageAppA/Android.mk
+++ b/hostsidetests/appsecurity/test-apps/StorageAppA/Android.mk
@@ -17,8 +17,10 @@
 include $(CLEAR_VARS)
 
 LOCAL_MODULE_TAGS := tests
-LOCAL_SDK_VERSION := current
-LOCAL_STATIC_JAVA_LIBRARIES := android-support-test legacy-android-test
+LOCAL_SDK_VERSION := test_current
+LOCAL_STATIC_JAVA_LIBRARIES := android-support-test
+
+LOCAL_JAVA_LIBRARIES := android.test.base.stubs
 
 LOCAL_SRC_FILES := $(call all-java-files-under, ../StorageApp/src/)
 
diff --git a/hostsidetests/appsecurity/test-apps/StorageAppB/Android.mk b/hostsidetests/appsecurity/test-apps/StorageAppB/Android.mk
index 5f85459..3a5462e 100644
--- a/hostsidetests/appsecurity/test-apps/StorageAppB/Android.mk
+++ b/hostsidetests/appsecurity/test-apps/StorageAppB/Android.mk
@@ -17,8 +17,10 @@
 include $(CLEAR_VARS)
 
 LOCAL_MODULE_TAGS := tests
-LOCAL_SDK_VERSION := current
-LOCAL_STATIC_JAVA_LIBRARIES := android-support-test legacy-android-test
+LOCAL_SDK_VERSION := test_current
+LOCAL_STATIC_JAVA_LIBRARIES := android-support-test
+
+LOCAL_JAVA_LIBRARIES := android.test.base.stubs
 
 LOCAL_SRC_FILES := $(call all-java-files-under, ../StorageApp/src/)
 
diff --git a/hostsidetests/appsecurity/test-apps/StorageStatsApp/Android.mk b/hostsidetests/appsecurity/test-apps/StorageStatsApp/Android.mk
index b5c30fb..16d78d7 100644
--- a/hostsidetests/appsecurity/test-apps/StorageStatsApp/Android.mk
+++ b/hostsidetests/appsecurity/test-apps/StorageStatsApp/Android.mk
@@ -18,7 +18,9 @@
 
 LOCAL_MODULE_TAGS := tests
 LOCAL_SDK_VERSION := test_current
-LOCAL_STATIC_JAVA_LIBRARIES := android-support-test ub-uiautomator legacy-android-test
+LOCAL_STATIC_JAVA_LIBRARIES := android-support-test ub-uiautomator
+
+LOCAL_JAVA_LIBRARIES := android.test.base.stubs
 
 LOCAL_SRC_FILES := $(call all-java-files-under, src) \
 	$(call all-java-files-under, ../StorageApp/src)
diff --git a/hostsidetests/appsecurity/test-apps/StorageStatsApp/src/com/android/cts/storagestatsapp/StorageStatsTest.java b/hostsidetests/appsecurity/test-apps/StorageStatsApp/src/com/android/cts/storagestatsapp/StorageStatsTest.java
index 713ba17..351c493 100644
--- a/hostsidetests/appsecurity/test-apps/StorageStatsApp/src/com/android/cts/storagestatsapp/StorageStatsTest.java
+++ b/hostsidetests/appsecurity/test-apps/StorageStatsApp/src/com/android/cts/storagestatsapp/StorageStatsTest.java
@@ -30,7 +30,6 @@
 import static com.android.cts.storageapp.Utils.getSizeManual;
 import static com.android.cts.storageapp.Utils.logCommand;
 import static com.android.cts.storageapp.Utils.makeUniqueFile;
-import static com.android.cts.storageapp.Utils.shouldHaveQuota;
 import static com.android.cts.storageapp.Utils.useFallocate;
 import static com.android.cts.storageapp.Utils.useSpace;
 import static com.android.cts.storageapp.Utils.useWrite;
@@ -46,13 +45,12 @@
 import android.content.Intent;
 import android.content.pm.ApplicationInfo;
 import android.content.pm.PackageManager;
+import android.os.Build;
 import android.os.Bundle;
 import android.os.Environment;
 import android.os.UserHandle;
 import android.os.storage.StorageManager;
 import android.support.test.uiautomator.UiDevice;
-import android.system.Os;
-import android.system.StructUtsname;
 import android.test.InstrumentationTestCase;
 import android.util.Log;
 import android.util.MutableLong;
@@ -76,19 +74,22 @@
     }
 
     /**
-     * Require that quota support be fully enabled on kernel 3.18 or newer. This
-     * test verifies that both kernel options and the fstab 'quota' option are
-     * enabled.
+     * Require that quota support be fully enabled on devices that first ship
+     * with P. This test verifies that both kernel options and the fstab 'quota'
+     * option are enabled.
      */
-    public void testVerifyQuota() throws Exception {
-        final StructUtsname uname = Os.uname();
-        if (shouldHaveQuota(uname)) {
+    public void testVerify() throws Exception {
+        if (Build.VERSION.FIRST_SDK_INT >= Build.VERSION_CODES.P) {
             final StorageStatsManager stats = getContext()
                     .getSystemService(StorageStatsManager.class);
-            assertTrue("You're running kernel 3.18 or newer (" + uname.release + ") which "
-                    + "means that CONFIG_QUOTA, CONFIG_QFMT_V2, CONFIG_QUOTACTL and the "
-                    + "'quota' fstab option on /data are required",
+            assertTrue("Devices that first ship with P or newer must enable quotas to "
+                    + "support StorageStatsManager APIs. You may need to enable the "
+                    + "CONFIG_QUOTA, CONFIG_QFMT_V2, CONFIG_QUOTACTL kernel options "
+                    + "and add the 'quota' fstab option on /data.",
                     stats.isQuotaSupported(UUID_DEFAULT));
+            assertTrue("Devices that first ship with P or newer must enable resgid to "
+                    + "preserve system stability in the face of abusive apps.",
+                    stats.isReservedSupported(UUID_DEFAULT));
         }
     }
 
diff --git a/hostsidetests/appsecurity/test-apps/UsePermissionApp22/Android.mk b/hostsidetests/appsecurity/test-apps/UsePermissionApp22/Android.mk
index 2b6c2b0..c98dbb1 100644
--- a/hostsidetests/appsecurity/test-apps/UsePermissionApp22/Android.mk
+++ b/hostsidetests/appsecurity/test-apps/UsePermissionApp22/Android.mk
@@ -25,7 +25,7 @@
     ctstestrunner \
     ub-uiautomator
 
-LOCAL_JAVA_LIBRARIES := legacy-android-test
+LOCAL_JAVA_LIBRARIES := android.test.base
 
 LOCAL_SRC_FILES := $(call all-java-files-under, src) \
     ../ExternalStorageApp/src/com/android/cts/externalstorageapp/CommonExternalStorageTest.java \
diff --git a/hostsidetests/appsecurity/test-apps/UsePermissionApp23/Android.mk b/hostsidetests/appsecurity/test-apps/UsePermissionApp23/Android.mk
index 305359c..22d4a5f 100644
--- a/hostsidetests/appsecurity/test-apps/UsePermissionApp23/Android.mk
+++ b/hostsidetests/appsecurity/test-apps/UsePermissionApp23/Android.mk
@@ -25,7 +25,7 @@
     ctstestrunner \
     ub-uiautomator
 
-LOCAL_JAVA_LIBRARIES := legacy-android-test
+LOCAL_JAVA_LIBRARIES := android.test.base
 
 LOCAL_SRC_FILES := $(call all-java-files-under, src) \
     ../ExternalStorageApp/src/com/android/cts/externalstorageapp/CommonExternalStorageTest.java
diff --git a/hostsidetests/appsecurity/test-apps/UsePermissionApp23/AndroidManifest.xml b/hostsidetests/appsecurity/test-apps/UsePermissionApp23/AndroidManifest.xml
index 71eadaa..9d25826 100644
--- a/hostsidetests/appsecurity/test-apps/UsePermissionApp23/AndroidManifest.xml
+++ b/hostsidetests/appsecurity/test-apps/UsePermissionApp23/AndroidManifest.xml
@@ -66,6 +66,8 @@
     <uses-permission android:name="android.permission.BODY_SENSORS"/>
 
     <application>
+        <uses-library android:name="android.test.runner" />
+
         <activity android:name="com.android.cts.usepermission.BasePermissionActivity" />
     </application>
 
diff --git a/hostsidetests/appsecurity/test-apps/UsePermissionApp25/Android.mk b/hostsidetests/appsecurity/test-apps/UsePermissionApp25/Android.mk
index ac4f272..7edb1d1 100644
--- a/hostsidetests/appsecurity/test-apps/UsePermissionApp25/Android.mk
+++ b/hostsidetests/appsecurity/test-apps/UsePermissionApp25/Android.mk
@@ -25,6 +25,8 @@
     ctstestrunner \
     ub-uiautomator
 
+LOCAL_JAVA_LIBRARIES := android.test.base
+
 LOCAL_SRC_FILES := $(call all-java-files-under, ../UsePermissionApp23/src) \
     ../ExternalStorageApp/src/com/android/cts/externalstorageapp/CommonExternalStorageTest.java
 LOCAL_RESOURCE_DIR := cts/hostsidetests/appsecurity/test-apps/UsePermissionApp23/res
diff --git a/hostsidetests/appsecurity/test-apps/UsePermissionApp25/AndroidManifest.xml b/hostsidetests/appsecurity/test-apps/UsePermissionApp25/AndroidManifest.xml
index acaeeb0..c6a6316 100644
--- a/hostsidetests/appsecurity/test-apps/UsePermissionApp25/AndroidManifest.xml
+++ b/hostsidetests/appsecurity/test-apps/UsePermissionApp25/AndroidManifest.xml
@@ -67,6 +67,8 @@
     <uses-permission android:name="android.permission.BODY_SENSORS"/>
 
     <application>
+        <uses-library android:name="android.test.runner" />
+
         <activity android:name="com.android.cts.usepermission.BasePermissionActivity" />
     </application>
 
diff --git a/hostsidetests/appsecurity/test-apps/UsePermissionApp26/AndroidManifest.xml b/hostsidetests/appsecurity/test-apps/UsePermissionApp26/AndroidManifest.xml
index 845e43d..9458db3 100644
--- a/hostsidetests/appsecurity/test-apps/UsePermissionApp26/AndroidManifest.xml
+++ b/hostsidetests/appsecurity/test-apps/UsePermissionApp26/AndroidManifest.xml
@@ -24,6 +24,8 @@
     <uses-permission android:name="android.permission.RECEIVE_SMS" />
 
     <application>
+        <uses-library android:name="android.test.runner" />
+
         <activity android:name="com.android.cts.usepermission.BasePermissionActivity" />
     </application>
 
diff --git a/hostsidetests/appsecurity/test-apps/UsePermissionAppLatest/AndroidManifest.xml b/hostsidetests/appsecurity/test-apps/UsePermissionAppLatest/AndroidManifest.xml
index cac6790..57a58ab 100644
--- a/hostsidetests/appsecurity/test-apps/UsePermissionAppLatest/AndroidManifest.xml
+++ b/hostsidetests/appsecurity/test-apps/UsePermissionAppLatest/AndroidManifest.xml
@@ -23,6 +23,8 @@
     <uses-permission android:name="android.permission.RECEIVE_SMS" />
 
     <application>
+        <uses-library android:name="android.test.runner" />
+
         <activity android:name="com.android.cts.usepermission.BasePermissionActivity" />
     </application>
 
diff --git a/hostsidetests/appsecurity/test-apps/UsePermissionDiffCert/Android.mk b/hostsidetests/appsecurity/test-apps/UsePermissionDiffCert/Android.mk
index 498e8ca..4605cfb 100644
--- a/hostsidetests/appsecurity/test-apps/UsePermissionDiffCert/Android.mk
+++ b/hostsidetests/appsecurity/test-apps/UsePermissionDiffCert/Android.mk
@@ -22,7 +22,9 @@
     ../PermissionDeclareApp/src/com/android/cts/permissiondeclareapp/GrantUriPermission.java
 
 LOCAL_SDK_VERSION := current
-LOCAL_STATIC_JAVA_LIBRARIES := android-support-test legacy-android-test
+LOCAL_STATIC_JAVA_LIBRARIES := android-support-test
+
+LOCAL_JAVA_LIBRARIES := android.test.base.stubs
 
 LOCAL_PACKAGE_NAME := CtsUsePermissionDiffCert
 
diff --git a/hostsidetests/appsecurity/test-apps/UsesLibraryApp/Android.mk b/hostsidetests/appsecurity/test-apps/UsesLibraryApp/Android.mk
index 2d67d62..df12f82 100644
--- a/hostsidetests/appsecurity/test-apps/UsesLibraryApp/Android.mk
+++ b/hostsidetests/appsecurity/test-apps/UsesLibraryApp/Android.mk
@@ -22,6 +22,8 @@
 LOCAL_SDK_VERSION := current
 LOCAL_STATIC_JAVA_LIBRARIES := android-support-test compatibility-device-util ctstestrunner ub-uiautomator
 
+LOCAL_JAVA_LIBRARIES := android.test.base.stubs
+
 LOCAL_SRC_FILES := $(call all-java-files-under, src) \
     ../ExternalStorageApp/src/com/android/cts/externalstorageapp/CommonExternalStorageTest.java
 
diff --git a/hostsidetests/appsecurity/test-apps/WriteExternalStorageApp/Android.mk b/hostsidetests/appsecurity/test-apps/WriteExternalStorageApp/Android.mk
index 8574d63..b1e84cb 100644
--- a/hostsidetests/appsecurity/test-apps/WriteExternalStorageApp/Android.mk
+++ b/hostsidetests/appsecurity/test-apps/WriteExternalStorageApp/Android.mk
@@ -20,8 +20,9 @@
 LOCAL_SDK_VERSION := current
 LOCAL_STATIC_JAVA_LIBRARIES := \
 	android-support-test \
-	compatibility-device-util \
-	legacy-android-test
+	compatibility-device-util
+
+LOCAL_JAVA_LIBRARIES := android.test.runner.stubs android.test.base.stubs
 
 LOCAL_SRC_FILES := $(call all-java-files-under, src) \
     ../ExternalStorageApp/src/com/android/cts/externalstorageapp/CommonExternalStorageTest.java
diff --git a/hostsidetests/appsecurity/test-apps/keysets/testApp/Android.mk b/hostsidetests/appsecurity/test-apps/keysets/testApp/Android.mk
index 907ae36..619f1ce 100644
--- a/hostsidetests/appsecurity/test-apps/keysets/testApp/Android.mk
+++ b/hostsidetests/appsecurity/test-apps/keysets/testApp/Android.mk
@@ -22,7 +22,8 @@
 LOCAL_MODULE_TAGS := tests
 LOCAL_SRC_FILES := $(call all-java-files-under, src)
 LOCAL_SDK_VERSION := current
-LOCAL_STATIC_JAVA_LIBRARIES := android-support-test legacy-android-test
+LOCAL_STATIC_JAVA_LIBRARIES := android-support-test
+LOCAL_JAVA_LIBRARIES := android.test.base.stubs
 LOCAL_PACKAGE_NAME := CtsKeySetTestApp
 LOCAL_DEX_PREOPT := false
 
diff --git a/hostsidetests/atrace/AndroidTest.xml b/hostsidetests/atrace/AndroidTest.xml
index 8262232..ecaa17a 100644
--- a/hostsidetests/atrace/AndroidTest.xml
+++ b/hostsidetests/atrace/AndroidTest.xml
@@ -14,6 +14,7 @@
      limitations under the License.
 -->
 <configuration description="Config for CTS atrace host test cases">
+    <option name="test-suite-tag" value="cts" />
     <option name="config-descriptor:metadata" key="component" value="uitoolkit" />
     <test class="com.android.compatibility.common.tradefed.testtype.JarHostTest" >
         <option name="jar" value="CtsAtraceHostTestCases.jar" />
diff --git a/hostsidetests/backup/AndroidTest.xml b/hostsidetests/backup/AndroidTest.xml
index 314a92e3..868e20f 100644
--- a/hostsidetests/backup/AndroidTest.xml
+++ b/hostsidetests/backup/AndroidTest.xml
@@ -14,6 +14,7 @@
      limitations under the License.
 -->
 <configuration description="Config for CTS Backup host test cases">
+    <option name="test-suite-tag" value="cts" />
     <option name="config-descriptor:metadata" key="component" value="backup" />
     <target_preparer class="com.android.tradefed.targetprep.suite.SuiteApkInstaller">
         <option name="cleanup-apks" value="true" />
diff --git a/hostsidetests/backup/SuccessNotificationApp/Android.mk b/hostsidetests/backup/SuccessNotificationApp/Android.mk
new file mode 100644
index 0000000..501cf14
--- /dev/null
+++ b/hostsidetests/backup/SuccessNotificationApp/Android.mk
@@ -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.
+
+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_STATIC_JAVA_LIBRARIES := android-support-test
+
+LOCAL_SRC_FILES := $(call all-java-files-under, src)
+
+# tag this module as a cts test artifact
+LOCAL_COMPATIBILITY_SUITE := cts
+
+LOCAL_PACKAGE_NAME := CtsBackupSuccessNotificationApp
+
+LOCAL_SDK_VERSION := current
+
+include $(BUILD_PACKAGE)
diff --git a/hostsidetests/backup/SuccessNotificationApp/AndroidManifest.xml b/hostsidetests/backup/SuccessNotificationApp/AndroidManifest.xml
new file mode 100644
index 0000000..b7f8c2c
--- /dev/null
+++ b/hostsidetests/backup/SuccessNotificationApp/AndroidManifest.xml
@@ -0,0 +1,32 @@
+<?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.successnotificationapp">
+
+    <application>
+        <receiver android:name=".SuccessNotificationReceiver">
+            <intent-filter>
+                <action android:name="android.intent.action.BACKUP_FINISHED" />
+            </intent-filter>
+        </receiver>
+    </application>
+
+    <instrumentation
+        android:name="android.support.test.runner.AndroidJUnitRunner"
+        android:targetPackage="android.cts.backup.successnotificationapp" />
+</manifest>
diff --git a/hostsidetests/backup/SuccessNotificationApp/src/android/cts/backup/successnotificationapp/SuccessNotificationReceiver.java b/hostsidetests/backup/SuccessNotificationApp/src/android/cts/backup/successnotificationapp/SuccessNotificationReceiver.java
new file mode 100644
index 0000000..2122151
--- /dev/null
+++ b/hostsidetests/backup/SuccessNotificationApp/src/android/cts/backup/successnotificationapp/SuccessNotificationReceiver.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.successnotificationapp;
+
+import android.content.BroadcastReceiver;
+import android.content.Context;
+import android.content.Intent;
+
+public class SuccessNotificationReceiver extends BroadcastReceiver {
+    private static final String BACKUP_FINISHED_ACTION = "android.intent.action.BACKUP_FINISHED";
+    private static final String BACKUP_FINISHED_PACKAGE_EXTRA = "packageName";
+
+    @Override
+    public void onReceive(Context context, Intent intent) {
+        if (!BACKUP_FINISHED_ACTION.equals(intent.getAction())) {
+            return;
+        }
+        final String packageName = intent.getStringExtra(BACKUP_FINISHED_PACKAGE_EXTRA);
+        if (packageName == null || packageName.isEmpty()) {
+            return;
+        }
+        context.getSharedPreferences(SuccessNotificationTest.PREFS_FILE, Context.MODE_PRIVATE)
+                .edit().putBoolean(packageName, true).commit();
+    }
+}
diff --git a/hostsidetests/backup/SuccessNotificationApp/src/android/cts/backup/successnotificationapp/SuccessNotificationTest.java b/hostsidetests/backup/SuccessNotificationApp/src/android/cts/backup/successnotificationapp/SuccessNotificationTest.java
new file mode 100644
index 0000000..3fb8ef2
--- /dev/null
+++ b/hostsidetests/backup/SuccessNotificationApp/src/android/cts/backup/successnotificationapp/SuccessNotificationTest.java
@@ -0,0 +1,52 @@
+/*
+ * 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.successnotificationapp;
+
+import static android.support.test.InstrumentationRegistry.getTargetContext;
+import static org.junit.Assert.assertTrue;
+
+import android.content.Context;
+import android.support.test.runner.AndroidJUnit4;
+
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+@RunWith(AndroidJUnit4.class)
+public class SuccessNotificationTest {
+    protected static final String PREFS_FILE = "android.cts.backup.successnotificationapp.PREFS";
+    private static final String KEY_VALUE_RESTORE_APP_PACKAGE =
+            "android.cts.backup.keyvaluerestoreapp";
+    private static final String FULLBACKUPONLY_APP_PACKAGE = "android.cts.backup.fullbackuponlyapp";
+
+    @Test
+    public void clearBackupSuccessNotificationsReceived() {
+        getTargetContext().getSharedPreferences(PREFS_FILE, Context.MODE_PRIVATE).edit().clear()
+                .commit();
+    }
+
+    @Test
+    public void verifyBackupSuccessNotificationReceivedForKeyValueApp() {
+        assertTrue(getTargetContext().getSharedPreferences(PREFS_FILE, Context.MODE_PRIVATE)
+                .getBoolean(KEY_VALUE_RESTORE_APP_PACKAGE, false));
+    }
+
+    @Test
+    public void verifyBackupSuccessNotificationReceivedForFullBackupApp() {
+        assertTrue(getTargetContext().getSharedPreferences(PREFS_FILE, Context.MODE_PRIVATE)
+                .getBoolean(FULLBACKUPONLY_APP_PACKAGE, false));
+    }
+}
diff --git a/hostsidetests/backup/src/android/cts/backup/AllowBackupHostSideTest.java b/hostsidetests/backup/src/android/cts/backup/AllowBackupHostSideTest.java
index 4f46936..73373d3 100644
--- a/hostsidetests/backup/src/android/cts/backup/AllowBackupHostSideTest.java
+++ b/hostsidetests/backup/src/android/cts/backup/AllowBackupHostSideTest.java
@@ -16,7 +16,7 @@
 
 package android.cts.backup;
 
-import static junit.framework.Assert.assertNull;
+import static org.junit.Assert.assertNull;
 
 import com.android.tradefed.device.DeviceNotAvailableException;
 import com.android.tradefed.log.LogUtil.CLog;
@@ -62,6 +62,7 @@
     private static final String ALLOWBACKUP_APP_APK = "BackupAllowedApp.apk";
 
     @After
+    @Override
     public void tearDown() throws Exception {
         super.tearDown();
 
diff --git a/hostsidetests/backup/src/android/cts/backup/BaseBackupHostSideTest.java b/hostsidetests/backup/src/android/cts/backup/BaseBackupHostSideTest.java
index acdb423..f0861c0 100644
--- a/hostsidetests/backup/src/android/cts/backup/BaseBackupHostSideTest.java
+++ b/hostsidetests/backup/src/android/cts/backup/BaseBackupHostSideTest.java
@@ -16,15 +16,13 @@
 
 package android.cts.backup;
 
-import static junit.framework.Assert.assertTrue;
-
 import static org.junit.Assert.assertEquals;
-import static org.junit.Assume.assumeTrue;
+import static org.junit.Assert.assertTrue;
 
-import com.android.compatibility.common.tradefed.testtype.CompatibilityHostTestBase;
 import com.android.tradefed.device.DeviceNotAvailableException;
 import com.android.tradefed.log.LogUtil.CLog;
 import com.android.tradefed.testtype.DeviceJUnit4ClassRunner;
+import com.android.tradefed.testtype.junit4.BaseHostJUnit4Test;
 
 import org.junit.After;
 import org.junit.Before;
@@ -38,7 +36,7 @@
  * Base class for CTS backup/restore hostside tests
  */
 @RunWith(DeviceJUnit4ClassRunner.class)
-public abstract class BaseBackupHostSideTest extends CompatibilityHostTestBase {
+public abstract class BaseBackupHostSideTest extends BaseHostJUnit4Test {
     protected boolean mIsBackupSupported;
 
     /** Value of PackageManager.FEATURE_BACKUP */
@@ -49,7 +47,7 @@
 
     @Before
     public void setUp() throws DeviceNotAvailableException, Exception {
-        mIsBackupSupported = mDevice.hasFeature("feature:" + FEATURE_BACKUP);
+        mIsBackupSupported = getDevice().hasFeature("feature:" + FEATURE_BACKUP);
         if (!mIsBackupSupported) {
             CLog.i("android.software.backup feature is not supported on this device");
             return;
@@ -71,7 +69,7 @@
      * Execute shell command "bmgr backupnow <packageName>" and return output from this command.
      */
     protected String backupNow(String packageName) throws DeviceNotAvailableException {
-        return mDevice.executeShellCommand("bmgr backupnow " + packageName);
+        return getDevice().executeShellCommand("bmgr backupnow " + packageName);
     }
 
     /**
@@ -96,14 +94,14 @@
      * Execute shell command "bmgr restore <packageName>" and return output from this command.
      */
     protected String restore(String packageName) throws DeviceNotAvailableException {
-        return mDevice.executeShellCommand("bmgr restore " + packageName);
+        return getDevice().executeShellCommand("bmgr restore " + packageName);
     }
 
     /**
      * Attempts to clear the device log.
      */
     protected void clearLogcat() throws DeviceNotAvailableException {
-        mDevice.executeAdbCommand("logcat", "-c");
+        getDevice().executeAdbCommand("logcat", "-c");
     }
 
     /**
@@ -174,7 +172,7 @@
 
     protected void startActivityInPackageAndWait(String packageName, String className)
             throws DeviceNotAvailableException {
-        mDevice.executeShellCommand(String.format(
+        getDevice().executeShellCommand(String.format(
                 "am start -W -a android.intent.action.MAIN -n %s/%s.%s", packageName,
                 packageName,
                 className));
@@ -187,19 +185,19 @@
       */
     protected void clearBackupDataInLocalTransport(String packageName)
             throws DeviceNotAvailableException {
-        mDevice.executeShellCommand(String.format("bmgr wipe %s %s", LOCAL_TRANSPORT, packageName));
+        getDevice().executeShellCommand(String.format("bmgr wipe %s %s", LOCAL_TRANSPORT, packageName));
     }
 
     /**
      * Clears package data
      */
     protected void clearPackageData(String packageName) throws DeviceNotAvailableException {
-        mDevice.executeShellCommand(String.format("pm clear %s", packageName));
+        getDevice().executeShellCommand(String.format("pm clear %s", packageName));
     }
 
     private boolean isBackupEnabled() throws DeviceNotAvailableException {
         boolean isEnabled;
-        String output = mDevice.executeShellCommand("bmgr enabled");
+        String output = getDevice().executeShellCommand("bmgr enabled");
         Pattern pattern = Pattern.compile("^Backup Manager currently (enabled|disabled)$");
         Matcher matcher = pattern.matcher(output.trim());
         if (matcher.find()) {
@@ -211,7 +209,7 @@
     }
 
     private String getCurrentTransport() throws DeviceNotAvailableException {
-        String output = mDevice.executeShellCommand("bmgr list transports");
+        String output = getDevice().executeShellCommand("bmgr list transports");
         Pattern pattern = Pattern.compile("\\* (.*)");
         Matcher matcher = pattern.matcher(output);
         if (matcher.find()) {
diff --git a/hostsidetests/backup/src/android/cts/backup/FullBackupOnlyHostSideTest.java b/hostsidetests/backup/src/android/cts/backup/FullBackupOnlyHostSideTest.java
index 89cfe40..6589993 100644
--- a/hostsidetests/backup/src/android/cts/backup/FullBackupOnlyHostSideTest.java
+++ b/hostsidetests/backup/src/android/cts/backup/FullBackupOnlyHostSideTest.java
@@ -16,7 +16,7 @@
 
 package android.cts.backup;
 
-import static junit.framework.Assert.assertNull;
+import static org.junit.Assert.assertNull;
 
 import com.android.tradefed.device.DeviceNotAvailableException;
 import com.android.tradefed.log.LogUtil.CLog;
@@ -76,6 +76,7 @@
 
 
     @After
+    @Override
     public void tearDown() throws Exception {
         super.tearDown();
 
diff --git a/hostsidetests/backup/src/android/cts/backup/FullbackupRulesHostSideTest.java b/hostsidetests/backup/src/android/cts/backup/FullbackupRulesHostSideTest.java
index 8dd589a..e137e3e 100644
--- a/hostsidetests/backup/src/android/cts/backup/FullbackupRulesHostSideTest.java
+++ b/hostsidetests/backup/src/android/cts/backup/FullbackupRulesHostSideTest.java
@@ -16,8 +16,6 @@
 
 package android.cts.backup;
 
-import static org.junit.Assert.assertTrue;
-
 import com.android.tradefed.log.LogUtil.CLog;
 import com.android.tradefed.testtype.DeviceJUnit4ClassRunner;
 
diff --git a/hostsidetests/backup/src/android/cts/backup/KeyValueBackupRestoreHostSideTest.java b/hostsidetests/backup/src/android/cts/backup/KeyValueBackupRestoreHostSideTest.java
index a1f4927..5ce86d9 100644
--- a/hostsidetests/backup/src/android/cts/backup/KeyValueBackupRestoreHostSideTest.java
+++ b/hostsidetests/backup/src/android/cts/backup/KeyValueBackupRestoreHostSideTest.java
@@ -16,7 +16,7 @@
 
 package android.cts.backup;
 
-import static junit.framework.Assert.assertNull;
+import static org.junit.Assert.assertNull;
 
 import com.android.tradefed.device.DeviceNotAvailableException;
 import com.android.tradefed.log.LogUtil.CLog;
@@ -27,8 +27,6 @@
 import org.junit.Test;
 import org.junit.runner.RunWith;
 
-import java.io.FileNotFoundException;
-
 /**
  * Test for checking that key/value backup and restore works correctly.
  * It interacts with the app that saves values in different shared preferences and files.
@@ -60,6 +58,7 @@
 
 
     @Before
+    @Override
     public void setUp() throws Exception {
         super.setUp();
         installPackage(KEY_VALUE_RESTORE_APP_APK);
@@ -70,6 +69,7 @@
     }
 
     @After
+    @Override
     public void tearDown() throws Exception {
         super.tearDown();
 
diff --git a/hostsidetests/backup/src/android/cts/backup/RestoreAnyVersionHostSideTest.java b/hostsidetests/backup/src/android/cts/backup/RestoreAnyVersionHostSideTest.java
index 7c1f37b..a62eab8 100644
--- a/hostsidetests/backup/src/android/cts/backup/RestoreAnyVersionHostSideTest.java
+++ b/hostsidetests/backup/src/android/cts/backup/RestoreAnyVersionHostSideTest.java
@@ -16,23 +16,17 @@
 
 package android.cts.backup;
 
-import static junit.framework.Assert.assertEquals;
-import static junit.framework.Assert.assertNull;
-import static junit.framework.Assert.assertTrue;
-
-import static org.junit.Assume.assumeTrue;
+import static org.junit.Assert.assertNull;
 
 import com.android.tradefed.device.DeviceNotAvailableException;
 import com.android.tradefed.log.LogUtil.CLog;
+import com.android.tradefed.targetprep.TargetSetupError;
 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.io.FileNotFoundException;
-
 /**
  * Test checking that restoreAnyVersion manifest flag is respected by backup manager.
  *
@@ -62,6 +56,7 @@
             "CtsBackupRestoreAnyVersionNoRestoreApp.apk";
 
     @After
+    @Override
     public void tearDown() throws Exception {
         super.tearDown();
 
@@ -157,21 +152,21 @@
     }
 
     private void installRestoreAnyVersionApp()
-            throws DeviceNotAvailableException, FileNotFoundException {
+            throws DeviceNotAvailableException, TargetSetupError {
         installPackage(RESTORE_ANY_VERSION_APP_APK, "-d", "-r");
 
         checkRestoreAnyVersionDeviceTest("checkAppVersionIsOld");
     }
 
     private void installNoRestoreAnyVersionApp()
-            throws DeviceNotAvailableException, FileNotFoundException {
+            throws DeviceNotAvailableException, TargetSetupError {
         installPackage(NO_RESTORE_ANY_VERSION_APK, "-d", "-r");
 
         checkRestoreAnyVersionDeviceTest("checkAppVersionIsOld");
     }
 
     private void installNewVersionApp()
-            throws DeviceNotAvailableException, FileNotFoundException {
+            throws DeviceNotAvailableException, TargetSetupError {
         installPackage(RESTORE_ANY_VERSION_UPDATE_APK, "-d", "-r");
 
         checkRestoreAnyVersionDeviceTest("checkAppVersionIsNew");
diff --git a/hostsidetests/backup/src/android/cts/backup/SuccessNotificationHostSideTest.java b/hostsidetests/backup/src/android/cts/backup/SuccessNotificationHostSideTest.java
new file mode 100644
index 0000000..414e3bc
--- /dev/null
+++ b/hostsidetests/backup/src/android/cts/backup/SuccessNotificationHostSideTest.java
@@ -0,0 +1,235 @@
+/*
+ * 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 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;
+
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.List;
+
+/**
+ * Tests for checking that an observer app is notified by a broadcast Intent whenever a backup
+ * succeeds.
+ *
+ * NB: The tests use "bmgr backupnow" for backup, which works on N+ devices.
+ */
+@RunWith(DeviceJUnit4ClassRunner.class)
+public class SuccessNotificationHostSideTest extends BaseBackupHostSideTest {
+
+    /** The name of the package that a key/value backup will be run for */
+    private static final String KEY_VALUE_BACKUP_APP_PACKAGE =
+            "android.cts.backup.keyvaluerestoreapp";
+
+    /** The name of the package that a full backup will be run for */
+    private static final String FULL_BACKUP_APP_PACKAGE = "android.cts.backup.fullbackuponlyapp";
+
+    /** The name of the package that observes backup results. */
+    private static final String SUCCESS_NOTIFICATION_APP_PACKAGE =
+            "android.cts.backup.successnotificationapp";
+
+    /** The name of the device side test class in the APK that a key/value backup will be run for */
+    private static final String KEY_VALUE_BACKUP_DEVICE_TEST_NAME =
+            KEY_VALUE_BACKUP_APP_PACKAGE + ".KeyValueBackupRestoreTest";
+
+    /** The name of the device side test class in the APK that a full backup will be run for */
+    private static final String FULL_BACKUP_DEVICE_TEST_CLASS_NAME =
+            FULL_BACKUP_APP_PACKAGE + ".FullBackupOnlyTest";
+
+    /** The name of the device side test class in the APK that observes backup results */
+    private static final String SUCCESS_NOTIFICATION_DEVICE_TEST_NAME =
+            SUCCESS_NOTIFICATION_APP_PACKAGE + ".SuccessNotificationTest";
+
+    /** The name of the APK that a key/value backup will be run for */
+    private static final String KEY_VALUE_BACKUP_APP_APK = "CtsKeyValueBackupRestoreApp.apk";
+
+    /** The name of the APK that a full backup will be run for */
+    private static final String FULL_BACKUP_APP_APK = "FullBackupOnlyFalseWithAgentApp.apk";
+
+    /** The name of the APK that observes backup results */
+    private static final String SUCCESS_NOTIFICATION_APP_APK =
+            "CtsBackupSuccessNotificationApp.apk";
+
+    /** Secure setting that holds the backup manager configuration as key-value pairs */
+    private static final String BACKUP_MANAGER_CONSTANTS_PREF = "backup_manager_constants";
+
+    /** Key for specifying the apps that the backup manager should notify of successful backups */
+    private static final String BACKUP_FINISHED_NOTIFICATION_RECEIVERS =
+            "backup_finished_notification_receivers";
+
+    /** The original backup manager configuration */
+    private String mOriginalBackupManagerConstants = null;
+
+    @Before
+    @Override
+    public void setUp() throws Exception {
+        super.setUp();
+
+        if (!mIsBackupSupported) {
+            CLog.i("android.software.backup feature is not supported on this device");
+            return;
+        }
+
+        installPackage(KEY_VALUE_BACKUP_APP_APK);
+        installPackage(FULL_BACKUP_APP_APK);
+
+        installPackage(SUCCESS_NOTIFICATION_APP_APK);
+        checkDeviceTest("clearBackupSuccessNotificationsReceived");
+        addBackupFinishedNotificationReceiver();
+    }
+
+    @After
+    @Override
+    public void tearDown() throws Exception {
+        super.tearDown();
+
+        if (!mIsBackupSupported) {
+            return;
+        }
+
+        restoreBackupFinishedNotificationReceivers();
+        assertNull(uninstallPackage(SUCCESS_NOTIFICATION_APP_PACKAGE));
+
+        // Clear backup data and uninstall the package (in that order!)
+        clearBackupDataInLocalTransport(KEY_VALUE_BACKUP_APP_PACKAGE);
+        assertNull(uninstallPackage(KEY_VALUE_BACKUP_APP_PACKAGE));
+
+        clearBackupDataInLocalTransport(FULL_BACKUP_APP_PACKAGE);
+        assertNull(uninstallPackage(FULL_BACKUP_APP_PACKAGE));
+    }
+
+    /**
+     * Test that the observer app is notified when a key/value backup succeeds.
+     *
+     * Test logic:
+     *   1. Change a test app's data, trigger a key/value backup and wait for it to complete.
+     *   2. Verify that the observer app was informed about the backup.
+     */
+    @Test
+    public void testSuccessNotificationForKeyValueBackup() throws Exception {
+        if (!mIsBackupSupported) {
+            return;
+        }
+
+        checkDeviceTest(KEY_VALUE_BACKUP_APP_PACKAGE, KEY_VALUE_BACKUP_DEVICE_TEST_NAME,
+                "saveSharedPreferencesAndNotifyBackupManager");
+        backupNowAndAssertSuccess(KEY_VALUE_BACKUP_APP_PACKAGE);
+
+        checkDeviceTest("verifyBackupSuccessNotificationReceivedForKeyValueApp");
+    }
+
+    /**
+     * Test that the observer app is notified when a full backup succeeds.
+     *
+     * Test logic:
+     *   1. Change a test app's data, trigger a full backup and wait for it to complete.
+     *   2. Verify that the observer app was informed about the backup.
+     */
+    @Test
+    public void testSuccessNotificationForFullBackup() throws Exception {
+        if (!mIsBackupSupported) {
+            return;
+        }
+
+        checkDeviceTest(FULL_BACKUP_APP_PACKAGE, FULL_BACKUP_DEVICE_TEST_CLASS_NAME, "createFiles");
+        backupNowAndAssertSuccess(FULL_BACKUP_APP_PACKAGE);
+
+        checkDeviceTest("verifyBackupSuccessNotificationReceivedForFullBackupApp");
+    }
+
+    /**
+     * Instructs the backup manager to notify the observer app whenever a backup succeeds. The old
+     * backup manager configuration is stored in a member variable and can be restored by calling
+     * {@link restoreBackupFinishedNotificationReceivers}.
+     */
+    private void addBackupFinishedNotificationReceiver()
+            throws DeviceNotAvailableException {
+        mOriginalBackupManagerConstants = getDevice().executeShellCommand(String.format(
+                "settings get secure %s", BACKUP_MANAGER_CONSTANTS_PREF)).trim();
+        if ("null".equals(mOriginalBackupManagerConstants)) {
+            mOriginalBackupManagerConstants = null;
+        }
+        String backupManagerConstants = null;
+
+        if (mOriginalBackupManagerConstants == null || mOriginalBackupManagerConstants.isEmpty()) {
+            backupManagerConstants =
+                    BACKUP_FINISHED_NOTIFICATION_RECEIVERS + "=" + SUCCESS_NOTIFICATION_APP_PACKAGE;
+        } else {
+            final List<String> keyValuePairs =
+                    new ArrayList<>(Arrays.asList(mOriginalBackupManagerConstants.split(",")));
+            boolean present = false;
+            for (int i = 0; i < keyValuePairs.size(); ++i) {
+                final String keyValuePair = keyValuePairs.get(i);
+                final String[] fields = keyValuePair.split("=");
+                final String key = fields[0].trim();
+                if (BACKUP_FINISHED_NOTIFICATION_RECEIVERS.equals(key)) {
+                    if (fields.length == 1 || fields[1].trim().isEmpty()) {
+                        keyValuePairs.set(i, key + "=" + SUCCESS_NOTIFICATION_APP_PACKAGE);
+                    } else {
+                        final String[] values = fields[1].split(":");
+                        for (int j = 0; j < values.length; ++j) {
+                            if (SUCCESS_NOTIFICATION_APP_PACKAGE.equals(values[j].trim())) {
+                                present = true;
+                                break;
+                            }
+                        }
+                        if (!present) {
+                            keyValuePairs.set(i,
+                                    keyValuePair + ":" + SUCCESS_NOTIFICATION_APP_PACKAGE);
+                        }
+                    }
+                    present = true;
+                    break;
+                }
+            }
+            if (!present) {
+                keyValuePairs.add(BACKUP_FINISHED_NOTIFICATION_RECEIVERS + "=" +
+                        SUCCESS_NOTIFICATION_APP_PACKAGE);
+            }
+            backupManagerConstants = String.join(",", keyValuePairs);
+        }
+        setBackupManagerConstants(backupManagerConstants);
+    }
+
+    /**
+     * Restores the backup manager configuration stored by a previous call to
+     * {@link addBackupFinishedNotificationReceiver}.
+     */
+    private void restoreBackupFinishedNotificationReceivers() throws DeviceNotAvailableException {
+        setBackupManagerConstants(mOriginalBackupManagerConstants);
+    }
+
+    private void setBackupManagerConstants(String backupManagerConstants)
+            throws DeviceNotAvailableException {
+        getDevice().executeShellCommand(String.format("settings put secure %s %s",
+                BACKUP_MANAGER_CONSTANTS_PREF, backupManagerConstants));
+    }
+
+    private void checkDeviceTest(String methodName) throws DeviceNotAvailableException {
+        checkDeviceTest(SUCCESS_NOTIFICATION_APP_PACKAGE, SUCCESS_NOTIFICATION_DEVICE_TEST_NAME,
+                methodName);
+    }
+}
diff --git a/hostsidetests/bootstats/AndroidTest.xml b/hostsidetests/bootstats/AndroidTest.xml
index 6d1be7d..5cac548 100644
--- a/hostsidetests/bootstats/AndroidTest.xml
+++ b/hostsidetests/bootstats/AndroidTest.xml
@@ -14,6 +14,7 @@
      limitations under the License.
 -->
 <configuration description="Config for the CTS Boot Stats host tests">
+    <option name="test-suite-tag" value="cts" />
     <option name="config-descriptor:metadata" key="component" value="sysui" />
     <test class="com.android.compatibility.common.tradefed.testtype.JarHostTest" >
         <option name="jar" value="CtsBootStatsTestCases.jar" />
diff --git a/hostsidetests/compilation/AndroidTest.xml b/hostsidetests/compilation/AndroidTest.xml
index 9cc9066..63d53e9 100644
--- a/hostsidetests/compilation/AndroidTest.xml
+++ b/hostsidetests/compilation/AndroidTest.xml
@@ -14,6 +14,7 @@
      limitations under the License.
 -->
 <configuration description="Config for CTS Compilation Test">
+    <option name="test-suite-tag" value="cts" />
     <option name="config-descriptor:metadata" key="component" value="libcore" />
     <test class="com.android.compatibility.common.tradefed.testtype.JarHostTest" >
         <option name="jar" value="CtsCompilationTestCases.jar" />
diff --git a/hostsidetests/compilation/assets/primary.prof.txt b/hostsidetests/compilation/assets/primary.prof.txt
index 0a8bded..fd55262 100644
--- a/hostsidetests/compilation/assets/primary.prof.txt
+++ b/hostsidetests/compilation/assets/primary.prof.txt
@@ -1,103 +1,103 @@
 Landroid/cts/compilation/CompilationTargetActivity;
-Landroid/cts/compilation/CompilationTargetActivity;->m0()I
-Landroid/cts/compilation/CompilationTargetActivity;->m1()I
-Landroid/cts/compilation/CompilationTargetActivity;->m2()I
-Landroid/cts/compilation/CompilationTargetActivity;->m3()I
-Landroid/cts/compilation/CompilationTargetActivity;->m4()I
-Landroid/cts/compilation/CompilationTargetActivity;->m5()I
-Landroid/cts/compilation/CompilationTargetActivity;->m6()I
-Landroid/cts/compilation/CompilationTargetActivity;->m7()I
-Landroid/cts/compilation/CompilationTargetActivity;->m8()I
-Landroid/cts/compilation/CompilationTargetActivity;->m9()I
-Landroid/cts/compilation/CompilationTargetActivity;->m10()I
-Landroid/cts/compilation/CompilationTargetActivity;->m11()I
-Landroid/cts/compilation/CompilationTargetActivity;->m12()I
-Landroid/cts/compilation/CompilationTargetActivity;->m13()I
-Landroid/cts/compilation/CompilationTargetActivity;->m14()I
-Landroid/cts/compilation/CompilationTargetActivity;->m15()I
-Landroid/cts/compilation/CompilationTargetActivity;->m16()I
-Landroid/cts/compilation/CompilationTargetActivity;->m17()I
-Landroid/cts/compilation/CompilationTargetActivity;->m18()I
-Landroid/cts/compilation/CompilationTargetActivity;->m19()I
-Landroid/cts/compilation/CompilationTargetActivity;->m20()I
-Landroid/cts/compilation/CompilationTargetActivity;->m21()I
-Landroid/cts/compilation/CompilationTargetActivity;->m22()I
-Landroid/cts/compilation/CompilationTargetActivity;->m23()I
-Landroid/cts/compilation/CompilationTargetActivity;->m24()I
-Landroid/cts/compilation/CompilationTargetActivity;->m25()I
-Landroid/cts/compilation/CompilationTargetActivity;->m26()I
-Landroid/cts/compilation/CompilationTargetActivity;->m27()I
-Landroid/cts/compilation/CompilationTargetActivity;->m28()I
-Landroid/cts/compilation/CompilationTargetActivity;->m29()I
-Landroid/cts/compilation/CompilationTargetActivity;->m30()I
-Landroid/cts/compilation/CompilationTargetActivity;->m31()I
-Landroid/cts/compilation/CompilationTargetActivity;->m32()I
-Landroid/cts/compilation/CompilationTargetActivity;->m33()I
-Landroid/cts/compilation/CompilationTargetActivity;->m34()I
-Landroid/cts/compilation/CompilationTargetActivity;->m35()I
-Landroid/cts/compilation/CompilationTargetActivity;->m36()I
-Landroid/cts/compilation/CompilationTargetActivity;->m37()I
-Landroid/cts/compilation/CompilationTargetActivity;->m38()I
-Landroid/cts/compilation/CompilationTargetActivity;->m39()I
-Landroid/cts/compilation/CompilationTargetActivity;->m40()I
-Landroid/cts/compilation/CompilationTargetActivity;->m41()I
-Landroid/cts/compilation/CompilationTargetActivity;->m42()I
-Landroid/cts/compilation/CompilationTargetActivity;->m43()I
-Landroid/cts/compilation/CompilationTargetActivity;->m44()I
-Landroid/cts/compilation/CompilationTargetActivity;->m45()I
-Landroid/cts/compilation/CompilationTargetActivity;->m46()I
-Landroid/cts/compilation/CompilationTargetActivity;->m47()I
-Landroid/cts/compilation/CompilationTargetActivity;->m48()I
-Landroid/cts/compilation/CompilationTargetActivity;->m49()I
-Landroid/cts/compilation/CompilationTargetActivity;->m50()I
-Landroid/cts/compilation/CompilationTargetActivity;->m51()I
-Landroid/cts/compilation/CompilationTargetActivity;->m52()I
-Landroid/cts/compilation/CompilationTargetActivity;->m53()I
-Landroid/cts/compilation/CompilationTargetActivity;->m54()I
-Landroid/cts/compilation/CompilationTargetActivity;->m55()I
-Landroid/cts/compilation/CompilationTargetActivity;->m56()I
-Landroid/cts/compilation/CompilationTargetActivity;->m57()I
-Landroid/cts/compilation/CompilationTargetActivity;->m58()I
-Landroid/cts/compilation/CompilationTargetActivity;->m59()I
-Landroid/cts/compilation/CompilationTargetActivity;->m60()I
-Landroid/cts/compilation/CompilationTargetActivity;->m61()I
-Landroid/cts/compilation/CompilationTargetActivity;->m62()I
-Landroid/cts/compilation/CompilationTargetActivity;->m63()I
-Landroid/cts/compilation/CompilationTargetActivity;->m64()I
-Landroid/cts/compilation/CompilationTargetActivity;->m65()I
-Landroid/cts/compilation/CompilationTargetActivity;->m66()I
-Landroid/cts/compilation/CompilationTargetActivity;->m67()I
-Landroid/cts/compilation/CompilationTargetActivity;->m68()I
-Landroid/cts/compilation/CompilationTargetActivity;->m69()I
-Landroid/cts/compilation/CompilationTargetActivity;->m70()I
-Landroid/cts/compilation/CompilationTargetActivity;->m71()I
-Landroid/cts/compilation/CompilationTargetActivity;->m72()I
-Landroid/cts/compilation/CompilationTargetActivity;->m73()I
-Landroid/cts/compilation/CompilationTargetActivity;->m74()I
-Landroid/cts/compilation/CompilationTargetActivity;->m75()I
-Landroid/cts/compilation/CompilationTargetActivity;->m76()I
-Landroid/cts/compilation/CompilationTargetActivity;->m77()I
-Landroid/cts/compilation/CompilationTargetActivity;->m78()I
-Landroid/cts/compilation/CompilationTargetActivity;->m79()I
-Landroid/cts/compilation/CompilationTargetActivity;->m80()I
-Landroid/cts/compilation/CompilationTargetActivity;->m81()I
-Landroid/cts/compilation/CompilationTargetActivity;->m82()I
-Landroid/cts/compilation/CompilationTargetActivity;->m83()I
-Landroid/cts/compilation/CompilationTargetActivity;->m84()I
-Landroid/cts/compilation/CompilationTargetActivity;->m85()I
-Landroid/cts/compilation/CompilationTargetActivity;->m86()I
-Landroid/cts/compilation/CompilationTargetActivity;->m87()I
-Landroid/cts/compilation/CompilationTargetActivity;->m88()I
-Landroid/cts/compilation/CompilationTargetActivity;->m89()I
-Landroid/cts/compilation/CompilationTargetActivity;->m90()I
-Landroid/cts/compilation/CompilationTargetActivity;->m91()I
-Landroid/cts/compilation/CompilationTargetActivity;->m92()I
-Landroid/cts/compilation/CompilationTargetActivity;->m93()I
-Landroid/cts/compilation/CompilationTargetActivity;->m94()I
-Landroid/cts/compilation/CompilationTargetActivity;->m95()I
-Landroid/cts/compilation/CompilationTargetActivity;->m96()I
-Landroid/cts/compilation/CompilationTargetActivity;->m97()I
-Landroid/cts/compilation/CompilationTargetActivity;->m98()I
-Landroid/cts/compilation/CompilationTargetActivity;->m99()I
-Landroid/cts/compilation/CompilationTargetActivity;->m100()I
-Landroid/cts/compilation/CompilationTargetActivity;->m101()I
\ No newline at end of file
+SHLandroid/cts/compilation/CompilationTargetActivity;->m0()I
+SHLandroid/cts/compilation/CompilationTargetActivity;->m1()I
+SHLandroid/cts/compilation/CompilationTargetActivity;->m2()I
+SHLandroid/cts/compilation/CompilationTargetActivity;->m3()I
+SHLandroid/cts/compilation/CompilationTargetActivity;->m4()I
+SHLandroid/cts/compilation/CompilationTargetActivity;->m5()I
+SHLandroid/cts/compilation/CompilationTargetActivity;->m6()I
+SHLandroid/cts/compilation/CompilationTargetActivity;->m7()I
+SHLandroid/cts/compilation/CompilationTargetActivity;->m8()I
+SHLandroid/cts/compilation/CompilationTargetActivity;->m9()I
+SHLandroid/cts/compilation/CompilationTargetActivity;->m10()I
+SHLandroid/cts/compilation/CompilationTargetActivity;->m11()I
+SHLandroid/cts/compilation/CompilationTargetActivity;->m12()I
+SHLandroid/cts/compilation/CompilationTargetActivity;->m13()I
+SHLandroid/cts/compilation/CompilationTargetActivity;->m14()I
+SHLandroid/cts/compilation/CompilationTargetActivity;->m15()I
+SHLandroid/cts/compilation/CompilationTargetActivity;->m16()I
+SHLandroid/cts/compilation/CompilationTargetActivity;->m17()I
+SHLandroid/cts/compilation/CompilationTargetActivity;->m18()I
+SHLandroid/cts/compilation/CompilationTargetActivity;->m19()I
+SHLandroid/cts/compilation/CompilationTargetActivity;->m20()I
+SHLandroid/cts/compilation/CompilationTargetActivity;->m21()I
+SHLandroid/cts/compilation/CompilationTargetActivity;->m22()I
+SHLandroid/cts/compilation/CompilationTargetActivity;->m23()I
+SHLandroid/cts/compilation/CompilationTargetActivity;->m24()I
+SHLandroid/cts/compilation/CompilationTargetActivity;->m25()I
+SHLandroid/cts/compilation/CompilationTargetActivity;->m26()I
+SHLandroid/cts/compilation/CompilationTargetActivity;->m27()I
+SHLandroid/cts/compilation/CompilationTargetActivity;->m28()I
+SHLandroid/cts/compilation/CompilationTargetActivity;->m29()I
+SHLandroid/cts/compilation/CompilationTargetActivity;->m30()I
+SHLandroid/cts/compilation/CompilationTargetActivity;->m31()I
+SHLandroid/cts/compilation/CompilationTargetActivity;->m32()I
+SHLandroid/cts/compilation/CompilationTargetActivity;->m33()I
+SHLandroid/cts/compilation/CompilationTargetActivity;->m34()I
+SHLandroid/cts/compilation/CompilationTargetActivity;->m35()I
+SHLandroid/cts/compilation/CompilationTargetActivity;->m36()I
+SHLandroid/cts/compilation/CompilationTargetActivity;->m37()I
+SHLandroid/cts/compilation/CompilationTargetActivity;->m38()I
+SHLandroid/cts/compilation/CompilationTargetActivity;->m39()I
+SHLandroid/cts/compilation/CompilationTargetActivity;->m40()I
+SHLandroid/cts/compilation/CompilationTargetActivity;->m41()I
+SHLandroid/cts/compilation/CompilationTargetActivity;->m42()I
+SHLandroid/cts/compilation/CompilationTargetActivity;->m43()I
+SHLandroid/cts/compilation/CompilationTargetActivity;->m44()I
+SHLandroid/cts/compilation/CompilationTargetActivity;->m45()I
+SHLandroid/cts/compilation/CompilationTargetActivity;->m46()I
+SHLandroid/cts/compilation/CompilationTargetActivity;->m47()I
+SHLandroid/cts/compilation/CompilationTargetActivity;->m48()I
+SHLandroid/cts/compilation/CompilationTargetActivity;->m49()I
+SHLandroid/cts/compilation/CompilationTargetActivity;->m50()I
+SHLandroid/cts/compilation/CompilationTargetActivity;->m51()I
+SHLandroid/cts/compilation/CompilationTargetActivity;->m52()I
+SHLandroid/cts/compilation/CompilationTargetActivity;->m53()I
+SHLandroid/cts/compilation/CompilationTargetActivity;->m54()I
+SHLandroid/cts/compilation/CompilationTargetActivity;->m55()I
+SHLandroid/cts/compilation/CompilationTargetActivity;->m56()I
+SHLandroid/cts/compilation/CompilationTargetActivity;->m57()I
+SHLandroid/cts/compilation/CompilationTargetActivity;->m58()I
+SHLandroid/cts/compilation/CompilationTargetActivity;->m59()I
+SHLandroid/cts/compilation/CompilationTargetActivity;->m60()I
+SHLandroid/cts/compilation/CompilationTargetActivity;->m61()I
+SHLandroid/cts/compilation/CompilationTargetActivity;->m62()I
+SHLandroid/cts/compilation/CompilationTargetActivity;->m63()I
+SHLandroid/cts/compilation/CompilationTargetActivity;->m64()I
+SHLandroid/cts/compilation/CompilationTargetActivity;->m65()I
+SHLandroid/cts/compilation/CompilationTargetActivity;->m66()I
+SHLandroid/cts/compilation/CompilationTargetActivity;->m67()I
+SHLandroid/cts/compilation/CompilationTargetActivity;->m68()I
+SHLandroid/cts/compilation/CompilationTargetActivity;->m69()I
+SHLandroid/cts/compilation/CompilationTargetActivity;->m70()I
+SHLandroid/cts/compilation/CompilationTargetActivity;->m71()I
+SHLandroid/cts/compilation/CompilationTargetActivity;->m72()I
+SHLandroid/cts/compilation/CompilationTargetActivity;->m73()I
+SHLandroid/cts/compilation/CompilationTargetActivity;->m74()I
+SHLandroid/cts/compilation/CompilationTargetActivity;->m75()I
+SHLandroid/cts/compilation/CompilationTargetActivity;->m76()I
+SHLandroid/cts/compilation/CompilationTargetActivity;->m77()I
+SHLandroid/cts/compilation/CompilationTargetActivity;->m78()I
+SHLandroid/cts/compilation/CompilationTargetActivity;->m79()I
+SHLandroid/cts/compilation/CompilationTargetActivity;->m80()I
+SHLandroid/cts/compilation/CompilationTargetActivity;->m81()I
+SHLandroid/cts/compilation/CompilationTargetActivity;->m82()I
+SHLandroid/cts/compilation/CompilationTargetActivity;->m83()I
+SHLandroid/cts/compilation/CompilationTargetActivity;->m84()I
+SHLandroid/cts/compilation/CompilationTargetActivity;->m85()I
+SHLandroid/cts/compilation/CompilationTargetActivity;->m86()I
+SHLandroid/cts/compilation/CompilationTargetActivity;->m87()I
+SHLandroid/cts/compilation/CompilationTargetActivity;->m88()I
+SHLandroid/cts/compilation/CompilationTargetActivity;->m89()I
+SHLandroid/cts/compilation/CompilationTargetActivity;->m90()I
+SHLandroid/cts/compilation/CompilationTargetActivity;->m91()I
+SHLandroid/cts/compilation/CompilationTargetActivity;->m92()I
+SHLandroid/cts/compilation/CompilationTargetActivity;->m93()I
+SHLandroid/cts/compilation/CompilationTargetActivity;->m94()I
+SHLandroid/cts/compilation/CompilationTargetActivity;->m95()I
+SHLandroid/cts/compilation/CompilationTargetActivity;->m96()I
+SHLandroid/cts/compilation/CompilationTargetActivity;->m97()I
+SHLandroid/cts/compilation/CompilationTargetActivity;->m98()I
+SHLandroid/cts/compilation/CompilationTargetActivity;->m99()I
+SHLandroid/cts/compilation/CompilationTargetActivity;->m100()I
+SHLandroid/cts/compilation/CompilationTargetActivity;->m101()I
\ No newline at end of file
diff --git a/hostsidetests/compilation/src/android/cts/compilation/AdbRootDependentCompilationTest.java b/hostsidetests/compilation/src/android/cts/compilation/AdbRootDependentCompilationTest.java
index 1f5d669..591dcb4 100644
--- a/hostsidetests/compilation/src/android/cts/compilation/AdbRootDependentCompilationTest.java
+++ b/hostsidetests/compilation/src/android/cts/compilation/AdbRootDependentCompilationTest.java
@@ -274,19 +274,15 @@
         executePush(apkFile.getAbsolutePath(), targetPathApk, targetDir);
         assertTrue("Failed to push APK from ", doesFileExist(targetPathApk));
         // Run profman to create the real profile on device.
-        try {
-            String pathSpec = executeSuShellAdbCommand(1, "pm", "path", APPLICATION_PACKAGE)[0];
-            pathSpec = pathSpec.replace("package:", "");
-            assertTrue("Failed find APK " + pathSpec, doesFileExist(pathSpec));
-            executeSuShellAdbCommand(
+        String pathSpec = executeSuShellAdbCommand(1, "pm", "path", APPLICATION_PACKAGE)[0];
+        pathSpec = pathSpec.replace("package:", "");
+        assertTrue("Failed find APK " + pathSpec, doesFileExist(pathSpec));
+        executeSuShellAdbCommand(
                 "profman",
                 "--create-profile-from=" + targetPathTemp,
                 "--apk=" + pathSpec,
                 "--dex-location=" + pathSpec,
                 "--reference-profile-file=" + targetPath);
-        } catch (Exception e) {
-            assertEquals("", e.toString());
-        }
         executeSuShellAdbCommand(0, "chown", owner, targetPath);
         // Verify that the file was written successfully
         assertTrue("failed to create profile file", doesFileExist(targetPath));
diff --git a/hostsidetests/content/AndroidTest.xml b/hostsidetests/content/AndroidTest.xml
index accf2a5..badbda7 100644
--- a/hostsidetests/content/AndroidTest.xml
+++ b/hostsidetests/content/AndroidTest.xml
@@ -14,6 +14,7 @@
      limitations under the License.
 -->
 <configuration description="Config for the CTS Content host tests">
+    <option name="test-suite-tag" value="cts" />
     <option name="config-descriptor:metadata" key="component" value="framework" />
     <test class="com.android.compatibility.common.tradefed.testtype.JarHostTest" >
         <option name="jar" value="CtsSyncContentHostTestCases.jar" />
diff --git a/hostsidetests/content/src/android/content/cts/SyncAdapterAccountAccessHostTest.java b/hostsidetests/content/src/android/content/cts/SyncAdapterAccountAccessHostTest.java
deleted file mode 100644
index f6f9d10..0000000
--- a/hostsidetests/content/src/android/content/cts/SyncAdapterAccountAccessHostTest.java
+++ /dev/null
@@ -1,108 +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.content.cts;
-
-import android.appsecurity.cts.Utils;
-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;
-
-/**
- * Set of tests that verify behavior of the content framework.
- */
-public class SyncAdapterAccountAccessHostTest extends DeviceTestCase
-        implements IAbiReceiver, IBuildReceiver {
-    private static final String ACCOUNT_ACCESS_TESTS_OTHER_CERT_APK =
-            "CtsSyncAccountAccessOtherCertTestCases.apk";
-    private static final String ACCOUNT_ACCESS_TESTS_OTHER_CERT_PKG =
-            "com.android.cts.content";
-
-    private static final String ACCOUNT_ACCESS_TESTS_SAME_CERT_APK =
-            "CtsSyncAccountAccessSameCertTestCases.apk";
-    private static final String ACCOUNT_ACCESS_TESTS_SAME_CERT_PKG =
-            "com.android.cts.content";
-
-    private static final String STUBS_APK =
-            "CtsSyncAccountAccessStubs.apk";
-    private static final String STUBS_PKG =
-            "com.android.cts.stub";
-
-    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();
-        getDevice().uninstallPackage(STUBS_PKG);
-
-        assertNull(getDevice().installPackage(mBuildHelper
-                .getTestFile(STUBS_APK), false, false));
-    }
-
-    @Override
-    protected void tearDown() throws Exception {
-        super.tearDown();
-        getDevice().uninstallPackage(STUBS_PKG);
-    }
-
-    public void testSameCertAuthenticatorCanSeeAccount() throws Exception {
-        getDevice().uninstallPackage(ACCOUNT_ACCESS_TESTS_SAME_CERT_PKG);
-
-        assertNull(getDevice().installPackage(mBuildHelper.getTestFile(
-                ACCOUNT_ACCESS_TESTS_SAME_CERT_APK), false, false));
-        try {
-            runDeviceTests(ACCOUNT_ACCESS_TESTS_SAME_CERT_PKG,
-                    "com.android.cts.content.CtsSyncAccountAccessSameCertTestCases",
-                    "testAccountAccess_sameCertAsAuthenticatorCanSeeAccount");
-        } finally {
-            getDevice().uninstallPackage(ACCOUNT_ACCESS_TESTS_SAME_CERT_PKG);
-        }
-    }
-
-    public void testOtherCertAuthenticatorCanSeeAccount() throws Exception {
-        getDevice().uninstallPackage(ACCOUNT_ACCESS_TESTS_OTHER_CERT_PKG);
-
-        assertNull(getDevice().installPackage(mBuildHelper.getTestFile(
-                ACCOUNT_ACCESS_TESTS_OTHER_CERT_APK), false, false));
-        try {
-            runDeviceTests(ACCOUNT_ACCESS_TESTS_OTHER_CERT_PKG,
-                    "com.android.cts.content.CtsSyncAccountAccessOtherCertTestCases",
-                    "testAccountAccess_otherCertAsAuthenticatorCanNotSeeAccount");
-        } finally {
-            getDevice().uninstallPackage(ACCOUNT_ACCESS_TESTS_OTHER_CERT_PKG);
-        }
-    }
-
-    private void runDeviceTests(String packageName, String testClassName, String testMethodName)
-            throws DeviceNotAvailableException {
-        Utils.runDeviceTests(getDevice(), packageName, testClassName, testMethodName);
-    }
-}
diff --git a/hostsidetests/content/test-apps/CtsSyncAccountAccessOtherCertTests/Android.mk b/hostsidetests/content/test-apps/CtsSyncAccountAccessOtherCertTests/Android.mk
deleted file mode 100644
index 116da94..0000000
--- a/hostsidetests/content/test-apps/CtsSyncAccountAccessOtherCertTests/Android.mk
+++ /dev/null
@@ -1,45 +0,0 @@
-#
-# Copyright (C) 2016 The Android Open Source Project
-#
-# Licensed under the Apache License, Version 2.0 (the "License");
-# you may not use this file except in compliance with the License.
-# You may obtain a copy of the License at
-#
-#      http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing, software
-# distributed under the License is distributed on an "AS IS" BASIS,
-# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-# See the License for the specific language governing permissions and
-# limitations under the License.
-#
-
-LOCAL_PATH := $(call my-dir)
-
-include $(CLEAR_VARS)
-
-LOCAL_MODULE_TAGS := tests
-
-LOCAL_STATIC_JAVA_LIBRARIES := \
-    android-support-test \
-    ctstestrunner \
-    ub-uiautomator \
-    compatibility-device-util
-
-LOCAL_SRC_FILES := $(call all-java-files-under, src) \
-  ../CtsSyncAccountAccessSameCertTests/src/com/android/cts/content/StubActivity.java \
-  ../CtsSyncAccountAccessSameCertTests/src/com/android/cts/content/SyncAdapter.java \
-  ../CtsSyncAccountAccessSameCertTests/src/com/android/cts/content/SyncService.java \
-  ../CtsSyncAccountAccessSameCertTests/src/com/android/cts/content/FlakyTestRule.java
-
-LOCAL_PACKAGE_NAME := CtsSyncAccountAccessOtherCertTestCases
-
-LOCAL_CERTIFICATE := cts/hostsidetests/appsecurity/certs/cts-testkey2
-
-LOCAL_COMPATIBILITY_SUITE := cts vts general-tests
-
-LOCAL_PROGUARD_ENABLED := disabled
-
-LOCAL_DEX_PREOPT := false
-
-include $(BUILD_CTS_SUPPORT_PACKAGE)
diff --git a/hostsidetests/content/test-apps/CtsSyncAccountAccessOtherCertTests/src/com/android/cts/content/CtsSyncAccountAccessOtherCertTestCases.java b/hostsidetests/content/test-apps/CtsSyncAccountAccessOtherCertTests/src/com/android/cts/content/CtsSyncAccountAccessOtherCertTestCases.java
deleted file mode 100644
index c2557ef..0000000
--- a/hostsidetests/content/test-apps/CtsSyncAccountAccessOtherCertTests/src/com/android/cts/content/CtsSyncAccountAccessOtherCertTestCases.java
+++ /dev/null
@@ -1,230 +0,0 @@
-/*
- * Copyright (C) 2016 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.cts.content;
-
-import android.accounts.Account;
-import android.accounts.AccountManager;
-import android.app.Activity;
-import android.content.ContentProviderClient;
-import android.content.ContentResolver;
-import android.content.Context;
-import android.content.Intent;
-import android.content.SyncRequest;
-import android.content.SyncResult;
-import android.content.cts.FlakyTestRule;
-import android.content.pm.PackageManager;
-import android.content.res.Configuration;
-import android.net.ConnectivityManager;
-import android.net.NetworkInfo;
-import android.os.Bundle;
-import android.os.Process;
-import android.os.SystemClock;
-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.UiObject2;
-import android.support.test.uiautomator.Until;
-import android.util.Log;
-import org.junit.After;
-import org.junit.Before;
-import org.junit.Rule;
-import org.junit.Test;
-import org.junit.rules.TestRule;
-import org.junit.runner.RunWith;
-
-import java.io.IOException;
-import java.util.concurrent.CountDownLatch;
-import java.util.concurrent.TimeUnit;
-
-import static junit.framework.Assert.assertFalse;
-import static junit.framework.Assert.assertTrue;
-
-import com.android.compatibility.common.util.SystemUtil;
-
-/**
- * Tests whether a sync adapter can access accounts.
- */
-@RunWith(AndroidJUnit4.class)
-public class CtsSyncAccountAccessOtherCertTestCases {
-    private static final long SYNC_TIMEOUT_MILLIS = 20000; // 20 sec
-    private static final long UI_TIMEOUT_MILLIS = 5000; // 5 sec
-
-    private static final String PERMISSION_REQUESTED = "Permission Requested";
-    public static final String TOKEN_TYPE_REMOVE_ACCOUNTS = "TOKEN_TYPE_REMOVE_ACCOUNTS";
-
-    @Rule
-    public final TestRule mFlakyTestRule = new FlakyTestRule(3);
-
-    @Before
-    public void setUp() throws Exception {
-        allowSyncAdapterRunInBackgroundAndDataInBackground();
-    }
-
-    @After
-    public void tearDown() throws Exception {
-        disallowSyncAdapterRunInBackgroundAndDataInBackground();
-    }
-
-    @Test
-    public void testAccountAccess_otherCertAsAuthenticatorCanNotSeeAccount() throws Exception {
-        if (!hasDataConnection() || !hasNotificationSupport()) {
-            return;
-        }
-
-        Intent intent = new Intent(getContext(), StubActivity.class);
-        Activity activity = InstrumentationRegistry.getInstrumentation().startActivitySync(intent);
-
-        AccountManager accountManager = getContext().getSystemService(AccountManager.class);
-        Bundle result = accountManager.addAccount("com.stub", null, null, null, activity,
-                null, null).getResult();
-
-        Account addedAccount = new Account(
-                result.getString(AccountManager.KEY_ACCOUNT_NAME),
-                result.getString(AccountManager.KEY_ACCOUNT_TYPE));
-
-        waitForSyncManagerAccountChangeUpdate();
-
-        try {
-            CountDownLatch latch = new CountDownLatch(1);
-
-            SyncAdapter.setOnPerformSyncDelegate((Account account, Bundle extras,
-                    String authority, ContentProviderClient provider, SyncResult syncResult)
-                    -> latch.countDown());
-
-            Bundle extras = new Bundle();
-            extras.putBoolean(ContentResolver.SYNC_EXTRAS_DO_NOT_RETRY, true);
-            extras.putBoolean(ContentResolver.SYNC_EXTRAS_PRIORITY, true);
-            extras.getBoolean(ContentResolver.SYNC_EXTRAS_IGNORE_SETTINGS, true);
-            SyncRequest request = new SyncRequest.Builder()
-                    .setSyncAdapter(null, "com.android.cts.stub.provider")
-                    .syncOnce()
-                    .setExtras(extras)
-                    .setExpedited(true)
-                    .setManual(true)
-                    .build();
-            ContentResolver.requestSync(request);
-
-            assertFalse(latch.await(SYNC_TIMEOUT_MILLIS, TimeUnit.MILLISECONDS));
-
-            UiDevice uiDevice = getUiDevice();
-            if (isWatch()) {
-                UiObject2 notification = findPermissionNotificationInStream(uiDevice);
-                notification.click();
-            } else {
-                uiDevice.openNotification();
-                uiDevice.wait(Until.hasObject(By.text(PERMISSION_REQUESTED)),
-                        UI_TIMEOUT_MILLIS);
-
-                uiDevice.findObject(By.text(PERMISSION_REQUESTED)).click();
-            }
-
-            uiDevice.wait(Until.hasObject(By.text("ALLOW")),
-                    UI_TIMEOUT_MILLIS);
-
-            uiDevice.findObject(By.text("ALLOW")).click();
-
-            ContentResolver.requestSync(request);
-
-            assertTrue(latch.await(SYNC_TIMEOUT_MILLIS, TimeUnit.MILLISECONDS));
-        } finally {
-            // Ask the differently signed authenticator to drop all accounts
-            accountManager.getAuthToken(addedAccount, TOKEN_TYPE_REMOVE_ACCOUNTS,
-                    null, false, null, null);
-            activity.finish();
-        }
-    }
-
-    private UiObject2 findPermissionNotificationInStream(UiDevice uiDevice) {
-        uiDevice.pressHome();
-        swipeUp(uiDevice);
-        if (uiDevice.hasObject(By.text(PERMISSION_REQUESTED))) {
-          return uiDevice.findObject(By.text(PERMISSION_REQUESTED));
-        }
-        for (int i = 0; i < 100; i++) {
-          if (!swipeUp(uiDevice)) {
-            // We have reached the end of the stream and not found the target.
-            break;
-          }
-          if (uiDevice.hasObject(By.text(PERMISSION_REQUESTED))) {
-            return uiDevice.findObject(By.text(PERMISSION_REQUESTED));
-          }
-        }
-        return null;
-    }
-
-    private boolean swipeUp(UiDevice uiDevice) {
-        int width = uiDevice.getDisplayWidth();
-        int height = uiDevice.getDisplayHeight();
-        return uiDevice.swipe(
-            width / 2 /* startX */,
-            height - 1 /* startY */,
-            width / 2 /* endX */,
-            1 /* endY */,
-            50 /* numberOfSteps */);
-    }
-
-    private boolean isWatch() {
-        return (getContext().getResources().getConfiguration().uiMode
-                & Configuration.UI_MODE_TYPE_MASK) == Configuration.UI_MODE_TYPE_WATCH;
-    }
-
-    private Context getContext() {
-        return InstrumentationRegistry.getInstrumentation().getContext();
-    }
-
-    private UiDevice getUiDevice() {
-        return UiDevice.getInstance(InstrumentationRegistry.getInstrumentation());
-    }
-
-    private void waitForSyncManagerAccountChangeUpdate() {
-        // Wait for the sync manager to be notified for the new account.
-        // Unfortunately, there is no way to detect this event, sigh...
-        SystemClock.sleep(SYNC_TIMEOUT_MILLIS);
-    }
-
-    private boolean hasDataConnection() {
-        ConnectivityManager connectivityManager = getContext().getSystemService(
-                ConnectivityManager.class);
-        NetworkInfo activeNetwork = connectivityManager.getActiveNetworkInfo();
-        return activeNetwork != null && activeNetwork.isConnectedOrConnecting();
-    }
-
-    private boolean hasNotificationSupport() {
-        final PackageManager manager = getContext().getPackageManager();
-        return !manager.hasSystemFeature(PackageManager.FEATURE_LEANBACK)
-                && !manager.hasSystemFeature(PackageManager.FEATURE_EMBEDDED);
-    }
-
-    private void allowSyncAdapterRunInBackgroundAndDataInBackground() throws IOException {
-        // Allow us to run in the background
-        SystemUtil.runShellCommand(InstrumentationRegistry.getInstrumentation(),
-                "cmd deviceidle whitelist +" + getContext().getPackageName());
-        // Allow us to use data in the background
-        SystemUtil.runShellCommand(InstrumentationRegistry.getInstrumentation(),
-                "cmd netpolicy add restrict-background-whitelist " + Process.myUid());
-    }
-
-    private void disallowSyncAdapterRunInBackgroundAndDataInBackground() throws IOException {
-        // Allow us to run in the background
-        SystemUtil.runShellCommand(InstrumentationRegistry.getInstrumentation(),
-                "cmd deviceidle whitelist -" + getContext().getPackageName());
-        // Allow us to use data in the background
-        SystemUtil.runShellCommand(InstrumentationRegistry.getInstrumentation(),
-                "cmd netpolicy remove restrict-background-whitelist " + Process.myUid());
-    }
-}
diff --git a/hostsidetests/content/test-apps/CtsSyncAccountAccessSameCertTests/Android.mk b/hostsidetests/content/test-apps/CtsSyncAccountAccessSameCertTests/Android.mk
deleted file mode 100644
index 0979e96..0000000
--- a/hostsidetests/content/test-apps/CtsSyncAccountAccessSameCertTests/Android.mk
+++ /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.
-#
-
-LOCAL_PATH := $(call my-dir)
-
-include $(CLEAR_VARS)
-
-LOCAL_MODULE_TAGS := tests
-
-LOCAL_STATIC_JAVA_LIBRARIES := \
-    android-support-test \
-    ctstestrunner \
-    ub-uiautomator \
-    compatibility-device-util
-
-LOCAL_SRC_FILES := $(call all-java-files-under, src)
-
-LOCAL_PACKAGE_NAME := CtsSyncAccountAccessSameCertTestCases
-
-LOCAL_COMPATIBILITY_SUITE := cts vts general-tests
-
-LOCAL_PROGUARD_ENABLED := disabled
-
-LOCAL_DEX_PREOPT := false
-
-include $(BUILD_CTS_SUPPORT_PACKAGE)
diff --git a/hostsidetests/content/test-apps/CtsSyncAccountAccessSameCertTests/AndroidManifest.xml b/hostsidetests/content/test-apps/CtsSyncAccountAccessSameCertTests/AndroidManifest.xml
deleted file mode 100644
index 2ecd27d..0000000
--- a/hostsidetests/content/test-apps/CtsSyncAccountAccessSameCertTests/AndroidManifest.xml
+++ /dev/null
@@ -1,41 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!-- 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.
--->
-
-<manifest xmlns:android="http://schemas.android.com/apk/res/android"
-        package="com.android.cts.content">
-
-    <uses-permission android:name="android.permission.ACCESS_NETWORK_STATE"/>
-
-    <application>
-        <uses-library android:name="android.test.runner" />
-
-        <activity android:name=".StubActivity"/>
-
-        <service android:name=".SyncService">
-            <intent-filter>
-                <action android:name="android.content.SyncAdapter"/>
-            </intent-filter>
-            <meta-data android:name="android.content.SyncAdapter"
-                android:resource="@xml/syncadapter" />
-        </service>
-
-    </application>
-
-    <instrumentation
-        android:name="android.support.test.runner.AndroidJUnitRunner"
-        android:targetPackage="com.android.cts.content" />
-
-</manifest>
diff --git a/hostsidetests/content/test-apps/CtsSyncAccountAccessSameCertTests/res/xml/syncadapter.xml b/hostsidetests/content/test-apps/CtsSyncAccountAccessSameCertTests/res/xml/syncadapter.xml
deleted file mode 100644
index f55a19a..0000000
--- a/hostsidetests/content/test-apps/CtsSyncAccountAccessSameCertTests/res/xml/syncadapter.xml
+++ /dev/null
@@ -1,25 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!-- 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.
--->
-
-<sync-adapter
-    xmlns:android="http://schemas.android.com/apk/res/android"
-    android:contentAuthority="com.android.cts.stub.provider"
-    android:accountType="com.stub"
-    android:userVisible="false"
-    android:supportsUploading="false"
-    android:allowParallelSyncs="false"
-    android:isAlwaysSyncable="true">
-</sync-adapter>
diff --git a/hostsidetests/content/test-apps/CtsSyncAccountAccessSameCertTests/src/com/android/cts/content/CtsSyncAccountAccessSameCertTestCases.java b/hostsidetests/content/test-apps/CtsSyncAccountAccessSameCertTests/src/com/android/cts/content/CtsSyncAccountAccessSameCertTestCases.java
deleted file mode 100644
index bfdd072..0000000
--- a/hostsidetests/content/test-apps/CtsSyncAccountAccessSameCertTests/src/com/android/cts/content/CtsSyncAccountAccessSameCertTestCases.java
+++ /dev/null
@@ -1,157 +0,0 @@
-/*
- * Copyright (C) 2016 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.cts.content;
-
-import static junit.framework.Assert.assertTrue;
-
-import android.accounts.Account;
-import android.accounts.AccountManager;
-import android.app.Activity;
-import android.content.ContentProviderClient;
-import android.content.ContentResolver;
-import android.content.Context;
-import android.content.Intent;
-import android.content.SyncRequest;
-import android.content.SyncResult;
-import android.content.cts.FlakyTestRule;
-import android.content.pm.PackageManager;
-import android.net.ConnectivityManager;
-import android.net.NetworkInfo;
-import android.os.Bundle;
-import android.os.Process;
-import android.os.SystemClock;
-import android.support.test.InstrumentationRegistry;
-import android.support.test.runner.AndroidJUnit4;
-import org.junit.After;
-import org.junit.Before;
-import org.junit.Rule;
-import org.junit.Test;
-import org.junit.rules.TestRule;
-import org.junit.runner.RunWith;
-
-import java.io.IOException;
-import java.util.concurrent.CountDownLatch;
-import java.util.concurrent.TimeUnit;
-
-import com.android.compatibility.common.util.SystemUtil;
-
-/**
- * Tests whether a sync adapter can access accounts.
- */
-@RunWith(AndroidJUnit4.class)
-public class CtsSyncAccountAccessSameCertTestCases {
-    private static final long SYNC_TIMEOUT_MILLIS = 20000; // 20 sec
-
-    @Rule
-    public final TestRule mFlakyTestTRule = new FlakyTestRule(3);
-
-    @Before
-    public void setUp() throws Exception {
-        allowSyncAdapterRunInBackgroundAndDataInBackground();
-    }
-
-    @After
-    public void tearDown() throws Exception {
-        disallowSyncAdapterRunInBackgroundAndDataInBackground();
-    }
-
-    @Test
-    public void testAccountAccess_sameCertAsAuthenticatorCanSeeAccount() throws Exception {
-        if (!hasDataConnection() || !hasNotificationSupport()) {
-            return;
-        }
-
-        Intent intent = new Intent(getContext(), StubActivity.class);
-        Activity activity = InstrumentationRegistry.getInstrumentation().startActivitySync(intent);
-
-        AccountManager accountManager = getContext().getSystemService(AccountManager.class);
-        Bundle result = accountManager.addAccount("com.stub", null, null, null, activity,
-                null, null).getResult();
-
-        Account addedAccount = new Account(
-                result.getString(AccountManager.KEY_ACCOUNT_NAME),
-                        result.getString(AccountManager.KEY_ACCOUNT_TYPE));
-
-        waitForSyncManagerAccountChangeUpdate();
-
-        try {
-            CountDownLatch latch = new CountDownLatch(1);
-
-            SyncAdapter.setOnPerformSyncDelegate((Account account, Bundle extras,
-                    String authority, ContentProviderClient provider, SyncResult syncResult)
-                    -> latch.countDown());
-
-            Bundle extras = new Bundle();
-            extras.putBoolean(ContentResolver.SYNC_EXTRAS_DO_NOT_RETRY, true);
-            extras.putBoolean(ContentResolver.SYNC_EXTRAS_PRIORITY, true);
-            extras.getBoolean(ContentResolver.SYNC_EXTRAS_IGNORE_SETTINGS, true);
-            SyncRequest request = new SyncRequest.Builder()
-                    .setSyncAdapter(null, "com.android.cts.stub.provider")
-                    .syncOnce()
-                    .setExtras(extras)
-                    .setExpedited(true)
-                    .setManual(true)
-                    .build();
-            ContentResolver.requestSync(request);
-
-            assertTrue(latch.await(SYNC_TIMEOUT_MILLIS, TimeUnit.MILLISECONDS));
-        } finally {
-            accountManager.removeAccount(addedAccount, activity, null, null);
-            activity.finish();
-        }
-    }
-
-    private Context getContext() {
-        return InstrumentationRegistry.getInstrumentation().getContext();
-    }
-
-    private void waitForSyncManagerAccountChangeUpdate() {
-        // Wait for the sync manager to be notified for the new account.
-        // Unfortunately, there is no way to detect this event, sigh...
-        SystemClock.sleep(SYNC_TIMEOUT_MILLIS);
-    }
-
-    private boolean hasDataConnection() {
-        ConnectivityManager connectivityManager = getContext().getSystemService(
-                ConnectivityManager.class);
-        NetworkInfo activeNetwork = connectivityManager.getActiveNetworkInfo();
-        return activeNetwork != null && activeNetwork.isConnectedOrConnecting();
-    }
-
-    private boolean hasNotificationSupport() {
-        return !getContext().getPackageManager()
-                .hasSystemFeature(PackageManager.FEATURE_LEANBACK);
-    }
-
-    private void allowSyncAdapterRunInBackgroundAndDataInBackground() throws IOException {
-        // Allow us to run in the background
-        SystemUtil.runShellCommand(InstrumentationRegistry.getInstrumentation(),
-                "cmd deviceidle whitelist +" + getContext().getPackageName());
-        // Allow us to use data in the background
-        SystemUtil.runShellCommand(InstrumentationRegistry.getInstrumentation(),
-                "cmd netpolicy add restrict-background-whitelist " + Process.myUid());
-    }
-
-    private void disallowSyncAdapterRunInBackgroundAndDataInBackground() throws IOException {
-        // Allow us to run in the background
-        SystemUtil.runShellCommand(InstrumentationRegistry.getInstrumentation(),
-                "cmd deviceidle whitelist -" + getContext().getPackageName());
-        // Allow us to use data in the background
-        SystemUtil.runShellCommand(InstrumentationRegistry.getInstrumentation(),
-                "cmd netpolicy remove restrict-background-whitelist " + Process.myUid());
-    }
-}
diff --git a/hostsidetests/content/test-apps/CtsSyncAccountAccessSameCertTests/src/com/android/cts/content/FlakyTestRule.java b/hostsidetests/content/test-apps/CtsSyncAccountAccessSameCertTests/src/com/android/cts/content/FlakyTestRule.java
deleted file mode 100644
index e5664f1..0000000
--- a/hostsidetests/content/test-apps/CtsSyncAccountAccessSameCertTests/src/com/android/cts/content/FlakyTestRule.java
+++ /dev/null
@@ -1,52 +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
- */
-
-package android.content.cts;
-
-import org.junit.rules.TestRule;
-import org.junit.runner.Description;
-import org.junit.runners.model.Statement;
-
-/**
- * Rule for running flaky tests that runs the test up to attempt
- * count and if one run succeeds reports the tests as passing.
- */
-// TODO: Move this puppy in a common place, so ppl can use it.
-public class FlakyTestRule implements TestRule {
-    private final int mAttemptCount;
-
-    public FlakyTestRule(int attemptCount) {
-        mAttemptCount = attemptCount;
-    }
-
-    @Override
-    public Statement apply(Statement statement, Description description) {
-        return new Statement() {
-            @Override
-            public void evaluate() throws Throwable {
-                Throwable throwable = null;
-                for (int i = 0; i < mAttemptCount; i++) {
-                    try {
-                        statement.evaluate();
-                        return;
-                    } catch (Throwable t) {
-                        throwable = t;
-                    }
-                }
-                throw throwable;
-            };
-        };
-    }
-}
diff --git a/hostsidetests/content/test-apps/CtsSyncAccountAccessSameCertTests/src/com/android/cts/content/SyncAdapter.java b/hostsidetests/content/test-apps/CtsSyncAccountAccessSameCertTests/src/com/android/cts/content/SyncAdapter.java
deleted file mode 100644
index e93a070..0000000
--- a/hostsidetests/content/test-apps/CtsSyncAccountAccessSameCertTests/src/com/android/cts/content/SyncAdapter.java
+++ /dev/null
@@ -1,57 +0,0 @@
-/*
- * Copyright (C) 2016 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.cts.content;
-
-import android.accounts.Account;
-import android.content.AbstractThreadedSyncAdapter;
-import android.content.ContentProviderClient;
-import android.content.Context;
-import android.content.SyncResult;
-import android.os.Bundle;
-
-public class SyncAdapter extends AbstractThreadedSyncAdapter {
-    private static final Object sLock = new Object();
-
-    private static OnPerformSyncDelegate sOnPerformSyncDelegate;
-
-    public interface OnPerformSyncDelegate {
-        void onPerformSync(Account account, Bundle extras, String authority,
-                ContentProviderClient provider, SyncResult syncResult);
-    }
-
-    public static void setOnPerformSyncDelegate(OnPerformSyncDelegate delegate) {
-        synchronized (sLock) {
-            sOnPerformSyncDelegate = delegate;
-        }
-    }
-
-    public SyncAdapter(Context context, boolean autoInitialize) {
-        super(context, autoInitialize);
-    }
-
-    @Override
-    public void onPerformSync(Account account, Bundle extras, String authority,
-            ContentProviderClient provider, SyncResult syncResult) {
-        OnPerformSyncDelegate delegate;
-        synchronized (sLock) {
-            delegate = sOnPerformSyncDelegate;
-        }
-        if (delegate != null) {
-            delegate.onPerformSync(account, extras, authority, provider, syncResult);
-        }
-    }
-}
diff --git a/hostsidetests/cpptools/AndroidTest.xml b/hostsidetests/cpptools/AndroidTest.xml
index 94a1154..6e60080 100644
--- a/hostsidetests/cpptools/AndroidTest.xml
+++ b/hostsidetests/cpptools/AndroidTest.xml
@@ -14,6 +14,7 @@
      limitations under the License.
 -->
 <configuration description="Config for CTS CPP host test cases">
+    <option name="test-suite-tag" value="cts" />
     <option name="config-descriptor:metadata" key="component" value="devtools" />
     <target_preparer class="com.android.tradefed.targetprep.suite.SuiteApkInstaller">
         <option name="cleanup-apks" value="true" />
diff --git a/hostsidetests/deviceidle/Android.mk b/hostsidetests/deviceidle/Android.mk
new file mode 100644
index 0000000..e73d70f
--- /dev/null
+++ b/hostsidetests/deviceidle/Android.mk
@@ -0,0 +1,32 @@
+# 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 := CtsDeviceIdleHostTestCases
+
+LOCAL_MODULE_TAGS := optional
+
+LOCAL_SRC_FILES := $(call all-java-files-under, src)
+
+LOCAL_JAVA_LIBRARIES := cts-tradefed tradefed compatibility-host-util
+
+LOCAL_CTS_TEST_PACKAGE := android.deviceidle
+
+# tag this module as a cts test artifact
+LOCAL_COMPATIBILITY_SUITE := cts vts general-tests
+
+include $(BUILD_CTS_HOST_JAVA_LIBRARY)
diff --git a/hostsidetests/deviceidle/AndroidTest.xml b/hostsidetests/deviceidle/AndroidTest.xml
new file mode 100644
index 0000000..fecade9
--- /dev/null
+++ b/hostsidetests/deviceidle/AndroidTest.xml
@@ -0,0 +1,23 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2017 The Android Open Source Project
+
+     Licensed under the Apache License, Version 2.0 (the "License");
+     you may not use this file except in compliance with the License.
+     You may obtain a copy of the License at
+
+          http://www.apache.org/licenses/LICENSE-2.0
+
+     Unless required by applicable law or agreed to in writing, software
+     distributed under the License is distributed on an "AS IS" BASIS,
+     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+     See the License for the specific language governing permissions and
+     limitations under the License.
+-->
+<configuration description="Config for the CTS Content host tests">
+    <option name="test-suite-tag" value="cts" />
+    <option name="config-descriptor:metadata" key="component" value="framework" />
+    <test class="com.android.compatibility.common.tradefed.testtype.JarHostTest" >
+        <option name="jar" value="CtsDeviceIdleHostTestCases.jar" />
+        <option name="runtime-hint" value="1m" />
+    </test>
+</configuration>
diff --git a/hostsidetests/deviceidle/src/com/android/cts/deviceidle/DeviceIdleWhitelistTest.java b/hostsidetests/deviceidle/src/com/android/cts/deviceidle/DeviceIdleWhitelistTest.java
new file mode 100644
index 0000000..df16a81
--- /dev/null
+++ b/hostsidetests/deviceidle/src/com/android/cts/deviceidle/DeviceIdleWhitelistTest.java
@@ -0,0 +1,98 @@
+/*
+ * 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.deviceidle;
+
+import com.android.tradefed.build.IBuildInfo;
+import com.android.tradefed.device.DeviceNotAvailableException;
+import com.android.tradefed.log.LogUtil;
+import com.android.tradefed.testtype.DeviceTestCase;
+import com.android.tradefed.testtype.IBuildReceiver;
+
+import org.junit.Assume;
+
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * Tests that it is possible to remove apps from the system whitelist
+ */
+public class DeviceIdleWhitelistTest extends DeviceTestCase implements IBuildReceiver {
+
+    private static final String DEVICE_IDLE_COMMAND_PREFIX = "cmd deviceidle sys-whitelist ";
+    private static final String RESET_SYS_WHITELIST_COMMAND = "cmd deviceidle sys-whitelist reset";
+    private static final String SHOW_SYS_WHITELIST_COMMAND = DEVICE_IDLE_COMMAND_PREFIX;
+
+    private List<String> mOriginalSystemWhitelist;
+    protected IBuildInfo mCtsBuild;
+
+    @Override
+    public void setBuild(IBuildInfo buildInfo) {
+        mCtsBuild = buildInfo;
+    }
+
+    @Override
+    protected void setUp() throws Exception {
+        super.setUp();
+        getDevice().executeShellCommand(RESET_SYS_WHITELIST_COMMAND);
+        mOriginalSystemWhitelist = getSystemWhitelist();
+        if (mOriginalSystemWhitelist.size() < 1) {
+            LogUtil.CLog.w("No packages found in system whitelist");
+            Assume.assumeTrue(false);
+        }
+    }
+
+    @Override
+    protected void tearDown() throws Exception {
+        super.tearDown();
+        getDevice().executeShellCommand(RESET_SYS_WHITELIST_COMMAND);
+    }
+
+    public void testRemoveFromSysWhitelist() throws Exception {
+        final String packageToRemove = mOriginalSystemWhitelist.get(0);
+        getDevice().executeShellCommand(DEVICE_IDLE_COMMAND_PREFIX + "-" + packageToRemove);
+        final List<String> newWhitelist = getSystemWhitelist();
+        assertFalse("Package " + packageToRemove + " not removed from whitelist",
+                newWhitelist.contains(packageToRemove));
+    }
+
+    public void testRemovesPersistedAcrossReboots() throws Exception {
+        for (int i = 0; i < mOriginalSystemWhitelist.size(); i+=2) {
+            // remove odd indexed packages from the whitelist
+            getDevice().executeShellCommand(
+                    DEVICE_IDLE_COMMAND_PREFIX + "-" + mOriginalSystemWhitelist.get(i));
+        }
+        final List<String> whitelistBeforeReboot = getSystemWhitelist();
+        Thread.sleep(10_000); // write to disk happens after 5 seconds
+        getDevice().reboot();
+        Thread.sleep(5_000); // to make sure service is initialized
+        final List<String> whitelistAfterReboot = getSystemWhitelist();
+        assertEquals(whitelistBeforeReboot.size(), whitelistAfterReboot.size());
+        for (int i = 0; i < whitelistBeforeReboot.size(); i++) {
+            assertTrue(whitelistAfterReboot.contains(whitelistBeforeReboot.get(i)));
+        }
+    }
+
+    private List<String> getSystemWhitelist() throws DeviceNotAvailableException {
+        final String output = getDevice().executeShellCommand(SHOW_SYS_WHITELIST_COMMAND).trim();
+        final List<String> packages = new ArrayList<>();
+        for (String line : output.split("\n")) {
+            final int i = line.indexOf(',');
+            packages.add(line.substring(0, i));
+        }
+        return packages;
+    }
+}
diff --git a/hostsidetests/devicepolicy/Android.mk b/hostsidetests/devicepolicy/Android.mk
index 9cbd49e..95af5e7 100644
--- a/hostsidetests/devicepolicy/Android.mk
+++ b/hostsidetests/devicepolicy/Android.mk
@@ -27,7 +27,7 @@
 LOCAL_CTS_TEST_PACKAGE := android.adminhostside
 
 # tag this module as a cts test artifact
-LOCAL_COMPATIBILITY_SUITE := cts vts general-tests
+LOCAL_COMPATIBILITY_SUITE := cts arcts vts general-tests
 
 include $(BUILD_CTS_HOST_JAVA_LIBRARY)
 
diff --git a/hostsidetests/devicepolicy/AndroidTest.xml b/hostsidetests/devicepolicy/AndroidTest.xml
index 51711dd..6fc38c8 100644
--- a/hostsidetests/devicepolicy/AndroidTest.xml
+++ b/hostsidetests/devicepolicy/AndroidTest.xml
@@ -14,6 +14,7 @@
      limitations under the License.
 -->
 <configuration description="Config for the CTS Device Policy host tests">
+    <option name="test-suite-tag" value="cts" />
     <option name="config-descriptor:metadata" key="component" value="framework" />
 
     <!-- Push the list of public APIs to device -->
diff --git a/hostsidetests/devicepolicy/app/AccountCheck/Auth/Android.mk b/hostsidetests/devicepolicy/app/AccountCheck/Auth/Android.mk
index 0873334..2943695 100644
--- a/hostsidetests/devicepolicy/app/AccountCheck/Auth/Android.mk
+++ b/hostsidetests/devicepolicy/app/AccountCheck/Auth/Android.mk
@@ -31,10 +31,9 @@
     android-support-v4  \
     ctstestrunner  \
     ub-uiautomator  \
-    android-support-test \
-    legacy-android-test
+    android-support-test
 
-LOCAL_JAVA_LIBRARIES := legacy-android-test
+LOCAL_JAVA_LIBRARIES := android.test.base.stubs
 
 LOCAL_SDK_VERSION := test_current
 
diff --git a/hostsidetests/devicepolicy/app/AccountCheck/Auth/AndroidManifest.xml b/hostsidetests/devicepolicy/app/AccountCheck/Auth/AndroidManifest.xml
index 1f488e5..97659b3 100644
--- a/hostsidetests/devicepolicy/app/AccountCheck/Auth/AndroidManifest.xml
+++ b/hostsidetests/devicepolicy/app/AccountCheck/Auth/AndroidManifest.xml
@@ -27,6 +27,8 @@
     <uses-permission android:name="android.permission.MANAGE_ACCOUNTS" />
 
     <application>
+        <uses-library android:name="android.test.runner" />
+
         <service android:name="com.android.cts.devicepolicy.accountcheck.TestAuthenticator"
             android:exported="true">
             <intent-filter>
diff --git a/hostsidetests/devicepolicy/app/AccountManagement/Android.mk b/hostsidetests/devicepolicy/app/AccountManagement/Android.mk
index b7d99bf..4b59d84 100644
--- a/hostsidetests/devicepolicy/app/AccountManagement/Android.mk
+++ b/hostsidetests/devicepolicy/app/AccountManagement/Android.mk
@@ -31,8 +31,9 @@
     android-support-v4 \
     ctstestrunner \
     ub-uiautomator \
-    android-support-test \
-    legacy-android-test
+    android-support-test
+
+LOCAL_JAVA_LIBRARIES := android.test.base.stubs
 
 LOCAL_SDK_VERSION := current
 
diff --git a/hostsidetests/devicepolicy/app/AccountManagement/AndroidManifest.xml b/hostsidetests/devicepolicy/app/AccountManagement/AndroidManifest.xml
index b1e1b53..9c31a62 100644
--- a/hostsidetests/devicepolicy/app/AccountManagement/AndroidManifest.xml
+++ b/hostsidetests/devicepolicy/app/AccountManagement/AndroidManifest.xml
@@ -22,6 +22,8 @@
     <uses-permission android:name="android.permission.MANAGE_ACCOUNTS" />
 
     <application>
+        <uses-library android:name="android.test.runner" />
+
         <service android:name=".MockAccountService" android:exported="true">
             <intent-filter>
                 <action android:name="android.accounts.AccountAuthenticator" />
diff --git a/hostsidetests/devicepolicy/app/Assistant/Android.mk b/hostsidetests/devicepolicy/app/Assistant/Android.mk
index 86144a2..308c0a1 100644
--- a/hostsidetests/devicepolicy/app/Assistant/Android.mk
+++ b/hostsidetests/devicepolicy/app/Assistant/Android.mk
@@ -29,7 +29,11 @@
 
 LOCAL_PACKAGE_NAME := CtsDevicePolicyAssistApp
 
-LOCAL_STATIC_JAVA_LIBRARIES = android-support-v4 compatibility-device-util android-support-test
+LOCAL_STATIC_JAVA_LIBRARIES := \
+    android-support-v4 \
+    compatibility-device-util \
+    android-support-test \
+
 
 LOCAL_SDK_VERSION := current
 
diff --git a/hostsidetests/devicepolicy/app/Assistant/AndroidManifest.xml b/hostsidetests/devicepolicy/app/Assistant/AndroidManifest.xml
index 5fc20de..17ca642 100644
--- a/hostsidetests/devicepolicy/app/Assistant/AndroidManifest.xml
+++ b/hostsidetests/devicepolicy/app/Assistant/AndroidManifest.xml
@@ -19,6 +19,8 @@
     package="com.android.cts.devicepolicy.assistapp" >
 
     <application>
+        <uses-library android:name="android.test.runner" />
+
 
         <service android:name=".MyInteractionService"
                 android:label="CTS test voice interaction service"
@@ -45,4 +47,4 @@
             android:targetPackage="com.android.cts.devicepolicy.assistapp"
             android:label="Assistant related device policy CTS" />
 
-</manifest>
\ No newline at end of file
+</manifest>
diff --git a/hostsidetests/devicepolicy/app/AutofillApp/AndroidManifest.xml b/hostsidetests/devicepolicy/app/AutofillApp/AndroidManifest.xml
index cf6b6e8..711f984 100644
--- a/hostsidetests/devicepolicy/app/AutofillApp/AndroidManifest.xml
+++ b/hostsidetests/devicepolicy/app/AutofillApp/AndroidManifest.xml
@@ -28,7 +28,7 @@
 
         <service
             android:name=".SimpleAutofillService"
-            android:permission="android.permission.BIND_AUTOFILL" >
+            android:permission="android.permission.BIND_AUTOFILL_SERVICE" >
             <intent-filter>
                 <action android:name="android.service.autofill.AutofillService" />
             </intent-filter>
diff --git a/hostsidetests/devicepolicy/app/CorpOwnedManagedProfile/Android.mk b/hostsidetests/devicepolicy/app/CorpOwnedManagedProfile/Android.mk
index c8872b4..8af6492 100644
--- a/hostsidetests/devicepolicy/app/CorpOwnedManagedProfile/Android.mk
+++ b/hostsidetests/devicepolicy/app/CorpOwnedManagedProfile/Android.mk
@@ -32,7 +32,7 @@
 
 LOCAL_AIDL_INCLUDES := $(LOCAL_PATH)/src
 
-LOCAL_JAVA_LIBRARIES := android.test.runner cts-junit
+LOCAL_JAVA_LIBRARIES := android.test.runner cts-junit android.test.base.stubs
 
 LOCAL_STATIC_JAVA_LIBRARIES := ctstestrunner compatibility-device-util
 
@@ -57,7 +57,7 @@
 
 LOCAL_AIDL_INCLUDES := $(LOCAL_PATH)/src
 
-LOCAL_JAVA_LIBRARIES := android.test.runner cts-junit
+LOCAL_JAVA_LIBRARIES := android.test.runner cts-junit android.test.base.stubs
 
 LOCAL_STATIC_JAVA_LIBRARIES := ctstestrunner compatibility-device-util
 
diff --git a/hostsidetests/devicepolicy/app/CorpOwnedManagedProfile/src/com/android/cts/comp/ICrossUserService.aidl b/hostsidetests/devicepolicy/app/CorpOwnedManagedProfile/src/com/android/cts/comp/ICrossUserService.aidl
index bc8d3d3..938ee07 100644
--- a/hostsidetests/devicepolicy/app/CorpOwnedManagedProfile/src/com/android/cts/comp/ICrossUserService.aidl
+++ b/hostsidetests/devicepolicy/app/CorpOwnedManagedProfile/src/com/android/cts/comp/ICrossUserService.aidl
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2016 The Android Open Source Project
+ * 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.
diff --git a/hostsidetests/devicepolicy/app/CrossProfileAppsTest/Android.mk b/hostsidetests/devicepolicy/app/CrossProfileAppsTest/Android.mk
new file mode 100644
index 0000000..dd20c9a
--- /dev/null
+++ b/hostsidetests/devicepolicy/app/CrossProfileAppsTest/Android.mk
@@ -0,0 +1,41 @@
+# 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_PACKAGE_NAME := CtsCrossProfileAppsTests
+
+LOCAL_MODULE_TAGS := optional
+
+LOCAL_MODULE_PATH := $(TARGET_OUT_DATA_APPS)
+
+LOCAL_SRC_FILES := $(call all-java-files-under, src)
+
+LOCAL_JAVA_LIBRARIES := cts-junit
+
+LOCAL_STATIC_JAVA_LIBRARIES = \
+	android-support-v4 \
+	ctstestrunner \
+	android-support-test \
+	truth-prebuilt \
+	ub-uiautomator
+
+LOCAL_SDK_VERSION := current
+
+# tag this module as a cts test artifact
+LOCAL_COMPATIBILITY_SUITE := cts vts general-tests
+
+include $(BUILD_CTS_PACKAGE)
diff --git a/hostsidetests/devicepolicy/app/CrossProfileAppsTest/AndroidManifest.xml b/hostsidetests/devicepolicy/app/CrossProfileAppsTest/AndroidManifest.xml
new file mode 100644
index 0000000..79093d6
--- /dev/null
+++ b/hostsidetests/devicepolicy/app/CrossProfileAppsTest/AndroidManifest.xml
@@ -0,0 +1,46 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2017 The Android Open Source Project
+
+     Licensed under the Apache License, Version 2.0 (the "License");
+     you may not use this file except in compliance with the License.
+     You may obtain a copy of the License at
+
+          http://www.apache.org/licenses/LICENSE-2.0
+
+     Unless required by applicable law or agreed to in writing, software
+     distributed under the License is distributed on an "AS IS" BASIS,
+     WITHOUT 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.crossprofileappstest">
+
+    <uses-sdk android:minSdkVersion="21" android:targetSdkVersion="25"/>
+
+    <application>
+        <uses-library android:name="android.test.runner" />
+        <activity android:name=".MainActivity" android:exported="true">
+            <intent-filter>
+                <action android:name="android.intent.action.MAIN"/>
+                <category android:name="android.intent.category.LAUNCHER"/>
+                <category android:name="android.intent.category.DEFAULT"/>
+            </intent-filter>
+        </activity>
+
+        <activity android:name=".NonMainActivity" android:exported="true"/>
+
+        <activity android:name=".NonExportedActivity" android:exported="false">
+            <intent-filter>
+                <action android:name="android.intent.action.MAIN"/>
+                <category android:name="android.intent.category.LAUNCHER"/>
+                <category android:name="android.intent.category.DEFAULT"/>
+            </intent-filter>
+        </activity>
+    </application>
+
+    <instrumentation android:name="android.support.test.runner.AndroidJUnitRunner"
+                     android:targetPackage="com.android.cts.crossprofileappstest"
+                     android:label="Launcher Apps CTS Tests"/>
+</manifest>
diff --git a/hostsidetests/devicepolicy/app/CrossProfileAppsTest/res/layout/main.xml b/hostsidetests/devicepolicy/app/CrossProfileAppsTest/res/layout/main.xml
new file mode 100644
index 0000000..877d890
--- /dev/null
+++ b/hostsidetests/devicepolicy/app/CrossProfileAppsTest/res/layout/main.xml
@@ -0,0 +1,27 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2017 The Android Open Source Project
+
+     Licensed under the Apache License, Version 2.0 (the "License");
+     you may not use this file except in compliance with the License.
+     You may obtain a copy of the License at
+
+          http://www.apache.org/licenses/LICENSE-2.0
+
+     Unless required by applicable law or agreed to in writing, software
+     distributed under the License is distributed on an "AS IS" BASIS,
+     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+     See the License for the specific language governing permissions and
+     limitations under the License.
+-->
+<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
+              android:orientation="vertical"
+              android:layout_width="match_parent"
+              android:layout_height="match_parent">
+
+    <TextView
+        android:id="@+id/user_textview"
+        android:layout_width="match_parent"
+        android:layout_height="wrap_content"
+    />
+
+</LinearLayout>
\ No newline at end of file
diff --git a/hostsidetests/devicepolicy/app/CrossProfileAppsTest/src/com/android/cts/crossprofileappstest/CrossProfileAppsNonTargetUserTest.java b/hostsidetests/devicepolicy/app/CrossProfileAppsTest/src/com/android/cts/crossprofileappstest/CrossProfileAppsNonTargetUserTest.java
new file mode 100644
index 0000000..79da49b
--- /dev/null
+++ b/hostsidetests/devicepolicy/app/CrossProfileAppsTest/src/com/android/cts/crossprofileappstest/CrossProfileAppsNonTargetUserTest.java
@@ -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 com.android.cts.crossprofileappstest;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import static junit.framework.Assert.assertNotNull;
+
+import android.content.Context;
+import android.content.pm.crossprofile.CrossProfileApps;
+import android.os.Bundle;
+import android.os.UserHandle;
+import android.os.UserManager;
+import android.support.test.InstrumentationRegistry;
+import android.support.test.runner.AndroidJUnit4;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+import java.util.List;
+
+/**
+ * Test that runs {@link CrossProfileApps} APIs against non-valid target user.
+ */
+@RunWith(AndroidJUnit4.class)
+public class CrossProfileAppsNonTargetUserTest {
+    private static final String PARAM_TARGET_USER = "TARGET_USER";
+
+    private CrossProfileApps mCrossProfileApps;
+    private UserHandle mTargetUser;
+    private Context mContext;
+
+    @Before
+    public void setupCrossProfileApps() throws Exception {
+        mContext = InstrumentationRegistry.getContext();
+        mCrossProfileApps = mContext.getSystemService(CrossProfileApps.class);
+    }
+
+    @Before
+    public void readTargetUser() {
+        Context context = InstrumentationRegistry.getContext();
+        Bundle arguments = InstrumentationRegistry.getArguments();
+        UserManager userManager = context.getSystemService(UserManager.class);
+        final int userSn = Integer.parseInt(arguments.getString(PARAM_TARGET_USER));
+        mTargetUser = userManager.getUserForSerialNumber(userSn);
+    }
+
+    @Test
+    public void testTargetUserIsNotInGetTargetProfiles() {
+        List<UserHandle> targetProfiles = mCrossProfileApps.getTargetUserProfiles();
+        assertThat(targetProfiles).doesNotContain(mTargetUser);
+    }
+
+    @Test(expected = SecurityException.class)
+    public void testCannotStartActivity() {
+        mCrossProfileApps.startMainActivity(
+                MainActivity.getComponentName(mContext), mTargetUser);
+    }
+}
+
diff --git a/hostsidetests/devicepolicy/app/CrossProfileAppsTest/src/com/android/cts/crossprofileappstest/CrossProfileAppsTargetUserTest.java b/hostsidetests/devicepolicy/app/CrossProfileAppsTest/src/com/android/cts/crossprofileappstest/CrossProfileAppsTargetUserTest.java
new file mode 100644
index 0000000..6b518b4
--- /dev/null
+++ b/hostsidetests/devicepolicy/app/CrossProfileAppsTest/src/com/android/cts/crossprofileappstest/CrossProfileAppsTargetUserTest.java
@@ -0,0 +1,126 @@
+/*
+ * 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.crossprofileappstest;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import static junit.framework.Assert.assertNotNull;
+
+import static org.junit.Assert.assertEquals;
+
+import android.content.Context;
+import android.content.pm.crossprofile.CrossProfileApps;
+import android.os.Bundle;
+import android.os.UserHandle;
+import android.os.UserManager;
+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.UiObject2;
+import android.support.test.uiautomator.Until;
+
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+import java.util.List;
+import java.util.concurrent.TimeUnit;
+
+/**
+ * Test that runs {@link CrossProfileApps} APIs against valid target user.
+ */
+@RunWith(AndroidJUnit4.class)
+public class CrossProfileAppsTargetUserTest {
+    private static final String PARAM_TARGET_USER = "TARGET_USER";
+    private static final String ID_USER_TEXTVIEW =
+            "com.android.cts.crossprofileappstest:id/user_textview";
+    private static final long TIMEOUT_WAIT_UI = TimeUnit.SECONDS.toMillis(10);
+
+    private CrossProfileApps mCrossProfileApps;
+    private UserHandle mTargetUser;
+    private Context mContext;
+    private UiDevice mDevice;
+    private long mUserSerialNumber;
+
+    @Before
+    public void setupCrossProfileApps() {
+        mContext = InstrumentationRegistry.getContext();
+        mCrossProfileApps = mContext.getSystemService(CrossProfileApps.class);
+    }
+
+    @Before
+    public void wakeupDeviceAndPressHome() throws Exception {
+        mDevice = UiDevice.getInstance(InstrumentationRegistry.getInstrumentation());
+        mDevice.wakeUp();
+        mDevice.pressMenu();
+        mDevice.pressHome();
+    }
+
+    @Before
+    public void readTargetUser() {
+        Context context = InstrumentationRegistry.getContext();
+        Bundle arguments = InstrumentationRegistry.getArguments();
+        UserManager userManager = context.getSystemService(UserManager.class);
+        mUserSerialNumber = Long.parseLong(arguments.getString(PARAM_TARGET_USER));
+        mTargetUser = userManager.getUserForSerialNumber(mUserSerialNumber);
+        assertNotNull(mTargetUser);
+    }
+
+    @After
+    public void pressHome() {
+        mDevice.pressHome();
+    }
+
+    @Test
+    public void testTargetUserIsIngetTargetUserProfiles() {
+        List<UserHandle> targetProfiles = mCrossProfileApps.getTargetUserProfiles();
+        assertThat(targetProfiles).contains(mTargetUser);
+    }
+
+    /**
+     * Verify we succeed to start the activity in another profile by checking UI element.
+     */
+    @Test
+    public void testCanStartMainActivity() throws Exception {
+        mCrossProfileApps.startMainActivity(
+                MainActivity.getComponentName(mContext), mTargetUser);
+
+        // Look for the text view to verify that MainActivity is started.
+        UiObject2 textView = mDevice.wait(
+                Until.findObject(By.res(ID_USER_TEXTVIEW)),
+                TIMEOUT_WAIT_UI);
+        assertNotNull("Failed to start activity in target user", textView);
+        // Look for the text in textview, it should be the serial number of target user.
+        assertEquals("Activity is started in wrong user",
+                String.valueOf(mUserSerialNumber),
+                textView.getText());
+    }
+
+    @Test(expected = SecurityException.class)
+    public void testCannotStartNotExportedActivity() throws Exception {
+        mCrossProfileApps.startMainActivity(
+                NonExportedActivity.getComponentName(mContext), mTargetUser);
+    }
+
+    @Test(expected = SecurityException.class)
+    public void testCannotStartNonMainActivity() throws Exception {
+        mCrossProfileApps.startMainActivity(
+                NonExportedActivity.getComponentName(mContext), mTargetUser);
+    }
+}
+
diff --git a/hostsidetests/devicepolicy/app/CrossProfileAppsTest/src/com/android/cts/crossprofileappstest/MainActivity.java b/hostsidetests/devicepolicy/app/CrossProfileAppsTest/src/com/android/cts/crossprofileappstest/MainActivity.java
new file mode 100644
index 0000000..c137e80
--- /dev/null
+++ b/hostsidetests/devicepolicy/app/CrossProfileAppsTest/src/com/android/cts/crossprofileappstest/MainActivity.java
@@ -0,0 +1,56 @@
+/*
+ * 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.crossprofileappstest;
+
+import android.app.Activity;
+import android.content.ComponentName;
+import android.content.Context;
+import android.os.Bundle;
+import android.os.Process;
+import android.os.UserManager;
+import android.util.Log;
+import android.widget.TextView;
+
+import java.lang.Override;
+
+/**
+ * An dummy activity that displays the serial number of the user that it is running into.
+ */
+public class MainActivity extends Activity {
+
+    @Override
+    public void onCreate(Bundle icicle) {
+        super.onCreate(icicle);
+        setContentView(R.layout.main);
+    }
+
+    @Override
+    public void onStart() {
+        super.onStart();
+        TextView textView = findViewById(R.id.user_textview);
+        textView.setText(Long.toString(getCurrentUserSerialNumber()));
+    }
+
+    public static ComponentName getComponentName(Context context) {
+        return new ComponentName(context, MainActivity.class);
+    }
+
+    private long getCurrentUserSerialNumber() {
+        UserManager userManager = getSystemService(UserManager.class);
+        return userManager.getSerialNumberForUser(Process.myUserHandle());
+    }
+}
diff --git a/hostsidetests/devicepolicy/app/CrossProfileAppsTest/src/com/android/cts/crossprofileappstest/NonExportedActivity.java b/hostsidetests/devicepolicy/app/CrossProfileAppsTest/src/com/android/cts/crossprofileappstest/NonExportedActivity.java
new file mode 100644
index 0000000..0876694
--- /dev/null
+++ b/hostsidetests/devicepolicy/app/CrossProfileAppsTest/src/com/android/cts/crossprofileappstest/NonExportedActivity.java
@@ -0,0 +1,27 @@
+/*
+ * 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.crossprofileappstest;
+
+import android.app.Activity;
+import android.content.ComponentName;
+import android.content.Context;
+
+public class NonExportedActivity extends Activity {
+
+    public static ComponentName getComponentName(Context context ){
+        return new ComponentName(context, NonExportedActivity.class);
+    }
+}
diff --git a/hostsidetests/devicepolicy/app/CrossProfileAppsTest/src/com/android/cts/crossprofileappstest/NonMainActivity.java b/hostsidetests/devicepolicy/app/CrossProfileAppsTest/src/com/android/cts/crossprofileappstest/NonMainActivity.java
new file mode 100644
index 0000000..56ec466
--- /dev/null
+++ b/hostsidetests/devicepolicy/app/CrossProfileAppsTest/src/com/android/cts/crossprofileappstest/NonMainActivity.java
@@ -0,0 +1,27 @@
+/*
+ * 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.crossprofileappstest;
+
+import android.app.Activity;
+import android.content.ComponentName;
+import android.content.Context;
+
+public class NonMainActivity extends Activity {
+
+    public static ComponentName getComponentName(Context context) {
+        return new ComponentName(context, NonMainActivity.class);
+    }
+}
diff --git a/hostsidetests/devicepolicy/app/CustomizationApp/Android.mk b/hostsidetests/devicepolicy/app/CustomizationApp/Android.mk
index 1e6e2f1..2350e24 100644
--- a/hostsidetests/devicepolicy/app/CustomizationApp/Android.mk
+++ b/hostsidetests/devicepolicy/app/CustomizationApp/Android.mk
@@ -26,8 +26,12 @@
 
 LOCAL_SRC_FILES := $(call all-java-files-under, src)
 
-LOCAL_STATIC_JAVA_LIBRARIES := android-support-test compatibility-device-util
+LOCAL_STATIC_JAVA_LIBRARIES := \
+    android-support-test \
+    compatibility-device-util \
+
+LOCAL_JAVA_LIBRARIES := android.test.base.stubs
 
 LOCAL_SDK_VERSION := current
 
-include $(BUILD_CTS_PACKAGE)
\ No newline at end of file
+include $(BUILD_CTS_PACKAGE)
diff --git a/hostsidetests/devicepolicy/app/CustomizationApp/AndroidManifest.xml b/hostsidetests/devicepolicy/app/CustomizationApp/AndroidManifest.xml
index 4b20829..be6249f 100644
--- a/hostsidetests/devicepolicy/app/CustomizationApp/AndroidManifest.xml
+++ b/hostsidetests/devicepolicy/app/CustomizationApp/AndroidManifest.xml
@@ -23,6 +23,7 @@
     <uses-permission android:name="android.permission.SET_WALLPAPER" />
 
     <application>
+        <uses-library android:name="android.test.runner" />
     </application>
 
     <instrumentation
diff --git a/hostsidetests/devicepolicy/app/DelegateApp/Android.mk b/hostsidetests/devicepolicy/app/DelegateApp/Android.mk
index a46eed8..e7e986d 100644
--- a/hostsidetests/devicepolicy/app/DelegateApp/Android.mk
+++ b/hostsidetests/devicepolicy/app/DelegateApp/Android.mk
@@ -24,13 +24,12 @@
 
 LOCAL_SRC_FILES := $(call all-java-files-under, src)
 
-LOCAL_JAVA_LIBRARIES := android.test.runner cts-junit
+LOCAL_JAVA_LIBRARIES := android.test.runner cts-junit android.test.base.stubs
 
 LOCAL_STATIC_JAVA_LIBRARIES = \
     android-support-v4 \
     ctstestrunner \
-    android-support-test \
-    legacy-android-test
+    android-support-test
 
 LOCAL_SDK_VERSION := current
 
diff --git a/hostsidetests/devicepolicy/app/DeviceAdmin/api23/Android.mk b/hostsidetests/devicepolicy/app/DeviceAdmin/api23/Android.mk
index a8f4f05..5cf7f05 100644
--- a/hostsidetests/devicepolicy/app/DeviceAdmin/api23/Android.mk
+++ b/hostsidetests/devicepolicy/app/DeviceAdmin/api23/Android.mk
@@ -28,10 +28,9 @@
 
 LOCAL_STATIC_JAVA_LIBRARIES := \
     ctstestrunner \
-    compatibility-device-util \
-    legacy-android-test
+    compatibility-device-util
 
-LOCAL_JAVA_LIBRARIES := legacy-android-test
+LOCAL_JAVA_LIBRARIES := android.test.runner.stubs android.test.base.stubs
 
 LOCAL_SDK_VERSION := current
 
diff --git a/hostsidetests/devicepolicy/app/DeviceAdmin/api24/Android.mk b/hostsidetests/devicepolicy/app/DeviceAdmin/api24/Android.mk
index 4e2cfb6..bdbbf89 100644
--- a/hostsidetests/devicepolicy/app/DeviceAdmin/api24/Android.mk
+++ b/hostsidetests/devicepolicy/app/DeviceAdmin/api24/Android.mk
@@ -28,10 +28,9 @@
 
 LOCAL_STATIC_JAVA_LIBRARIES := \
     ctstestrunner \
-    compatibility-device-util \
-    legacy-android-test
+    compatibility-device-util
 
-LOCAL_JAVA_LIBRARIES := legacy-android-test
+LOCAL_JAVA_LIBRARIES := android.test.runner.stubs android.test.base.stubs
 
 LOCAL_SDK_VERSION := current
 
diff --git a/hostsidetests/devicepolicy/app/DeviceAdminService/package1/Android.mk b/hostsidetests/devicepolicy/app/DeviceAdminService/package1/Android.mk
index 72d2bb0..55cf2b3 100644
--- a/hostsidetests/devicepolicy/app/DeviceAdminService/package1/Android.mk
+++ b/hostsidetests/devicepolicy/app/DeviceAdminService/package1/Android.mk
@@ -28,6 +28,8 @@
 
 LOCAL_STATIC_JAVA_LIBRARIES := ctstestrunner compatibility-device-util
 
+LOCAL_JAVA_LIBRARIES := android.test.base.stubs
+
 LOCAL_SDK_VERSION := current
 
 # tag this module as a cts test artifact
diff --git a/hostsidetests/devicepolicy/app/DeviceAdminService/package2/Android.mk b/hostsidetests/devicepolicy/app/DeviceAdminService/package2/Android.mk
index e2f9b8d..8725fb7 100644
--- a/hostsidetests/devicepolicy/app/DeviceAdminService/package2/Android.mk
+++ b/hostsidetests/devicepolicy/app/DeviceAdminService/package2/Android.mk
@@ -28,6 +28,8 @@
 
 LOCAL_STATIC_JAVA_LIBRARIES := ctstestrunner compatibility-device-util
 
+LOCAL_JAVA_LIBRARIES := android.test.base.stubs
+
 LOCAL_SDK_VERSION := current
 
 # tag this module as a cts test artifact
diff --git a/hostsidetests/devicepolicy/app/DeviceAdminService/package3/Android.mk b/hostsidetests/devicepolicy/app/DeviceAdminService/package3/Android.mk
index b88d537..fdc7e7a 100644
--- a/hostsidetests/devicepolicy/app/DeviceAdminService/package3/Android.mk
+++ b/hostsidetests/devicepolicy/app/DeviceAdminService/package3/Android.mk
@@ -28,6 +28,8 @@
 
 LOCAL_STATIC_JAVA_LIBRARIES := ctstestrunner compatibility-device-util
 
+LOCAL_JAVA_LIBRARIES := android.test.base.stubs
+
 LOCAL_SDK_VERSION := current
 
 # tag this module as a cts test artifact
diff --git a/hostsidetests/devicepolicy/app/DeviceAdminService/package4/Android.mk b/hostsidetests/devicepolicy/app/DeviceAdminService/package4/Android.mk
index 7f72ddf..02bf1e5 100644
--- a/hostsidetests/devicepolicy/app/DeviceAdminService/package4/Android.mk
+++ b/hostsidetests/devicepolicy/app/DeviceAdminService/package4/Android.mk
@@ -28,6 +28,8 @@
 
 LOCAL_STATIC_JAVA_LIBRARIES := ctstestrunner compatibility-device-util
 
+LOCAL_JAVA_LIBRARIES := android.test.base.stubs
+
 LOCAL_SDK_VERSION := current
 
 # tag this module as a cts test artifact
diff --git a/hostsidetests/devicepolicy/app/DeviceAdminService/packageb/Android.mk b/hostsidetests/devicepolicy/app/DeviceAdminService/packageb/Android.mk
index 8c66638..cf21905 100644
--- a/hostsidetests/devicepolicy/app/DeviceAdminService/packageb/Android.mk
+++ b/hostsidetests/devicepolicy/app/DeviceAdminService/packageb/Android.mk
@@ -28,6 +28,8 @@
 
 LOCAL_STATIC_JAVA_LIBRARIES := ctstestrunner compatibility-device-util
 
+LOCAL_JAVA_LIBRARIES := android.test.base.stubs
+
 LOCAL_SDK_VERSION := current
 
 # tag this module as a cts test artifact
diff --git a/hostsidetests/devicepolicy/app/DeviceAndProfileOwner/api23/Android.mk b/hostsidetests/devicepolicy/app/DeviceAndProfileOwner/api23/Android.mk
index c818856..71aeff6 100644
--- a/hostsidetests/devicepolicy/app/DeviceAndProfileOwner/api23/Android.mk
+++ b/hostsidetests/devicepolicy/app/DeviceAndProfileOwner/api23/Android.mk
@@ -24,7 +24,11 @@
 
 LOCAL_SRC_FILES := $(call all-java-files-under, ../src)
 
-LOCAL_JAVA_LIBRARIES = conscrypt legacy-android-test
+LOCAL_JAVA_LIBRARIES := \
+    conscrypt \
+    android.test.runner.stubs \
+    android.test.base.stubs \
+
 
 LOCAL_STATIC_JAVA_LIBRARIES = android-support-v4 compatibility-device-util ctstestrunner ub-uiautomator
 
diff --git a/hostsidetests/devicepolicy/app/DeviceAndProfileOwner/api25/Android.mk b/hostsidetests/devicepolicy/app/DeviceAndProfileOwner/api25/Android.mk
index 1c50763..6e6227c 100644
--- a/hostsidetests/devicepolicy/app/DeviceAndProfileOwner/api25/Android.mk
+++ b/hostsidetests/devicepolicy/app/DeviceAndProfileOwner/api25/Android.mk
@@ -24,7 +24,11 @@
 
 LOCAL_SRC_FILES := $(call all-java-files-under, ../src)
 
-LOCAL_JAVA_LIBRARIES = conscrypt legacy-android-test
+LOCAL_JAVA_LIBRARIES := \
+    conscrypt \
+    android.test.runner.stubs \
+    android.test.base.stubs \
+
 
 LOCAL_STATIC_JAVA_LIBRARIES = android-support-v4 compatibility-device-util ctstestrunner ub-uiautomator
 
diff --git a/hostsidetests/devicepolicy/app/DeviceAndProfileOwner/latest/Android.mk b/hostsidetests/devicepolicy/app/DeviceAndProfileOwner/latest/Android.mk
index 81a23d2..070c282 100644
--- a/hostsidetests/devicepolicy/app/DeviceAndProfileOwner/latest/Android.mk
+++ b/hostsidetests/devicepolicy/app/DeviceAndProfileOwner/latest/Android.mk
@@ -24,7 +24,11 @@
 
 LOCAL_SRC_FILES := $(call all-java-files-under, ../src)
 
-LOCAL_JAVA_LIBRARIES = conscrypt
+LOCAL_JAVA_LIBRARIES := \
+    conscrypt \
+    android.test.runner.stubs \
+    android.test.base.stubs \
+
 
 LOCAL_STATIC_JAVA_LIBRARIES = android-support-v4 compatibility-device-util ctstestrunner ub-uiautomator
 
diff --git a/hostsidetests/devicepolicy/app/DeviceAndProfileOwner/src/com/android/cts/deviceandprofileowner/AudioRestrictionTest.java b/hostsidetests/devicepolicy/app/DeviceAndProfileOwner/src/com/android/cts/deviceandprofileowner/AudioRestrictionTest.java
index efc7115..f26d228 100644
--- a/hostsidetests/devicepolicy/app/DeviceAndProfileOwner/src/com/android/cts/deviceandprofileowner/AudioRestrictionTest.java
+++ b/hostsidetests/devicepolicy/app/DeviceAndProfileOwner/src/com/android/cts/deviceandprofileowner/AudioRestrictionTest.java
@@ -83,8 +83,8 @@
         }
 
         try {
-            // Set volume of ringtone to be 1.
-            mAudioManager.setStreamVolume(AudioManager.STREAM_RING, 1, /* flag= */ 0);
+            // Set volume of music to be 1.
+            mAudioManager.setStreamVolume(AudioManager.STREAM_MUSIC, 1, /* flag= */ 0);
 
             // Disallow adjusting volume.
             mDevicePolicyManager.addUserRestriction(ADMIN_RECEIVER_COMPONENT,
@@ -93,7 +93,7 @@
 
             // Verify that volume can't be changed.
             mAudioManager.adjustVolume(AudioManager.ADJUST_RAISE, /* flag= */ 0);
-            assertEquals(1, mAudioManager.getStreamVolume(AudioManager.STREAM_RING));
+            assertEquals(1, mAudioManager.getStreamVolume(AudioManager.STREAM_MUSIC));
 
             // Allowing adjusting volume.
             mDevicePolicyManager.clearUserRestriction(ADMIN_RECEIVER_COMPONENT,
@@ -105,7 +105,7 @@
             waitUntil(2, new Callable<Integer>() {
                 @Override
                 public Integer call() throws Exception {
-                    return mAudioManager.getStreamVolume(AudioManager.STREAM_RING);
+                    return mAudioManager.getStreamVolume(AudioManager.STREAM_MUSIC);
                 }
             });
         } finally {
diff --git a/hostsidetests/devicepolicy/app/DeviceAndProfileOwner/src/com/android/cts/deviceandprofileowner/ClearApplicationDataTest.java b/hostsidetests/devicepolicy/app/DeviceAndProfileOwner/src/com/android/cts/deviceandprofileowner/ClearApplicationDataTest.java
new file mode 100644
index 0000000..4c1a974
--- /dev/null
+++ b/hostsidetests/devicepolicy/app/DeviceAndProfileOwner/src/com/android/cts/deviceandprofileowner/ClearApplicationDataTest.java
@@ -0,0 +1,43 @@
+/*
+ * Copyright 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.deviceandprofileowner;
+
+import android.os.AsyncTask;
+
+import java.util.concurrent.Semaphore;
+import java.util.concurrent.TimeUnit;
+
+/**
+ * Test class that calls DPM.clearApplicationUserData and verifies that it doesn't time out.
+ */
+public class ClearApplicationDataTest extends BaseDeviceAdminTest {
+    private static final String TEST_PKG = "com.android.cts.intent.receiver";
+    private static final Semaphore mSemaphore = new Semaphore(0);
+    private static final long CLEAR_APPLICATION_DATA_TIMEOUT_S = 10;
+
+    public void testClearApplicationData() throws Exception {
+        mDevicePolicyManager.clearApplicationUserData(ADMIN_RECEIVER_COMPONENT, TEST_PKG,
+                (String pkg, boolean succeeded) -> {
+                    assertEquals(TEST_PKG, pkg);
+                    assertTrue(succeeded);
+                    mSemaphore.release();
+                }, AsyncTask.THREAD_POOL_EXECUTOR);
+
+        assertTrue("Clearing application data took too long",
+                mSemaphore.tryAcquire(CLEAR_APPLICATION_DATA_TIMEOUT_S, TimeUnit.SECONDS));
+    }
+}
diff --git a/hostsidetests/devicepolicy/app/DeviceAndProfileOwner/src/com/android/cts/deviceandprofileowner/PasswordBlacklistTest.java b/hostsidetests/devicepolicy/app/DeviceAndProfileOwner/src/com/android/cts/deviceandprofileowner/PasswordBlacklistTest.java
new file mode 100644
index 0000000..7856aff
--- /dev/null
+++ b/hostsidetests/devicepolicy/app/DeviceAndProfileOwner/src/com/android/cts/deviceandprofileowner/PasswordBlacklistTest.java
@@ -0,0 +1,307 @@
+/*
+ * 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.deviceandprofileowner;
+
+import android.app.admin.DevicePolicyManager;
+import android.util.Log;
+
+import java.util.Arrays;
+import java.util.ArrayList;
+import java.util.List;
+
+public final class PasswordBlacklistTest extends BaseDeviceAdminTest {
+    private static final String TAG = "PasswordBlacklistTest";
+    private static final byte[] TOKEN = "abcdefghijklmnopqrstuvwxyz0123456789".getBytes();
+
+    private boolean mShouldRun = true;
+
+    @Override
+    protected void setUp() throws Exception {
+        super.setUp();
+
+        // Set up a token to reset the password. This is used to check the blacklist is being
+        // enforced.
+        try {
+            // On devices with password token disabled, calling this method will throw
+            // a security exception. If that's anticipated, then return early without failing.
+            assertTrue(mDevicePolicyManager.setResetPasswordToken(ADMIN_RECEIVER_COMPONENT,
+                    TOKEN));
+        } catch (SecurityException e) {
+            if (e.getMessage().equals("Escrow token is disabled on the current user")) {
+                Log.i(TAG, "Skip some password blacklist test because escrow token is disabled");
+                mShouldRun = false;
+            } else {
+                throw e;
+            }
+        }
+    }
+
+    @Override
+    protected void tearDown() throws Exception {
+        if (!mShouldRun) {
+            return;
+        }
+        // Remove the blacklist, password and password reset token
+        mDevicePolicyManager.setPasswordQuality(ADMIN_RECEIVER_COMPONENT,
+                DevicePolicyManager.PASSWORD_QUALITY_UNSPECIFIED);
+        assertTrue(mDevicePolicyManager.setPasswordBlacklist(ADMIN_RECEIVER_COMPONENT, null, null));
+        assertTrue(mDevicePolicyManager.resetPasswordWithToken(
+                ADMIN_RECEIVER_COMPONENT, null, TOKEN, 0));
+        assertTrue(mDevicePolicyManager.clearResetPasswordToken(ADMIN_RECEIVER_COMPONENT));
+    }
+
+    public void testSettingEmptyBlacklist() {
+        if (!mShouldRun) {
+            return;
+        }
+        final String notInBlacklist = "4ur3>#C$a#rC3W9Rhs";
+
+        testPasswordBlacklist(null, notInBlacklist);
+    }
+
+    public void testClearingBlacklist() {
+        if (!mShouldRun) {
+            return;
+        }
+        final String notInBlacklist = "4ur3>#C$a#rC3W9Rhs";
+
+        testPasswordBlacklist(Arrays.asList(notInBlacklist), null);
+        testPasswordBlacklist(null, notInBlacklist);
+    }
+
+    public void testSettingBlacklist() {
+        if (!mShouldRun) {
+            return;
+        }
+        final List<String> blacklist = Arrays.asList("password", "letmein", "football");
+        final String notInBlacklist = "falseponycellfastener";
+
+        testPasswordBlacklist(blacklist, notInBlacklist);
+    }
+
+    public void testChangingBlacklist() {
+        if (!mShouldRun) {
+            return;
+        }
+        final List<String> blacklist = Arrays.asList("password", "letmein", "football");
+        final String notInBlacklist = "falseponycellfastener";
+
+        testPasswordBlacklist(Arrays.asList(notInBlacklist), null);
+        testPasswordBlacklist(blacklist, notInBlacklist);
+    }
+
+    public void testBlacklistNotTreatedAsRegex() {
+        if (!mShouldRun) {
+            return;
+        }
+        final List<String> blacklist = Arrays.asList("hi\\d*", ".*", "[^adb]{2}.\\d.\\S");
+        final String notInBlacklist = "hi123";
+
+        testPasswordBlacklist(blacklist, notInBlacklist);
+    }
+
+    public void testBlacklistCaseInsensitive() {
+        if (!mShouldRun) {
+            return;
+        }
+        final List<String> blacklist = Arrays.asList("baseball", "MONKEY", "ShAdOw");
+        final String notInBlacklist = "falsecellfastenerpony";
+
+        testPasswordBlacklist(blacklist, notInBlacklist);
+
+        // These are also blocked by the blacklist as they only differ in case
+        final List<String> inBlacklist = Arrays.asList(
+                "baseball", "BASEBALL", "BASEball",
+                "monkey", "MONKEY", "moNKEy",
+                "shadow", "SHADOW", "ShAdOw");
+        for (final String password : inBlacklist) {
+            assertFalse(mDevicePolicyManager.resetPasswordWithToken(
+                    ADMIN_RECEIVER_COMPONENT, password, TOKEN, 0));
+        }
+    }
+
+    public void testMaxBlacklistSize() {
+        assertTrue(mDevicePolicyManager.setPasswordBlacklist(
+                ADMIN_RECEIVER_COMPONENT, "max size", generateMaxBlacklist()));
+    }
+
+    public void testBlacklistTooBig() {
+        try {
+            mDevicePolicyManager.setPasswordBlacklist(
+                    ADMIN_RECEIVER_COMPONENT, "too big", generateJustTooBigBlacklist());
+            fail("Did not throw IllegalArgumentException");
+        } catch (IllegalArgumentException e) {
+            return;
+        }
+    }
+
+    public void testNullNameWhenSettingBlacklist() {
+        if (!mShouldRun) {
+            return;
+        }
+        final String password = "bad one";
+        try {
+            mDevicePolicyManager.setPasswordBlacklist(
+                    ADMIN_RECEIVER_COMPONENT, null, Arrays.asList(password));
+            fail("Did not throw NullPointerException");
+        } catch (NullPointerException e) {
+            assertTrue(mDevicePolicyManager.resetPasswordWithToken(
+                    ADMIN_RECEIVER_COMPONENT, password, TOKEN, 0));
+            return;
+        }
+    }
+
+    public void testNullAdminWhenSettingBlacklist() {
+        if (!mShouldRun) {
+            return;
+        }
+        final String password = "example";
+        try {
+            mDevicePolicyManager.setPasswordBlacklist(null, "no admin", Arrays.asList(password));
+            fail("Did not throw NullPointerException");
+        } catch (NullPointerException e) {
+            assertTrue(mDevicePolicyManager.resetPasswordWithToken(
+                    ADMIN_RECEIVER_COMPONENT, password, TOKEN, 0));
+            return;
+        }
+    }
+
+    public void testPasswordBlacklistName() {
+        if (!mShouldRun) {
+            return;
+        }
+        final String name = "Version 1.0";
+        final List<String> blacklist = Arrays.asList("one", "1", "i");
+        assertTrue(mDevicePolicyManager.setPasswordBlacklist(
+                ADMIN_RECEIVER_COMPONENT, name, blacklist));
+        assertEquals(
+                mDevicePolicyManager.getPasswordBlacklistName(ADMIN_RECEIVER_COMPONENT), name);
+        for (final String password : blacklist) {
+            assertFalse(mDevicePolicyManager.resetPasswordWithToken(
+                    ADMIN_RECEIVER_COMPONENT, password, TOKEN, 0));
+        }
+        assertTrue(mDevicePolicyManager.resetPasswordWithToken(
+                ADMIN_RECEIVER_COMPONENT, "notintheblacklist", TOKEN, 0));
+    }
+
+    public void testPasswordBlacklistWithEmptyName() {
+        final String emptyName = "";
+        assertTrue(mDevicePolicyManager.setPasswordBlacklist(
+                ADMIN_RECEIVER_COMPONENT, emptyName, Arrays.asList("test", "empty", "name")));
+        assertEquals(
+                mDevicePolicyManager.getPasswordBlacklistName(ADMIN_RECEIVER_COMPONENT), emptyName);
+    }
+
+    public void testBlacklistNameCanBeChanged() {
+        final String firstName = "original";
+        assertTrue(mDevicePolicyManager.setPasswordBlacklist(
+                ADMIN_RECEIVER_COMPONENT, firstName, Arrays.asList("a")));
+        assertEquals(
+                mDevicePolicyManager.getPasswordBlacklistName(ADMIN_RECEIVER_COMPONENT), firstName);
+
+        final String newName = "different";
+        assertTrue(mDevicePolicyManager.setPasswordBlacklist(
+                ADMIN_RECEIVER_COMPONENT, newName, Arrays.asList("a")));
+        assertEquals(
+                mDevicePolicyManager.getPasswordBlacklistName(ADMIN_RECEIVER_COMPONENT), newName);
+    }
+
+    public void testCannotNameClearedBlacklist() {
+        final String name = "empty!";
+        assertTrue(mDevicePolicyManager.setPasswordBlacklist(
+                ADMIN_RECEIVER_COMPONENT, name, null));
+        assertTrue(mDevicePolicyManager.getPasswordBlacklistName(ADMIN_RECEIVER_COMPONENT) == null);
+    }
+
+    public void testClearingBlacklistClearsName() {
+        final String firstName = "gotone";
+        assertTrue(mDevicePolicyManager.setPasswordBlacklist(
+                ADMIN_RECEIVER_COMPONENT, firstName, Arrays.asList("something")));
+        assertEquals(
+                mDevicePolicyManager.getPasswordBlacklistName(ADMIN_RECEIVER_COMPONENT), firstName);
+
+        final String newName = "empty!";
+        assertTrue(mDevicePolicyManager.setPasswordBlacklist(
+                ADMIN_RECEIVER_COMPONENT, newName, null));
+        assertTrue(mDevicePolicyManager.getPasswordBlacklistName(ADMIN_RECEIVER_COMPONENT) == null);
+    }
+
+    public void testNullAdminWhenGettingBlacklistName() {
+        try {
+            mDevicePolicyManager.getPasswordBlacklistName(null);
+            fail("Did not throw NullPointerException");
+        } catch (NullPointerException e) {
+            return;
+        }
+    }
+
+    public void testBlacklistNotConsideredByIsActivePasswordSufficient() {
+        if (!mShouldRun) {
+            return;
+        }
+        mDevicePolicyManager.setPasswordQuality(ADMIN_RECEIVER_COMPONENT,
+                DevicePolicyManager.PASSWORD_QUALITY_COMPLEX);
+        final String complexPassword = ".password123";
+        assertTrue(mDevicePolicyManager.resetPasswordWithToken(
+                ADMIN_RECEIVER_COMPONENT, complexPassword, TOKEN, 0));
+        assertTrue(mDevicePolicyManager.setPasswordBlacklist(
+                ADMIN_RECEIVER_COMPONENT, "Sufficient", Arrays.asList(complexPassword)));
+        assertPasswordSufficiency(true);
+    }
+
+    private static final int MAX_BLACKLIST_ITEM_SIZE = 8;
+
+    /* Generate a list based on the 128 thousand character limit */
+    private List<String> generateMaxBlacklist() {
+        final int numItems = (128 * 1000) / MAX_BLACKLIST_ITEM_SIZE;
+        assertTrue(numItems == 16 * 1000);
+        final List<String> blacklist = new ArrayList(numItems);
+        final String item = new String(new char[MAX_BLACKLIST_ITEM_SIZE]).replace('\0', 'a');
+        for (int i = 0; i < numItems; ++i) {
+            blacklist.add(item);
+        }
+        return blacklist;
+    }
+
+    private List<String> generateJustTooBigBlacklist() {
+        List<String> list = generateMaxBlacklist();
+        list.set(0, new String(new char[MAX_BLACKLIST_ITEM_SIZE + 1]).replace('\0', 'a'));
+        return list;
+    }
+
+    /**
+     * Install a blacklist, ensure items match and don't match it correctly.
+     */
+    private void testPasswordBlacklist(List<String> blacklist, String notInBlacklist) {
+        assertTrue(mDevicePolicyManager.setPasswordBlacklist(
+                ADMIN_RECEIVER_COMPONENT, "Test Blacklist", blacklist));
+
+        if (blacklist != null) {
+            // These are blacklisted so can't be set
+            for (final String password : blacklist) {
+                assertFalse(mDevicePolicyManager.resetPasswordWithToken(
+                        ADMIN_RECEIVER_COMPONENT, password, TOKEN, 0));
+            }
+        }
+
+        if (notInBlacklist != null) {
+            // This isn't blacklisted so can be set
+            assertTrue(mDevicePolicyManager.resetPasswordWithToken(
+                    ADMIN_RECEIVER_COMPONENT, notInBlacklist, TOKEN, 0));
+        }
+    }
+}
diff --git a/hostsidetests/devicepolicy/app/DeviceAndProfileOwner/src/com/android/cts/deviceandprofileowner/PermissionsTest.java b/hostsidetests/devicepolicy/app/DeviceAndProfileOwner/src/com/android/cts/deviceandprofileowner/PermissionsTest.java
index 6c706ee..70a5d09 100644
--- a/hostsidetests/devicepolicy/app/DeviceAndProfileOwner/src/com/android/cts/deviceandprofileowner/PermissionsTest.java
+++ b/hostsidetests/devicepolicy/app/DeviceAndProfileOwner/src/com/android/cts/deviceandprofileowner/PermissionsTest.java
@@ -22,7 +22,6 @@
 import android.content.Intent;
 import android.content.IntentFilter;
 import android.content.pm.PackageManager;
-import android.os.UserManager;
 import android.support.test.uiautomator.By;
 import android.support.test.uiautomator.BySelector;
 import android.support.test.uiautomator.UiDevice;
@@ -47,6 +46,7 @@
     private static final String SIMPLE_PRE_M_APP_PACKAGE_NAME =
             "com.android.cts.launcherapps.simplepremapp";
     private static final String PERMISSION_NAME = "android.permission.READ_CONTACTS";
+    private static final String DEVELOPMENT_PERMISSION = "android.permission.INTERACT_ACROSS_USERS";
 
     private static final String PERMISSIONS_ACTIVITY_NAME
             = PERMISSION_APP_PACKAGE_NAME + ".PermissionActivity";
@@ -227,6 +227,15 @@
         assertSetPermissionGrantStatePreMApp(DevicePolicyManager.PERMISSION_GRANT_STATE_GRANTED);
     }
 
+    public void testPermissionGrantState_developmentPermission() throws Exception {
+        assertFailedToSetDevelopmentPermissionGrantState(
+                DevicePolicyManager.PERMISSION_GRANT_STATE_DENIED);
+        assertFailedToSetDevelopmentPermissionGrantState(
+                DevicePolicyManager.PERMISSION_GRANT_STATE_DEFAULT);
+        assertFailedToSetDevelopmentPermissionGrantState(
+                DevicePolicyManager.PERMISSION_GRANT_STATE_GRANTED);
+    }
+
     private void assertPermissionRequest(int expected) throws Exception {
         assertPermissionRequest(expected, null);
     }
@@ -274,6 +283,18 @@
                 value);
     }
 
+    private void assertFailedToSetDevelopmentPermissionGrantState(int value) throws Exception {
+        assertFalse(mDevicePolicyManager.setPermissionGrantState(ADMIN_RECEIVER_COMPONENT,
+                PERMISSION_APP_PACKAGE_NAME, DEVELOPMENT_PERMISSION, value));
+        assertEquals(mDevicePolicyManager.getPermissionGrantState(ADMIN_RECEIVER_COMPONENT,
+                PERMISSION_APP_PACKAGE_NAME, DEVELOPMENT_PERMISSION),
+                DevicePolicyManager.PERMISSION_GRANT_STATE_DEFAULT);
+        assertEquals(mPackageManager.checkPermission(DEVELOPMENT_PERMISSION,
+                PERMISSION_APP_PACKAGE_NAME),
+                PackageManager.PERMISSION_DENIED);
+    }
+
+
     private void assertSetPermissionGrantStatePreMApp(int value) throws Exception {
         assertFalse(mDevicePolicyManager.setPermissionGrantState(ADMIN_RECEIVER_COMPONENT,
                 SIMPLE_PRE_M_APP_PACKAGE_NAME, PERMISSION_NAME,
diff --git a/hostsidetests/devicepolicy/app/DeviceAndProfileOwner/src/com/android/cts/deviceandprofileowner/userrestrictions/BaseUserRestrictionsTest.java b/hostsidetests/devicepolicy/app/DeviceAndProfileOwner/src/com/android/cts/deviceandprofileowner/userrestrictions/BaseUserRestrictionsTest.java
index 39235b0..0361fd8 100644
--- a/hostsidetests/devicepolicy/app/DeviceAndProfileOwner/src/com/android/cts/deviceandprofileowner/userrestrictions/BaseUserRestrictionsTest.java
+++ b/hostsidetests/devicepolicy/app/DeviceAndProfileOwner/src/com/android/cts/deviceandprofileowner/userrestrictions/BaseUserRestrictionsTest.java
@@ -60,6 +60,7 @@
             UserManager.DISALLOW_SMS,
             UserManager.DISALLOW_FUN,
             UserManager.DISALLOW_CREATE_WINDOWS,
+            UserManager.DISALLOW_SYSTEM_ERROR_DIALOGS,
             UserManager.DISALLOW_CROSS_PROFILE_COPY_PASTE,
             UserManager.DISALLOW_OUTGOING_BEAM,
             UserManager.DISALLOW_SAFE_BOOT,
@@ -91,7 +92,8 @@
 
             // PO can set them too, but when DO sets them, they're global.
             UserManager.DISALLOW_ADJUST_VOLUME,
-            UserManager.DISALLOW_UNMUTE_MICROPHONE
+            UserManager.DISALLOW_UNMUTE_MICROPHONE,
+            UserManager.DISALLOW_SYSTEM_ERROR_DIALOGS
     };
 
     public static final String[] HIDDEN_AND_PROHIBITED = new String[] {
diff --git a/hostsidetests/devicepolicy/app/DeviceAndProfileOwner/src/com/android/cts/deviceandprofileowner/userrestrictions/DeviceOwnerUserRestrictionsTest.java b/hostsidetests/devicepolicy/app/DeviceAndProfileOwner/src/com/android/cts/deviceandprofileowner/userrestrictions/DeviceOwnerUserRestrictionsTest.java
index b218341..9743840 100644
--- a/hostsidetests/devicepolicy/app/DeviceAndProfileOwner/src/com/android/cts/deviceandprofileowner/userrestrictions/DeviceOwnerUserRestrictionsTest.java
+++ b/hostsidetests/devicepolicy/app/DeviceAndProfileOwner/src/com/android/cts/deviceandprofileowner/userrestrictions/DeviceOwnerUserRestrictionsTest.java
@@ -48,6 +48,7 @@
             UserManager.DISALLOW_SMS,
             UserManager.DISALLOW_FUN,
             UserManager.DISALLOW_CREATE_WINDOWS,
+            UserManager.DISALLOW_SYSTEM_ERROR_DIALOGS,
             UserManager.DISALLOW_CROSS_PROFILE_COPY_PASTE,
             UserManager.DISALLOW_OUTGOING_BEAM,
             UserManager.DISALLOW_SAFE_BOOT,
@@ -55,7 +56,8 @@
             // UserManager.DISALLOW_DATA_ROAMING, // Has unrecoverable side effects.
             UserManager.DISALLOW_SET_USER_ICON,
             UserManager.DISALLOW_BLUETOOTH,
-            UserManager.DISALLOW_AUTOFILL
+            UserManager.DISALLOW_AUTOFILL,
+            UserManager.DISALLOW_UNIFIED_PASSWORD,
     };
 
     public static final String[] DISALLOWED = new String[] {
diff --git a/hostsidetests/devicepolicy/app/DeviceAndProfileOwner/src/com/android/cts/deviceandprofileowner/userrestrictions/SecondaryProfileOwnerUserRestrictionsTest.java b/hostsidetests/devicepolicy/app/DeviceAndProfileOwner/src/com/android/cts/deviceandprofileowner/userrestrictions/SecondaryProfileOwnerUserRestrictionsTest.java
index 5c7f243..2a9db15 100644
--- a/hostsidetests/devicepolicy/app/DeviceAndProfileOwner/src/com/android/cts/deviceandprofileowner/userrestrictions/SecondaryProfileOwnerUserRestrictionsTest.java
+++ b/hostsidetests/devicepolicy/app/DeviceAndProfileOwner/src/com/android/cts/deviceandprofileowner/userrestrictions/SecondaryProfileOwnerUserRestrictionsTest.java
@@ -38,11 +38,13 @@
             UserManager.DISALLOW_UNMUTE_MICROPHONE,
             UserManager.DISALLOW_ADJUST_VOLUME,
             UserManager.DISALLOW_OUTGOING_CALLS,
+            UserManager.DISALLOW_SYSTEM_ERROR_DIALOGS,
             UserManager.DISALLOW_CROSS_PROFILE_COPY_PASTE,
             UserManager.DISALLOW_OUTGOING_BEAM,
             UserManager.ALLOW_PARENT_PROFILE_APP_LINKING,
             UserManager.DISALLOW_SET_USER_ICON,
-            UserManager.DISALLOW_AUTOFILL
+            UserManager.DISALLOW_AUTOFILL,
+            UserManager.DISALLOW_UNIFIED_PASSWORD,
     };
 
     public static final String[] DISALLOWED = new String[] {
diff --git a/hostsidetests/devicepolicy/app/DeviceOwner/Android.mk b/hostsidetests/devicepolicy/app/DeviceOwner/Android.mk
index 4cc041a..e0ea522 100644
--- a/hostsidetests/devicepolicy/app/DeviceOwner/Android.mk
+++ b/hostsidetests/devicepolicy/app/DeviceOwner/Android.mk
@@ -22,16 +22,25 @@
 
 LOCAL_MODULE_PATH := $(TARGET_OUT_DATA_APPS)
 
-LOCAL_SRC_FILES := $(call all-java-files-under, src)
+LOCAL_SRC_FILES := $(call all-java-files-under, src) \
+                   $(call all-Iaidl-files-under, src)
 
-LOCAL_JAVA_LIBRARIES := android.test.runner conscrypt cts-junit
+LOCAL_AIDL_INCLUDES := $(LOCAL_PATH)/src
+
+LOCAL_JAVA_LIBRARIES := \
+    android.test.runner \
+    conscrypt \
+    cts-junit \
+    android.test.base.stubs \
+    bouncycastle
 
 LOCAL_STATIC_JAVA_LIBRARIES := \
     ctstestrunner \
     compatibility-device-util \
     android-support-v4 \
     android-support-test \
-    legacy-android-test
+    bouncycastle \
+    cts-security-test-support-library
 
 LOCAL_SDK_VERSION := test_current
 
diff --git a/hostsidetests/devicepolicy/app/DeviceOwner/AndroidManifest.xml b/hostsidetests/devicepolicy/app/DeviceOwner/AndroidManifest.xml
index 9a09007..bdb5d3b 100644
--- a/hostsidetests/devicepolicy/app/DeviceOwner/AndroidManifest.xml
+++ b/hostsidetests/devicepolicy/app/DeviceOwner/AndroidManifest.xml
@@ -35,7 +35,7 @@
 
         <uses-library android:name="android.test.runner" />
         <receiver
-            android:name="com.android.cts.deviceowner.BaseDeviceOwnerTest$BasicAdminReceiver"
+            android:name="com.android.cts.deviceowner.BasicAdminReceiver"
             android:permission="android.permission.BIND_DEVICE_ADMIN">
             <meta-data android:name="android.app.device_admin"
                        android:resource="@xml/device_admin" />
@@ -52,6 +52,20 @@
                 <action android:name="android.app.action.DEVICE_ADMIN_ENABLED" />
             </intent-filter>
         </receiver>
+        <receiver
+                android:name="com.android.cts.deviceowner.CreateAndManageUserTest$SecondaryUserAdminReceiver"
+                android:permission="android.permission.BIND_DEVICE_ADMIN">
+            <meta-data android:name="android.app.device_admin"
+                       android:resource="@xml/device_admin" />
+            <intent-filter>
+                <action android:name="android.app.action.DEVICE_ADMIN_ENABLED" />
+            </intent-filter>
+        </receiver>
+
+        <service android:name="com.android.cts.deviceowner.CreateAndManageUserTest$PrimaryUserService"
+                 android:exported="true"
+                 android:permission="android.permission.BIND_DEVICE_ADMIN">
+        </service>
 
         <activity
             android:name="com.android.cts.deviceowner.KeyManagementActivity"
@@ -70,14 +84,6 @@
             </intent-filter>
         </activity>
 
-        <!-- we need to give a different taskAffinity so that when we use
-             FLAG_ACTIVITY_NEW_TASK, the system tries to start it in a different task
-        -->
-        <activity
-            android:name="com.android.cts.deviceowner.LockTaskTest$IntentReceivingActivity"
-            android:taskAffinity="com.android.cts.deviceowner.LockTaskTest.IntentReceivingActivity"
-            />
-
         <activity
             android:name=".SetPolicyActivity"
             android:launchMode="singleTop">
diff --git a/hostsidetests/devicepolicy/app/DeviceOwner/src/com/android/cts/deviceowner/AffiliationTest.java b/hostsidetests/devicepolicy/app/DeviceOwner/src/com/android/cts/deviceowner/AffiliationTest.java
index a9b43c6..0afc16e 100644
--- a/hostsidetests/devicepolicy/app/DeviceOwner/src/com/android/cts/deviceowner/AffiliationTest.java
+++ b/hostsidetests/devicepolicy/app/DeviceOwner/src/com/android/cts/deviceowner/AffiliationTest.java
@@ -46,7 +46,7 @@
         Context context = InstrumentationRegistry.getContext();
         mDevicePolicyManager = (DevicePolicyManager)
                 context.getSystemService(Context.DEVICE_POLICY_SERVICE);
-        mAdminComponent = BaseDeviceOwnerTest.BasicAdminReceiver.getComponentName(context);
+        mAdminComponent = BasicAdminReceiver.getComponentName(context);
     }
 
     @Test
diff --git a/hostsidetests/devicepolicy/app/DeviceOwner/src/com/android/cts/deviceowner/BaseAffiliatedProfileOwnerTest.java b/hostsidetests/devicepolicy/app/DeviceOwner/src/com/android/cts/deviceowner/BaseAffiliatedProfileOwnerTest.java
new file mode 100644
index 0000000..9490eff
--- /dev/null
+++ b/hostsidetests/devicepolicy/app/DeviceOwner/src/com/android/cts/deviceowner/BaseAffiliatedProfileOwnerTest.java
@@ -0,0 +1,54 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.cts.deviceowner;
+
+import android.app.admin.DevicePolicyManager;
+import android.content.ComponentName;
+import android.test.AndroidTestCase;
+
+/**
+ * Base class for affiliated profile-owner based tests.
+ *
+ * This class handles making sure that the test is the affiliated profile owner and that it has an
+ * active admin registered, so that all tests may assume these are done. The admin component can be
+ * accessed through {@link #getWho()}.
+ */
+public abstract class BaseAffiliatedProfileOwnerTest extends AndroidTestCase {
+
+    protected DevicePolicyManager mDevicePolicyManager;
+
+    @Override
+    protected void setUp() throws Exception {
+        super.setUp();
+
+        mDevicePolicyManager = mContext.getSystemService(DevicePolicyManager.class);
+        assertDeviceOrAffiliatedProfileOwner();
+    }
+
+    private void assertDeviceOrAffiliatedProfileOwner() {
+        assertNotNull(mDevicePolicyManager);
+        assertTrue(mDevicePolicyManager.isAdminActive(getWho()));
+        boolean isDeviceOwner = mDevicePolicyManager.isDeviceOwnerApp(mContext.getPackageName());
+        boolean isAffiliatedProfileOwner = mDevicePolicyManager.isProfileOwnerApp(
+                mContext.getPackageName())
+                && mDevicePolicyManager.isAffiliatedUser();
+        assertTrue(isDeviceOwner || isAffiliatedProfileOwner);
+    }
+
+    protected ComponentName getWho() {
+        return BasicAdminReceiver.getComponentName(mContext);
+    }
+}
diff --git a/hostsidetests/devicepolicy/app/DeviceOwner/src/com/android/cts/deviceowner/BaseDeviceOwnerTest.java b/hostsidetests/devicepolicy/app/DeviceOwner/src/com/android/cts/deviceowner/BaseDeviceOwnerTest.java
index 94aaeb2..6ed1ca7 100644
--- a/hostsidetests/devicepolicy/app/DeviceOwner/src/com/android/cts/deviceowner/BaseDeviceOwnerTest.java
+++ b/hostsidetests/devicepolicy/app/DeviceOwner/src/com/android/cts/deviceowner/BaseDeviceOwnerTest.java
@@ -15,15 +15,8 @@
  */
 package com.android.cts.deviceowner;
 
-import android.app.admin.DeviceAdminReceiver;
 import android.app.admin.DevicePolicyManager;
 import android.content.ComponentName;
-import android.content.Context;
-import android.content.Intent;
-import android.net.Uri;
-import android.os.Process;
-import android.os.UserHandle;
-import android.support.v4.content.LocalBroadcastManager;
 import android.test.AndroidTestCase;
 
 /**
@@ -36,77 +29,24 @@
  */
 public abstract class BaseDeviceOwnerTest extends AndroidTestCase {
 
-    final static String ACTION_USER_ADDED = "com.android.cts.deviceowner.action.USER_ADDED";
-    final static String ACTION_USER_REMOVED = "com.android.cts.deviceowner.action.USER_REMOVED";
-    final static String EXTRA_USER_HANDLE = "com.android.cts.deviceowner.extra.USER_HANDLE";
-    final static String ACTION_NETWORK_LOGS_AVAILABLE =
-            "com.android.cts.deviceowner.action.ACTION_NETWORK_LOGS_AVAILABLE";
-    final static String EXTRA_NETWORK_LOGS_BATCH_TOKEN =
-            "com.android.cts.deviceowner.extra.NETWORK_LOGS_BATCH_TOKEN";
-
-    public static class BasicAdminReceiver extends DeviceAdminReceiver {
-
-        public static ComponentName getComponentName(Context context) {
-            return new ComponentName(context, BasicAdminReceiver.class);
-        }
-
-        @Override
-        public String onChoosePrivateKeyAlias(Context context, Intent intent, int uid, Uri uri,
-                String suggestedAlias) {
-            if (uid != Process.myUid() || uri == null) {
-                return null;
-            }
-            return uri.getQueryParameter("alias");
-        }
-
-        @Override
-        public void onUserAdded(Context context, Intent intent, UserHandle userHandle) {
-            sendUserAddedOrRemovedBroadcast(context, ACTION_USER_ADDED, userHandle);
-        }
-
-        @Override
-        public void onUserRemoved(Context context, Intent intent, UserHandle userHandle) {
-            sendUserAddedOrRemovedBroadcast(context, ACTION_USER_REMOVED, userHandle);
-        }
-
-        @Override
-        public void onNetworkLogsAvailable(Context context, Intent intent, long batchToken,
-                int networkLogsCount) {
-            // send the broadcast, the rest of the test happens in NetworkLoggingTest
-            Intent batchIntent = new Intent(ACTION_NETWORK_LOGS_AVAILABLE);
-            batchIntent.putExtra(EXTRA_NETWORK_LOGS_BATCH_TOKEN, batchToken);
-            LocalBroadcastManager.getInstance(context).sendBroadcast(batchIntent);
-        }
-
-        private void sendUserAddedOrRemovedBroadcast(Context context, String action,
-                UserHandle userHandle) {
-            Intent intent = new Intent(action);
-            intent.putExtra(EXTRA_USER_HANDLE, userHandle);
-            LocalBroadcastManager.getInstance(context).sendBroadcast(intent);
-        }
-    }
-
-    public static final String PACKAGE_NAME = BaseDeviceOwnerTest.class.getPackage().getName();
-
     protected DevicePolicyManager mDevicePolicyManager;
 
     @Override
     protected void setUp() throws Exception {
         super.setUp();
 
-        mDevicePolicyManager = (DevicePolicyManager)
-                mContext.getSystemService(Context.DEVICE_POLICY_SERVICE);
-        assertDeviceOwner(mDevicePolicyManager);
+        mDevicePolicyManager = mContext.getSystemService(DevicePolicyManager.class);
+        assertDeviceOwner();
     }
 
-    static void assertDeviceOwner(DevicePolicyManager dpm) {
-        assertNotNull(dpm);
-        assertTrue(dpm.isAdminActive(getWho()));
-        assertTrue(dpm.isDeviceOwnerApp(PACKAGE_NAME));
-        assertFalse(dpm.isManagedProfile(getWho()));
+    private void assertDeviceOwner() {
+        assertNotNull(mDevicePolicyManager);
+        assertTrue(mDevicePolicyManager.isAdminActive(getWho()));
+        assertTrue(mDevicePolicyManager.isDeviceOwnerApp(mContext.getPackageName()));
+        assertFalse(mDevicePolicyManager.isManagedProfile(getWho()));
     }
 
-    protected static ComponentName getWho() {
-        return new ComponentName(PACKAGE_NAME, BasicAdminReceiver.class.getName());
+    protected ComponentName getWho() {
+        return BasicAdminReceiver.getComponentName(mContext);
     }
 }
diff --git a/hostsidetests/devicepolicy/app/DeviceOwner/src/com/android/cts/deviceowner/BasicAdminReceiver.java b/hostsidetests/devicepolicy/app/DeviceOwner/src/com/android/cts/deviceowner/BasicAdminReceiver.java
new file mode 100644
index 0000000..c4d710b
--- /dev/null
+++ b/hostsidetests/devicepolicy/app/DeviceOwner/src/com/android/cts/deviceowner/BasicAdminReceiver.java
@@ -0,0 +1,76 @@
+/*
+ * 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.deviceowner;
+
+import android.app.admin.DeviceAdminReceiver;
+import android.content.ComponentName;
+import android.content.Context;
+import android.content.Intent;
+import android.net.Uri;
+import android.os.Process;
+import android.os.UserHandle;
+import android.support.v4.content.LocalBroadcastManager;
+
+public class BasicAdminReceiver extends DeviceAdminReceiver {
+
+    final static String ACTION_USER_ADDED = "com.android.cts.deviceowner.action.USER_ADDED";
+    final static String ACTION_USER_REMOVED = "com.android.cts.deviceowner.action.USER_REMOVED";
+    final static String EXTRA_USER_HANDLE = "com.android.cts.deviceowner.extra.USER_HANDLE";
+    final static String ACTION_NETWORK_LOGS_AVAILABLE =
+            "com.android.cts.deviceowner.action.ACTION_NETWORK_LOGS_AVAILABLE";
+    final static String EXTRA_NETWORK_LOGS_BATCH_TOKEN =
+            "com.android.cts.deviceowner.extra.NETWORK_LOGS_BATCH_TOKEN";
+
+    public static ComponentName getComponentName(Context context) {
+        return new ComponentName(context, BasicAdminReceiver.class);
+    }
+
+    @Override
+    public String onChoosePrivateKeyAlias(Context context, Intent intent, int uid, Uri uri,
+            String suggestedAlias) {
+        if (uid != Process.myUid() || uri == null) {
+            return null;
+        }
+        return uri.getQueryParameter("alias");
+    }
+
+    @Override
+    public void onUserAdded(Context context, Intent intent, UserHandle userHandle) {
+        sendUserAddedOrRemovedBroadcast(context, ACTION_USER_ADDED, userHandle);
+    }
+
+    @Override
+    public void onUserRemoved(Context context, Intent intent, UserHandle userHandle) {
+        sendUserAddedOrRemovedBroadcast(context, ACTION_USER_REMOVED,
+                userHandle);
+    }
+
+    @Override
+    public void onNetworkLogsAvailable(Context context, Intent intent, long batchToken,
+            int networkLogsCount) {
+        // send the broadcast, the rest of the test happens in NetworkLoggingTest
+        Intent batchIntent = new Intent(ACTION_NETWORK_LOGS_AVAILABLE);
+        batchIntent.putExtra(EXTRA_NETWORK_LOGS_BATCH_TOKEN, batchToken);
+        LocalBroadcastManager.getInstance(context).sendBroadcast(batchIntent);
+    }
+
+    private void sendUserAddedOrRemovedBroadcast(Context context, String action,
+            UserHandle userHandle) {
+        Intent intent = new Intent(action);
+        intent.putExtra(EXTRA_USER_HANDLE, userHandle);
+        LocalBroadcastManager.getInstance(context).sendBroadcast(intent);
+    }
+}
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 a540c27..aa71bc2 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
@@ -17,6 +17,7 @@
 package com.android.cts.deviceowner;
 
 import android.app.ActivityManager;
+import android.app.Service;
 import android.app.admin.DeviceAdminReceiver;
 import android.app.admin.DevicePolicyManager;
 import android.content.BroadcastReceiver;
@@ -24,16 +25,21 @@
 import android.content.Context;
 import android.content.Intent;
 import android.content.IntentFilter;
+import android.content.ServiceConnection;
 import android.content.pm.PackageManager;
-import android.os.Bundle;
+import android.os.IBinder;
 import android.os.PersistableBundle;
 import android.os.Process;
+import android.os.RemoteException;
 import android.os.UserHandle;
 import android.os.UserManager;
 import android.provider.Settings;
 import android.support.v4.content.LocalBroadcastManager;
 import android.util.Log;
-import java.lang.reflect.Field;
+
+import java.util.Collections;
+import java.util.List;
+import java.util.concurrent.Semaphore;
 import java.util.concurrent.SynchronousQueue;
 import java.util.concurrent.TimeUnit;
 
@@ -50,6 +56,12 @@
     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 long ON_ENABLED_TIMEOUT_SECONDS = 120;
+
+
     private PackageManager mPackageManager;
     private ActivityManager mActivityManager;
     private volatile boolean mReceived;
@@ -122,75 +134,74 @@
         }
     }
 
-// Disabled due to b/29072728
-//    // 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) {
-//            mUserHandle = mDevicePolicyManager.createAndManageUser(getWho(), testUserName,
-//                    TestProfileOwner.getComponentName(), bundle, flags);
-//            assertNotNull(mUserHandle);
-//
-//            mDevicePolicyManager.switchUser(getWho(), mUserHandle);
-//            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(), mUserHandle));
-//
-//            mUserHandle = null;
-//        }
-//
-//        mContext.unregisterReceiver(receiver);
-//    }
+    // 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) {
+            mUserHandle = mDevicePolicyManager.createAndManageUser(getWho(), testUserName,
+                    TestProfileOwner.getComponentName(), bundle, flags);
+            assertNotNull(mUserHandle);
+
+            mDevicePolicyManager.switchUser(getWho(), mUserHandle);
+            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(), mUserHandle));
+
+            mUserHandle = null;
+        }
+
+        mContext.unregisterReceiver(receiver);
+    }
 
     /**
      * Test creating an ephemeral user using the {@link DevicePolicyManager#createAndManageUser}
@@ -204,59 +215,27 @@
     public void testCreateAndManageEphemeralUser() throws Exception {
         String testUserName = "TestUser_" + System.currentTimeMillis();
 
-        // Use reflection to get the value of the hidden flag to make the new user ephemeral.
-        Field field = DevicePolicyManager.class.getField("MAKE_USER_EPHEMERAL");
-        int makeEphemeralFlag = field.getInt(null);
-
         // Do not assign return value to mUserHandle, so it is not removed in tearDown.
         mDevicePolicyManager.createAndManageUser(
                 getWho(),
                 testUserName,
                 getWho(),
                 null,
-                makeEphemeralFlag);
+                DevicePolicyManager.MAKE_USER_EPHEMERAL);
     }
 
-    /**
-     * Test creating an ephemeral user using the {@link DevicePolicyManager#createAndManageUser}
-     * method fails on systems without the split system user.
-     *
-     * <p>To be used by host-side test on systems without the split system user.
-     */
-    public void testCreateAndManageEphemeralUserFails() throws Exception {
-        String testUserName = "TestUser_" + System.currentTimeMillis();
+    public void testCreateAndManageUser_SkipSetupWizard() {
+        createAndManageUserTest(DevicePolicyManager.SKIP_SETUP_WIZARD);
+    }
 
-        // Use reflection to get the value of the hidden flag to make the new user ephemeral.
-        Field field = DevicePolicyManager.class.getField("MAKE_USER_EPHEMERAL");
-        int makeEphemeralFlag = field.getInt(null);
-
-        try {
-            mDevicePolicyManager.createAndManageUser(
-                    getWho(),
-                    testUserName,
-                    getWho(),
-                    null,
-                    makeEphemeralFlag);
-        } catch (IllegalArgumentException e) {
-            // Success, the expected exception was thrown.
-            return;
+    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);
         }
-        fail("createAndManageUser should have thrown IllegalArgumentException");
     }
 
-// Disabled due to b/29072728
-//    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);
@@ -283,7 +262,8 @@
         LocalBroadcastReceiver receiver = new LocalBroadcastReceiver();
         LocalBroadcastManager localBroadcastManager = LocalBroadcastManager.getInstance(
                 getContext());
-        localBroadcastManager.registerReceiver(receiver, new IntentFilter(ACTION_USER_ADDED));
+        localBroadcastManager.registerReceiver(receiver,
+                new IntentFilter(BasicAdminReceiver.ACTION_USER_ADDED));
         try {
             mUserHandle = mDevicePolicyManager.createAndManageUser(getWho(), "Test User", getWho(),
                     null, 0);
@@ -292,7 +272,8 @@
         } finally {
             localBroadcastManager.unregisterReceiver(receiver);
         }
-        localBroadcastManager.registerReceiver(receiver, new IntentFilter(ACTION_USER_REMOVED));
+        localBroadcastManager.registerReceiver(receiver,
+                new IntentFilter(BasicAdminReceiver.ACTION_USER_REMOVED));
         try {
             assertTrue(mDevicePolicyManager.removeUser(getWho(), mUserHandle));
             assertEquals(mUserHandle, receiver.waitForBroadcastReceived());
@@ -302,12 +283,34 @@
         }
     }
 
+    public void testCreateAndManageUser_StartUserInBackground() throws Exception {
+        String testUserName = "TestUser_" + System.currentTimeMillis();
+
+        // Set affiliation id to allow communication
+        mDevicePolicyManager.setAffiliationIds(getWho(), Collections.singleton(AFFILIATION_ID));
+
+        // Pack the affiliation id in a bundle so the secondary user can get it
+        PersistableBundle bundle = new PersistableBundle();
+        bundle.putString(EXTRA_AFFILIATION_ID, AFFILIATION_ID);
+
+        // Do not assign return value to mUserHandle, so it is not removed in tearDown.
+        UserHandle uh = mDevicePolicyManager.createAndManageUser(
+                getWho(),
+                testUserName,
+                SecondaryUserAdminReceiver.getComponentName(mContext),
+                bundle,
+                DevicePolicyManager.START_USER_IN_BACKGROUND);
+        Log.d(TAG, "User create: " + uh);
+
+        PrimaryUserService.assertCrossUserCallArrived();
+    }
+
     static class LocalBroadcastReceiver extends BroadcastReceiver {
         private SynchronousQueue<UserHandle> mQueue = new SynchronousQueue<UserHandle>();
 
         @Override
         public void onReceive(Context context, Intent intent) {
-            UserHandle userHandle = intent.getParcelableExtra(EXTRA_USER_HANDLE);
+            UserHandle userHandle = intent.getParcelableExtra(BasicAdminReceiver.EXTRA_USER_HANDLE);
             Log.d(TAG, "broadcast receiver received " + intent + " with userHandle "
                     + userHandle);
             mQueue.offer(userHandle);
@@ -318,4 +321,75 @@
             return mQueue.poll(BROADCAST_TIMEOUT, TimeUnit.MILLISECONDS);
         }
     }
+
+    public static final class PrimaryUserService extends Service {
+        private static final Semaphore mSemaphore = new Semaphore(0);
+
+        private final ICrossUserService.Stub mBinder = new ICrossUserService.Stub() {
+            public void onEnabledCalled() {
+                Log.d(TAG, "onEnabledCalled on primary user");
+                mSemaphore.release();
+            }
+        };
+
+        @Override
+        public IBinder onBind(Intent intent) {
+            return mBinder;
+        }
+
+        static void assertCrossUserCallArrived() throws Exception {
+            assertTrue(mSemaphore.tryAcquire(ON_ENABLED_TIMEOUT_SECONDS, TimeUnit.SECONDS));
+        }
+    }
+
+    public static final class SecondaryUserAdminReceiver extends DeviceAdminReceiver {
+        @Override
+        public void onEnabled(Context context, Intent intent) {
+            Log.d(TAG, "onEnabled called");
+            DevicePolicyManager dpm = context.getSystemService(DevicePolicyManager.class);
+            // Set affiliation ids
+            String affiliationId = intent.getStringExtra(EXTRA_AFFILIATION_ID);
+            dpm.setAffiliationIds(getComponentName(context),
+                    Collections.singleton(affiliationId));
+            // Call all affiliated users
+            final List<UserHandle> targetUsers = dpm.getBindDeviceAdminTargetUsers(
+                    getComponentName(context));
+            assertEquals(1, targetUsers.size());
+            pingTargetUser(context, dpm, targetUsers.get(0));
+        }
+
+        private void pingTargetUser(Context context, DevicePolicyManager dpm, UserHandle target) {
+            Log.d(TAG, "Pinging target: " + target);
+            final ServiceConnection serviceConnection = new ServiceConnection() {
+                @Override
+                public void onServiceConnected(ComponentName name, IBinder service) {
+                    Log.d(TAG, "onServiceConnected is called in " + Thread.currentThread().getName());
+                    ICrossUserService crossUserService = ICrossUserService
+                            .Stub.asInterface(service);
+                    try {
+                        crossUserService.onEnabledCalled();
+                    } catch (RemoteException re) {
+                        Log.e(TAG, "Error when calling primary user", re);
+                        // Do nothing, primary user will time out
+                    }
+                }
+
+                @Override
+                public void onServiceDisconnected(ComponentName name) {
+                    Log.d(TAG, "onServiceDisconnected is called");
+                }
+            };
+            final Intent serviceIntent = new Intent(context, PrimaryUserService.class);
+            assertTrue(dpm.bindDeviceAdminServiceAsUser(
+                    getComponentName(context),
+                    serviceIntent,
+                    serviceConnection,
+                    Context.BIND_AUTO_CREATE,
+                    target));
+        }
+
+        public static ComponentName getComponentName(Context context) {
+            return new ComponentName(context, SecondaryUserAdminReceiver.class);
+        }
+    }
 }
diff --git a/hostsidetests/devicepolicy/app/DeviceOwner/src/com/android/cts/deviceowner/ICrossUserService.aidl b/hostsidetests/devicepolicy/app/DeviceOwner/src/com/android/cts/deviceowner/ICrossUserService.aidl
new file mode 100644
index 0000000..0cf5dcf
--- /dev/null
+++ b/hostsidetests/devicepolicy/app/DeviceOwner/src/com/android/cts/deviceowner/ICrossUserService.aidl
@@ -0,0 +1,21 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.cts.deviceowner;
+
+interface ICrossUserService {
+    void onEnabledCalled();
+}
\ No newline at end of file
diff --git a/hostsidetests/devicepolicy/app/DeviceOwner/src/com/android/cts/deviceowner/KeyManagementTest.java b/hostsidetests/devicepolicy/app/DeviceOwner/src/com/android/cts/deviceowner/KeyManagementTest.java
index 0b783a0..4050f69 100755
--- a/hostsidetests/devicepolicy/app/DeviceOwner/src/com/android/cts/deviceowner/KeyManagementTest.java
+++ b/hostsidetests/devicepolicy/app/DeviceOwner/src/com/android/cts/deviceowner/KeyManagementTest.java
@@ -15,41 +15,52 @@
  */
 package com.android.cts.deviceowner;
 
+import static android.keystore.cts.CertificateUtils.createCertificate;
 import static com.android.compatibility.common.util.FakeKeys.FAKE_RSA_1;
-import static com.android.cts.deviceowner.BaseDeviceOwnerTest.getWho;
+import static android.app.admin.DevicePolicyManager.ID_TYPE_SERIAL;
 
-import android.app.Activity;
 import android.app.admin.DevicePolicyManager;
+import android.content.ComponentName;
+import android.content.Context;
+import android.content.res.AssetManager;
+import android.keystore.cts.Attestation;
 import android.net.Uri;
+import android.security.AttestedKeyPair;
 import android.security.KeyChain;
 import android.security.KeyChainAliasCallback;
 import android.security.KeyChainException;
+import android.security.keystore.KeyGenParameterSpec;
+import android.security.keystore.KeyProperties;
 import android.test.ActivityInstrumentationTestCase2;
 
 import java.io.ByteArrayInputStream;
 import java.io.ByteArrayOutputStream;
-import java.io.DataInputStream;
 import java.io.IOException;
 import java.io.InputStream;
 import java.io.UnsupportedEncodingException;
 import java.net.URLEncoder;
-import java.security.cert.CertificateException;
-import java.security.cert.CertificateFactory;
-import java.security.cert.X509Certificate;
-import java.security.cert.Certificate;
+import java.security.GeneralSecurityException;
 import java.security.KeyFactory;
+import java.security.KeyPair;
 import java.security.NoSuchAlgorithmException;
 import java.security.PrivateKey;
+import java.security.PublicKey;
+import java.security.Signature;
+import java.security.cert.Certificate;
+import java.security.cert.CertificateException;
+import java.security.cert.CertificateFactory;
+import java.security.cert.CertificateParsingException;
+import java.security.cert.X509Certificate;
 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.concurrent.CountDownLatch;
 import java.util.concurrent.TimeUnit;
-
-import android.content.ComponentName;
-import android.content.Context;
-import android.content.res.AssetManager;
+import java.util.List;
+import java.util.Set;
+import javax.security.auth.x500.X500Principal;
 
 public class KeyManagementTest extends ActivityInstrumentationTestCase2<KeyManagementActivity> {
 
@@ -67,7 +78,7 @@
         // Confirm our DeviceOwner is set up
         mDevicePolicyManager = (DevicePolicyManager)
                 getActivity().getSystemService(Context.DEVICE_POLICY_SERVICE);
-        BaseDeviceOwnerTest.assertDeviceOwner(mDevicePolicyManager);
+        assertDeviceOwner(mDevicePolicyManager);
 
         // Hostside test has set a device lockscreen in order to enable credential storage
     }
@@ -129,17 +140,23 @@
         assertGranted(withhold, false);
     }
 
+    private List<Certificate> loadCertificateChain(String assetName) throws Exception {
+        final Collection<Certificate> certs = loadCertificatesFromAsset(assetName);
+        final ArrayList<Certificate> certChain = new ArrayList(certs);
+        // Some sanity check on the cert chain
+        assertTrue(certs.size() > 1);
+        for (int i = 1; i < certChain.size(); i++) {
+            certChain.get(i - 1).verify(certChain.get(i).getPublicKey());
+        }
+        return certChain;
+    }
+
     public void testCanInstallCertChain() throws Exception {
         // Use assets/generate-client-cert-chain.sh to regenerate the client cert chain.
         final PrivateKey privKey = loadPrivateKeyFromAsset("user-cert-chain.key");
-        final Collection<Certificate> certs = loadCertificatesFromAsset("user-cert-chain.crt");
-        final Certificate[] certChain = certs.toArray(new Certificate[certs.size()]);
+        final Certificate[] certChain = loadCertificateChain("user-cert-chain.crt")
+                .toArray(new Certificate[0]);
         final String alias = "com.android.test.clientkeychain";
-        // Some sanity check on the cert chain
-        assertTrue(certs.size() > 1);
-        for (int i = 1; i < certs.size(); i++) {
-            certChain[i - 1].verify(certChain[i].getPublicKey());
-        }
 
         // Install keypairs.
         assertTrue(mDevicePolicyManager.installKeyPair(getWho(), privKey, certChain, alias, true));
@@ -215,11 +232,231 @@
         }
     }
 
+    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 Certificate cert = getCertificate(FAKE_RSA_1.caCertificate);
+
+        // Install keypair.
+        assertTrue(mDevicePolicyManager.installKeyPair(
+            getWho(), privKey, new Certificate[] {cert}, alias, false, false));
+        try {
+            // Request and retrieve using the alias.
+            assertGranted(alias, false);
+            assertEquals(alias, new KeyChainAliasFuture(alias).get());
+            assertGranted(alias, true);
+        } finally {
+            // Delete regardless of whether the test succeeded.
+            assertTrue(mDevicePolicyManager.removeKeyPair(getWho(), alias));
+        }
+    }
+
+    byte[] signDataWithKey(String algoIdentifier, PrivateKey privateKey) throws Exception {
+        byte[] data = new String("hello").getBytes();
+        Signature sign = Signature.getInstance(algoIdentifier);
+        sign.initSign(privateKey);
+        sign.update(data);
+        return sign.sign();
+    }
+
+    void verifySignature(String algoIdentifier, PublicKey publicKey, byte[] signature)
+            throws Exception {
+        byte[] data = new String("hello").getBytes();
+        Signature verify = Signature.getInstance(algoIdentifier);
+        verify.initVerify(publicKey);
+        verify.update(data);
+        assertTrue(verify.verify(signature));
+    }
+
+    void verifySignatureOverData(String algoIdentifier, KeyPair keyPair) throws Exception {
+        verifySignature(algoIdentifier, keyPair.getPublic(),
+                signDataWithKey(algoIdentifier, keyPair.getPrivate()));
+    }
+
+    public void testCanGenerateRSAKeyPair() throws Exception {
+        final String alias = "com.android.test.generated-rsa-1";
+        try {
+            KeyGenParameterSpec spec = new KeyGenParameterSpec.Builder(
+                    alias,
+                    KeyProperties.PURPOSE_SIGN | KeyProperties.PURPOSE_VERIFY)
+                    .setKeySize(2048)
+                    .setDigests(KeyProperties.DIGEST_SHA256)
+                    .setSignaturePaddings(KeyProperties.SIGNATURE_PADDING_RSA_PSS,
+                        KeyProperties.SIGNATURE_PADDING_RSA_PKCS1)
+                    .build();
+
+            AttestedKeyPair generated = mDevicePolicyManager.generateKeyPair(
+                    getWho(), "RSA", spec, 0);
+            assertNotNull(generated);
+            verifySignatureOverData("SHA256withRSA", generated.getKeyPair());
+        } finally {
+            assertTrue(mDevicePolicyManager.removeKeyPair(getWho(), alias));
+        }
+    }
+
+    public void testCanGenerateECKeyPair() throws Exception {
+        final String alias = "com.android.test.generated-ec-1";
+        try {
+            KeyGenParameterSpec spec = new KeyGenParameterSpec.Builder(
+                    alias,
+                    KeyProperties.PURPOSE_SIGN | KeyProperties.PURPOSE_VERIFY)
+                    .setDigests(KeyProperties.DIGEST_SHA256)
+                    .build();
+
+            AttestedKeyPair generated = mDevicePolicyManager.generateKeyPair(
+                    getWho(), "EC", spec, 0);
+            assertNotNull(generated);
+            verifySignatureOverData("SHA256withECDSA", generated.getKeyPair());
+        } finally {
+            assertTrue(mDevicePolicyManager.removeKeyPair(getWho(), alias));
+        }
+    }
+
+    private void validateAttestationRecord(List<Certificate> attestation,
+            byte[] providedChallenge) throws CertificateParsingException {
+        assertNotNull(attestation);
+        assertTrue(attestation.size() >= 2);
+        X509Certificate leaf = (X509Certificate) attestation.get(0);
+        Attestation attestationRecord = new Attestation(leaf);
+        assertTrue(Arrays.equals(providedChallenge,
+                    attestationRecord.getAttestationChallenge()));
+    }
+
+    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()));
+        // Check that the certificate chain is valid.
+        for (int i = 1; i < chain.size(); i++) {
+            X509Certificate intermediate = (X509Certificate) chain.get(i);
+            PublicKey intermediateKey = intermediate.getPublicKey();
+            leaf.verify(intermediateKey);
+            leaf = intermediate;
+        }
+
+        // leaf is now the root, verify the root is self-signed.
+        PublicKey rootKey = leaf.getPublicKey();
+        leaf.verify(rootKey);
+    }
+
+    public void testCanGenerateECKeyPairWithKeyAttestation() throws Exception {
+        final String alias = "com.android.test.attested-ec-1";
+        byte[] attestationChallenge = new byte[] {0x01, 0x02, 0x03};
+        try {
+            KeyGenParameterSpec spec = new KeyGenParameterSpec.Builder(
+                    alias,
+                    KeyProperties.PURPOSE_SIGN | KeyProperties.PURPOSE_VERIFY)
+                    .setDigests(KeyProperties.DIGEST_SHA256)
+                    .setAttestationChallenge(attestationChallenge)
+                    .build();
+            AttestedKeyPair generated = mDevicePolicyManager.generateKeyPair(
+                    getWho(), "EC", spec, 0);
+            assertNotNull(generated);
+            final KeyPair keyPair = generated.getKeyPair();
+            final String algorithmIdentifier = "SHA256withECDSA";
+            verifySignatureOverData(algorithmIdentifier, keyPair);
+            List<Certificate> attestation = generated.getAttestationRecord();
+            validateAttestationRecord(attestation, attestationChallenge);
+            validateSignatureChain(attestation, keyPair.getPublic());
+        } finally {
+            assertTrue(mDevicePolicyManager.removeKeyPair(getWho(), alias));
+        }
+    }
+
+    public void testCanGenerateECKeyPairWithDeviceIdAttestation() throws Exception {
+        final String alias = "com.android.test.devid-attested-ec-1";
+        byte[] attestationChallenge = new byte[] {0x01, 0x02, 0x03};
+        try {
+            KeyGenParameterSpec spec = new KeyGenParameterSpec.Builder(
+                    alias,
+                    KeyProperties.PURPOSE_SIGN | KeyProperties.PURPOSE_VERIFY)
+                    .setDigests(KeyProperties.DIGEST_SHA256)
+                    .setAttestationChallenge(attestationChallenge)
+                    .build();
+            AttestedKeyPair generated = mDevicePolicyManager.generateKeyPair(
+                    getWho(), "EC", spec, ID_TYPE_SERIAL);
+            if (generated == null) {
+                // Since supporting Device ID attestation is optional, do not fail the test if no
+                // attestation record was generated.
+                return;
+            }
+            final KeyPair keyPair = generated.getKeyPair();
+            verifySignatureOverData("SHA256withECDSA", keyPair);
+            List<Certificate> attestation = generated.getAttestationRecord();
+            validateAttestationRecord(attestation, attestationChallenge);
+            validateSignatureChain(attestation, keyPair.getPublic());
+        } finally {
+            assertTrue(mDevicePolicyManager.removeKeyPair(getWho(), alias));
+        }
+    }
+
+    public void testCanSetKeyPairCert() throws Exception {
+        final String alias = "com.android.test.set-ec-1";
+        try {
+            KeyGenParameterSpec spec = new KeyGenParameterSpec.Builder(
+                    alias,
+                    KeyProperties.PURPOSE_SIGN | KeyProperties.PURPOSE_VERIFY)
+                    .setDigests(KeyProperties.DIGEST_SHA256)
+                    .build();
+
+            AttestedKeyPair generated = mDevicePolicyManager.generateKeyPair(
+                    getWho(), "EC", spec, 0);
+            assertNotNull(generated);
+            // 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");
+            X509Certificate cert = createCertificate(generated.getKeyPair(), subject, issuer);
+            // Set the certificate chain
+            List<Certificate> certs = new ArrayList<Certificate>();
+            certs.add(cert);
+            mDevicePolicyManager.setKeyPairCertificate(getWho(), alias, certs, true);
+            // Make sure that the alias can now be obtained.
+            assertEquals(alias, new KeyChainAliasFuture(alias).get());
+            // And can be retrieved from KeyChain
+            X509Certificate[] fetchedCerts = KeyChain.getCertificateChain(getActivity(), alias);
+            assertEquals(fetchedCerts.length, certs.size());
+            assertTrue(Arrays.equals(fetchedCerts[0].getEncoded(), certs.get(0).getEncoded()));
+        } finally {
+            assertTrue(mDevicePolicyManager.removeKeyPair(getWho(), alias));
+        }
+    }
+
+    public void testCanSetKeyPairCertChain() throws Exception {
+        final String alias = "com.android.test.set-ec-2";
+        try {
+            KeyGenParameterSpec spec = new KeyGenParameterSpec.Builder(
+                    alias,
+                    KeyProperties.PURPOSE_SIGN | KeyProperties.PURPOSE_VERIFY)
+                    .setDigests(KeyProperties.DIGEST_SHA256)
+                    .build();
+
+            AttestedKeyPair generated = mDevicePolicyManager.generateKeyPair(
+                    getWho(), "EC", spec, 0);
+            assertNotNull(generated);
+            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());
+            // And can be retrieved from KeyChain
+            X509Certificate[] fetchedCerts = KeyChain.getCertificateChain(getActivity(), alias);
+            assertEquals(fetchedCerts.length, chain.size());
+            for (int i = 0; i < chain.size(); i++) {
+                assertTrue(Arrays.equals(fetchedCerts[i].getEncoded(), chain.get(i).getEncoded()));
+            }
+        } finally {
+            assertTrue(mDevicePolicyManager.removeKeyPair(getWho(), alias));
+        }
+    }
+
     private void assertGranted(String alias, boolean expected) throws InterruptedException {
         boolean granted = false;
         try {
             granted = (KeyChain.getPrivateKey(getActivity(), alias) != null);
         } catch (KeyChainException e) {
+            if (expected) {
+                e.printStackTrace();
+            }
         }
         assertEquals("Grant for alias: \"" + alias + "\"", expected, granted);
     }
@@ -289,4 +526,15 @@
             return mChosenAlias;
         }
     }
+
+    private void assertDeviceOwner(DevicePolicyManager devicePolicyManager) {
+        assertNotNull(devicePolicyManager);
+        assertTrue(devicePolicyManager.isAdminActive(getWho()));
+        assertTrue(devicePolicyManager.isDeviceOwnerApp(getActivity().getPackageName()));
+        assertFalse(devicePolicyManager.isManagedProfile(getWho()));
+    }
+
+    private ComponentName getWho() {
+        return BasicAdminReceiver.getComponentName(getActivity());
+    }
 }
diff --git a/hostsidetests/devicepolicy/app/DeviceOwner/src/com/android/cts/deviceowner/LockTaskHostDrivenTest.java b/hostsidetests/devicepolicy/app/DeviceOwner/src/com/android/cts/deviceowner/LockTaskHostDrivenTest.java
index ce79922..f9f9a33 100644
--- a/hostsidetests/devicepolicy/app/DeviceOwner/src/com/android/cts/deviceowner/LockTaskHostDrivenTest.java
+++ b/hostsidetests/devicepolicy/app/DeviceOwner/src/com/android/cts/deviceowner/LockTaskHostDrivenTest.java
@@ -45,10 +45,6 @@
 
     private static final String TAG = LockTaskHostDrivenTest.class.getName();
 
-    private static final String PACKAGE_NAME = LockTaskHostDrivenTest.class.getPackage().getName();
-    private static final ComponentName ADMIN_COMPONENT =
-            new ComponentName(PACKAGE_NAME, BaseDeviceOwnerTest.BasicAdminReceiver.class.getName());
-
     private static final String LOCK_TASK_ACTIVITY
             = LockTaskUtilityActivityIfWhitelisted.class.getName();
 
@@ -60,7 +56,7 @@
     @Before
     public void setUp() {
         mContext = InstrumentationRegistry.getContext();
-        mDevicePolicyManager =  mContext.getSystemService(DevicePolicyManager.class);
+        mDevicePolicyManager = mContext.getSystemService(DevicePolicyManager.class);
         mActivityManager = mContext.getSystemService(ActivityManager.class);
         mUiDevice = UiDevice.getInstance(InstrumentationRegistry.getInstrumentation());
     }
@@ -96,9 +92,11 @@
 
     @Test
     public void clearDefaultHomeIntentReceiver() {
-        mDevicePolicyManager.clearPackagePersistentPreferredActivities(ADMIN_COMPONENT,
-                PACKAGE_NAME);
-        mDevicePolicyManager.setLockTaskPackages(ADMIN_COMPONENT, new String[0]);
+        mDevicePolicyManager.clearPackagePersistentPreferredActivities(
+                BasicAdminReceiver.getComponentName(mContext),
+                mContext.getPackageName());
+        mDevicePolicyManager.setLockTaskPackages(BasicAdminReceiver.getComponentName(mContext),
+                new String[0]);
     }
 
     private void checkLockedActivityIsRunning() throws Exception {
@@ -113,19 +111,20 @@
     }
 
     private void launchLockTaskActivity() {
-        Intent intent = new Intent();
-        intent.setClassName(PACKAGE_NAME, LOCK_TASK_ACTIVITY);
+        Intent intent = new Intent(mContext, LockTaskUtilityActivityIfWhitelisted.class);
         intent.addFlags(Intent.FLAG_ACTIVITY_SINGLE_TOP | Intent.FLAG_ACTIVITY_NEW_TASK);
         intent.putExtra(LockTaskUtilityActivity.START_LOCK_TASK, true);
         mContext.startActivity(intent);
     }
 
     private void setDefaultHomeIntentReceiver() {
-        mDevicePolicyManager.setLockTaskPackages(ADMIN_COMPONENT, new String[] { PACKAGE_NAME });
+        mDevicePolicyManager.setLockTaskPackages(BasicAdminReceiver.getComponentName(mContext),
+                new String[]{mContext.getPackageName()});
         IntentFilter intentFilter = new IntentFilter(Intent.ACTION_MAIN);
         intentFilter.addCategory(Intent.CATEGORY_HOME);
         intentFilter.addCategory(Intent.CATEGORY_DEFAULT);
-        mDevicePolicyManager.addPersistentPreferredActivity(ADMIN_COMPONENT, intentFilter,
-                new ComponentName(PACKAGE_NAME, LOCK_TASK_ACTIVITY));
+        mDevicePolicyManager.addPersistentPreferredActivity(
+                BasicAdminReceiver.getComponentName(mContext), intentFilter,
+                new ComponentName(mContext.getPackageName(), LOCK_TASK_ACTIVITY));
     }
 }
diff --git a/hostsidetests/devicepolicy/app/DeviceOwner/src/com/android/cts/deviceowner/LockTaskTest.java b/hostsidetests/devicepolicy/app/DeviceOwner/src/com/android/cts/deviceowner/LockTaskTest.java
index 9c5380a..6ef2ba1 100644
--- a/hostsidetests/devicepolicy/app/DeviceOwner/src/com/android/cts/deviceowner/LockTaskTest.java
+++ b/hostsidetests/devicepolicy/app/DeviceOwner/src/com/android/cts/deviceowner/LockTaskTest.java
@@ -15,14 +15,22 @@
  */
 package com.android.cts.deviceowner;
 
+import static android.app.admin.DevicePolicyManager.LOCK_TASK_FEATURE_GLOBAL_ACTIONS;
+import static android.app.admin.DevicePolicyManager.LOCK_TASK_FEATURE_HOME;
+import static android.app.admin.DevicePolicyManager.LOCK_TASK_FEATURE_KEYGUARD;
+import static android.app.admin.DevicePolicyManager.LOCK_TASK_FEATURE_NONE;
+import static android.app.admin.DevicePolicyManager.LOCK_TASK_FEATURE_NOTIFICATIONS;
+import static android.app.admin.DevicePolicyManager.LOCK_TASK_FEATURE_RECENTS;
+import static android.app.admin.DevicePolicyManager.LOCK_TASK_FEATURE_SYSTEM_INFO;
+
 import static junit.framework.Assert.assertEquals;
 import static junit.framework.Assert.assertFalse;
 import static junit.framework.Assert.assertTrue;
 
 import static org.junit.Assert.assertArrayEquals;
 
-import android.app.Activity;
 import android.app.ActivityManager;
+import android.app.ActivityOptions;
 import android.app.admin.DevicePolicyManager;
 import android.content.BroadcastReceiver;
 import android.content.ComponentName;
@@ -39,6 +47,8 @@
 import org.junit.Test;
 import org.junit.runner.RunWith;
 
+import java.util.concurrent.TimeUnit;
+
 @RunWith(AndroidJUnit4.class)
 public class LockTaskTest {
 
@@ -46,7 +56,7 @@
 
     private static final String PACKAGE_NAME = LockTaskTest.class.getPackage().getName();
     private static final ComponentName ADMIN_COMPONENT =
-            new ComponentName(PACKAGE_NAME, BaseDeviceOwnerTest.BasicAdminReceiver.class.getName());
+            new ComponentName(PACKAGE_NAME, BasicAdminReceiver.class.getName());
     private static final String TEST_PACKAGE = "com.google.android.example.somepackage";
 
     private static final String UTILITY_ACTIVITY
@@ -54,18 +64,23 @@
     private static final String UTILITY_ACTIVITY_IF_WHITELISTED
             = "com.android.cts.deviceowner.LockTaskUtilityActivityIfWhitelisted";
 
-    private static final String RECEIVING_ACTIVITY_PACKAGE_NAME
-            = "com.android.cts.intent.receiver";
-    private static final String RECEIVING_ACTIVITY_NAME
-            = "com.android.cts.intent.receiver.IntentReceiverActivity";
+    private static final String RECEIVER_ACTIVITY_PACKAGE_NAME =
+            "com.android.cts.intent.receiver";
+    private static final String RECEIVER_ACTIVITY_NAME =
+            "com.android.cts.intent.receiver.IntentReceiverActivity";
     private static final String ACTION_JUST_CREATE =
             "com.android.cts.action.JUST_CREATE";
+    private static final String ACTION_CREATE_AND_WAIT =
+            "com.android.cts.action.CREATE_AND_WAIT";
+    private static final String RECEIVER_ACTIVITY_CREATED_ACTION =
+            "com.android.cts.deviceowner.action.RECEIVER_ACTIVITY_CREATED";
+    private static final String RECEIVER_ACTIVITY_DESTROYED_ACTION =
+            "com.android.cts.deviceowner.action.RECEIVER_ACTIVITY_DESTROYED";
 
-    private static final int ACTIVITY_RESUMED_TIMEOUT_MILLIS = 20000;  // 20 seconds
-    private static final int ACTIVITY_RUNNING_TIMEOUT_MILLIS = 10000;  // 10 seconds
-    private static final int ACTIVITY_DESTROYED_TIMEOUT_MILLIS = 60000;  // 60 seconds
-    public static final String RECEIVING_ACTIVITY_CREATED_ACTION
-            = "com.android.cts.deviceowner.RECEIVING_ACTIVITY_CREATED_ACTION";
+    private static final long ACTIVITY_RESUMED_TIMEOUT_MILLIS = TimeUnit.SECONDS.toMillis(20);
+    private static final long ACTIVITY_RUNNING_TIMEOUT_MILLIS = TimeUnit.SECONDS.toMillis(10);
+    private static final long ACTIVITY_DESTROYED_TIMEOUT_MILLIS = TimeUnit.SECONDS.toMillis(60);
+
     /**
      * The tests below need to keep detailed track of the state of the activity
      * that is started and stopped frequently.  To do this it sends a number of
@@ -105,31 +120,27 @@
                     mIntentHandled = true;
                     LockTaskTest.this.notify();
                 }
-            } else if (RECEIVING_ACTIVITY_CREATED_ACTION.equals(action)) {
-                synchronized(mReceivingActivityCreatedLock) {
-                    mReceivingActivityWasCreated = true;
-                    mReceivingActivityCreatedLock.notify();
+            } else if (RECEIVER_ACTIVITY_CREATED_ACTION.equals(action)) {
+                synchronized(mReceiverActivityRunningLock) {
+                    mIsReceiverActivityRunning = true;
+                    mReceiverActivityRunningLock.notify();
+                }
+            } else if (RECEIVER_ACTIVITY_DESTROYED_ACTION.equals(action)) {
+                synchronized (mReceiverActivityRunningLock) {
+                    mIsReceiverActivityRunning = false;
+                    mReceiverActivityRunningLock.notify();
                 }
             }
         }
     };
 
-    public static class IntentReceivingActivity extends Activity {
-        @Override
-        public void onCreate(Bundle savedInstanceState) {
-            super.onCreate(savedInstanceState);
-            sendBroadcast(new Intent(RECEIVING_ACTIVITY_CREATED_ACTION));
-            finish();
-        }
-    }
-
     private volatile boolean mIsActivityRunning;
     private volatile boolean mIsActivityResumed;
-    private volatile boolean mReceivingActivityWasCreated;
+    private volatile boolean mIsReceiverActivityRunning;
     private volatile boolean mIntentHandled;
     private final Object mActivityRunningLock = new Object();
     private final Object mActivityResumedLock = new Object();
-    private final Object mReceivingActivityCreatedLock = new Object();
+    private final Object mReceiverActivityRunningLock = new Object();
 
     private Context mContext;
     private ActivityManager mActivityManager;
@@ -139,17 +150,17 @@
     public void setUp() {
         mContext = InstrumentationRegistry.getContext();
 
-        mDevicePolicyManager = (DevicePolicyManager)
-                mContext.getSystemService(Context.DEVICE_POLICY_SERVICE);
+        mDevicePolicyManager = mContext.getSystemService(DevicePolicyManager.class);
         mDevicePolicyManager.setLockTaskPackages(ADMIN_COMPONENT, new String[0]);
-        mActivityManager = (ActivityManager) mContext.getSystemService(Context.ACTIVITY_SERVICE);
+        mActivityManager = mContext.getSystemService(ActivityManager.class);
         IntentFilter filter = new IntentFilter();
         filter.addAction(LockTaskUtilityActivity.CREATE_ACTION);
         filter.addAction(LockTaskUtilityActivity.DESTROY_ACTION);
         filter.addAction(LockTaskUtilityActivity.INTENT_ACTION);
         filter.addAction(LockTaskUtilityActivity.RESUME_ACTION);
         filter.addAction(LockTaskUtilityActivity.PAUSE_ACTION);
-        filter.addAction(RECEIVING_ACTIVITY_CREATED_ACTION);
+        filter.addAction(RECEIVER_ACTIVITY_CREATED_ACTION);
+        filter.addAction(RECEIVER_ACTIVITY_DESTROYED_ACTION);
         mContext.registerReceiver(mReceiver, filter);
     }
 
@@ -172,6 +183,34 @@
         assertFalse(mDevicePolicyManager.isLockTaskPermitted(TEST_PACKAGE));
     }
 
+    // Setting and unsetting the lock task features. The actual UI behavior is tested with CTS
+    // verifier.
+    @Test
+    public void testSetLockTaskFeatures() {
+        final int[] flags = new int[] {
+                LOCK_TASK_FEATURE_SYSTEM_INFO,
+                LOCK_TASK_FEATURE_NOTIFICATIONS,
+                LOCK_TASK_FEATURE_HOME,
+                LOCK_TASK_FEATURE_RECENTS,
+                LOCK_TASK_FEATURE_GLOBAL_ACTIONS,
+                LOCK_TASK_FEATURE_KEYGUARD
+        };
+
+        int cumulative = LOCK_TASK_FEATURE_NONE;
+        for (int flag : flags) {
+            mDevicePolicyManager.setLockTaskFeatures(ADMIN_COMPONENT, flag);
+            assertEquals(flag, mDevicePolicyManager.getLockTaskFeatures(ADMIN_COMPONENT));
+
+            cumulative |= flag;
+            mDevicePolicyManager.setLockTaskFeatures(ADMIN_COMPONENT, cumulative);
+            assertEquals(cumulative, mDevicePolicyManager.getLockTaskFeatures(ADMIN_COMPONENT));
+
+            mDevicePolicyManager.setLockTaskFeatures(ADMIN_COMPONENT, LOCK_TASK_FEATURE_NONE);
+            assertEquals(LOCK_TASK_FEATURE_NONE,
+                    mDevicePolicyManager.getLockTaskFeatures(ADMIN_COMPONENT));
+        }
+    }
+
     // Start lock task, verify that ActivityManager knows thats what is going on.
     @Test
     public void testStartLockTask() throws Exception {
@@ -219,6 +258,40 @@
         assertFalse(mIsActivityResumed);
     }
 
+    // Verifies that removing the whitelist authorization immediately finishes the corresponding
+    // locked task. The other locked task(s) should remain locked.
+    @Test
+    public void testUpdateWhitelisting_twoTasks() throws Exception {
+        mDevicePolicyManager.setLockTaskPackages(ADMIN_COMPONENT, new String[] { PACKAGE_NAME,
+                RECEIVER_ACTIVITY_PACKAGE_NAME});
+
+        // Start first locked task
+        startLockTask(UTILITY_ACTIVITY);
+        waitForResume();
+
+        // Start the other task from the running activity
+        mIsReceiverActivityRunning = false;
+        Intent launchIntent = createReceiverActivityIntent(true /*newTask*/, true /*shouldWait*/);
+        mContext.startActivity(launchIntent);
+        synchronized (mReceiverActivityRunningLock) {
+            mReceiverActivityRunningLock.wait(ACTIVITY_RESUMED_TIMEOUT_MILLIS);
+            assertTrue(mIsReceiverActivityRunning);
+        }
+
+        // Remove whitelist authorization of the second task
+        mDevicePolicyManager.setLockTaskPackages(ADMIN_COMPONENT, new String[] { PACKAGE_NAME });
+        synchronized (mReceiverActivityRunningLock) {
+            mReceiverActivityRunningLock.wait(ACTIVITY_DESTROYED_TIMEOUT_MILLIS);
+            assertFalse(mIsReceiverActivityRunning);
+        }
+
+        assertLockTaskModeActive();
+        assertTrue(mIsActivityRunning);
+        assertTrue(mIsActivityResumed);
+
+        stopAndFinish(UTILITY_ACTIVITY);
+    }
+
     // This launches an activity that is in the current task.
     // This should always be permitted as a part of lock task (since it isn't a new task).
     @Test
@@ -227,15 +300,15 @@
         startLockTask(UTILITY_ACTIVITY);
         waitForResume();
 
-        mReceivingActivityWasCreated = false;
-        Intent launchIntent = getIntentReceivingActivityIntent(0);
+        mIsReceiverActivityRunning = false;
+        Intent launchIntent = createReceiverActivityIntent(false /*newTask*/, false /*shouldWait*/);
         Intent lockTaskUtility = getLockTaskUtility(UTILITY_ACTIVITY);
         lockTaskUtility.putExtra(LockTaskUtilityActivity.START_ACTIVITY, launchIntent);
         mContext.startActivity(lockTaskUtility);
 
-        synchronized (mReceivingActivityCreatedLock) {
-            mReceivingActivityCreatedLock.wait(ACTIVITY_RESUMED_TIMEOUT_MILLIS);
-            assertTrue(mReceivingActivityWasCreated);
+        synchronized (mReceiverActivityRunningLock) {
+            mReceiverActivityRunningLock.wait(ACTIVITY_RESUMED_TIMEOUT_MILLIS);
+            assertTrue(mIsReceiverActivityRunning);
         }
         stopAndFinish(UTILITY_ACTIVITY);
     }
@@ -245,17 +318,16 @@
     @Test
     public void testStartActivity_outsideTaskWhitelisted() throws Exception {
         mDevicePolicyManager.setLockTaskPackages(ADMIN_COMPONENT, new String[] { PACKAGE_NAME,
-                RECEIVING_ACTIVITY_PACKAGE_NAME });
+                RECEIVER_ACTIVITY_PACKAGE_NAME});
         startLockTask(UTILITY_ACTIVITY);
         waitForResume();
 
-        mReceivingActivityWasCreated = false;
-        Intent launchIntent = getIntentReceivingActivityIntent(0);
-        launchIntent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
+        mIsReceiverActivityRunning = false;
+        Intent launchIntent = createReceiverActivityIntent(true /*newTask*/, false /*shouldWait*/);
         mContext.startActivity(launchIntent);
-        synchronized (mReceivingActivityCreatedLock) {
-            mReceivingActivityCreatedLock.wait(ACTIVITY_RESUMED_TIMEOUT_MILLIS);
-            assertTrue(mReceivingActivityWasCreated);
+        synchronized (mReceiverActivityRunningLock) {
+            mReceiverActivityRunningLock.wait(ACTIVITY_RESUMED_TIMEOUT_MILLIS);
+            assertTrue(mIsReceiverActivityRunning);
         }
         stopAndFinish(UTILITY_ACTIVITY);
     }
@@ -268,11 +340,11 @@
         startLockTask(UTILITY_ACTIVITY);
         waitForResume();
 
-        Intent launchIntent = getIntentReceivingActivityIntent(Intent.FLAG_ACTIVITY_NEW_TASK);
+        Intent launchIntent = createReceiverActivityIntent(true /*newTask*/, false /*shouldWait*/);
         mContext.startActivity(launchIntent);
         synchronized (mActivityResumedLock) {
             mActivityResumedLock.wait(ACTIVITY_RESUMED_TIMEOUT_MILLIS);
-            assertFalse(mReceivingActivityWasCreated);
+            assertFalse(mIsReceiverActivityRunning);
         }
         stopAndFinish(UTILITY_ACTIVITY);
     }
@@ -341,6 +413,27 @@
         assertFalse(mIsActivityResumed);
     }
 
+    // Start lock task with ActivityOptions
+    @Test
+    public void testActivityOptions_whitelisted() throws Exception {
+        mDevicePolicyManager.setLockTaskPackages(ADMIN_COMPONENT, new String[] { PACKAGE_NAME });
+        startLockTaskWithOptions(UTILITY_ACTIVITY);
+        waitForResume();
+
+        // Verify that activity open and activity manager is in lock task.
+        assertLockTaskModeActive();
+        assertTrue(mIsActivityRunning);
+        assertTrue(mIsActivityResumed);
+
+        stopAndFinish(UTILITY_ACTIVITY);
+    }
+
+    // Starting a non-whitelisted activity with ActivityOptions is not allowed
+    @Test(expected = SecurityException.class)
+    public void testActivityOptions_nonWhitelisted() throws Exception {
+        startLockTaskWithOptions(UTILITY_ACTIVITY);
+    }
+
     /**
      * Checks that lock task mode is active and fails the test if it isn't.
      */
@@ -412,6 +505,15 @@
     }
 
     /**
+     * Starts LockTaskUtilityActivity with {@link ActivityOptions#setLockTaskMode(boolean)}
+     */
+    private void startLockTaskWithOptions(String className) throws InterruptedException {
+        Intent intent = getLockTaskUtility(className);
+        Bundle options = ActivityOptions.makeBasic().setLockTaskMode(true).toBundle();
+        startAndWait(intent, options);
+    }
+
+    /**
      * Calls stopLockTask on the LockTaskUtilityActivity
      */
     private void stopLockTask(String className) throws InterruptedException {
@@ -435,9 +537,16 @@
      * the command.
      */
     private void startAndWait(Intent intent) throws InterruptedException {
+        startAndWait(intent, null);
+    }
+
+    /**
+     * Same as {@link #startAndWait(Intent)}, but with additional {@link ActivityOptions}.
+     */
+    private void startAndWait(Intent intent, Bundle options) throws InterruptedException {
         mIntentHandled = false;
         synchronized (this) {
-            mContext.startActivity(intent);
+            mContext.startActivity(intent, options);
             // Give 20 secs to finish.
             wait(ACTIVITY_RUNNING_TIMEOUT_MILLIS);
             assertTrue(mIntentHandled);
@@ -456,12 +565,13 @@
         return intent;
     }
 
-    private Intent getIntentReceivingActivityIntent(int flags) {
-        Intent intent = new Intent();
+    /** Create an intent to launch {@link #RECEIVER_ACTIVITY_NAME}. */
+    private Intent createReceiverActivityIntent(boolean newTask, boolean shouldWait) {
+        final Intent intent = new Intent();
         intent.setComponent(
-                new ComponentName(RECEIVING_ACTIVITY_PACKAGE_NAME, RECEIVING_ACTIVITY_NAME));
-        intent.setAction(ACTION_JUST_CREATE);
-        intent.setFlags(flags);
+                new ComponentName(RECEIVER_ACTIVITY_PACKAGE_NAME, RECEIVER_ACTIVITY_NAME));
+        intent.setAction(shouldWait ? ACTION_CREATE_AND_WAIT : ACTION_JUST_CREATE);
+        intent.setFlags(newTask ? Intent.FLAG_ACTIVITY_NEW_TASK : 0);
         return intent;
     }
 }
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 6b62e88..cb723d5 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
@@ -22,6 +22,7 @@
 import android.content.Context;
 import android.content.Intent;
 import android.content.IntentFilter;
+import android.support.test.InstrumentationRegistry;
 import android.support.v4.content.LocalBroadcastManager;
 import android.util.Log;
 
@@ -36,6 +37,7 @@
 import java.net.ServerSocket;
 import java.net.Socket;
 import java.net.URL;
+import java.util.ArrayList;
 import java.util.List;
 import java.util.concurrent.CountDownLatch;
 import java.util.concurrent.TimeUnit;
@@ -43,6 +45,7 @@
 public class NetworkLoggingTest extends BaseDeviceOwnerTest {
 
     private static final String TAG = "NetworkLoggingTest";
+    private static final String ARG_BATCH_COUNT = "batchCount";
     private static final int FAKE_BATCH_TOKEN = -666; // real batch tokens are always non-negative
     private static final int FULL_LOG_BATCH_SIZE = 1200;
     private static final String CTS_APP_PACKAGE_NAME = "com.android.cts.deviceowner";
@@ -71,20 +74,28 @@
 
         @Override
         public void onReceive(Context context, Intent intent) {
-            if (BaseDeviceOwnerTest.ACTION_NETWORK_LOGS_AVAILABLE.equals(intent.getAction())) {
-                mGenerateNetworkTraffic = false;
-                mCurrentBatchToken = intent.getLongExtra(
-                        BaseDeviceOwnerTest.EXTRA_NETWORK_LOGS_BATCH_TOKEN, FAKE_BATCH_TOKEN);
-                if (mCountDownLatch != null) {
-                    mCountDownLatch.countDown();
+            if (BasicAdminReceiver.ACTION_NETWORK_LOGS_AVAILABLE.equals(intent.getAction())) {
+                long token =
+                        intent.getLongExtra(BasicAdminReceiver.EXTRA_NETWORK_LOGS_BATCH_TOKEN,
+                                FAKE_BATCH_TOKEN);
+                // Retrieve network logs.
+                final List<NetworkEvent> events = mDevicePolicyManager.retrieveNetworkLogs(getWho(),
+                        token);
+                if (events == null) {
+                    fail("Failed to retrieve batch of network logs with batch token " + token);
+                    return;
                 }
+                if (mBatchCountDown.getCount() > 0) {
+                    mNetworkEvents.addAll(events);
+                }
+                mBatchCountDown.countDown();
             }
         }
     };
 
-    private CountDownLatch mCountDownLatch;
-    private long mCurrentBatchToken;
-    private volatile boolean mGenerateNetworkTraffic;
+    private CountDownLatch mBatchCountDown;
+    private ArrayList<NetworkEvent> mNetworkEvents = new ArrayList<>();
+    private int mBatchesRequested = 1;
 
     @Override
     protected void tearDown() throws Exception {
@@ -124,12 +135,11 @@
      * traffic, so that the batch of logs is created
      */
     public void testNetworkLoggingAndRetrieval() throws Exception {
-        mCountDownLatch = new CountDownLatch(1);
-        mCurrentBatchToken = FAKE_BATCH_TOKEN;
-        mGenerateNetworkTraffic = true;
+        mBatchesRequested = InstrumentationRegistry.getArguments().getInt(ARG_BATCH_COUNT, 1);
+        mBatchCountDown = new CountDownLatch(mBatchesRequested);
         // register a receiver that listens for DeviceAdminReceiver#onNetworkLogsAvailable()
         final IntentFilter filterNetworkLogsAvailable = new IntentFilter(
-                BaseDeviceOwnerTest.ACTION_NETWORK_LOGS_AVAILABLE);
+                BasicAdminReceiver.ACTION_NETWORK_LOGS_AVAILABLE);
         LocalBroadcastManager.getInstance(mContext).registerReceiver(mNetworkLogsReceiver,
                 filterNetworkLogsAvailable);
 
@@ -144,36 +154,41 @@
 
         // TODO: here test that facts about logging are shown in the UI
 
+        // Fetch and verify the batches of events.
+        generateBatches();
+    }
+
+    private void generateBatches() throws Exception {
         // visit websites to verify their dns lookups are logged
         for (final String url : LOGGED_URLS_LIST) {
             connectToWebsite(url);
         }
 
-        // generate enough traffic to fill a batch.
-        int dummyReqNo = generateDummyTraffic();
+        // generate enough traffic to fill the batches.
+        int dummyReqNo = 0;
+        for (int i = 0; i < mBatchesRequested; i++) {
+            dummyReqNo += generateDummyTraffic();
+        }
 
         // if DeviceAdminReceiver#onNetworkLogsAvailable() hasn't been triggered yet, wait for up to
-        // 3 minutes just in case
-        mCountDownLatch.await(3, TimeUnit.MINUTES);
+        // 3 minutes per batch just in case
+        int timeoutMins = 3 * mBatchesRequested;
+        mBatchCountDown.await(timeoutMins, TimeUnit.MINUTES);
         LocalBroadcastManager.getInstance(mContext).unregisterReceiver(mNetworkLogsReceiver);
-        if (mGenerateNetworkTraffic) {
-            fail("Carried out 100 iterations and waited for 3 minutes, but still didn't get"
+        if (mBatchCountDown.getCount() > 0) {
+            fail("Generated events for " + mBatchesRequested + " batches and waited for "
+                    + timeoutMins + " minutes, but still didn't get"
                     + " DeviceAdminReceiver#onNetworkLogsAvailable() callback");
         }
 
-        // retrieve and verify network logs
-        final List<NetworkEvent> networkEvents = mDevicePolicyManager.retrieveNetworkLogs(getWho(),
-                mCurrentBatchToken);
-        if (networkEvents == null) {
-            fail("Failed to retrieve batch of network logs with batch token " + mCurrentBatchToken);
-            return;
-        }
-
+        // Verify network logs.
+        assertEquals("First event has the wrong id.", 0L, mNetworkEvents.get(0).getId());
         // For each of the real URLs we have two events: one DNS and one connect. Dummy requests
         // don't require DNS queries.
         int eventsExpected =
-                Math.min(FULL_LOG_BATCH_SIZE, 2 * LOGGED_URLS_LIST.length + dummyReqNo);
-        verifyNetworkLogs(networkEvents, eventsExpected);
+                Math.min(FULL_LOG_BATCH_SIZE * mBatchesRequested,
+                        2 * LOGGED_URLS_LIST.length + dummyReqNo);
+        verifyNetworkLogs(mNetworkEvents, eventsExpected);
     }
 
     private void verifyNetworkLogs(List<NetworkEvent> networkEvents, int eventsExpected) {
@@ -189,6 +204,10 @@
             if (i > 0) {
                 assertTrue(currentEvent.getTimestamp() >= networkEvents.get(i - 1).getTimestamp());
             }
+            // verify that the event IDs are monotonically increasing
+            if (i > 0) {
+                assertTrue(currentEvent.getId() == (networkEvents.get(i - 1).getId() + 1));
+            }
             // count how many events come from the CTS app
             if (CTS_APP_PACKAGE_NAME.equals(currentEvent.getPackageName())) {
                 ctsPackageNameCounter++;
@@ -270,8 +289,8 @@
 
     private int makeDummyRequests(int port) {
         int reqNo;
-        final String DUMMY_SERVER = "127.0.0.1:" + port + "";
-        for (reqNo = 0; reqNo < FULL_LOG_BATCH_SIZE && mGenerateNetworkTraffic; reqNo++) {
+        final String DUMMY_SERVER = "127.0.0.1:" + port;
+        for (reqNo = 0; reqNo < FULL_LOG_BATCH_SIZE && mBatchCountDown.getCount() > 0; reqNo++) {
             connectToWebsite(DUMMY_SERVER);
             try {
                 // Just to prevent choking the server.
@@ -303,7 +322,7 @@
                     output.flush();
                     output.close();
                 } catch (IOException e) {
-                    if (mGenerateNetworkTraffic) {
+                    if (mBatchCountDown.getCount() > 0) {
                         Log.w(TAG, "Failed to serve connection", e);
                     } else {
                         break;
diff --git a/hostsidetests/devicepolicy/app/DeviceOwner/src/com/android/cts/deviceowner/PackageInstallTest.java b/hostsidetests/devicepolicy/app/DeviceOwner/src/com/android/cts/deviceowner/PackageInstallTest.java
new file mode 100644
index 0000000..5280e06
--- /dev/null
+++ b/hostsidetests/devicepolicy/app/DeviceOwner/src/com/android/cts/deviceowner/PackageInstallTest.java
@@ -0,0 +1,140 @@
+package com.android.cts.deviceowner;
+
+import android.app.PendingIntent;
+import android.content.Intent;
+import android.content.IntentSender;
+import android.content.pm.PackageInstaller;
+import android.content.pm.PackageManager;
+
+import com.android.compatibility.common.util.BlockingBroadcastReceiver;
+
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.InputStream;
+import java.io.OutputStream;
+import java.util.Collections;
+
+/**
+ * Test case for package install and uninstall.
+ */
+public class PackageInstallTest extends BaseAffiliatedProfileOwnerTest {
+    private static final String TEST_APP_LOCATION =
+            "/data/local/tmp/cts/packageinstaller/CtsEmptyTestApp.apk";
+    private static final String TEST_APP_PKG = "android.packageinstaller.emptytestapp.cts";
+    private static final int REQUEST_CODE = 0;
+    private static final String ACTION_INSTALL_COMMIT =
+            "com.android.cts.deviceowner.INTENT_PACKAGE_INSTALL_COMMIT";
+    private static final int PACKAGE_INSTALLER_STATUS_UNDEFINED = -1000;
+
+    private PackageManager mPackageManager;
+    private PackageInstaller mPackageInstaller;
+    private PackageInstaller.Session mSession;
+    private BlockingBroadcastReceiver mBroadcastReceiver;
+
+    @Override
+    protected void setUp() throws Exception {
+        super.setUp();
+        mPackageManager = mContext.getPackageManager();
+        mPackageInstaller = mPackageManager.getPackageInstaller();
+        assertNotNull(mPackageInstaller);
+
+        mBroadcastReceiver = new BlockingBroadcastReceiver(mContext, ACTION_INSTALL_COMMIT);
+        mBroadcastReceiver.register();
+    }
+
+    @Override
+    protected void tearDown() throws Exception {
+        mBroadcastReceiver.unregisterQuietly();
+        if (mSession != null) {
+            mSession.abandon();
+        }
+
+        super.tearDown();
+    }
+
+    public void testPackageInstall() throws Exception {
+        assertFalse(isPackageInstalled(TEST_APP_PKG));
+
+        // Install the package.
+        installPackage(TEST_APP_LOCATION);
+
+        Intent intent = mBroadcastReceiver.awaitForBroadcast();
+        assertNotNull(intent);
+        assertEquals(PackageInstaller.STATUS_SUCCESS,
+                intent.getIntExtra(PackageInstaller.EXTRA_STATUS,
+                        PACKAGE_INSTALLER_STATUS_UNDEFINED));
+        assertEquals(TEST_APP_PKG, intent.getStringExtra(
+                PackageInstaller.EXTRA_PACKAGE_NAME));
+        assertTrue(isPackageInstalled(TEST_APP_PKG));
+    }
+
+    public void testPackageUninstall() throws Exception {
+        assertTrue(isPackageInstalled(TEST_APP_PKG));
+
+        // Uninstall the package.
+        mPackageInstaller.uninstall(TEST_APP_PKG, getCommitCallback());
+
+        Intent intent = mBroadcastReceiver.awaitForBroadcast();
+        assertNotNull(intent);
+        assertEquals(PackageInstaller.STATUS_SUCCESS,
+                intent.getIntExtra(PackageInstaller.EXTRA_STATUS,
+                        PACKAGE_INSTALLER_STATUS_UNDEFINED));
+        assertEquals(TEST_APP_PKG, intent.getStringExtra(
+                PackageInstaller.EXTRA_PACKAGE_NAME));
+        assertFalse(isPackageInstalled(TEST_APP_PKG));
+    }
+
+    public void testKeepPackageCache() throws Exception {
+        // Set keep package cache.
+        mDevicePolicyManager.setKeepUninstalledPackages(getWho(),
+                Collections.singletonList(TEST_APP_PKG));
+    }
+
+    public void testInstallExistingPackage() throws Exception {
+        assertFalse(isPackageInstalled(TEST_APP_PKG));
+
+        // Install the existing package.
+        assertTrue(mDevicePolicyManager.installExistingPackage(getWho(), TEST_APP_PKG));
+        assertTrue(isPackageInstalled(TEST_APP_PKG));
+    }
+
+    private void installPackage(String packageLocation) throws Exception {
+        PackageInstaller.SessionParams params = new PackageInstaller.SessionParams(
+                PackageInstaller.SessionParams.MODE_FULL_INSTALL);
+        params.setAppPackageName(TEST_APP_PKG);
+        int sessionId = mPackageInstaller.createSession(params);
+        mSession = mPackageInstaller.openSession(sessionId);
+
+        File file = new File(packageLocation);
+        InputStream in = new FileInputStream(file);
+        OutputStream out = mSession.openWrite("PackageInstallTest", 0, file.length());
+        byte[] buffer = new byte[65536];
+        int c;
+        while ((c = in.read(buffer)) != -1) {
+            out.write(buffer, 0, c);
+        }
+        mSession.fsync(out);
+        out.close();
+        mSession.commit(getCommitCallback());
+        mSession.close();
+    }
+
+    private IntentSender getCommitCallback() {
+        // Create a PendingIntent and use it to generate the IntentSender
+        Intent broadcastIntent = new Intent(ACTION_INSTALL_COMMIT);
+        PendingIntent pendingIntent = PendingIntent.getBroadcast(
+                mContext,
+                REQUEST_CODE,
+                broadcastIntent,
+                PendingIntent.FLAG_UPDATE_CURRENT);
+        return pendingIntent.getIntentSender();
+    }
+
+    private boolean isPackageInstalled(String packageName) {
+        try {
+            return mPackageManager.getPackageInfo(packageName, 0) != null;
+        } catch (PackageManager.NameNotFoundException e) {
+            return false;
+        }
+    }
+}
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 201f382..bc4fe8d 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
@@ -17,13 +17,14 @@
 
 import android.app.admin.SecurityLog.SecurityEvent;
 import android.os.Parcel;
-import android.os.SystemClock;
+import android.support.test.InstrumentationRegistry;
 
 import java.util.Arrays;
 import java.util.List;
-import java.util.concurrent.TimeUnit;
 
 public class SecurityLoggingTest extends BaseDeviceOwnerTest {
+    private static final String ARG_BATCH_NUMBER = "batchNumber";
+    private static final int BUFFER_ENTRIES_NOTIFICATION_LEVEL = 1024;
 
     /**
      * Test: retrieving security logs can only be done if there's one user on the device or all
@@ -56,14 +57,27 @@
      */
     public void testGetSecurityLogs() {
         List<SecurityEvent> events = mDevicePolicyManager.retrieveSecurityLogs(getWho());
+        String param = InstrumentationRegistry.getArguments().getString(ARG_BATCH_NUMBER);
+        int batchNumber = param == null ? 0 : Integer.parseInt(param);
+        verifySecurityLogs(batchNumber, events);
+    }
 
-        // There must be at least some events, e.g. PackageManager logs all process launches.
+    private static void verifySecurityLogs(int batchNumber, List<SecurityEvent> events) {
         assertTrue("Unable to get events", events != null && events.size() > 0);
-
+        assertTrue(
+                "First id in batch " + events.get(0).getId() + " is too small for the batch number "
+                        + batchNumber,
+                events.get(0).getId() >= (BUFFER_ENTRIES_NOTIFICATION_LEVEL * batchNumber));
         // We don't know much about the events, so just call public API methods.
         for (int i = 0; i < events.size(); i++) {
             SecurityEvent event = events.get(i);
 
+            // Test id for monotonically increasing.
+            if (i > 0) {
+                assertEquals("Event IDs are not monotonically increasing within the batch",
+                        events.get(i - 1).getId() + 1, event.getId());
+            }
+
             // Test parcelling: flatten to a parcel.
             Parcel p = Parcel.obtain();
             event.writeToParcel(p, 0);
@@ -81,6 +95,8 @@
                 assertEquals("Parcelling changed the result of getData",
                         event.getData(), restored.getData());
             }
+            assertEquals("Parcelling changed the result of getId",
+                    event.getId(), restored.getId());
             assertEquals("Parcelling changed the result of getTag",
                     event.getTag(), restored.getTag());
             assertEquals("Parcelling changed the result of getTimeNanos",
@@ -114,7 +130,7 @@
      * Test: retrieving security logs should be rate limited - subsequent attempts should return
      * null.
      */
-    public void testRetrievingSecurityLogsNotPossibleImmediatelyAfterPreviousSuccessfulRetrieval() {
+    public void testSecurityLoggingRetrievalRateLimited() {
         List<SecurityEvent> logs = mDevicePolicyManager.retrieveSecurityLogs(getWho());
         // if logs is null it means that that attempt was rate limited => test PASS
         if (logs != null) {
diff --git a/hostsidetests/devicepolicy/app/DeviceOwner/src/com/android/cts/deviceowner/SetPolicyActivity.java b/hostsidetests/devicepolicy/app/DeviceOwner/src/com/android/cts/deviceowner/SetPolicyActivity.java
index 028bf2e..514b9e8 100644
--- a/hostsidetests/devicepolicy/app/DeviceOwner/src/com/android/cts/deviceowner/SetPolicyActivity.java
+++ b/hostsidetests/devicepolicy/app/DeviceOwner/src/com/android/cts/deviceowner/SetPolicyActivity.java
@@ -63,7 +63,7 @@
                 getSystemService(Context.DEVICE_POLICY_SERVICE);
         String command = intent.getStringExtra(EXTRA_COMMAND);
         Log.i(TAG, "Command: \"" + command);
-        ComponentName admin = BaseDeviceOwnerTest.getWho();
+        ComponentName admin = BasicAdminReceiver.getComponentName(this);
         if (ADD_RESTRICTION_COMMAND.equals(command)) {
             String restrictionKey = intent.getStringExtra(EXTRA_RESTRICTION_KEY);
             dpm.addUserRestriction(admin, restrictionKey);
diff --git a/hostsidetests/devicepolicy/app/IntentReceiver/Android.mk b/hostsidetests/devicepolicy/app/IntentReceiver/Android.mk
index 08edf44..e475215 100644
--- a/hostsidetests/devicepolicy/app/IntentReceiver/Android.mk
+++ b/hostsidetests/devicepolicy/app/IntentReceiver/Android.mk
@@ -24,12 +24,11 @@
 
 LOCAL_SRC_FILES := $(call all-java-files-under, src)
 
-LOCAL_JAVA_LIBRARIES := android.test.runner
+LOCAL_JAVA_LIBRARIES := android.test.runner android.test.base.stubs
 
 LOCAL_STATIC_JAVA_LIBRARIES := \
     android-support-v4 \
-    ctstestrunner \
-    legacy-android-test
+    ctstestrunner
 
 LOCAL_SDK_VERSION := current
 
diff --git a/hostsidetests/devicepolicy/app/IntentReceiver/AndroidManifest.xml b/hostsidetests/devicepolicy/app/IntentReceiver/AndroidManifest.xml
index da0e16c..84c5de6 100644
--- a/hostsidetests/devicepolicy/app/IntentReceiver/AndroidManifest.xml
+++ b/hostsidetests/devicepolicy/app/IntentReceiver/AndroidManifest.xml
@@ -35,6 +35,7 @@
                 <action android:name="com.android.cts.action.NOTIFY_URI_CHANGE"/>
                 <action android:name="com.android.cts.action.OBSERVE_URI_CHANGE"/>
                 <action android:name="com.android.cts.action.JUST_CREATE" />
+                <action android:name="com.android.cts.action.CREATE_AND_WAIT" />
                 <category android:name="android.intent.category.DEFAULT" />
             </intent-filter>
         </activity>
diff --git a/hostsidetests/devicepolicy/app/IntentReceiver/src/com/android/cts/intent/receiver/ClearApplicationDataTest.java b/hostsidetests/devicepolicy/app/IntentReceiver/src/com/android/cts/intent/receiver/ClearApplicationDataTest.java
new file mode 100644
index 0000000..0d2a990
--- /dev/null
+++ b/hostsidetests/devicepolicy/app/IntentReceiver/src/com/android/cts/intent/receiver/ClearApplicationDataTest.java
@@ -0,0 +1,58 @@
+/*
+ * Copyright 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.intent.receiver;
+
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertTrue;
+
+import android.content.Context;
+import android.content.SharedPreferences;
+import android.support.test.InstrumentationRegistry;
+import android.support.test.filters.SmallTest;
+
+import org.junit.Before;
+import org.junit.Test;
+
+/**
+ * Test class that writes to shared preference and verifies that the shared preference gets cleared
+ * after DPM.clearApplicationUserData was called.
+ */
+@SmallTest
+public class ClearApplicationDataTest {
+    private static final String SHARED_PREFERENCE_NAME = "test-preference";
+    private static final String I_WAS_HERE = "I-Was-Here";
+
+    private Context mContext;
+    private SharedPreferences mSharedPrefs;
+
+    @Before
+    public void setUp() {
+        mContext = InstrumentationRegistry.getTargetContext();
+        mSharedPrefs = mContext.getSharedPreferences(SHARED_PREFERENCE_NAME, Context.MODE_PRIVATE);
+    }
+
+    @Test
+    public void testWriteToSharedPreference() {
+        mSharedPrefs.edit().putBoolean(I_WAS_HERE, true).commit();
+        assertTrue(mSharedPrefs.contains(I_WAS_HERE));
+    }
+
+    @Test
+    public void testSharedPreferenceCleared() {
+        assertFalse(mSharedPrefs.contains(I_WAS_HERE));
+    }
+}
diff --git a/hostsidetests/devicepolicy/app/IntentReceiver/src/com/android/cts/intent/receiver/IntentReceiverActivity.java b/hostsidetests/devicepolicy/app/IntentReceiver/src/com/android/cts/intent/receiver/IntentReceiverActivity.java
index a645a87..5f04be0 100644
--- a/hostsidetests/devicepolicy/app/IntentReceiver/src/com/android/cts/intent/receiver/IntentReceiverActivity.java
+++ b/hostsidetests/devicepolicy/app/IntentReceiver/src/com/android/cts/intent/receiver/IntentReceiverActivity.java
@@ -57,8 +57,14 @@
     private static final String ACTION_JUST_CREATE =
             "com.android.cts.action.JUST_CREATE";
 
-    public static final String RECEIVING_ACTIVITY_CREATED_ACTION
-            = "com.android.cts.deviceowner.RECEIVING_ACTIVITY_CREATED_ACTION";
+    private static final String ACTION_CREATE_AND_WAIT =
+            "com.android.cts.action.CREATE_AND_WAIT";
+
+    private static final String RECEIVER_ACTIVITY_CREATED_ACTION =
+            "com.android.cts.deviceowner.action.RECEIVER_ACTIVITY_CREATED";
+
+    private static final String RECEIVER_ACTIVITY_DESTROYED_ACTION =
+            "com.android.cts.deviceowner.action.RECEIVER_ACTIVITY_DESTROYED";
 
     public static final String ACTION_NOTIFY_URI_CHANGE
             = "com.android.cts.action.NOTIFY_URI_CHANGE";
@@ -131,10 +137,19 @@
                 getContentResolver().unregisterContentObserver(uriObserver);
                 handlerThread.quit();
             }
-        } else if (ACTION_JUST_CREATE.equals(action)) {
-            sendBroadcast(new Intent(RECEIVING_ACTIVITY_CREATED_ACTION));
+        } else if (ACTION_JUST_CREATE.equals(action) || ACTION_CREATE_AND_WAIT.equals(action)) {
+            sendBroadcast(new Intent(RECEIVER_ACTIVITY_CREATED_ACTION));
         }
-        finish();
+
+        if (!ACTION_CREATE_AND_WAIT.equals(action)) {
+            finish();
+        }
+    }
+
+    @Override
+    public void onDestroy() {
+        sendBroadcast(new Intent(RECEIVER_ACTIVITY_DESTROYED_ACTION));
+        super.onDestroy();
     }
 
     private class UriObserver extends ContentObserver {
diff --git a/hostsidetests/devicepolicy/app/IntentSender/Android.mk b/hostsidetests/devicepolicy/app/IntentSender/Android.mk
index b71ddfb..bb1f6cc 100644
--- a/hostsidetests/devicepolicy/app/IntentSender/Android.mk
+++ b/hostsidetests/devicepolicy/app/IntentSender/Android.mk
@@ -24,13 +24,12 @@
 
 LOCAL_SRC_FILES := $(call all-java-files-under, src)
 
-LOCAL_JAVA_LIBRARIES := android.test.runner
+LOCAL_JAVA_LIBRARIES := android.test.runner android.test.base.stubs
 
 LOCAL_STATIC_JAVA_LIBRARIES := \
 	android-support-v4 \
 	ctstestrunner \
-	ub-uiautomator \
-	legacy-android-test
+	ub-uiautomator
 
 LOCAL_SDK_VERSION := current
 
diff --git a/hostsidetests/devicepolicy/app/LauncherTests/Android.mk b/hostsidetests/devicepolicy/app/LauncherTests/Android.mk
index ed4943f..85e369b 100644
--- a/hostsidetests/devicepolicy/app/LauncherTests/Android.mk
+++ b/hostsidetests/devicepolicy/app/LauncherTests/Android.mk
@@ -24,13 +24,15 @@
 
 LOCAL_SRC_FILES := $(call all-java-files-under, src)
 
-LOCAL_JAVA_LIBRARIES := legacy-android-test cts-junit
+LOCAL_JAVA_LIBRARIES := cts-junit android.test.base.stubs
 
 LOCAL_STATIC_JAVA_LIBRARIES = \
 	android-support-v4 \
 	ctstestrunner \
 	android-support-test \
-	legacy-android-test
+	compatibility-device-util \
+	ShortcutManagerTestUtils \
+	testng
 
 LOCAL_SDK_VERSION := current
 
diff --git a/hostsidetests/devicepolicy/app/LauncherTests/src/com/android/cts/launchertests/QuietModeTest.java b/hostsidetests/devicepolicy/app/LauncherTests/src/com/android/cts/launchertests/QuietModeTest.java
new file mode 100644
index 0000000..0349594
--- /dev/null
+++ b/hostsidetests/devicepolicy/app/LauncherTests/src/com/android/cts/launchertests/QuietModeTest.java
@@ -0,0 +1,222 @@
+/*
+ * 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.launchertests;
+
+import static com.android.server.pm.shortcutmanagertest.ShortcutManagerTestUtils.setDefaultLauncher;
+
+import static junit.framework.Assert.assertFalse;
+import static junit.framework.Assert.assertNotNull;
+import static junit.framework.Assert.assertTrue;
+
+import static org.testng.Assert.assertThrows;
+
+import android.content.BroadcastReceiver;
+import android.content.ComponentName;
+import android.content.Context;
+import android.content.Intent;
+import android.os.Bundle;
+import android.os.UserHandle;
+import android.os.UserManager;
+import android.support.test.InstrumentationRegistry;
+import android.support.test.runner.AndroidJUnit4;
+import android.support.test.uiautomator.UiDevice;
+import android.text.TextUtils;
+
+import com.android.compatibility.common.util.BlockingBroadcastReceiver;
+
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+import java.util.concurrent.LinkedBlockingQueue;
+import java.util.concurrent.TimeUnit;
+
+/**
+ * Test that runs {@link UserManager#trySetQuietModeEnabled(boolean, UserHandle)} API
+ * against valid target user.
+ */
+@RunWith(AndroidJUnit4.class)
+public class QuietModeTest {
+    private static final String PARAM_TARGET_USER = "TARGET_USER";
+    private static final String PARAM_ORIGINAL_DEFAULT_LAUNCHER = "ORIGINAL_DEFAULT_LAUNCHER";
+    private static final ComponentName LAUNCHER_ACTIVITY =
+            new ComponentName(
+                    "com.android.cts.launchertests.support",
+                    "com.android.cts.launchertests.support.LauncherActivity");
+
+    private static final ComponentName COMMAND_RECEIVER =
+            new ComponentName(
+                    "com.android.cts.launchertests.support",
+                    "com.android.cts.launchertests.support.QuietModeCommandReceiver");
+
+    private UserManager mUserManager;
+    private UserHandle mTargetUser;
+    private Context mContext;
+    private String mOriginalLauncher;
+    private UiDevice mUiDevice;
+
+    @Before
+    public void setupUserManager() throws Exception {
+        mContext = InstrumentationRegistry.getTargetContext();
+        mUserManager = mContext.getSystemService(UserManager.class);
+    }
+
+    @Before
+    public void readParams() {
+        Context context = InstrumentationRegistry.getContext();
+        Bundle arguments = InstrumentationRegistry.getArguments();
+        UserManager userManager = context.getSystemService(UserManager.class);
+        final int userSn = Integer.parseInt(arguments.getString(PARAM_TARGET_USER));
+        mTargetUser = userManager.getUserForSerialNumber(userSn);
+        mOriginalLauncher = arguments.getString(PARAM_ORIGINAL_DEFAULT_LAUNCHER);
+    }
+
+    @Before
+    public void wakeupDeviceAndUnlock() throws Exception {
+        mUiDevice = UiDevice.getInstance(InstrumentationRegistry.getInstrumentation());
+        mUiDevice.wakeUp();
+        mUiDevice.pressMenu();
+    }
+
+    @Before
+    @After
+    public void revertToDefaultLauncher() throws Exception {
+        if (TextUtils.isEmpty(mOriginalLauncher)) {
+            return;
+        }
+        setDefaultLauncher(InstrumentationRegistry.getInstrumentation(), mOriginalLauncher);
+        startActivitySync(mOriginalLauncher);
+    }
+
+    @Test
+    public void testTryEnableQuietMode_defaultForegroundLauncher() throws Exception {
+        setTestAppAsDefaultLauncher();
+        startLauncherActivityInTestApp();
+
+        Intent intent = trySetQuietModeEnabled(true);
+        assertNotNull("Failed to receive ACTION_MANAGED_PROFILE_UNAVAILABLE broadcast", intent);
+        assertTrue(mUserManager.isQuietModeEnabled(mTargetUser));
+
+        intent = trySetQuietModeEnabled(false);
+        assertNotNull("Failed to receive ACTION_MANAGED_PROFILE_AVAILABLE broadcast", intent);
+        assertFalse(mUserManager.isQuietModeEnabled(mTargetUser));
+    }
+
+    @Test
+    public void testTryEnableQuietMode_notForegroundLauncher() throws InterruptedException {
+        setTestAppAsDefaultLauncher();
+
+        assertThrows(SecurityException.class, () -> trySetQuietModeEnabled(true));
+        assertFalse(mUserManager.isQuietModeEnabled(mTargetUser));
+    }
+
+    @Test
+    public void testTryEnableQuietMode_notDefaultLauncher() throws Exception {
+        startLauncherActivityInTestApp();
+
+        assertThrows(SecurityException.class, () -> trySetQuietModeEnabled(true));
+        assertFalse(mUserManager.isQuietModeEnabled(mTargetUser));
+    }
+
+    private Intent trySetQuietModeEnabled(boolean enabled) throws Exception {
+        final String action = enabled
+                ? Intent.ACTION_MANAGED_PROFILE_UNAVAILABLE
+                : Intent.ACTION_MANAGED_PROFILE_AVAILABLE;
+
+        BlockingBroadcastReceiver receiver =
+                new BlockingBroadcastReceiver(mContext, action);
+        try {
+            receiver.register();
+
+            boolean notShowingConfirmCredential = askLauncherSupportAppToSetQuietMode(enabled);
+            assertTrue(notShowingConfirmCredential);
+
+            return receiver.awaitForBroadcast();
+        } finally {
+            receiver.unregisterQuietly();
+        }
+    }
+
+    /**
+     * Ask launcher support test app to set quiet mode by sending broadcast.
+     * <p>
+     * We cannot simply make this package the launcher and call the API because instrumentation
+     * process would always considered to be in the foreground. The trick here is to send
+     * broadcast to another test app which is launcher itself and call the API through it.
+     * The receiver will then send back the result, and it should be either true, false or
+     * security-exception.
+     * <p>
+     * All the constants defined here should be aligned with
+     * com.android.cts.launchertests.support.QuietModeCommandReceiver.
+     */
+    private boolean askLauncherSupportAppToSetQuietMode(boolean enabled) throws Exception {
+        Intent intent = new Intent("toggle_quiet_mode");
+        intent.setComponent(COMMAND_RECEIVER);
+        intent.putExtra("quiet_mode", enabled);
+        intent.putExtra(Intent.EXTRA_USER, mTargetUser);
+
+        // Ask launcher support app to set quiet mode by sending broadcast.
+        LinkedBlockingQueue<String> blockingQueue = new LinkedBlockingQueue<>();
+        mContext.sendOrderedBroadcast(intent, null, new BroadcastReceiver() {
+            @Override
+            public void onReceive(Context context, Intent intent) {
+                blockingQueue.offer(getResultData());
+            }
+        }, null, 0, "", null);
+
+        // Wait for the result.
+        String result = null;
+        for (int i = 0; i < 10; i++) {
+            // Broadcast won't be delivered when the device is sleeping, so wake up the device
+            // in between each attempt.
+            wakeupDeviceAndUnlock();
+            result = blockingQueue.poll(10, TimeUnit.SECONDS);
+            if (!TextUtils.isEmpty(result)) {
+                break;
+            }
+        }
+
+        // Parse the result.
+        assertNotNull(result);
+        if ("true".equalsIgnoreCase(result)) {
+            return true;
+        } else if ("false".equalsIgnoreCase(result)) {
+            return false;
+        } else if ("security-exception".equals(result)) {
+            throw new SecurityException();
+        }
+        throw new IllegalStateException("Unexpected result : " + result);
+    }
+
+    private void startActivitySync(String activity) throws Exception {
+        mUiDevice.executeShellCommand("am start -W -n " + activity);
+    }
+
+    /**
+     * Start the launcher activity in the test app to make it foreground.
+     */
+    private void startLauncherActivityInTestApp() throws Exception {
+        startActivitySync(LAUNCHER_ACTIVITY.flattenToString());
+    }
+
+    private void setTestAppAsDefaultLauncher() {
+        setDefaultLauncher(
+                InstrumentationRegistry.getInstrumentation(),
+                LAUNCHER_ACTIVITY.flattenToString());
+    }
+}
+
diff --git a/hostsidetests/devicepolicy/app/LauncherTestsSupport/AndroidManifest.xml b/hostsidetests/devicepolicy/app/LauncherTestsSupport/AndroidManifest.xml
index dc71264..14abd1a 100644
--- a/hostsidetests/devicepolicy/app/LauncherTestsSupport/AndroidManifest.xml
+++ b/hostsidetests/devicepolicy/app/LauncherTestsSupport/AndroidManifest.xml
@@ -26,5 +26,19 @@
                 <action android:name="com.android.cts.launchertests.support.REGISTER_CALLBACK" />
             </intent-filter>
         </service>
+
+        <activity android:name=".LauncherActivity">
+            <intent-filter>
+                <action android:name="android.intent.action.MAIN"/>
+                <category android:name="android.intent.category.HOME"/>
+                <category android:name="android.intent.category.DEFAULT"/>
+            </intent-filter>
+        </activity>
+
+        <receiver android:name=".QuietModeCommandReceiver" android:exported="true">
+            <intent-filter>
+                <action android:name="toggle_quiet_mode"/>
+            </intent-filter>
+        </receiver>
     </application>
 </manifest>
diff --git a/hostsidetests/devicepolicy/app/LauncherTestsSupport/src/com/android/cts/launchertests/support/LauncherActivity.java b/hostsidetests/devicepolicy/app/LauncherTestsSupport/src/com/android/cts/launchertests/support/LauncherActivity.java
new file mode 100644
index 0000000..348e46c
--- /dev/null
+++ b/hostsidetests/devicepolicy/app/LauncherTestsSupport/src/com/android/cts/launchertests/support/LauncherActivity.java
@@ -0,0 +1,21 @@
+/*
+ * 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.launchertests.support;
+
+import android.app.Activity;
+
+public class LauncherActivity extends Activity {
+}
diff --git a/hostsidetests/devicepolicy/app/LauncherTestsSupport/src/com/android/cts/launchertests/support/QuietModeCommandReceiver.java b/hostsidetests/devicepolicy/app/LauncherTestsSupport/src/com/android/cts/launchertests/support/QuietModeCommandReceiver.java
new file mode 100644
index 0000000..460e629
--- /dev/null
+++ b/hostsidetests/devicepolicy/app/LauncherTestsSupport/src/com/android/cts/launchertests/support/QuietModeCommandReceiver.java
@@ -0,0 +1,55 @@
+/*
+ * 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.launchertests.support;
+
+import android.content.BroadcastReceiver;
+import android.content.Context;
+import android.content.Intent;
+import android.os.UserHandle;
+import android.os.UserManager;
+import android.util.Log;
+
+/**
+ * Runs {@link UserManager#trySetQuietModeEnabled(boolean, UserHandle)} APIs by receiving
+ * broadcast and returns the result back to the broadcast sender.
+ */
+public class QuietModeCommandReceiver extends BroadcastReceiver {
+    private static final String TAG = "QuietModeReceiver";
+    private static final String ACTION_TOGGLE_QUIET_MODE = "toggle_quiet_mode";
+    private static final String EXTRA_QUIET_MODE = "quiet_mode";
+    private static final String RESULT_SECURITY_EXCEPTION = "security-exception";
+
+    @Override
+    public void onReceive(Context context, Intent intent) {
+        if (!ACTION_TOGGLE_QUIET_MODE.equals(intent.getAction())) {
+            return;
+        }
+        final UserManager userManager = context.getSystemService(UserManager.class);
+        final boolean enableQuietMode = intent.getBooleanExtra(EXTRA_QUIET_MODE, false);
+        final UserHandle targetUser = intent.getParcelableExtra(Intent.EXTRA_USER);
+        String result;
+        try {
+            final boolean setQuietModeResult =
+                    userManager.trySetQuietModeEnabled(enableQuietMode, targetUser);
+            result = Boolean.toString(setQuietModeResult);
+            Log.i(TAG, "trySetQuietModeEnabled returns " + setQuietModeResult);
+        } catch (SecurityException ex) {
+            Log.i(TAG, "trySetQuietModeEnabled throws security exception", ex);
+            result = RESULT_SECURITY_EXCEPTION;
+        }
+        setResultData(result);
+    }
+}
diff --git a/hostsidetests/devicepolicy/app/ManagedProfile/Android.mk b/hostsidetests/devicepolicy/app/ManagedProfile/Android.mk
index b947a61..3e81296 100644
--- a/hostsidetests/devicepolicy/app/ManagedProfile/Android.mk
+++ b/hostsidetests/devicepolicy/app/ManagedProfile/Android.mk
@@ -24,7 +24,7 @@
 
 LOCAL_SRC_FILES := $(call all-java-files-under, src)
 
-LOCAL_JAVA_LIBRARIES := android.test.runner cts-junit
+LOCAL_JAVA_LIBRARIES := android.test.runner cts-junit android.test.base.stubs
 
 LOCAL_STATIC_JAVA_LIBRARIES = \
 	android-support-v4 \
@@ -32,8 +32,7 @@
 	compatibility-device-util \
 	ub-uiautomator \
 	android-support-test \
-	guava \
-	legacy-android-test
+	guava
 
 LOCAL_SDK_VERSION := test_current
 
diff --git a/hostsidetests/devicepolicy/app/ManagedProfile/AndroidManifest.xml b/hostsidetests/devicepolicy/app/ManagedProfile/AndroidManifest.xml
index 05ebac0..26db11b 100644
--- a/hostsidetests/devicepolicy/app/ManagedProfile/AndroidManifest.xml
+++ b/hostsidetests/devicepolicy/app/ManagedProfile/AndroidManifest.xml
@@ -145,6 +145,11 @@
             </intent-filter>
         </activity>
 
+        <activity android:name=".WebViewActivity"
+            android:process=":testProcess"/>
+
+        <activity android:name=".TimeoutActivity" android:exported="true"/>
+
         <service
             android:name=".CrossProfileNotificationListenerService"
             android:label="CrossProfileNotificationListenerService"
diff --git a/hostsidetests/devicepolicy/app/ManagedProfile/src/com/android/cts/managedprofile/BaseManagedProfileTest.java b/hostsidetests/devicepolicy/app/ManagedProfile/src/com/android/cts/managedprofile/BaseManagedProfileTest.java
index 905f7d5..f10822c 100644
--- a/hostsidetests/devicepolicy/app/ManagedProfile/src/com/android/cts/managedprofile/BaseManagedProfileTest.java
+++ b/hostsidetests/devicepolicy/app/ManagedProfile/src/com/android/cts/managedprofile/BaseManagedProfileTest.java
@@ -19,7 +19,6 @@
 import android.app.admin.DevicePolicyManager;
 import android.content.ComponentName;
 import android.content.Context;
-import android.support.test.uiautomator.UiDevice;
 import android.test.InstrumentationTestCase;
 
 /**
diff --git a/hostsidetests/devicepolicy/app/ManagedProfile/src/com/android/cts/managedprofile/CurrentApiHelper.java b/hostsidetests/devicepolicy/app/ManagedProfile/src/com/android/cts/managedprofile/CurrentApiHelper.java
index 550f1d3..4e2f58f 100644
--- a/hostsidetests/devicepolicy/app/ManagedProfile/src/com/android/cts/managedprofile/CurrentApiHelper.java
+++ b/hostsidetests/devicepolicy/app/ManagedProfile/src/com/android/cts/managedprofile/CurrentApiHelper.java
@@ -168,6 +168,9 @@
             typeName = typeName.substring(0, typeName.length() - 2);
         }
 
+        // Resolve inner classes
+        typeName = typeName.replaceAll("([A-Z].*)\\.", "$1\\$");
+
         // Remove type parameters, if any
         typeName = typeName.replaceAll("<.*>$", "");
 
diff --git a/hostsidetests/devicepolicy/app/ManagedProfile/src/com/android/cts/managedprofile/IsUsingUnifiedPasswordTest.java b/hostsidetests/devicepolicy/app/ManagedProfile/src/com/android/cts/managedprofile/IsUsingUnifiedPasswordTest.java
new file mode 100644
index 0000000..971f144
--- /dev/null
+++ b/hostsidetests/devicepolicy/app/ManagedProfile/src/com/android/cts/managedprofile/IsUsingUnifiedPasswordTest.java
@@ -0,0 +1,30 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.cts.managedprofile;
+
+/**
+ * Test to check reporting the state of profile challenge.
+ */
+public class IsUsingUnifiedPasswordTest extends BaseManagedProfileTest {
+
+    public void testNotUsingUnifiedPassword() {
+        assertFalse(mDevicePolicyManager.isUsingUnifiedPassword(ADMIN_RECEIVER_COMPONENT));
+    }
+
+    public void testUsingUnifiedPassword() {
+        assertTrue(mDevicePolicyManager.isUsingUnifiedPassword(ADMIN_RECEIVER_COMPONENT));
+    }
+}
diff --git a/hostsidetests/devicepolicy/app/ManagedProfile/src/com/android/cts/managedprofile/LockNowTest.java b/hostsidetests/devicepolicy/app/ManagedProfile/src/com/android/cts/managedprofile/LockNowTest.java
index fa74db7..fd5fcd2 100644
--- a/hostsidetests/devicepolicy/app/ManagedProfile/src/com/android/cts/managedprofile/LockNowTest.java
+++ b/hostsidetests/devicepolicy/app/ManagedProfile/src/com/android/cts/managedprofile/LockNowTest.java
@@ -18,8 +18,6 @@
 
 import android.app.admin.DevicePolicyManager;
 
-import com.android.cts.managedprofile.BaseManagedProfileTest.BasicAdminReceiver;
-
 /**
  * Test lockNow() for use in a managed profile. If called from a managed profile. lockNow() can be
  * passed a flag to evict the CE key of the profile.
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
new file mode 100644
index 0000000..a386baa
--- /dev/null
+++ b/hostsidetests/devicepolicy/app/ManagedProfile/src/com/android/cts/managedprofile/ProfileTimeoutTestHelper.java
@@ -0,0 +1,66 @@
+/*
+ * 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.managedprofile;
+
+import android.app.KeyguardManager;
+import android.app.admin.DevicePolicyManager;
+import android.content.ComponentName;
+import android.content.Context;
+import android.test.InstrumentationTestCase;
+
+import com.android.cts.managedprofile.BaseManagedProfileTest.BasicAdminReceiver;
+
+/**
+ * Helper to set lock timeouts and check if the profile is locked.
+ */
+public class ProfileTimeoutTestHelper extends InstrumentationTestCase {
+    // This should be sufficiently smaller than ManagedProfileTest.PROFILE_TIMEOUT_DELAY_SEC.
+    private static final int TIMEOUT_MS = 5_000;
+    private static final ComponentName ADMIN_COMPONENT = new ComponentName(
+            BasicAdminReceiver.class.getPackage().getName(), BasicAdminReceiver.class.getName());
+
+    private KeyguardManager mKm;
+    private DevicePolicyManager mDpm;
+
+    @Override
+    public void setUp() throws Exception {
+        super.setUp();
+        final Context context = getInstrumentation().getContext();
+        mDpm = (DevicePolicyManager) context.getSystemService(Context.DEVICE_POLICY_SERVICE);
+        mKm = (KeyguardManager) context.getSystemService(Context.KEYGUARD_SERVICE);
+    }
+
+    public void testSetWorkProfileTimeout() {
+        assertProfileOwner();
+        mDpm.setMaximumTimeToLock(ADMIN_COMPONENT, TIMEOUT_MS);
+        assertEquals("Failed to set timeout",
+                TIMEOUT_MS, mDpm.getMaximumTimeToLock(ADMIN_COMPONENT));
+    }
+
+    public void testDeviceLocked() {
+        assertTrue("Device not locked", mKm.isDeviceLocked());
+    }
+
+    public void testDeviceNotLocked() {
+        assertFalse("Device locked", mKm.isDeviceLocked());
+    }
+
+    private void assertProfileOwner() {
+        assertTrue(mDpm.isProfileOwnerApp(ADMIN_COMPONENT.getPackageName()));
+        assertTrue(mDpm.isManagedProfile(ADMIN_COMPONENT));
+    }
+}
diff --git a/hostsidetests/devicepolicy/app/ManagedProfile/src/com/android/cts/managedprofile/TimeoutActivity.java b/hostsidetests/devicepolicy/app/ManagedProfile/src/com/android/cts/managedprofile/TimeoutActivity.java
new file mode 100644
index 0000000..a574477
--- /dev/null
+++ b/hostsidetests/devicepolicy/app/ManagedProfile/src/com/android/cts/managedprofile/TimeoutActivity.java
@@ -0,0 +1,42 @@
+/*
+ * 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.managedprofile;
+
+import android.app.Activity;
+import android.os.Bundle;
+import android.view.View;
+import android.view.WindowManager;
+
+/**
+ * Activity to test profile lock timeout.
+ */
+public class TimeoutActivity extends Activity {
+    private static final String KEEP_SCREEN_ON = "keep_screen_on";
+
+    @Override
+    protected void onCreate(Bundle savedInstanceState) {
+        super.onCreate(savedInstanceState);
+
+        final View blankView = new View(this);
+        setContentView(blankView);
+
+        if (getIntent().getBooleanExtra(KEEP_SCREEN_ON, false)) {
+            getWindow().addFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON);
+        }
+    }
+}
+
diff --git a/hostsidetests/devicepolicy/app/ManagedProfile/src/com/android/cts/managedprofile/WebViewActivity.java b/hostsidetests/devicepolicy/app/ManagedProfile/src/com/android/cts/managedprofile/WebViewActivity.java
new file mode 100644
index 0000000..b5e16f4
--- /dev/null
+++ b/hostsidetests/devicepolicy/app/ManagedProfile/src/com/android/cts/managedprofile/WebViewActivity.java
@@ -0,0 +1,14 @@
+package com.android.cts.managedprofile;
+
+import android.app.Activity;
+import android.os.Bundle;
+import android.webkit.WebView;
+
+public class WebViewActivity extends Activity {
+
+  @Override
+  protected void onCreate(Bundle savedInstanceState) {
+    super.onCreate(savedInstanceState);
+    setContentView(new WebView(this));
+  }
+}
diff --git a/hostsidetests/devicepolicy/app/ManagedProfile/src/com/android/cts/managedprofile/WipeDataTest.java b/hostsidetests/devicepolicy/app/ManagedProfile/src/com/android/cts/managedprofile/WipeDataTest.java
index 7ef3a0a..26f627e 100644
--- a/hostsidetests/devicepolicy/app/ManagedProfile/src/com/android/cts/managedprofile/WipeDataTest.java
+++ b/hostsidetests/devicepolicy/app/ManagedProfile/src/com/android/cts/managedprofile/WipeDataTest.java
@@ -15,16 +15,8 @@
  */
 package com.android.cts.managedprofile;
 
-
-import com.android.cts.managedprofile.BaseManagedProfileTest.BasicAdminReceiver;
-
-import org.junit.Ignore;
-
-/**
- * Test wipeData() for use in managed profile. If called from a managed profile, wipeData() should
- * remove the current managed profile. Also, no erasing of external storage should be allowed.
- */
 public class WipeDataTest extends BaseManagedProfileTest {
+    private static final String TEST_WIPE_DATA_REASON = "cts test for WipeDataWithReason";
 
     @Override
     protected void setUp() throws Exception {
@@ -35,8 +27,25 @@
         assertTrue(mDevicePolicyManager.isProfileOwnerApp(ADMIN_RECEIVER_COMPONENT.getPackageName()));
     }
 
+    /**
+     * Test wipeData() for use in managed profile. If called from a managed profile, wipeData()
+     * should remove the current managed profile. Also, no erasing of external storage should be
+     * allowed.
+     */
     public void testWipeData() throws InterruptedException {
         mDevicePolicyManager.wipeData(0);
-        // the test that the profile will indeed be removed is done in the host.
+        // The test that the profile will indeed be removed is done in the host.
+    }
+
+    /**
+     * Test wipeDataWithReason() for use in managed profile. If called from a managed profile,
+     * wipeDataWithReason() should remove the current managed profile.In the mean time, it should
+     * send out a notification containing the reason for wiping data to user. Also, no erasing of
+     * external storage should be allowed.
+     */
+    public void testWipeDataWithReason() throws InterruptedException {
+        mDevicePolicyManager.wipeDataWithReason(0, TEST_WIPE_DATA_REASON);
+        // The test that the profile will indeed be removed is done in the host.
+        // Notification verification is done in another test.
     }
 }
diff --git a/hostsidetests/devicepolicy/app/ManagedProfile/src/com/android/cts/managedprofile/WipeDataWithReasonVerificationTest.java b/hostsidetests/devicepolicy/app/ManagedProfile/src/com/android/cts/managedprofile/WipeDataWithReasonVerificationTest.java
new file mode 100644
index 0000000..3b35cb4
--- /dev/null
+++ b/hostsidetests/devicepolicy/app/ManagedProfile/src/com/android/cts/managedprofile/WipeDataWithReasonVerificationTest.java
@@ -0,0 +1,37 @@
+package com.android.cts.managedprofile;
+
+import android.support.test.InstrumentationRegistry;
+import android.support.test.uiautomator.By;
+import android.support.test.uiautomator.UiDevice;
+import android.support.test.uiautomator.UiObjectNotFoundException;
+import android.support.test.uiautomator.Until;
+import android.test.AndroidTestCase;
+
+/**
+ * Test wipeDataWithReason() has indeed shown the notification.
+ * The function wipeDataWithReason() is called and executed in another test.
+ */
+public class WipeDataWithReasonVerificationTest extends AndroidTestCase {
+
+    private static final String WIPE_DATA_TITLE = "Work profile deleted";
+    // This reason string should be aligned with the one in WipeDataTest as this is a followup test.
+    private static final String TEST_WIPE_DATA_REASON = "cts test for WipeDataWithReason";
+    private static final long UI_TIMEOUT_MILLI = 5000;
+
+    public void testWipeDataWithReasonVerification() throws UiObjectNotFoundException,
+            InterruptedException {
+        // The function wipeDataWithReason() is called and executed in another test.
+        // The data should be wiped and notification should be sent before.
+        UiDevice uiDevice = UiDevice.getInstance(InstrumentationRegistry.getInstrumentation());
+        uiDevice.openNotification();
+        Boolean wipeDataTitleExist = uiDevice.wait(Until.hasObject(By.text(WIPE_DATA_TITLE)),
+                UI_TIMEOUT_MILLI);
+        Boolean wipeDataReasonExist = uiDevice.wait(Until.hasObject(By.text(TEST_WIPE_DATA_REASON)),
+                UI_TIMEOUT_MILLI);
+
+        // Verify the notification is there.
+        assertEquals("Wipe notification title not found", Boolean.TRUE, wipeDataTitleExist);
+        assertEquals("Wipe notification content not found",
+                Boolean.TRUE, wipeDataReasonExist);
+    }
+}
diff --git a/hostsidetests/devicepolicy/app/PackageInstaller/Android.mk b/hostsidetests/devicepolicy/app/PackageInstaller/Android.mk
index 11680e9..a52ed2a 100644
--- a/hostsidetests/devicepolicy/app/PackageInstaller/Android.mk
+++ b/hostsidetests/devicepolicy/app/PackageInstaller/Android.mk
@@ -24,13 +24,12 @@
 
 LOCAL_SRC_FILES := $(call all-java-files-under, src)
 
-LOCAL_JAVA_LIBRARIES := android.test.runner
+LOCAL_JAVA_LIBRARIES := android.test.runner android.test.base.stubs
 
 LOCAL_STATIC_JAVA_LIBRARIES := \
     android-support-v4 \
     ctstestrunner \
-    ub-uiautomator \
-    legacy-android-test
+    ub-uiautomator
 
 LOCAL_SDK_VERSION := test_current
 
diff --git a/hostsidetests/devicepolicy/app/ProfileOwner/Android.mk b/hostsidetests/devicepolicy/app/ProfileOwner/Android.mk
index 34b8e08..e19b119 100644
--- a/hostsidetests/devicepolicy/app/ProfileOwner/Android.mk
+++ b/hostsidetests/devicepolicy/app/ProfileOwner/Android.mk
@@ -24,13 +24,17 @@
 
 LOCAL_SRC_FILES := $(call all-java-files-under, src)
 
-LOCAL_JAVA_LIBRARIES := android.test.runner conscrypt cts-junit
+LOCAL_JAVA_LIBRARIES := \
+    android.test.runner \
+    conscrypt \
+    cts-junit \
+    android.test.base.stubs \
+
 
 LOCAL_STATIC_JAVA_LIBRARIES := \
     ctstestrunner \
     compatibility-device-util \
-    ub-uiautomator \
-    legacy-android-test
+    ub-uiautomator
 
 LOCAL_SDK_VERSION := test_current
 
diff --git a/hostsidetests/devicepolicy/app/SingleAdminApp/Android.mk b/hostsidetests/devicepolicy/app/SingleAdminApp/Android.mk
index 426dbf2..4e5d874 100644
--- a/hostsidetests/devicepolicy/app/SingleAdminApp/Android.mk
+++ b/hostsidetests/devicepolicy/app/SingleAdminApp/Android.mk
@@ -31,8 +31,7 @@
     ctstestrunner \
     compatibility-device-util \
     ub-uiautomator \
-    android-support-test \
-    legacy-android-test
+    android-support-test
 
 LOCAL_SDK_VERSION := test_current
 
diff --git a/hostsidetests/devicepolicy/app/TransferOwnerIncomingApp/Android.mk b/hostsidetests/devicepolicy/app/TransferOwnerIncomingApp/Android.mk
new file mode 100644
index 0000000..31e2dc4
--- /dev/null
+++ b/hostsidetests/devicepolicy/app/TransferOwnerIncomingApp/Android.mk
@@ -0,0 +1,42 @@
+# 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_PACKAGE_NAME := CtsTransferOwnerIncomingApp
+
+LOCAL_MODULE_TAGS := optional
+
+LOCAL_MODULE_PATH := $(TARGET_OUT_DATA_APPS)
+
+LOCAL_SRC_FILES := $(call all-java-files-under, src)
+
+LOCAL_JAVA_LIBRARIES := android.test.runner conscrypt cts-junit
+
+LOCAL_STATIC_JAVA_LIBRARIES := \
+    android-support-v4 \
+    ctstestrunner \
+    compatibility-device-util \
+    ub-uiautomator \
+    android-support-test \
+    testng
+
+LOCAL_SDK_VERSION := test_current
+
+# tag this module as a cts test artifact
+LOCAL_COMPATIBILITY_SUITE := cts vts general-tests
+
+include $(BUILD_CTS_PACKAGE)
diff --git a/hostsidetests/devicepolicy/app/TransferOwnerIncomingApp/AndroidManifest.xml b/hostsidetests/devicepolicy/app/TransferOwnerIncomingApp/AndroidManifest.xml
new file mode 100644
index 0000000..2a64ca2
--- /dev/null
+++ b/hostsidetests/devicepolicy/app/TransferOwnerIncomingApp/AndroidManifest.xml
@@ -0,0 +1,42 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2017 The Android Open Source Project
+
+     Licensed under the Apache License, Version 2.0 (the "License");
+     you may not use this file except in compliance with the License.
+     You may obtain a copy of the License at
+
+          http://www.apache.org/licenses/LICENSE-2.0
+
+     Unless required by applicable law or agreed to in writing, software
+     distributed under the License is distributed on an "AS IS" BASIS,
+     WITHOUT 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.transferownerincoming">
+
+    <uses-sdk android:minSdkVersion="24"/>
+
+    <uses-permission android:name="android.permission.WRITE_SECURE_SETTINGS"/>
+
+    <application
+        android:testOnly="true">
+
+        <uses-library android:name="android.test.runner"/>
+        <receiver
+            android:name="com.android.cts.transferowner.DeviceAndProfileOwnerTransferIncomingTest$BasicAdminReceiver"
+            android:permission="android.permission.BIND_DEVICE_ADMIN">
+            <meta-data android:name="android.app.device_admin"
+                       android:resource="@xml/device_admin"/>
+            <intent-filter>
+                <action android:name="android.app.action.DEVICE_ADMIN_ENABLED"/>
+            </intent-filter>
+        </receiver>
+    </application>
+
+    <instrumentation android:name="android.support.test.runner.AndroidJUnitRunner"
+                     android:targetPackage="com.android.cts.transferownerincoming"
+                     android:label="Transfer Owner CTS tests"/>
+</manifest>
diff --git a/hostsidetests/devicepolicy/app/TransferOwnerIncomingApp/res/xml/device_admin.xml b/hostsidetests/devicepolicy/app/TransferOwnerIncomingApp/res/xml/device_admin.xml
new file mode 100644
index 0000000..66a3730
--- /dev/null
+++ b/hostsidetests/devicepolicy/app/TransferOwnerIncomingApp/res/xml/device_admin.xml
@@ -0,0 +1,12 @@
+<device-admin xmlns:android="http://schemas.android.com/apk/res/android" android:visible="false">
+    <uses-policies>
+        <limit-password/>
+        <watch-login/>
+        <reset-password/>
+        <force-lock/>
+        <wipe-data/>
+        <expire-password/>
+        <encrypted-storage/>
+        <disable-camera/>
+    </uses-policies>
+</device-admin>
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
new file mode 100644
index 0000000..0d22c38
--- /dev/null
+++ b/hostsidetests/devicepolicy/app/TransferOwnerIncomingApp/src/com/android/cts/transferowner/DeviceAndProfileOwnerTransferIncomingTest.java
@@ -0,0 +1,42 @@
+/*
+ * 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.transferowner;
+
+import android.app.admin.DeviceAdminReceiver;
+import android.app.admin.DevicePolicyManager;
+import android.content.ComponentName;
+import android.content.Context;
+import android.support.test.InstrumentationRegistry;
+import android.support.test.filters.SmallTest;
+
+import org.junit.Before;
+
+@SmallTest
+public class DeviceAndProfileOwnerTransferIncomingTest {
+    public static class BasicAdminReceiver extends DeviceAdminReceiver {
+        public BasicAdminReceiver() {}
+    }
+
+    protected ComponentName mIncomingComponentName;
+    protected DevicePolicyManager mDevicePolicyManager;
+
+    @Before
+    public void setUp() throws Exception {
+        Context context = InstrumentationRegistry.getTargetContext();
+        mDevicePolicyManager = context.getSystemService(DevicePolicyManager.class);
+        mIncomingComponentName = new ComponentName(context, BasicAdminReceiver.class.getName());
+    }
+}
diff --git a/hostsidetests/devicepolicy/app/TransferOwnerIncomingApp/src/com/android/cts/transferowner/TransferDeviceOwnerIncomingTest.java b/hostsidetests/devicepolicy/app/TransferOwnerIncomingApp/src/com/android/cts/transferowner/TransferDeviceOwnerIncomingTest.java
new file mode 100644
index 0000000..2b20c59
--- /dev/null
+++ b/hostsidetests/devicepolicy/app/TransferOwnerIncomingApp/src/com/android/cts/transferowner/TransferDeviceOwnerIncomingTest.java
@@ -0,0 +1,52 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.cts.transferowner;
+
+import static junit.framework.Assert.assertTrue;
+
+import static org.junit.Assert.assertEquals;
+import static org.testng.Assert.assertThrows;
+
+import android.app.admin.SystemUpdatePolicy;
+import android.support.test.filters.SmallTest;
+
+import org.junit.Test;
+
+import java.util.Collections;
+
+@SmallTest
+public class TransferDeviceOwnerIncomingTest extends DeviceAndProfileOwnerTransferIncomingTest {
+    @Test
+    public void testTransferPoliciesAreRetainedAfterTransfer() {
+        assertTrue(mDevicePolicyManager.isAdminActive(mIncomingComponentName));
+        assertTrue(mDevicePolicyManager.isDeviceOwnerApp(mIncomingComponentName.getPackageName()));
+        assertTrue(mDevicePolicyManager.getCameraDisabled(mIncomingComponentName));
+        assertEquals(Collections.singletonList("test.package"),
+                mDevicePolicyManager.getKeepUninstalledPackages(mIncomingComponentName));
+        assertEquals(123, mDevicePolicyManager.getPasswordMinimumLength(mIncomingComponentName));
+        assertTrue(areSystemPoliciesEqual(SystemUpdatePolicy.createWindowedInstallPolicy(123, 456),
+                mDevicePolicyManager.getSystemUpdatePolicy()));
+        assertThrows(SecurityException.class, () -> {
+            mDevicePolicyManager.getParentProfileInstance(mIncomingComponentName);
+        });
+    }
+
+    private boolean areSystemPoliciesEqual(SystemUpdatePolicy policy1, SystemUpdatePolicy policy2) {
+        return policy1.getPolicyType() == policy2.getPolicyType()
+                && policy1.getInstallWindowStart() == policy2.getInstallWindowStart()
+                && policy1.getInstallWindowEnd() == policy2.getInstallWindowEnd();
+    }
+}
diff --git a/hostsidetests/devicepolicy/app/TransferOwnerIncomingApp/src/com/android/cts/transferowner/TransferProfileOwnerIncomingTest.java b/hostsidetests/devicepolicy/app/TransferOwnerIncomingApp/src/com/android/cts/transferowner/TransferProfileOwnerIncomingTest.java
new file mode 100644
index 0000000..ef7e8ac
--- /dev/null
+++ b/hostsidetests/devicepolicy/app/TransferOwnerIncomingApp/src/com/android/cts/transferowner/TransferProfileOwnerIncomingTest.java
@@ -0,0 +1,47 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.cts.transferowner;
+
+import static junit.framework.Assert.assertTrue;
+
+import static org.junit.Assert.assertEquals;
+
+import android.app.admin.DevicePolicyManager;
+import android.support.test.filters.SmallTest;
+
+import org.junit.Test;
+
+@SmallTest
+public class TransferProfileOwnerIncomingTest extends DeviceAndProfileOwnerTransferIncomingTest {
+    @Test
+    public void testTransferPoliciesAreRetainedAfterTransfer() {
+        int passwordLength = 123;
+        int passwordExpirationTimeout = 456;
+        assertTrue(mDevicePolicyManager.isAdminActive(mIncomingComponentName));
+        assertTrue(mDevicePolicyManager.isProfileOwnerApp(mIncomingComponentName.getPackageName()));
+        assertTrue(mDevicePolicyManager.getCameraDisabled(mIncomingComponentName));
+        assertTrue(mDevicePolicyManager.getCrossProfileCallerIdDisabled(mIncomingComponentName));
+        assertEquals(
+                passwordLength,
+                mDevicePolicyManager.getPasswordMinimumLength(mIncomingComponentName));
+
+        DevicePolicyManager targetParentProfileInstance =
+                mDevicePolicyManager.getParentProfileInstance(mIncomingComponentName);
+        assertEquals(
+                passwordExpirationTimeout,
+                targetParentProfileInstance.getPasswordExpirationTimeout(mIncomingComponentName));
+    }
+}
diff --git a/hostsidetests/devicepolicy/app/TransferOwnerOutgoingApp/Android.mk b/hostsidetests/devicepolicy/app/TransferOwnerOutgoingApp/Android.mk
new file mode 100644
index 0000000..6f6297d
--- /dev/null
+++ b/hostsidetests/devicepolicy/app/TransferOwnerOutgoingApp/Android.mk
@@ -0,0 +1,42 @@
+# 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_PACKAGE_NAME := CtsTransferOwnerOutgoingApp
+
+LOCAL_MODULE_TAGS := optional
+
+LOCAL_MODULE_PATH := $(TARGET_OUT_DATA_APPS)
+
+LOCAL_SRC_FILES := $(call all-java-files-under, src)
+
+LOCAL_JAVA_LIBRARIES := android.test.runner conscrypt cts-junit
+
+LOCAL_STATIC_JAVA_LIBRARIES := \
+    android-support-v4 \
+    ctstestrunner \
+    compatibility-device-util \
+    ub-uiautomator \
+    android-support-test \
+    testng
+
+LOCAL_SDK_VERSION := test_current
+
+# tag this module as a cts test artifact
+LOCAL_COMPATIBILITY_SUITE := cts vts general-tests
+
+include $(BUILD_CTS_PACKAGE)
diff --git a/hostsidetests/devicepolicy/app/TransferOwnerOutgoingApp/AndroidManifest.xml b/hostsidetests/devicepolicy/app/TransferOwnerOutgoingApp/AndroidManifest.xml
new file mode 100644
index 0000000..59feeb3
--- /dev/null
+++ b/hostsidetests/devicepolicy/app/TransferOwnerOutgoingApp/AndroidManifest.xml
@@ -0,0 +1,42 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2017 The Android Open Source Project
+
+     Licensed under the Apache License, Version 2.0 (the "License");
+     you may not use this file except in compliance with the License.
+     You may obtain a copy of the License at
+
+          http://www.apache.org/licenses/LICENSE-2.0
+
+     Unless required by applicable law or agreed to in writing, software
+     distributed under the License is distributed on an "AS IS" BASIS,
+     WITHOUT 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.transferowneroutgoing">
+
+    <uses-sdk android:minSdkVersion="24"/>
+
+    <uses-permission android:name="android.permission.WRITE_SECURE_SETTINGS"/>
+
+    <application
+        android:testOnly="true">
+
+        <uses-library android:name="android.test.runner"/>
+        <receiver
+            android:name="com.android.cts.transferowner.DeviceAndProfileOwnerTransferOutgoingTest$BasicAdminReceiver"
+            android:permission="android.permission.BIND_DEVICE_ADMIN">
+            <meta-data android:name="android.app.device_admin"
+                       android:resource="@xml/device_admin"/>
+            <intent-filter>
+                <action android:name="android.app.action.DEVICE_ADMIN_ENABLED"/>
+            </intent-filter>
+        </receiver>
+    </application>
+
+    <instrumentation android:name="android.support.test.runner.AndroidJUnitRunner"
+                     android:targetPackage="com.android.cts.transferowneroutgoing"
+                     android:label="Transfer Owner CTS tests"/>
+</manifest>
diff --git a/hostsidetests/devicepolicy/app/TransferOwnerOutgoingApp/res/xml/device_admin.xml b/hostsidetests/devicepolicy/app/TransferOwnerOutgoingApp/res/xml/device_admin.xml
new file mode 100644
index 0000000..66a3730
--- /dev/null
+++ b/hostsidetests/devicepolicy/app/TransferOwnerOutgoingApp/res/xml/device_admin.xml
@@ -0,0 +1,12 @@
+<device-admin xmlns:android="http://schemas.android.com/apk/res/android" android:visible="false">
+    <uses-policies>
+        <limit-password/>
+        <watch-login/>
+        <reset-password/>
+        <force-lock/>
+        <wipe-data/>
+        <expire-password/>
+        <encrypted-storage/>
+        <disable-camera/>
+    </uses-policies>
+</device-admin>
diff --git a/hostsidetests/devicepolicy/app/TransferOwnerOutgoingApp/src/com/android/cts/transferowner/DeviceAndProfileOwnerTransferOutgoingTest.java b/hostsidetests/devicepolicy/app/TransferOwnerOutgoingApp/src/com/android/cts/transferowner/DeviceAndProfileOwnerTransferOutgoingTest.java
new file mode 100644
index 0000000..888ca49
--- /dev/null
+++ b/hostsidetests/devicepolicy/app/TransferOwnerOutgoingApp/src/com/android/cts/transferowner/DeviceAndProfileOwnerTransferOutgoingTest.java
@@ -0,0 +1,90 @@
+/*
+ * 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.transferowner;
+
+import static org.testng.Assert.assertThrows;
+
+import android.app.admin.DeviceAdminReceiver;
+import android.app.admin.DevicePolicyManager;
+import android.content.ComponentName;
+import android.content.Context;
+import android.os.PersistableBundle;
+import android.support.test.InstrumentationRegistry;
+
+import org.junit.Before;
+import org.junit.Test;
+
+import java.lang.reflect.InvocationTargetException;
+
+public abstract class DeviceAndProfileOwnerTransferOutgoingTest {
+    public static class BasicAdminReceiver extends DeviceAdminReceiver {
+        public BasicAdminReceiver() {}
+    }
+
+    private static final String TRANSFER_OWNER_INCOMING_PKG =
+            "com.android.cts.transferownerincoming";
+    private static final String TRANSFER_OWNER_INCOMING_TEST_RECEIVER_CLASS =
+            "com.android.cts.transferowner.DeviceAndProfileOwnerTransferIncomingTest$BasicAdminReceiver";
+    static final ComponentName mIncomingComponentName =
+            new ComponentName(
+                    TRANSFER_OWNER_INCOMING_PKG, TRANSFER_OWNER_INCOMING_TEST_RECEIVER_CLASS);
+    private static final ComponentName mInvalidTargetComponent =
+            new ComponentName("com.android.cts.intent.receiver", ".BroadcastIntentReceiver");
+
+    protected DevicePolicyManager mDevicePolicyManager;
+    protected ComponentName mOutgoingComponentName;
+
+    @Before
+    public void setUp() throws Exception {
+        Context context = InstrumentationRegistry.getTargetContext();
+        mDevicePolicyManager = context.getSystemService(DevicePolicyManager.class);
+        mOutgoingComponentName = new ComponentName(context, BasicAdminReceiver.class.getName());
+    }
+
+    @Test
+    public void testTransferSameAdmin() {
+        PersistableBundle b = new PersistableBundle();
+        assertThrows(
+                IllegalArgumentException.class,
+                () -> {
+                    transferOwner(
+                            mOutgoingComponentName, mOutgoingComponentName, b);
+                });
+    }
+
+    @Test
+    public void testTransferInvalidTarget() {
+        PersistableBundle b = new PersistableBundle();
+        assertThrows(
+                IllegalArgumentException.class,
+                () -> {
+                    transferOwner(
+                            mOutgoingComponentName, mInvalidTargetComponent, b);
+                });
+    }
+
+    protected void transferOwner(ComponentName outgoing, ComponentName incoming,
+            PersistableBundle parameters)
+            throws Throwable {
+        try {
+            mDevicePolicyManager.getClass().getMethod("transferOwner",
+                    ComponentName.class, ComponentName.class, PersistableBundle.class)
+                    .invoke(mDevicePolicyManager, outgoing, incoming, parameters);
+        } catch (InvocationTargetException e) {
+            throw e.getTargetException();
+        }
+    }
+}
diff --git a/hostsidetests/devicepolicy/app/TransferOwnerOutgoingApp/src/com/android/cts/transferowner/TransferDeviceOwnerOutgoingTest.java b/hostsidetests/devicepolicy/app/TransferOwnerOutgoingApp/src/com/android/cts/transferowner/TransferDeviceOwnerOutgoingTest.java
new file mode 100644
index 0000000..67c3760
--- /dev/null
+++ b/hostsidetests/devicepolicy/app/TransferOwnerOutgoingApp/src/com/android/cts/transferowner/TransferDeviceOwnerOutgoingTest.java
@@ -0,0 +1,60 @@
+/*
+ * 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.transferowner;
+
+import static junit.framework.Assert.assertTrue;
+
+import static org.testng.Assert.assertFalse;
+import static org.testng.Assert.assertThrows;
+
+import android.app.admin.SystemUpdatePolicy;
+import android.os.PersistableBundle;
+import android.support.test.filters.SmallTest;
+
+import org.junit.Test;
+
+import java.util.Collections;
+
+@SmallTest
+public class TransferDeviceOwnerOutgoingTest extends DeviceAndProfileOwnerTransferOutgoingTest {
+    @Test
+    public void testTransferWithPoliciesOutgoing() throws Throwable {
+        int passwordLength = 123;
+        mDevicePolicyManager.setCameraDisabled(mOutgoingComponentName, true);
+        mDevicePolicyManager.setPasswordMinimumLength(mOutgoingComponentName, passwordLength);
+        mDevicePolicyManager.setKeepUninstalledPackages(mOutgoingComponentName,
+                Collections.singletonList("test.package"));
+        mDevicePolicyManager.setSystemUpdatePolicy(mOutgoingComponentName,
+                SystemUpdatePolicy.createWindowedInstallPolicy(123, 456));
+
+        PersistableBundle b = new PersistableBundle();
+        transferOwner(mOutgoingComponentName, mIncomingComponentName, b);
+    }
+
+    @Test
+    public void testTransfer() throws Throwable {
+        PersistableBundle b = new PersistableBundle();
+        transferOwner(mOutgoingComponentName, mIncomingComponentName, b);
+        assertTrue(mDevicePolicyManager.isAdminActive(mIncomingComponentName));
+        assertTrue(mDevicePolicyManager.isDeviceOwnerApp(mIncomingComponentName.getPackageName()));
+        assertFalse(
+                mDevicePolicyManager.isDeviceOwnerApp(mOutgoingComponentName.getPackageName()));
+        assertFalse(mDevicePolicyManager.isAdminActive(mOutgoingComponentName));
+        assertThrows(SecurityException.class, () -> {
+            mDevicePolicyManager.getSecondaryUsers(mOutgoingComponentName);
+        });
+    }
+}
diff --git a/hostsidetests/devicepolicy/app/TransferOwnerOutgoingApp/src/com/android/cts/transferowner/TransferProfileOwnerOutgoingTest.java b/hostsidetests/devicepolicy/app/TransferOwnerOutgoingApp/src/com/android/cts/transferowner/TransferProfileOwnerOutgoingTest.java
new file mode 100644
index 0000000..370539d
--- /dev/null
+++ b/hostsidetests/devicepolicy/app/TransferOwnerOutgoingApp/src/com/android/cts/transferowner/TransferProfileOwnerOutgoingTest.java
@@ -0,0 +1,61 @@
+/*
+ * 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.transferowner;
+
+import static junit.framework.Assert.assertTrue;
+
+import static org.testng.Assert.assertFalse;
+import static org.testng.Assert.assertThrows;
+
+import android.app.admin.DevicePolicyManager;
+import android.os.PersistableBundle;
+import android.support.test.filters.SmallTest;
+
+import org.junit.Test;
+
+@SmallTest
+public class TransferProfileOwnerOutgoingTest extends DeviceAndProfileOwnerTransferOutgoingTest {
+    @Test
+    public void testTransferWithPoliciesOutgoing() throws Throwable {
+        int passwordLength = 123;
+        int passwordExpirationTimeout = 456;
+        DevicePolicyManager parentDevicePolicyManager =
+                mDevicePolicyManager.getParentProfileInstance(mOutgoingComponentName);
+        mDevicePolicyManager.setCameraDisabled(mOutgoingComponentName, true);
+        mDevicePolicyManager.setPasswordMinimumLength(mOutgoingComponentName, passwordLength);
+        mDevicePolicyManager.setCrossProfileCallerIdDisabled(mOutgoingComponentName, true);
+        parentDevicePolicyManager.setPasswordExpirationTimeout(
+                mOutgoingComponentName, passwordExpirationTimeout);
+
+        PersistableBundle b = new PersistableBundle();
+        transferOwner(mOutgoingComponentName, mIncomingComponentName, b);
+    }
+
+    @Test
+    public void testTransfer() throws Throwable {
+        PersistableBundle b = new PersistableBundle();
+        transferOwner(mOutgoingComponentName, mIncomingComponentName, b);
+        assertTrue(mDevicePolicyManager.isAdminActive(mIncomingComponentName));
+        assertTrue(mDevicePolicyManager.isProfileOwnerApp(mIncomingComponentName.getPackageName()));
+        assertFalse(
+                mDevicePolicyManager.isProfileOwnerApp(mOutgoingComponentName.getPackageName()));
+        assertFalse(mDevicePolicyManager.isAdminActive(mOutgoingComponentName));
+        assertThrows(SecurityException.class, () -> {
+            mDevicePolicyManager.setCrossProfileCallerIdDisabled(mOutgoingComponentName,
+                    false);
+        });
+    }
+}
diff --git a/hostsidetests/devicepolicy/app/WifiConfigCreator/AndroidManifest.xml b/hostsidetests/devicepolicy/app/WifiConfigCreator/AndroidManifest.xml
index 1b98259..cd3f9b7 100644
--- a/hostsidetests/devicepolicy/app/WifiConfigCreator/AndroidManifest.xml
+++ b/hostsidetests/devicepolicy/app/WifiConfigCreator/AndroidManifest.xml
@@ -22,6 +22,8 @@
     <uses-permission android:name="android.permission.CHANGE_WIFI_STATE" />
 
     <application>
+        <uses-library android:name="android.test.runner" />
+
         <activity android:name=".WifiConfigCreatorActivity"
             android:exported="true"
             android:theme="@android:style/Theme.NoDisplay"
diff --git a/hostsidetests/devicepolicy/src/com/android/cts/devicepolicy/BaseDevicePolicyTest.java b/hostsidetests/devicepolicy/src/com/android/cts/devicepolicy/BaseDevicePolicyTest.java
index a612cee..f88c380 100644
--- a/hostsidetests/devicepolicy/src/com/android/cts/devicepolicy/BaseDevicePolicyTest.java
+++ b/hostsidetests/devicepolicy/src/com/android/cts/devicepolicy/BaseDevicePolicyTest.java
@@ -27,10 +27,16 @@
 import com.android.tradefed.device.DeviceNotAvailableException;
 import com.android.tradefed.log.LogUtil.CLog;
 import com.android.tradefed.result.CollectingTestListener;
+import com.android.tradefed.result.FileInputStreamSource;
+import com.android.tradefed.result.LogDataType;
 import com.android.tradefed.testtype.DeviceTestCase;
 import com.android.tradefed.testtype.IBuildReceiver;
+import com.android.tradefed.util.FileUtil;
+import com.android.tradefed.util.TarUtil;
 
+import java.io.File;
 import java.io.FileNotFoundException;
+import java.io.IOException;
 import java.util.ArrayList;
 import java.util.Arrays;
 import java.util.Collections;
@@ -229,16 +235,21 @@
         return 0;
     }
 
+    private void stopUserAsync(int userId) throws Exception {
+        String stopUserCommand = "am stop-user -f " + userId;
+        CLog.d("starting command \"" + stopUserCommand);
+        CLog.d("Output for command " + stopUserCommand + ": "
+                + getDevice().executeShellCommand(stopUserCommand));
+    }
+
     protected void stopUser(int userId) throws Exception {
-        // Wait for the broadcast queue to be idle first to workaround the stop-user timeout issue.
-        waitForBroadcastIdle();
         String stopUserCommand = "am stop-user -w -f " + userId;
         CLog.d("starting command \"" + stopUserCommand + "\" and waiting.");
         CLog.d("Output for command " + stopUserCommand + ": "
                 + getDevice().executeShellCommand(stopUserCommand));
     }
 
-    private void waitForBroadcastIdle() throws DeviceNotAvailableException {
+    protected void waitForBroadcastIdle() throws DeviceNotAvailableException, IOException {
         CollectingOutputReceiver receiver = new CollectingOutputReceiver();
         try {
             // we allow 8min for the command to complete and 4min for the command to start to
@@ -249,6 +260,24 @@
             String output = receiver.getOutput();
             CLog.d("Output from 'am wait-for-broadcast-idle': %s", output);
             if (!output.contains("All broadcast queues are idle!")) {
+                // Gather the system_server dump data for investigation.
+                File heapDump = getDevice().dumpHeap("system_server", "/data/local/tmp/dump.hprof");
+                if (heapDump != null) {
+                    // If file is too too big, tar if with TarUtil.
+                    String pid = getDevice().getProcessPid("system_server");
+                    // gzip the file it's quite big
+                    File heapDumpGz = TarUtil.gzip(heapDump);
+                    try (FileInputStreamSource source = new FileInputStreamSource(heapDumpGz)) {
+                        addTestLog(
+                                String.format("system_server_dump.%s.%s.hprof",
+                                        pid, getDevice().getDeviceDate()),
+                                LogDataType.GZIP, source);
+                    } finally {
+                        FileUtil.deleteFile(heapDump);
+                    }
+                } else {
+                    CLog.e("Failed to capture the dumpheap from system_server");
+                }
                 // the call most likely failed we should fail the test
                 fail("'am wait-for-broadcase-idle' did not complete.");
                 // TODO: consider adding a reboot or recovery before failing if necessary
@@ -268,7 +297,16 @@
     }
 
     protected void removeTestUsers() throws Exception {
-        for (int userId : getUsersCreatedByTests()) {
+        List<Integer> usersCreatedByTests = getUsersCreatedByTests();
+
+        // The time spent on stopUser is depend on how busy the broadcast queue is.
+        // To optimize the time to remove multiple test users, we mark all users as
+        // stopping first, so no more broadcasts will be sent to these users, which make the queue
+        // less busy.
+        for (int userId : usersCreatedByTests) {
+            stopUserAsync(userId);
+        }
+        for (int userId : usersCreatedByTests) {
             removeUser(userId);
         }
     }
@@ -807,4 +845,28 @@
         executeShellCommand("input keyevent KEYCODE_WAKEUP");
         executeShellCommand("wm dismiss-keyguard");
     }
+
+    protected void startActivityAsUser(int userId, String packageName, String activityName)
+        throws Exception {
+        wakeupAndDismissKeyguard();
+        String command = "am start -W --user " + userId + " " + packageName + "/" + activityName;
+        getDevice().executeShellCommand(command);
+    }
+
+    protected String getDefaultLauncher() throws Exception {
+        final String PREFIX = "Launcher: ComponentInfo{";
+        final String POSTFIX = "}";
+        final String commandOutput =
+                getDevice().executeShellCommand("cmd shortcut get-default-launcher");
+        if (commandOutput == null) {
+            return null;
+        }
+        String[] lines = commandOutput.split("\\r?\\n");
+        for (String line : lines) {
+            if (line.startsWith(PREFIX) && line.endsWith(POSTFIX)) {
+                return line.substring(PREFIX.length(), line.length() - POSTFIX.length());
+            }
+        }
+        throw new Exception("Default launcher not found");
+    }
 }
diff --git a/hostsidetests/devicepolicy/src/com/android/cts/devicepolicy/CrossProfileAppsHostSideTest.java b/hostsidetests/devicepolicy/src/com/android/cts/devicepolicy/CrossProfileAppsHostSideTest.java
new file mode 100644
index 0000000..b1cf3ff
--- /dev/null
+++ b/hostsidetests/devicepolicy/src/com/android/cts/devicepolicy/CrossProfileAppsHostSideTest.java
@@ -0,0 +1,99 @@
+package com.android.cts.devicepolicy;
+
+import java.util.Collections;
+import java.util.Map;
+
+/**
+ * In the test, managed profile and secondary user are created. We then verify
+ * {@link android.content.pm.crossprofile.CrossProfileApps} APIs in different directions, like
+ * primary user to managed profile.
+ */
+public class CrossProfileAppsHostSideTest extends BaseDevicePolicyTest {
+    private static final String TEST_PACKAGE = "com.android.cts.crossprofileappstest";
+    private static final String NON_TARGET_USER_TEST_CLASS = ".CrossProfileAppsNonTargetUserTest";
+    private static final String TARGET_USER_TEST_CLASS = ".CrossProfileAppsTargetUserTest";
+    private static final String PARAM_TARGET_USER = "TARGET_USER";
+    private static final String EXTRA_TEST_APK = "CtsCrossProfileAppsTests.apk";
+
+    private int mProfileId;
+    private int mSecondaryUserId;
+    private boolean mHasManagedUserFeature;
+
+    @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.
+        mHasManagedUserFeature = hasDeviceFeature("android.software.managed_users");
+        installAppAsUser(EXTRA_TEST_APK, mPrimaryUserId);
+
+        if (mHasManagedUserFeature) {
+            createAndStartManagedProfile();
+            installAppAsUser(EXTRA_TEST_APK, mProfileId);
+        }
+        if (mSupportsMultiUser) {
+            mSecondaryUserId = createUser();
+            installAppAsUser(EXTRA_TEST_APK, mSecondaryUserId);
+        }
+    }
+
+    public void testPrimaryUserToPrimaryUser() throws Exception {
+        verifyCrossProfileAppsApi(mPrimaryUserId, mPrimaryUserId, NON_TARGET_USER_TEST_CLASS);
+    }
+
+    public void testPrimaryUserToManagedProfile() throws Exception {
+        if (!mHasManagedUserFeature) {
+            return;
+        }
+        verifyCrossProfileAppsApi(mPrimaryUserId, mProfileId, TARGET_USER_TEST_CLASS);
+    }
+
+    public void testManagedProfileToPrimaryUser() throws Exception {
+        if (!mHasManagedUserFeature) {
+            return;
+        }
+        verifyCrossProfileAppsApi(mProfileId, mPrimaryUserId, TARGET_USER_TEST_CLASS);
+    }
+
+    public void testPrimaryUserToSecondaryUser() throws Exception {
+        if (!mSupportsMultiUser) {
+            return;
+        }
+        verifyCrossProfileAppsApi(mPrimaryUserId, mSecondaryUserId, NON_TARGET_USER_TEST_CLASS);
+    }
+
+    public void testSecondaryUserToManagedProfile() throws Exception {
+        if (!mSupportsMultiUser || !mHasManagedUserFeature) {
+            return;
+        }
+        verifyCrossProfileAppsApi(mSecondaryUserId, mProfileId, NON_TARGET_USER_TEST_CLASS);
+
+    }
+
+    public void testManagedProfileToSecondaryUser() throws Exception {
+        if (!mSupportsMultiUser || !mHasManagedUserFeature) {
+            return;
+        }
+        verifyCrossProfileAppsApi(mProfileId, mSecondaryUserId, NON_TARGET_USER_TEST_CLASS);
+    }
+
+    private void verifyCrossProfileAppsApi(int fromUserId, int targetUserId, String testClass)
+            throws Exception {
+        runDeviceTestsAsUser(
+                TEST_PACKAGE,
+                testClass,
+                null,
+                fromUserId,
+                createTargetUserParam(targetUserId));
+    }
+
+    private void createAndStartManagedProfile() throws Exception {
+        mProfileId = createManagedProfile(mPrimaryUserId);
+        switchUser(mPrimaryUserId);
+        startUser(mProfileId);
+    }
+
+    private Map<String, String> createTargetUserParam(int targetUserId) throws Exception {
+        return Collections.singletonMap(PARAM_TARGET_USER,
+                Integer.toString(getUserSerialNumber(targetUserId)));
+    }
+}
diff --git a/hostsidetests/devicepolicy/src/com/android/cts/devicepolicy/CustomDeviceOwnerTest.java b/hostsidetests/devicepolicy/src/com/android/cts/devicepolicy/CustomDeviceOwnerTest.java
index 4042fd5..7b568af 100644
--- a/hostsidetests/devicepolicy/src/com/android/cts/devicepolicy/CustomDeviceOwnerTest.java
+++ b/hostsidetests/devicepolicy/src/com/android/cts/devicepolicy/CustomDeviceOwnerTest.java
@@ -31,7 +31,7 @@
     private static final String DEVICE_OWNER_PKG = "com.android.cts.deviceowner";
     private static final String DEVICE_OWNER_APK = "CtsDeviceOwnerApp.apk";
     private static final String DEVICE_OWNER_ADMIN
-            = DEVICE_OWNER_PKG + ".BaseDeviceOwnerTest$BasicAdminReceiver";
+            = DEVICE_OWNER_PKG + ".BasicAdminReceiver";
     private static final String DEVICE_OWNER_ADMIN_COMPONENT
             = DEVICE_OWNER_PKG + "/" + DEVICE_OWNER_ADMIN;
 
diff --git a/hostsidetests/devicepolicy/src/com/android/cts/devicepolicy/DeviceAndProfileOwnerTest.java b/hostsidetests/devicepolicy/src/com/android/cts/devicepolicy/DeviceAndProfileOwnerTest.java
index 6b7d89b..3b85093 100644
--- a/hostsidetests/devicepolicy/src/com/android/cts/devicepolicy/DeviceAndProfileOwnerTest.java
+++ b/hostsidetests/devicepolicy/src/com/android/cts/devicepolicy/DeviceAndProfileOwnerTest.java
@@ -253,6 +253,15 @@
         executeDeviceTestMethod(".PermissionsTest", "testPermissionGrantState");
     }
 
+    public void testPermissionGrant_developmentPermission() throws Exception {
+        if (!mHasFeature) {
+            return;
+        }
+        installAppPermissionAppAsUser();
+        executeDeviceTestMethod(
+                ".PermissionsTest", "testPermissionGrantState_developmentPermission");
+    }
+
     /**
      * Require a device for tests that use the network stack. Headless Androids running in
      * data centres might need their network rules un-tampered-with in order to keep the ADB / VNC
@@ -774,6 +783,13 @@
 
     }
 
+    public void testPasswordBlacklist() throws Exception {
+        if (!mHasFeature) {
+            return;
+        }
+        executeDeviceTestClass(".PasswordBlacklistTest");
+    }
+
     public void testRequiredStrongAuthTimeout() throws Exception {
         if (!mHasFeature) {
             return;
@@ -814,6 +830,18 @@
                 Collections.singletonMap(ARG_ALLOW_FAILURE, Boolean.toString(allowFailures)));
     }
 
+    public void testClearApplicationData() throws Exception {
+        if (!mHasFeature) {
+            return;
+        }
+        installAppAsUser(INTENT_RECEIVER_APK, mUserId);
+        runDeviceTestsAsUser(INTENT_RECEIVER_PKG, INTENT_RECEIVER_PKG + ".ClearApplicationDataTest",
+                "testWriteToSharedPreference", mUserId);
+        executeDeviceTestMethod(".ClearApplicationDataTest", "testClearApplicationData");
+        runDeviceTestsAsUser(INTENT_RECEIVER_PKG, INTENT_RECEIVER_PKG + ".ClearApplicationDataTest",
+                "testSharedPreferenceCleared", mUserId);
+    }
+
     protected void executeDeviceTestClass(String className) throws Exception {
         runDeviceTestsAsUser(DEVICE_ADMIN_PKG, className, mUserId);
     }
@@ -904,10 +932,7 @@
      */
     protected void startSimpleActivityAsUser(int userId) throws Exception {
         installAppAsUser(TEST_APP_APK, userId);
-        wakeupAndDismissKeyguard();
-        String command = "am start -W --user " + userId + " " + TEST_APP_PKG + "/"
-                + TEST_APP_PKG + ".SimpleActivity";
-        getDevice().executeShellCommand(command);
+        startActivityAsUser(userId, TEST_APP_PKG, TEST_APP_PKG + ".SimpleActivity");
     }
 
     protected void setScreenCaptureDisabled(int userId, boolean disabled) throws Exception {
diff --git a/hostsidetests/devicepolicy/src/com/android/cts/devicepolicy/DeviceAndProfileOwnerTransferTest.java b/hostsidetests/devicepolicy/src/com/android/cts/devicepolicy/DeviceAndProfileOwnerTransferTest.java
new file mode 100644
index 0000000..b9cb16b
--- /dev/null
+++ b/hostsidetests/devicepolicy/src/com/android/cts/devicepolicy/DeviceAndProfileOwnerTransferTest.java
@@ -0,0 +1,75 @@
+package com.android.cts.devicepolicy;
+
+public class DeviceAndProfileOwnerTransferTest extends BaseDevicePolicyTest {
+    protected static final String TRANSFER_OWNER_OUTGOING_PKG =
+            "com.android.cts.transferowneroutgoing";
+    protected static final String TRANSFER_OWNER_OUTGOING_APK = "CtsTransferOwnerOutgoingApp.apk";
+    protected static final String TRANSFER_OWNER_OUTGOING_TEST_RECEIVER =
+            TRANSFER_OWNER_OUTGOING_PKG
+                    + "/com.android.cts.transferowner"
+                    + ".DeviceAndProfileOwnerTransferOutgoingTest$BasicAdminReceiver";
+    protected static final String TRANSFER_OWNER_INCOMING_PKG =
+            "com.android.cts.transferownerincoming";
+    protected static final String TRANSFER_OWNER_INCOMING_APK = "CtsTransferOwnerIncomingApp.apk";
+    protected static final String INVALID_TARGET_APK = "CtsIntentReceiverApp.apk";
+
+    protected int mUserId;
+    protected String mOutgoingTestClassName;
+    protected String mIncomingTestClassName;
+
+    public void testTransfer() throws Exception {
+        if (!mHasFeature) {
+            return;
+        }
+        installAppAsUser(TRANSFER_OWNER_INCOMING_APK, mUserId);
+        runDeviceTestsAsUser(TRANSFER_OWNER_OUTGOING_PKG,
+                mOutgoingTestClassName,
+                "testTransfer", mUserId);
+    }
+
+    public void testTransferSameAdmin() throws Exception {
+        if (!mHasFeature) {
+            return;
+        }
+        installAppAsUser(TRANSFER_OWNER_INCOMING_APK, mUserId);
+        runDeviceTestsAsUser(TRANSFER_OWNER_OUTGOING_PKG,
+                mOutgoingTestClassName,
+                "testTransferSameAdmin", mUserId);
+    }
+
+    public void testTransferInvalidTarget() throws Exception {
+        if (!mHasFeature) {
+            return;
+        }
+        installAppAsUser(INVALID_TARGET_APK, mUserId);
+        runDeviceTestsAsUser(TRANSFER_OWNER_OUTGOING_PKG,
+                mOutgoingTestClassName,
+                "testTransferInvalidTarget", mUserId);
+    }
+
+    public void testTransferPolicies() throws Exception {
+        if (!mHasFeature) {
+            return;
+        }
+        installAppAsUser(TRANSFER_OWNER_INCOMING_APK, mUserId);
+        runDeviceTestsAsUser(TRANSFER_OWNER_OUTGOING_PKG,
+                mOutgoingTestClassName,
+                "testTransferWithPoliciesOutgoing", mUserId);
+        runDeviceTestsAsUser(TRANSFER_OWNER_INCOMING_PKG,
+                mIncomingTestClassName,
+                "testTransferPoliciesAreRetainedAfterTransfer", mUserId);
+    }
+
+    protected void setupTestParameters(int userId, String outgoingTestClassName,
+            String incomingTestClassName) {
+        mUserId = userId;
+        mOutgoingTestClassName = outgoingTestClassName;
+        mIncomingTestClassName = incomingTestClassName;
+    }
+
+    /* TODO: Add tests for:
+    * 1. startServiceForOwner
+    * 2. passwordOwner
+    *
+    * */
+}
diff --git a/hostsidetests/devicepolicy/src/com/android/cts/devicepolicy/DeviceOwnerTest.java b/hostsidetests/devicepolicy/src/com/android/cts/devicepolicy/DeviceOwnerTest.java
index 7a07d7c..e37c494 100644
--- a/hostsidetests/devicepolicy/src/com/android/cts/devicepolicy/DeviceOwnerTest.java
+++ b/hostsidetests/devicepolicy/src/com/android/cts/devicepolicy/DeviceOwnerTest.java
@@ -16,7 +16,12 @@
 
 package com.android.cts.devicepolicy;
 
+import com.android.compatibility.common.tradefed.build.CompatibilityBuildHelper;
+
+import java.io.File;
+import java.util.Collections;
 import java.util.List;
+import java.util.Map;
 import java.util.concurrent.TimeUnit;
 
 /**
@@ -42,18 +47,30 @@
     private static final String WIFI_CONFIG_CREATOR_APK = "CtsWifiConfigCreator.apk";
 
     private static final String ADMIN_RECEIVER_TEST_CLASS =
-            DEVICE_OWNER_PKG + ".BaseDeviceOwnerTest$BasicAdminReceiver";
+            DEVICE_OWNER_PKG + ".BasicAdminReceiver";
     private static final String DEVICE_OWNER_COMPONENT = DEVICE_OWNER_PKG + "/"
             + ADMIN_RECEIVER_TEST_CLASS;
 
-    /** The ephemeral users are implemented and supported on the device. */
-    private boolean mHasEphemeralUserFeature;
+    private static final String TEST_APP_APK = "CtsEmptyTestApp.apk";
+    private static final String TEST_APP_PKG = "android.packageinstaller.emptytestapp.cts";
+    private static final String TEST_APP_LOCATION = "/data/local/tmp/cts/packageinstaller/";
+
+    private static final String ARG_SECURITY_LOGGING_BATCH_NUMBER = "batchNumber";
+    private static final int BUFFER_SECURITY_ENTRIES_NOTIFICATION_LEVEL = 1024;
+
+    private static final String ARG_NETWORK_LOGGING_BATCH_COUNT = "batchCount";
+
+    /** Forcing ephemeral users is implemented and supported on the device. */
+    private boolean mHasForceEphemeralUserFeature;
 
     /**
-     * Ephemeral users are implemented, but unsupported on the device (because of missing split
-     * system user).
+     * Force ephemeral users feature is implemented, but unsupported on the device (because of
+     * missing split system user).
      */
-    private boolean mHasDisabledEphemeralUserFeature;
+    private boolean mHasDisabledForceEphemeralUserFeature;
+
+    /** CreateAndManageUser is available and an additional user can be created. */
+    private boolean mHasCreateAndManageUserFeature;
 
     @Override
     protected void setUp() throws Exception {
@@ -67,9 +84,11 @@
                 fail("Failed to set device owner");
             }
         }
-        mHasEphemeralUserFeature = mHasFeature && canCreateAdditionalUsers(1) && hasUserSplit();
-        mHasDisabledEphemeralUserFeature =
-                mHasFeature && canCreateAdditionalUsers(1) && !hasUserSplit();
+        mHasForceEphemeralUserFeature = mHasFeature && canCreateAdditionalUsers(1)
+                && hasUserSplit();
+        mHasDisabledForceEphemeralUserFeature = mHasFeature && canCreateAdditionalUsers(1)
+                && !hasUserSplit();
+        mHasCreateAndManageUserFeature = mHasFeature && canCreateAdditionalUsers(1);
     }
 
     @Override
@@ -125,7 +144,7 @@
 
     /** Tries to toggle the force-ephemeral-users on and checks it was really set. */
     public void testSetForceEphemeralUsers() throws Exception {
-        if (!mHasEphemeralUserFeature) {
+        if (!mHasForceEphemeralUserFeature) {
             return;
         }
         // Set force-ephemeral-users policy and verify it was set.
@@ -136,7 +155,7 @@
      * Setting force-ephemeral-users policy to true without a split system user should fail.
      */
     public void testSetForceEphemeralUsersFailsWithoutSplitSystemUser() throws Exception {
-        if (mHasDisabledEphemeralUserFeature) {
+        if (mHasDisabledForceEphemeralUserFeature) {
             executeDeviceTestMethod(".ForceEphemeralUsersTest", "testSetForceEphemeralUsersFails");
         }
     }
@@ -148,7 +167,7 @@
      * <p>If the current user is the system user, the other users are removed straight away.
      */
     public void testRemoveUsersOnSetForceEphemeralUsers() throws Exception {
-        if (!mHasEphemeralUserFeature) {
+        if (!mHasForceEphemeralUserFeature) {
             return;
         }
 
@@ -171,7 +190,7 @@
      * before all other users are removed.
      */
     public void testRemoveUsersOnSetForceEphemeralUsersWithUserSwitch() throws Exception {
-        if (!mHasEphemeralUserFeature) {
+        if (!mHasForceEphemeralUserFeature) {
             return;
         }
 
@@ -206,7 +225,7 @@
 
     /** The users created after setting force-ephemeral-users policy to true must be ephemeral. */
     public void testCreateUserAfterSetForceEphemeralUsers() throws Exception {
-        if (!mHasEphemeralUserFeature) {
+        if (!mHasForceEphemeralUserFeature) {
             return;
         }
 
@@ -221,7 +240,7 @@
      * Test creating an epehemeral user using the DevicePolicyManager's createAndManageUser method.
      */
     public void testCreateAndManageEphemeralUser() throws Exception {
-        if (!mHasEphemeralUserFeature) {
+        if (!mHasFeature || !canCreateAdditionalUsers(1)) {
             return;
         }
 
@@ -236,50 +255,45 @@
         assertEquals("Ephemeral flag must be set", FLAG_EPHEMERAL, flags & FLAG_EPHEMERAL);
     }
 
-    /**
-     * Test that creating an epehemeral user using the DevicePolicyManager's createAndManageUser
-     * method fails on systems without the split system user.
-     */
-    public void testCreateAndManageEphemeralUserFailsWithoutSplitSystemUser() throws Exception {
-        if (mHasDisabledEphemeralUserFeature) {
-            executeDeviceTestMethod(
-                    ".CreateAndManageUserTest", "testCreateAndManageEphemeralUserFails");
+    public void testCreateAndManageUser_SkipSetupWizard() throws Exception {
+        if (mHasCreateAndManageUserFeature) {
+            executeDeviceTestMethod(".CreateAndManageUserTest",
+                    "testCreateAndManageUser_SkipSetupWizard");
         }
     }
 
-// Disabled due to b/29072728
-//    public void testCreateAndManageUser_SkipSetupWizard() throws Exception {
-//        if (mHasCreateAndManageUserFeature) {
-//            executeDeviceTestMethod(".CreateAndManageUserTest",
-//                "testCreateAndManageUser_SkipSetupWizard");
-//        }
-//    }
-//
-//    public void testCreateAndManageUser_DontSkipSetupWizard() throws Exception {
-//        if (mHasCreateAndManageUserFeature) {
-//            executeDeviceTestMethod(".CreateAndManageUserTest",
-//                "testCreateAndManageUser_DontSkipSetupWizard");
-//        }
-//    }
+    public void testCreateAndManageUser_DontSkipSetupWizard() throws Exception {
+        if (mHasCreateAndManageUserFeature) {
+            executeDeviceTestMethod(".CreateAndManageUserTest",
+                    "testCreateAndManageUser_DontSkipSetupWizard");
+        }
+    }
 
     public void testCreateAndManageUser_AddRestrictionSet() throws Exception {
         if (mHasFeature && canCreateAdditionalUsers(1)) {
             executeDeviceTestMethod(".CreateAndManageUserTest",
-                "testCreateAndManageUser_AddRestrictionSet");
+                    "testCreateAndManageUser_AddRestrictionSet");
         }
     }
 
     public void testCreateAndManageUser_RemoveRestrictionSet() throws Exception {
         if (mHasFeature && canCreateAdditionalUsers(1)) {
             executeDeviceTestMethod(".CreateAndManageUserTest",
-                "testCreateAndManageUser_RemoveRestrictionSet");
+                    "testCreateAndManageUser_RemoveRestrictionSet");
+        }
+    }
+
+    public void testCreateAndManageUser_StartUserInBackground() throws Exception {
+        if (mHasFeature && canCreateAdditionalUsers(1)) {
+            executeDeviceTestMethod(".CreateAndManageUserTest",
+                    "testCreateAndManageUser_StartUserInBackground");
         }
     }
 
     public void testUserAddedOrRemovedBroadcasts() throws Exception {
         if (mHasFeature && canCreateAdditionalUsers(1)) {
             executeDeviceTestMethod(".CreateAndManageUserTest",
-                "testUserAddedOrRemovedBroadcasts");
+                    "testUserAddedOrRemovedBroadcasts");
         }
     }
 
@@ -307,22 +321,53 @@
         if (!mHasFeature) {
             return;
         }
-        executeDeviceTestMethod(".SecurityLoggingTest",
-                "testRetrievingSecurityLogsNotPossibleImmediatelyAfterPreviousSuccessfulRetrieval");
+        executeDeviceTestMethod(".SecurityLoggingTest", "testDisablingSecurityLogging");
+
+        // Generate more than enough events for a batch of security events.
+        int batchSize = BUFFER_SECURITY_ENTRIES_NOTIFICATION_LEVEL + 100;
         try {
             executeDeviceTestMethod(".SecurityLoggingTest", "testEnablingSecurityLogging");
+            // Reboot to ensure ro.device_owner is set to true in logd.
             rebootAndWaitUntilReady();
-            // Sleep for ~1 minute so that SecurityLogMonitor fetches the security events. This is
-            // an implementation detail we shouldn't rely on but there is no other way to do it
-            // currently.
-            Thread.sleep(TimeUnit.SECONDS.toMillis(70));
-            executeDeviceTestMethod(".SecurityLoggingTest", "testGetSecurityLogs");
+
+            // First batch: retrieve and verify the events.
+            runBatch(0, batchSize);
+
+            // Verify event ids are consistent across a consecutive batch.
+            runBatch(1, batchSize);
+
+            // Reboot the device, so the security event ids are reset.
+            rebootAndWaitUntilReady();
+
+            // First batch after reboot: retrieve and verify the events.
+            runBatch(0 /* batch number */, batchSize);
+
+            // Immediately attempting to fetch events again should fail.
+            executeDeviceTestMethod(".SecurityLoggingTest",
+                    "testSecurityLoggingRetrievalRateLimited");
         } finally {
             // Always attempt to disable security logging to bring the device to initial state.
             executeDeviceTestMethod(".SecurityLoggingTest", "testDisablingSecurityLogging");
         }
     }
 
+    private void runBatch(int batchNumber, int batchSize) throws Exception {
+        // Trigger security events of type TAG_ADB_SHELL_CMD.
+        for (int i = 0; i < batchSize; i++) {
+            getDevice().executeShellCommand("adb shell echo just_testing_" + i);
+        }
+
+        // Sleep for ~1 minute so that SecurityLogMonitor fetches the security events. This is
+        // an implementation detail we shouldn't rely on but there is no other way to do it
+        // currently.
+        Thread.sleep(TimeUnit.SECONDS.toMillis(70));
+
+        // Verify the contents of the batch.
+        executeDeviceTestMethod(".SecurityLoggingTest", "testGetSecurityLogs",
+                Collections.singletonMap(ARG_SECURITY_LOGGING_BATCH_NUMBER,
+                        Integer.toString(batchNumber)));
+    }
+
     public void testNetworkLoggingWithTwoUsers() throws Exception {
         if (!mHasFeature || !canCreateAdditionalUsers(1)) {
             return;
@@ -343,11 +388,34 @@
         if (!mHasFeature) {
             return;
         }
-
         executeDeviceTestMethod(".NetworkLoggingTest", "testProvidingWrongBatchTokenReturnsNull");
-        executeDeviceTestMethod(".NetworkLoggingTest", "testNetworkLoggingAndRetrieval");
+        executeDeviceTestMethod(".NetworkLoggingTest", "testNetworkLoggingAndRetrieval",
+                Collections.singletonMap(ARG_NETWORK_LOGGING_BATCH_COUNT, Integer.toString(1)));
     }
 
+    public void testNetworkLogging_multipleBatches() throws Exception {
+        if (!mHasFeature) {
+            return;
+        }
+        executeDeviceTestMethod(".NetworkLoggingTest", "testNetworkLoggingAndRetrieval",
+                Collections.singletonMap(ARG_NETWORK_LOGGING_BATCH_COUNT, Integer.toString(2)));
+    }
+
+    public void testNetworkLogging_rebootResetsId() throws Exception {
+        if (!mHasFeature) {
+            return;
+        }
+        // First batch: retrieve and verify the events.
+        executeDeviceTestMethod(".NetworkLoggingTest", "testNetworkLoggingAndRetrieval",
+                Collections.singletonMap(ARG_NETWORK_LOGGING_BATCH_COUNT, Integer.toString(1)));
+        // Reboot the device, so the security event IDs are re-set.
+        rebootAndWaitUntilReady();
+        // First batch after reboot: retrieve and verify the events.
+        executeDeviceTestMethod(".NetworkLoggingTest", "testNetworkLoggingAndRetrieval",
+                Collections.singletonMap(ARG_NETWORK_LOGGING_BATCH_COUNT, Integer.toString(1)));
+    }
+
+
     public void testSetAffiliationId_IllegalArgumentException() throws Exception {
         if (!mHasFeature) {
             return;
@@ -433,21 +501,8 @@
         if (!mHasFeature || !canCreateAdditionalUsers(1)) {
             return;
         }
-
-        final int userId = createUser();
-        installAppAsUser(INTENT_RECEIVER_APK, userId);
-        installAppAsUser(DEVICE_OWNER_APK, userId);
-        setProfileOwnerOrFail(DEVICE_OWNER_COMPONENT, userId);
-
-        switchUser(userId);
-        wakeupAndDismissKeyguard();
-
-        // Setting the same affiliation ids on both users and running the lock task tests.
-        runDeviceTestsAsUser(
-                DEVICE_OWNER_PKG, ".AffiliationTest", "testSetAffiliationId1", mPrimaryUserId);
-        runDeviceTestsAsUser(
-                DEVICE_OWNER_PKG, ".AffiliationTest", "testSetAffiliationId1", userId);
-        runDeviceTestsAsUser(DEVICE_OWNER_PKG, ".LockTaskTest", userId);
+        final int userId = createAffiliatedSecondaryUser();
+        executeAffiliatedProfileOwnerTest("LockTaskTest", userId);
     }
 
     public void testSystemUpdatePolicy() throws Exception {
@@ -503,16 +558,18 @@
 
     // Execute HardwarePropertiesManagerTest as a device owner.
     public void testHardwarePropertiesManagerAsDeviceOwner() throws Exception {
-        if (!mHasFeature)
+        if (!mHasFeature) {
             return;
+        }
 
         executeDeviceTestMethod(".HardwarePropertiesManagerTest", "testHardwarePropertiesManager");
     }
 
     // Execute VrTemperatureTest as a device owner.
     public void testVrTemperaturesAsDeviceOwner() throws Exception {
-        if (!mHasFeature)
+        if (!mHasFeature) {
             return;
+        }
 
         executeDeviceTestMethod(".VrTemperatureTest", "testVrTemperatures");
     }
@@ -593,6 +650,92 @@
         executeDeviceOwnerTest("BackupServiceEnabledTest");
     }
 
+    public void testPackageInstallCache() throws Exception {
+        if (!mHasFeature) {
+            return;
+        }
+        CompatibilityBuildHelper buildHelper = new CompatibilityBuildHelper(mCtsBuild);
+        final File apk = buildHelper.getTestFile(TEST_APP_APK);
+        try {
+            getDevice().uninstallPackage(TEST_APP_PKG);
+            assertTrue(getDevice().pushFile(apk, TEST_APP_LOCATION + apk.getName()));
+
+            // Install the package in primary user
+            runDeviceTestsAsUser(DEVICE_OWNER_PKG, ".PackageInstallTest",
+                    "testPackageInstall", mPrimaryUserId);
+
+            runDeviceTestsAsUser(DEVICE_OWNER_PKG, ".PackageInstallTest",
+                    "testKeepPackageCache", mPrimaryUserId);
+
+            // Remove the package in primary user
+            runDeviceTestsAsUser(DEVICE_OWNER_PKG, ".PackageInstallTest",
+                    "testPackageUninstall", mPrimaryUserId);
+
+            // Should be able to enable the cached package in primary user
+            runDeviceTestsAsUser(DEVICE_OWNER_PKG, ".PackageInstallTest",
+                    "testInstallExistingPackage", mPrimaryUserId);
+        } finally {
+            String command = "rm " + TEST_APP_LOCATION + apk.getName();
+            getDevice().executeShellCommand(command);
+            getDevice().uninstallPackage(TEST_APP_PKG);
+        }
+    }
+
+    public void testPackageInstallCache_multiUser() throws Exception {
+        if (!mHasFeature || !canCreateAdditionalUsers(1)) {
+            return;
+        }
+        final int userId = createAffiliatedSecondaryUser();
+        CompatibilityBuildHelper buildHelper = new CompatibilityBuildHelper(mCtsBuild);
+        final File apk = buildHelper.getTestFile(TEST_APP_APK);
+        try {
+            getDevice().uninstallPackage(TEST_APP_PKG);
+            assertTrue(getDevice().pushFile(apk, TEST_APP_LOCATION + apk.getName()));
+
+            // Install the package in primary user
+            runDeviceTestsAsUser(DEVICE_OWNER_PKG, ".PackageInstallTest",
+                    "testPackageInstall", mPrimaryUserId);
+
+            // Should be able to enable the package in secondary user
+            runDeviceTestsAsUser(DEVICE_OWNER_PKG, ".PackageInstallTest",
+                    "testInstallExistingPackage", userId);
+
+            // Remove the package in both user
+            runDeviceTestsAsUser(DEVICE_OWNER_PKG, ".PackageInstallTest",
+                    "testPackageUninstall", mPrimaryUserId);
+            runDeviceTestsAsUser(DEVICE_OWNER_PKG, ".PackageInstallTest",
+                    "testPackageUninstall", userId);
+
+            // Install the package in secondary user
+            runDeviceTestsAsUser(DEVICE_OWNER_PKG, ".PackageInstallTest",
+                    "testPackageInstall", userId);
+
+            // Should be able to enable the package in primary user
+            runDeviceTestsAsUser(DEVICE_OWNER_PKG, ".PackageInstallTest",
+                    "testInstallExistingPackage", mPrimaryUserId);
+
+            // Keep the package in cache
+            runDeviceTestsAsUser(DEVICE_OWNER_PKG, ".PackageInstallTest",
+                    "testKeepPackageCache", mPrimaryUserId);
+
+            // Remove the package in both user
+            runDeviceTestsAsUser(DEVICE_OWNER_PKG, ".PackageInstallTest",
+                    "testPackageUninstall", mPrimaryUserId);
+            runDeviceTestsAsUser(DEVICE_OWNER_PKG, ".PackageInstallTest",
+                    "testPackageUninstall", userId);
+
+            // Should be able to enable the cached package in both users
+            runDeviceTestsAsUser(DEVICE_OWNER_PKG, ".PackageInstallTest",
+                    "testInstallExistingPackage", userId);
+            runDeviceTestsAsUser(DEVICE_OWNER_PKG, ".PackageInstallTest",
+                    "testInstallExistingPackage", mPrimaryUserId);
+        } finally {
+            String command = "rm " + TEST_APP_LOCATION + apk.getName();
+            getDevice().executeShellCommand(command);
+            getDevice().uninstallPackage(TEST_APP_PKG);
+        }
+    }
+
     private void executeDeviceOwnerTest(String testClassName) throws Exception {
         if (!mHasFeature) {
             return;
@@ -601,6 +744,15 @@
         runDeviceTestsAsUser(DEVICE_OWNER_PKG, testClass, mPrimaryUserId);
     }
 
+    private void executeAffiliatedProfileOwnerTest(String testClassName, int userId)
+            throws Exception {
+        if (!mHasFeature) {
+            return;
+        }
+        String testClass = DEVICE_OWNER_PKG + "." + testClassName;
+        runDeviceTestsAsUser(DEVICE_OWNER_PKG, testClass, userId);
+    }
+
     private void executeDeviceTestMethod(String className, String testName) throws Exception {
         if (!mHasFeature) {
             return;
@@ -608,4 +760,30 @@
         runDeviceTestsAsUser(DEVICE_OWNER_PKG, className, testName,
                 /* deviceOwnerUserId */ mPrimaryUserId);
     }
-}
\ No newline at end of file
+
+    private int createAffiliatedSecondaryUser() throws Exception {
+        final int userId = createUser();
+        installAppAsUser(INTENT_RECEIVER_APK, userId);
+        installAppAsUser(DEVICE_OWNER_APK, userId);
+        setProfileOwnerOrFail(DEVICE_OWNER_COMPONENT, userId);
+
+        switchUser(userId);
+        wakeupAndDismissKeyguard();
+
+        // Setting the same affiliation ids on both users and running the lock task tests.
+        runDeviceTestsAsUser(
+                DEVICE_OWNER_PKG, ".AffiliationTest", "testSetAffiliationId1", mPrimaryUserId);
+        runDeviceTestsAsUser(
+                DEVICE_OWNER_PKG, ".AffiliationTest", "testSetAffiliationId1", userId);
+        return userId;
+    }
+
+    private void executeDeviceTestMethod(String className, String testName,
+            Map<String, String> params) throws Exception {
+        if (!mHasFeature) {
+            return;
+        }
+        runDeviceTestsAsUser(DEVICE_OWNER_PKG, className, testName,
+                /* deviceOwnerUserId */ mPrimaryUserId, params);
+    }
+}
diff --git a/hostsidetests/devicepolicy/src/com/android/cts/devicepolicy/EphemeralUserTest.java b/hostsidetests/devicepolicy/src/com/android/cts/devicepolicy/EphemeralUserTest.java
index df4a47c..84bdd7d 100644
--- a/hostsidetests/devicepolicy/src/com/android/cts/devicepolicy/EphemeralUserTest.java
+++ b/hostsidetests/devicepolicy/src/com/android/cts/devicepolicy/EphemeralUserTest.java
@@ -17,7 +17,7 @@
 package com.android.cts.devicepolicy;
 
 /**
- * Tests for emphemeral users and profiles.
+ * Tests for ephemeral users and profiles.
  */
 public class EphemeralUserTest extends BaseDevicePolicyTest {
 
@@ -35,7 +35,7 @@
 
     /** The user should have the ephemeral flag set if it was created as ephemeral. */
     public void testCreateEphemeralUser() throws Exception {
-        if (!mHasFeature || !hasUserSplit()) {
+        if (!mHasFeature) {
             return;
         }
         int userId = createUser(FLAG_EPHEMERAL);
@@ -59,7 +59,8 @@
      */
     public void testProfileInheritsEphemeral() throws Exception {
         if (!mHasFeature || !hasDeviceFeature("android.software.managed_users")
-                || !hasUserSplit() || !canCreateAdditionalUsers(2)) {
+                || !canCreateAdditionalUsers(2)
+                || !hasUserSplit()) {
             return;
         }
         int userId = createUser(FLAG_EPHEMERAL);
@@ -72,7 +73,7 @@
      * Ephemeral user should be automatically removed after it is stopped.
      */
     public void testRemoveEphemeralOnStop() throws Exception {
-        if (!mHasFeature || !hasUserSplit()) {
+        if (!mHasFeature) {
             return;
         }
         int userId = createUser(FLAG_EPHEMERAL);
@@ -87,7 +88,7 @@
      * and not ephemeral when the feature is not set.
      */
     public void testEphemeralGuestFeature() throws Exception {
-        if (!mHasFeature || !hasUserSplit()) {
+        if (!mHasFeature) {
             return;
         }
         // Create a guest user.
@@ -103,20 +104,6 @@
         }
     }
 
-    /**
-     * Test that creating an ephemeral user fails on systems without the split system user.
-     */
-    public void testCreateEphemeralWithoutUserSplitFails() throws Exception {
-        if (!mHasFeature || hasUserSplit()) {
-            return;
-        }
-        String command ="pm create-user --ephemeral " + "TestUser_" + System.currentTimeMillis();
-        String commandOutput = getDevice().executeShellCommand(command);
-
-        assertEquals("Creating the epehemeral user should fail.",
-                "Error: couldn't create User.", commandOutput.trim());
-    }
-
     private boolean getGuestUsersEphemeral() throws Exception {
         String commandOutput = getDevice().executeShellCommand("dumpsys user");
         String[] outputLines = commandOutput.split("\n");
diff --git a/hostsidetests/devicepolicy/src/com/android/cts/devicepolicy/ManagedProfileProvisioningSingleAdminTest.java b/hostsidetests/devicepolicy/src/com/android/cts/devicepolicy/ManagedProfileProvisioningSingleAdminTest.java
index ef7099b..b71c4c87 100644
--- a/hostsidetests/devicepolicy/src/com/android/cts/devicepolicy/ManagedProfileProvisioningSingleAdminTest.java
+++ b/hostsidetests/devicepolicy/src/com/android/cts/devicepolicy/ManagedProfileProvisioningSingleAdminTest.java
@@ -15,8 +15,6 @@
  */
 package com.android.cts.devicepolicy;
 
-import org.junit.Test;
-
 /**
  * This class tests the provisioning flow with an APK that declares a single receiver with
  * BIND_DEVICE_ADMIN permissions, which was a requirement for the app sending the
@@ -54,7 +52,6 @@
         super.tearDown();
     }
 
-    @Test
     public void testEXTRA_PROVISIONING_DEVICE_ADMIN_PACKAGE_NAME() throws Exception {
         if (!mHasFeature) {
             return;
diff --git a/hostsidetests/devicepolicy/src/com/android/cts/devicepolicy/ManagedProfileProvisioningTest.java b/hostsidetests/devicepolicy/src/com/android/cts/devicepolicy/ManagedProfileProvisioningTest.java
index fa3627a..93a17e0 100644
--- a/hostsidetests/devicepolicy/src/com/android/cts/devicepolicy/ManagedProfileProvisioningTest.java
+++ b/hostsidetests/devicepolicy/src/com/android/cts/devicepolicy/ManagedProfileProvisioningTest.java
@@ -111,6 +111,17 @@
                 "testAccountExist", mParentUserId);
     }
 
+    public void testWebview() throws Exception {
+        if (!mHasFeature) {
+            return;
+        }
+
+        // We start an activity containing WebView in another process and run provisioning to
+        // test that the process is not killed.
+        startActivityAsUser(mParentUserId, MANAGED_PROFILE_PKG, ".WebViewActivity");
+        provisionManagedProfile();
+    }
+
     private void provisionManagedProfile() throws Exception {
         runDeviceTestsAsUser(MANAGED_PROFILE_PKG, ".ProvisioningTest",
                 "testProvisionManagedProfile", mParentUserId);
diff --git a/hostsidetests/devicepolicy/src/com/android/cts/devicepolicy/ManagedProfileTest.java b/hostsidetests/devicepolicy/src/com/android/cts/devicepolicy/ManagedProfileTest.java
index 157c59c..39825b5 100644
--- a/hostsidetests/devicepolicy/src/com/android/cts/devicepolicy/ManagedProfileTest.java
+++ b/hostsidetests/devicepolicy/src/com/android/cts/devicepolicy/ManagedProfileTest.java
@@ -47,7 +47,6 @@
     private static final String INTENT_RECEIVER_PKG = "com.android.cts.intent.receiver";
     private static final String INTENT_RECEIVER_APK = "CtsIntentReceiverApp.apk";
 
-    private static final String WIFI_CONFIG_CREATOR_PKG = "com.android.cts.wificonfigcreator";
     private static final String WIFI_CONFIG_CREATOR_APK = "CtsWifiConfigCreator.apk";
 
     private static final String WIDGET_PROVIDER_APK = "CtsWidgetProviderApp.apk";
@@ -65,8 +64,6 @@
     private static final String NOTIFICATION_APK = "CtsNotificationSenderApp.apk";
     private static final String NOTIFICATION_PKG =
             "com.android.cts.managedprofiletests.notificationsender";
-    private static final String NOTIFICATION_ACTIVITY =
-            NOTIFICATION_PKG + ".SendNotification";
 
     private static final String ADMIN_RECEIVER_TEST_CLASS =
             MANAGED_PROFILE_PKG + ".BaseManagedProfileTest$BasicAdminReceiver";
@@ -78,7 +75,6 @@
     private static final String FEATURE_CONNECTION_SERVICE = "android.software.connectionservice";
 
     private static final String SIMPLE_APP_APK = "CtsSimpleApp.apk";
-    private static final String SIMPLE_APP_PKG = "com.android.cts.launcherapps.simpleapp";
 
     private static final long TIMEOUT_USER_LOCKED_MILLIS = TimeUnit.SECONDS.toMillis(30);
 
@@ -87,11 +83,14 @@
     // Password needs to be in sync with ResetPasswordWithTokenTest.PASSWORD1
     private static final String RESET_PASSWORD_TEST_DEFAULT_PASSWORD = "123456";
 
+    private static final String PROFILE_CREDENTIAL = "1234";
+    // This should be sufficiently larger than ProfileTimeoutTestHelper.TIMEOUT_MS
+    private static final int PROFILE_TIMEOUT_DELAY_MS = 10_000;
+
     private int mParentUserId;
 
     // ID of the profile we'll create. This will always be a profile of the parent.
     private int mProfileUserId;
-    private String mPackageVerifier;
 
     private boolean mHasNfcFeature;
 
@@ -138,6 +137,29 @@
                 mProfileUserId);
     }
 
+    public void testWipeDataWithReason() throws Exception {
+        if (!mHasFeature) {
+            return;
+        }
+        assertTrue(listUsers().contains(mProfileUserId));
+        runDeviceTestsAsUser(
+                MANAGED_PROFILE_PKG,
+                ".WipeDataTest",
+                "testWipeDataWithReason",
+                mProfileUserId);
+        // Note: the managed profile is removed by this test, which will make removeUserCommand in
+        // tearDown() to complain, but that should be OK since its result is not asserted.
+        assertUserGetsRemoved(mProfileUserId);
+        // testWipeDataWithReason() removes the managed profile,
+        // so it needs to separated from other tests.
+        // Check the notification is presented after work profile got removed, so profile user no
+        // longer exists, verification should be run in primary user.
+        runDeviceTestsAsUser(
+                MANAGED_PROFILE_PKG,
+                ".WipeDataWithReasonVerificationTest",
+                mParentUserId);
+    }
+
     /**
      *  wipeData() test removes the managed profile, so it needs to separated from other tests.
      */
@@ -147,7 +169,8 @@
         }
         assertTrue(listUsers().contains(mProfileUserId));
         runDeviceTestsAsUser(
-                MANAGED_PROFILE_PKG, MANAGED_PROFILE_PKG + ".WipeDataTest", mProfileUserId);
+                MANAGED_PROFILE_PKG, ".WipeDataTest",
+                "testWipeData", mProfileUserId);
         // Note: the managed profile is removed by this test, which will make removeUserCommand in
         // tearDown() to complain, but that should be OK since its result is not asserted.
         assertUserGetsRemoved(mProfileUserId);
@@ -178,6 +201,84 @@
                 TIMEOUT_USER_LOCKED_MILLIS);
     }
 
+    /** Profile should get locked if it is not in foreground no matter what. */
+    public void testWorkProfileTimeoutBackground() throws Exception {
+        setUpWorkProfileTimeout();
+
+        startDummyActivity(mPrimaryUserId, true);
+        simulateUserInteraction(PROFILE_TIMEOUT_DELAY_MS);
+
+        verifyOnlyProfileLocked(true);
+    }
+
+    /** Profile should get locked if it is in foreground but with no user activity. */
+    public void testWorkProfileTimeoutIdleActivity() throws Exception {
+        setUpWorkProfileTimeout();
+
+        startDummyActivity(mProfileUserId, false);
+        Thread.sleep(PROFILE_TIMEOUT_DELAY_MS);
+
+        verifyOnlyProfileLocked(true);
+    }
+
+    /** User activity in profile should prevent it from locking. */
+    public void testWorkProfileTimeoutUserActivity() throws Exception {
+        setUpWorkProfileTimeout();
+
+        startDummyActivity(mProfileUserId, false);
+        simulateUserInteraction(PROFILE_TIMEOUT_DELAY_MS);
+
+        verifyOnlyProfileLocked(false);
+    }
+
+    /** Keep screen on window flag in the profile should prevent it from locking. */
+    public void testWorkProfileTimeoutKeepScreenOnWindow() throws Exception {
+        setUpWorkProfileTimeout();
+
+        startDummyActivity(mProfileUserId, true);
+        Thread.sleep(PROFILE_TIMEOUT_DELAY_MS);
+
+        verifyOnlyProfileLocked(false);
+    }
+
+    private void setUpWorkProfileTimeout() throws DeviceNotAvailableException {
+        // Set separate challenge.
+        changeUserCredential(PROFILE_CREDENTIAL, null, mProfileUserId);
+
+        // Make sure the profile is not prematurely locked.
+        verifyUserCredential(PROFILE_CREDENTIAL, mProfileUserId);
+        verifyOnlyProfileLocked(false);
+        // Set profile timeout to 5 seconds.
+        runProfileTimeoutTest("testSetWorkProfileTimeout", mProfileUserId);
+    }
+
+    private void verifyOnlyProfileLocked(boolean locked) throws DeviceNotAvailableException {
+        final String expectedResultTest = locked ? "testDeviceLocked" : "testDeviceNotLocked";
+        runProfileTimeoutTest(expectedResultTest, mProfileUserId);
+        // Primary profile shouldn't be locked.
+        runProfileTimeoutTest("testDeviceNotLocked", mPrimaryUserId);
+    }
+
+    private void simulateUserInteraction(int timeMs) throws Exception {
+        final UserActivityEmulator helper = new UserActivityEmulator(getDevice());
+        for (int i = 0; i < timeMs; i += timeMs/10) {
+            Thread.sleep(timeMs/10);
+            helper.tapScreenCenter();
+        }
+    }
+
+    private void runProfileTimeoutTest(String method, int userId)
+            throws DeviceNotAvailableException {
+        runDeviceTestsAsUser(MANAGED_PROFILE_PKG, MANAGED_PROFILE_PKG + ".ProfileTimeoutTestHelper",
+                method, userId);
+    }
+
+    private void startDummyActivity(int profileUserId, boolean keepScreenOn) throws Exception {
+        getDevice().executeShellCommand(String.format(
+                "am start-activity -W --user %d --ez keep_screen_on %s %s/.TimeoutActivity",
+                profileUserId, keepScreenOn, MANAGED_PROFILE_PKG));
+    }
+
     public void testMaxOneManagedProfile() throws Exception {
         int newUserId = -1;
         try {
@@ -1028,6 +1129,26 @@
         }
     }
 
+    public void testIsUsingUnifiedPassword() throws Exception {
+        if (!mHasFeature) {
+            return;
+        }
+
+        // Freshly created profile profile has no separate challenge.
+        verifyUnifiedPassword(true);
+
+        // Set separate challenge and verify that the API reports it correctly.
+        changeUserCredential("1234" /* newCredential */, null /* oldCredential */, mProfileUserId);
+        verifyUnifiedPassword(false);
+    }
+
+    private void verifyUnifiedPassword(boolean unified) throws DeviceNotAvailableException {
+        final String testMethod =
+                unified ? "testUsingUnifiedPassword" : "testNotUsingUnifiedPassword";
+        runDeviceTestsAsUser(MANAGED_PROFILE_PKG, ".IsUsingUnifiedPasswordTest",
+                testMethod, mProfileUserId);
+    }
+
     private void disableActivityForUser(String activityName, int userId)
             throws DeviceNotAvailableException {
         String command = "am start -W --user " + userId
diff --git a/hostsidetests/devicepolicy/src/com/android/cts/devicepolicy/MixedDeviceOwnerTransferTest.java b/hostsidetests/devicepolicy/src/com/android/cts/devicepolicy/MixedDeviceOwnerTransferTest.java
new file mode 100644
index 0000000..6fbc087
--- /dev/null
+++ b/hostsidetests/devicepolicy/src/com/android/cts/devicepolicy/MixedDeviceOwnerTransferTest.java
@@ -0,0 +1,46 @@
+/*
+ * 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.devicepolicy;
+
+/**
+ * Tests the DPC transfer functionality for device owner. Testing is done by having two dummy DPCs,
+ * CtsTransferOwnerOutgoingApp and CtsTransferOwnerIncomingApp. The former is the current DPC
+ * 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.
+ */
+public class MixedDeviceOwnerTransferTest extends DeviceAndProfileOwnerTransferTest {
+    private static final String TRANSFER_DEVICE_OWNER_OUTGOING_TEST =
+            "com.android.cts.transferowner.TransferDeviceOwnerOutgoingTest";
+    private static final String TRANSFER_DEVICE_OWNER_INCOMING_TEST =
+            "com.android.cts.transferowner.TransferDeviceOwnerIncomingTest";
+
+    @Override
+    protected void setUp() throws Exception {
+        super.setUp();
+        if (mHasFeature) {
+            setupDeviceOwner(TRANSFER_OWNER_OUTGOING_APK,
+                    TRANSFER_OWNER_OUTGOING_TEST_RECEIVER);
+            setupTestParameters(mPrimaryUserId, TRANSFER_DEVICE_OWNER_OUTGOING_TEST,
+                    TRANSFER_DEVICE_OWNER_INCOMING_TEST);
+        }
+    }
+
+    private void setupDeviceOwner(String apkName, String adminReceiverClassName) throws Exception {
+        installAppAsUser(apkName, mPrimaryUserId);
+        setDeviceOwnerOrFail(adminReceiverClassName, mPrimaryUserId);
+    }
+}
diff --git a/hostsidetests/devicepolicy/src/com/android/cts/devicepolicy/MixedProfileOwnerTransferTest.java b/hostsidetests/devicepolicy/src/com/android/cts/devicepolicy/MixedProfileOwnerTransferTest.java
new file mode 100644
index 0000000..3167729
--- /dev/null
+++ b/hostsidetests/devicepolicy/src/com/android/cts/devicepolicy/MixedProfileOwnerTransferTest.java
@@ -0,0 +1,52 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.cts.devicepolicy;
+
+/**
+ * Tests the DPC transfer functionality for profile 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.
+ */
+public class MixedProfileOwnerTransferTest extends DeviceAndProfileOwnerTransferTest {
+    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 {
+        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) {
+            int profileOwnerUserId = setupManagedProfile(TRANSFER_OWNER_OUTGOING_APK,
+                    TRANSFER_OWNER_OUTGOING_TEST_RECEIVER);
+            setupTestParameters(profileOwnerUserId, TRANSFER_PROFILE_OWNER_OUTGOING_TEST,
+                    TRANSFER_PROFILE_OWNER_INCOMING_TEST);
+        }
+    }
+
+    private int setupManagedProfile(String apkName, String adminReceiverClassName)
+            throws Exception {
+        final int userId = createManagedProfile(mPrimaryUserId);
+        installAppAsUser(apkName, userId);
+        setProfileOwnerOrFail(adminReceiverClassName, userId);
+        startUser(userId);
+        return userId;
+    }
+}
diff --git a/hostsidetests/devicepolicy/src/com/android/cts/devicepolicy/QuietModeHostsideTest.java b/hostsidetests/devicepolicy/src/com/android/cts/devicepolicy/QuietModeHostsideTest.java
new file mode 100644
index 0000000..e2f3f9c
--- /dev/null
+++ b/hostsidetests/devicepolicy/src/com/android/cts/devicepolicy/QuietModeHostsideTest.java
@@ -0,0 +1,73 @@
+package com.android.cts.devicepolicy;
+
+import java.util.HashMap;
+import java.util.Map;
+
+/**
+ * CTS to verify toggling quiet mode in work profile by using
+ * {@link android.os.UserManager#trySetQuietModeEnabled(boolean, android.os.UserHandle)}.
+ */
+public class QuietModeHostsideTest extends BaseDevicePolicyTest {
+    private static final String TEST_PACKAGE = "com.android.cts.launchertests";
+    private static final String TEST_CLASS = ".QuietModeTest";
+    private static final String PARAM_TARGET_USER = "TARGET_USER";
+    private static final String PARAM_ORIGINAL_DEFAULT_LAUNCHER = "ORIGINAL_DEFAULT_LAUNCHER";
+    private static final String TEST_APK = "CtsLauncherAppsTests.apk";
+
+    private static final String TEST_LAUNCHER_PACKAGE = "com.android.cts.launchertests.support";
+    private static final String TEST_LAUNCHER_APK = "CtsLauncherAppsTestsSupport.apk";
+
+    private int mProfileId;
+    private String mOriginalLauncher;
+
+    @Override
+    protected void setUp() throws Exception {
+        super.setUp();
+
+        mHasFeature = mHasFeature & hasDeviceFeature("android.software.managed_users");
+
+        if(mHasFeature) {
+            mOriginalLauncher = getDefaultLauncher();
+
+            installAppAsUser(TEST_APK, mPrimaryUserId);
+            installAppAsUser(TEST_LAUNCHER_APK, mPrimaryUserId);
+
+            createAndStartManagedProfile();
+            installAppAsUser(TEST_APK, mProfileId);
+
+            waitForBroadcastIdle();
+            wakeupAndDismissKeyguard();
+        }
+    }
+
+    @Override
+    protected void tearDown() throws Exception {
+        if (mHasFeature) {
+            getDevice().uninstallPackage(TEST_PACKAGE);
+            getDevice().uninstallPackage(TEST_LAUNCHER_PACKAGE);
+        }
+        super.tearDown();
+    }
+
+    public void testQuietMode() throws Exception {
+        runDeviceTestsAsUser(
+                TEST_PACKAGE,
+                TEST_CLASS,
+                null,
+                mPrimaryUserId,
+                createParams(mProfileId));
+    }
+
+    private void createAndStartManagedProfile() throws Exception {
+        mProfileId = createManagedProfile(mPrimaryUserId);
+        switchUser(mPrimaryUserId);
+        startUser(mProfileId);
+    }
+
+    private Map<String, String> createParams(int targetUserId) throws Exception {
+        Map<String, String> params = new HashMap<>();
+        params.put(PARAM_TARGET_USER, Integer.toString(getUserSerialNumber(targetUserId)));
+        params.put(PARAM_ORIGINAL_DEFAULT_LAUNCHER, mOriginalLauncher);
+        return params;
+    }
+}
\ No newline at end of file
diff --git a/hostsidetests/devicepolicy/src/com/android/cts/devicepolicy/UserActivityEmulator.java b/hostsidetests/devicepolicy/src/com/android/cts/devicepolicy/UserActivityEmulator.java
new file mode 100644
index 0000000..0b17b17
--- /dev/null
+++ b/hostsidetests/devicepolicy/src/com/android/cts/devicepolicy/UserActivityEmulator.java
@@ -0,0 +1,42 @@
+/*
+ * 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.devicepolicy;
+
+import com.android.tradefed.device.DeviceNotAvailableException;
+import com.android.tradefed.device.ITestDevice;
+
+/**
+ * Helper to simulate user activity on the device.
+ */
+class UserActivityEmulator {
+    private final int mWidth;
+    private final int mHeight;
+    private final ITestDevice mDevice;
+
+    public UserActivityEmulator(ITestDevice device) throws DeviceNotAvailableException {
+        // Figure out screen size. Output is something like "Physical size: 1440x2880".
+        mDevice = device;
+        final String output = mDevice.executeShellCommand("wm size");
+        final String[] sizes = output.split(" ")[2].split("x");
+        mWidth = Integer.valueOf(sizes[0].trim());
+        mHeight = Integer.valueOf(sizes[1].trim());
+    }
+
+    public void tapScreenCenter() throws DeviceNotAvailableException {
+        mDevice.executeShellCommand(String.format("input tap %d %d", mWidth / 2, mHeight / 2));
+    }
+}
diff --git a/hostsidetests/devicepolicy/src/com/android/cts/devicepolicy/crossprofile/CrossProfileAppsManagedProfileTest.java b/hostsidetests/devicepolicy/src/com/android/cts/devicepolicy/crossprofile/CrossProfileAppsManagedProfileTest.java
new file mode 100644
index 0000000..cf02aef
--- /dev/null
+++ b/hostsidetests/devicepolicy/src/com/android/cts/devicepolicy/crossprofile/CrossProfileAppsManagedProfileTest.java
@@ -0,0 +1,8 @@
+//package com.android.cts.devicepolicy.crossprofile;
+//
+//import com.android.cts.devicepolicy.BaseDevicePolicyTest;
+//
+//public class CrossProfileAppsManagedProfileTest extends BaseDevicePolicyTest {
+//
+//
+//}
diff --git a/hostsidetests/dumpsys/AndroidTest.xml b/hostsidetests/dumpsys/AndroidTest.xml
index 252bf90..0ac7c2e 100644
--- a/hostsidetests/dumpsys/AndroidTest.xml
+++ b/hostsidetests/dumpsys/AndroidTest.xml
@@ -14,6 +14,7 @@
      limitations under the License.
 -->
 <configuration description="Config for the CTS dumpsys host tests">
+    <option name="test-suite-tag" value="cts" />
     <option name="config-descriptor:metadata" key="component" value="framework" />
     <test class="com.android.compatibility.common.tradefed.testtype.JarHostTest">
         <option name="jar" value="CtsDumpsysHostTestCases.jar" />
diff --git a/hostsidetests/dumpsys/apps/ProcStatsHelperApp/AndroidManifest.xml b/hostsidetests/dumpsys/apps/ProcStatsHelperApp/AndroidManifest.xml
index 1a84011..01b9ca8 100644
--- a/hostsidetests/dumpsys/apps/ProcStatsHelperApp/AndroidManifest.xml
+++ b/hostsidetests/dumpsys/apps/ProcStatsHelperApp/AndroidManifest.xml
@@ -21,6 +21,8 @@
     <uses-sdk android:minSdkVersion="24" android:targetSdkVersion="24"/>
 
     <application>
+        <uses-library android:name="android.test.runner" />
+
         <activity android:name=".MainActivity"
             android:exported="true" />
         <service android:name=".ProcStatsHelperServiceMain"
diff --git a/hostsidetests/dumpsys/src/android/dumpsys/cts/BatteryStatsDumpsysTest.java b/hostsidetests/dumpsys/src/android/dumpsys/cts/BatteryStatsDumpsysTest.java
index 4d51330..2ae5265 100644
--- a/hostsidetests/dumpsys/src/android/dumpsys/cts/BatteryStatsDumpsysTest.java
+++ b/hostsidetests/dumpsys/src/android/dumpsys/cts/BatteryStatsDumpsysTest.java
@@ -442,7 +442,7 @@
     }
 
     private void checkBatteryDischarge(String[] parts) {
-        assertEquals(12, parts.length);
+        assertEquals(14, parts.length);
         assertInteger(parts[4]); // low
         assertInteger(parts[5]); // high
         assertInteger(parts[6]); // screenOn
@@ -451,6 +451,8 @@
         assertInteger(parts[9]); // dischargeScreenOffMah
         assertInteger(parts[10]); // dischargeDozeCount
         assertInteger(parts[11]); // dischargeDozeMah
+        assertInteger(parts[12]); // dischargeLightDozeMah
+        assertInteger(parts[13]); // dischargeDeepDozeMah
     }
 
     private void checkBatteryLevel(String[] parts) {
@@ -723,7 +725,8 @@
                 for (int i = 0; i < TIMESTAMP_COUNT; i++) {
                     numparts[i] = assertInteger(parts[i]);
                 }
-                if (numparts[0] != 0) {
+                // Flags = 1 just means the first frame of the window
+                if (numparts[0] != 0 && numparts[0] != 1) {
                     continue;
                 }
                 // assert VSYNC >= INTENDED_VSYNC
diff --git a/hostsidetests/edi/AndroidTest.xml b/hostsidetests/edi/AndroidTest.xml
index 9449d72..aef3086 100644
--- a/hostsidetests/edi/AndroidTest.xml
+++ b/hostsidetests/edi/AndroidTest.xml
@@ -14,6 +14,7 @@
      limitations under the License.
 -->
 <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" />
     <test class="com.android.compatibility.common.tradefed.testtype.JarHostTest" >
         <option name="jar" value="CtsEdiHostTestCases.jar" />
diff --git a/hostsidetests/gputools/Android.mk b/hostsidetests/gputools/Android.mk
new file mode 100644
index 0000000..b2946be
--- /dev/null
+++ b/hostsidetests/gputools/Android.mk
@@ -0,0 +1,33 @@
+# 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_SRC_FILES := $(call all-java-files-under, src)
+
+LOCAL_MODULE_TAGS := tests
+
+# tag this module as a cts test artifact
+LOCAL_COMPATIBILITY_SUITE := cts
+
+LOCAL_MODULE := CtsGpuToolsHostTestCases
+
+LOCAL_JAVA_LIBRARIES := cts-tradefed tradefed compatibility-host-util
+
+LOCAL_STATIC_JAVA_LIBRARIES := platform-test-annotations-host
+
+include $(BUILD_HOST_JAVA_LIBRARY)
+
+include $(call all-makefiles-under,$(LOCAL_PATH))
diff --git a/hostsidetests/gputools/AndroidTest.xml b/hostsidetests/gputools/AndroidTest.xml
new file mode 100644
index 0000000..98e41a7
--- /dev/null
+++ b/hostsidetests/gputools/AndroidTest.xml
@@ -0,0 +1,35 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2017 The Android Open Source Project
+
+     Licensed under the Apache License, Version 2.0 (the "License");
+     you may not use this file except in compliance with the License.
+     You may obtain a copy of the License at
+
+          http://www.apache.org/licenses/LICENSE-2.0
+
+     Unless required by applicable law or agreed to in writing, software
+     distributed under the License is distributed on an "AS IS" BASIS,
+     WITHOUT 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 CtsGpuToolsHostTestCases">
+    <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="CtsGpuToolsRootlessGpuDebugApp-DEBUG.apk" />
+    </target_preparer>
+    <target_preparer class="com.android.tradefed.targetprep.suite.SuiteApkInstaller">
+        <option name="cleanup-apks" value="true" />
+        <option name="test-file-name" value="CtsGpuToolsRootlessGpuDebugApp-RELEASE.apk" />
+    </target_preparer>
+    <target_preparer class="com.android.tradefed.targetprep.suite.SuiteApkInstaller">
+        <option name="cleanup-apks" value="true" />
+        <option name="test-file-name" value="CtsGpuToolsRootlessGpuDebugApp-LAYERS.apk" />
+    </target_preparer>
+    <test class="com.android.compatibility.common.tradefed.testtype.JarHostTest" >
+        <option name="jar" value="CtsGpuToolsHostTestCases.jar" />
+    </test>
+</configuration>
diff --git a/hostsidetests/gputools/apps/Android.mk b/hostsidetests/gputools/apps/Android.mk
new file mode 100644
index 0000000..2a2cdd9
--- /dev/null
+++ b/hostsidetests/gputools/apps/Android.mk
@@ -0,0 +1,69 @@
+# 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 := libctsgputools_jni
+LOCAL_SRC_FILES := \
+    jni/CtsGpuToolsJniOnLoad.cpp \
+    jni/android_gputools_cts_RootlessGpuDebug.cpp
+LOCAL_CFLAGS += -std=c++14 -Wall -Werror
+LOCAL_SHARED_LIBRARIES := libandroid libvulkan liblog
+LOCAL_NDK_STL_VARIANT := c++_static
+LOCAL_SDK_VERSION := current
+include $(BUILD_SHARED_LIBRARY)
+
+
+include $(CLEAR_VARS)
+LOCAL_SRC_FILES := $(call all-java-files-under, src)
+LOCAL_MODULE_TAGS := tests
+LOCAL_PACKAGE_NAME := CtsGpuToolsRootlessGpuDebugApp-DEBUG
+
+# tag this module as a cts test artifact
+LOCAL_COMPATIBILITY_SUITE := cts
+
+LOCAL_MULTILIB := both
+
+LOCAL_JNI_SHARED_LIBRARIES := \
+libctsgputools_jni
+
+LOCAL_AAPT_FLAGS := \
+--rename-manifest-package android.rootlessgpudebug.DEBUG.app \
+--debug-mode
+
+include $(call all-makefiles-under,$(LOCAL_PATH))
+include $(BUILD_CTS_SUPPORT_PACKAGE)
+
+
+include $(CLEAR_VARS)
+LOCAL_SRC_FILES := $(call all-java-files-under, src)
+LOCAL_MODULE_TAGS := tests
+LOCAL_PACKAGE_NAME := CtsGpuToolsRootlessGpuDebugApp-RELEASE
+
+# tag this module as a cts test artifact
+LOCAL_COMPATIBILITY_SUITE := cts
+
+LOCAL_MULTILIB := both
+
+LOCAL_JNI_SHARED_LIBRARIES := \
+libctsgputools_jni \
+libVkLayer_nullLayerC
+
+LOCAL_AAPT_FLAGS := \
+--rename-manifest-package android.rootlessgpudebug.RELEASE.app
+
+include $(call all-makefiles-under,$(LOCAL_PATH))
+
+include $(BUILD_CTS_SUPPORT_PACKAGE)
diff --git a/hostsidetests/gputools/apps/AndroidManifest.xml b/hostsidetests/gputools/apps/AndroidManifest.xml
new file mode 100755
index 0000000..de8130f
--- /dev/null
+++ b/hostsidetests/gputools/apps/AndroidManifest.xml
@@ -0,0 +1,32 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+# Copyright (C) 2017 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#      http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT 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.rootlessgpudebug.app">>
+
+    <application>
+        <activity android:name=".RootlessGpuDebugDeviceActivity" >
+            <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/gputools/apps/jni/CtsGpuToolsJniOnLoad.cpp b/hostsidetests/gputools/apps/jni/CtsGpuToolsJniOnLoad.cpp
new file mode 100644
index 0000000..2761aea
--- /dev/null
+++ b/hostsidetests/gputools/apps/jni/CtsGpuToolsJniOnLoad.cpp
@@ -0,0 +1,29 @@
+/*
+ * Copyright 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <jni.h>
+#include <stdio.h>
+
+extern int register_android_gputools_cts_RootlessGpuDebug(JNIEnv*);
+
+jint JNI_OnLoad(JavaVM* vm, void* /*reserved*/) {
+    JNIEnv* env = nullptr;
+    if (vm->GetEnv((void**)&env, JNI_VERSION_1_4) != JNI_OK)
+        return JNI_ERR;
+    if (register_android_gputools_cts_RootlessGpuDebug(env))
+        return JNI_ERR;
+    return JNI_VERSION_1_4;
+}
diff --git a/hostsidetests/gputools/apps/jni/android_gputools_cts_RootlessGpuDebug.cpp b/hostsidetests/gputools/apps/jni/android_gputools_cts_RootlessGpuDebug.cpp
new file mode 100644
index 0000000..4fdddbc
--- /dev/null
+++ b/hostsidetests/gputools/apps/jni/android_gputools_cts_RootlessGpuDebug.cpp
@@ -0,0 +1,87 @@
+/*
+ * Copyright 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.
+ *
+ */
+
+#define LOG_TAG "RootlessGpuDebug"
+
+#include <android/log.h>
+#include <jni.h>
+#include <string>
+#include <vulkan/vulkan.h>
+
+#define ALOGI(msg, ...) \
+    __android_log_print(ANDROID_LOG_INFO, LOG_TAG, (msg), __VA_ARGS__)
+#define ALOGE(msg, ...) \
+    __android_log_print(ANDROID_LOG_ERROR, LOG_TAG, (msg), __VA_ARGS__)
+
+namespace {
+
+std::string initVulkan()
+{
+    std::string result = "";
+
+    const VkApplicationInfo app_info = {
+        VK_STRUCTURE_TYPE_APPLICATION_INFO,
+        nullptr,            // pNext
+        "RootlessGpuDebug", // app name
+        0,                  // app version
+        nullptr,            // engine name
+        0,                  // engine version
+        VK_API_VERSION_1_0,
+    };
+    const VkInstanceCreateInfo instance_info = {
+        VK_STRUCTURE_TYPE_INSTANCE_CREATE_INFO,
+        nullptr,   // pNext
+        0,         // flags
+        &app_info,
+        0,         // layer count
+        nullptr,   // layers
+        0,         // extension count
+        nullptr,   // extensions
+    };
+    VkInstance instance;
+    VkResult vkResult = vkCreateInstance(&instance_info, nullptr, &instance);
+    if (vkResult == VK_ERROR_INITIALIZATION_FAILED) {
+        result = "vkCreateInstance failed, meaning layers could not be chained.";
+    } else {
+        result = "vkCreateInstance succeeded.";
+    }
+
+    return result;
+}
+
+jstring android_gputools_cts_RootlessGpuDebug_nativeInitVulkan(JNIEnv* env,
+    jclass /*clazz*/)
+{
+    std::string result;
+
+    result = initVulkan();
+
+    return env->NewStringUTF(result.c_str());
+}
+
+static JNINativeMethod gMethods[] = {
+    {    "nativeInitVulkan", "()Ljava/lang/String;",
+         (void*) android_gputools_cts_RootlessGpuDebug_nativeInitVulkan },
+};
+
+} // anonymous namespace
+
+int register_android_gputools_cts_RootlessGpuDebug(JNIEnv* env) {
+    jclass clazz = env->FindClass("android/rootlessgpudebug/app/RootlessGpuDebugDeviceActivity");
+    return env->RegisterNatives(clazz, gMethods,
+            sizeof(gMethods) / sizeof(JNINativeMethod));
+}
diff --git a/hostsidetests/gputools/apps/src/android/rootlessgpudebug/app/RootlessGpuDebugDeviceActivity.java b/hostsidetests/gputools/apps/src/android/rootlessgpudebug/app/RootlessGpuDebugDeviceActivity.java
new file mode 100644
index 0000000..ec5052b
--- /dev/null
+++ b/hostsidetests/gputools/apps/src/android/rootlessgpudebug/app/RootlessGpuDebugDeviceActivity.java
@@ -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.
+ */
+
+package android.rootlessgpudebug.app;
+
+import android.app.Activity;
+import android.os.Bundle;
+import android.util.Log;
+
+import java.lang.Override;
+
+public class RootlessGpuDebugDeviceActivity extends Activity {
+
+    static {
+        System.loadLibrary("ctsgputools_jni");
+    }
+
+    private static final String TAG = RootlessGpuDebugDeviceActivity.class.getSimpleName();
+
+    @Override
+    public void onCreate(Bundle icicle) {
+        super.onCreate(icicle);
+
+        String result = nativeInitVulkan();
+        Log.i(TAG, result);
+    }
+
+    private static native String nativeInitVulkan();
+
+}
+
diff --git a/hostsidetests/gputools/layers/Android.mk b/hostsidetests/gputools/layers/Android.mk
new file mode 100644
index 0000000..69f6930
--- /dev/null
+++ b/hostsidetests/gputools/layers/Android.mk
@@ -0,0 +1,70 @@
+# 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 := libVkLayer_nullLayerA
+LOCAL_MODULE_TAGS := tests
+LOCAL_SRC_FILES := jni/nullLayer.cpp
+LOCAL_CFLAGS += -std=c++14 -Wall -Werror -fvisibility=hidden -DLAYERNAME="A"
+LOCAL_SHARED_LIBRARIES := libandroid libvulkan liblog
+LOCAL_NDK_STL_VARIANT := c++_static
+LOCAL_SDK_VERSION := current
+include $(BUILD_SHARED_LIBRARY)
+
+
+include $(CLEAR_VARS)
+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_SHARED_LIBRARIES := libandroid libvulkan liblog
+LOCAL_NDK_STL_VARIANT := c++_static
+LOCAL_SDK_VERSION := current
+include $(BUILD_SHARED_LIBRARY)
+
+
+include $(CLEAR_VARS)
+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_SHARED_LIBRARIES := libandroid libvulkan liblog
+LOCAL_NDK_STL_VARIANT := c++_static
+LOCAL_SDK_VERSION := current
+include $(BUILD_SHARED_LIBRARY)
+
+
+
+include $(CLEAR_VARS)
+
+LOCAL_MODULE_TAGS := tests
+
+LOCAL_PACKAGE_NAME := CtsGpuToolsRootlessGpuDebugApp-LAYERS
+
+# tag this module as a cts test artifact
+LOCAL_COMPATIBILITY_SUITE := cts
+
+LOCAL_MULTILIB := both
+
+LOCAL_JNI_SHARED_LIBRARIES := \
+libVkLayer_nullLayerA \
+libVkLayer_nullLayerB \
+libVkLayer_nullLayerC
+
+include $(call all-makefiles-under,$(LOCAL_PATH))
+
+include $(BUILD_CTS_SUPPORT_PACKAGE)
diff --git a/hostsidetests/gputools/layers/AndroidManifest.xml b/hostsidetests/gputools/layers/AndroidManifest.xml
new file mode 100755
index 0000000..957b047
--- /dev/null
+++ b/hostsidetests/gputools/layers/AndroidManifest.xml
@@ -0,0 +1,26 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+# Copyright (C) 2017 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#      http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT 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.rootlessgpudebug.LAYERS.app">
+
+    <application android:debuggable="false" android:hasCode="false">
+    </application>
+
+</manifest>
+
+
diff --git a/hostsidetests/gputools/layers/jni/nullLayer.cpp b/hostsidetests/gputools/layers/jni/nullLayer.cpp
new file mode 100644
index 0000000..7253b5c
--- /dev/null
+++ b/hostsidetests/gputools/layers/jni/nullLayer.cpp
@@ -0,0 +1,173 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <android/log.h>
+#include <cstring>
+#include <vulkan/vulkan.h>
+#include "vk_layer_interface.h"
+
+#define xstr(a) str(a)
+#define str(a) #a
+
+#define LOG_TAG "nullLayer" xstr(LAYERNAME)
+
+#define ALOGI(msg, ...) \
+    __android_log_print(ANDROID_LOG_INFO, LOG_TAG, (msg), __VA_ARGS__)
+
+
+// Announce if anything loads this layer.  LAYERNAME is defined in Android.mk
+class StaticLogMessage {
+    public:
+        StaticLogMessage(const char* msg) {
+            ALOGI("%s", msg);
+    }
+};
+StaticLogMessage g_initMessage("nullLayer" xstr(LAYERNAME) " loaded");
+
+
+namespace {
+
+
+// Minimal dispatch table for this simple layer
+struct {
+    PFN_vkGetInstanceProcAddr GetInstanceProcAddr;
+} g_VulkanDispatchTable;
+
+
+template<class T>
+VkResult getProperties(const uint32_t count, const T *properties, uint32_t *pCount,
+                            T *pProperties) {
+    uint32_t copySize;
+
+    if (pProperties == NULL || properties == NULL) {
+        *pCount = count;
+        return VK_SUCCESS;
+    }
+
+    copySize = *pCount < count ? *pCount : count;
+    memcpy(pProperties, properties, copySize * sizeof(T));
+    *pCount = copySize;
+    if (copySize < count) {
+        return VK_INCOMPLETE;
+    }
+
+    return VK_SUCCESS;
+}
+
+static const VkLayerProperties LAYER_PROPERTIES = {
+    "VK_LAYER_ANDROID_nullLayer" xstr(LAYERNAME), VK_MAKE_VERSION(1, 0, VK_HEADER_VERSION), 1, "Layer: nullLayer" xstr(LAYERNAME),
+};
+
+VKAPI_ATTR VkResult VKAPI_CALL EnumerateInstanceLayerProperties(uint32_t *pCount, VkLayerProperties *pProperties) {
+    return getProperties<VkLayerProperties>(1, &LAYER_PROPERTIES, pCount, pProperties);
+}
+
+VKAPI_ATTR VkResult VKAPI_CALL EnumerateDeviceLayerProperties(VkPhysicalDevice /* physicalDevice */, uint32_t *pCount,
+                                                              VkLayerProperties *pProperties) {
+    return getProperties<VkLayerProperties>(0, NULL, pCount, pProperties);
+}
+
+VKAPI_ATTR VkResult VKAPI_CALL EnumerateInstanceExtensionProperties(const char* /* pLayerName */, uint32_t *pCount,
+                                                                    VkExtensionProperties *pProperties) {
+    return getProperties<VkExtensionProperties>(0, NULL, pCount, pProperties);
+}
+
+VKAPI_ATTR VkResult VKAPI_CALL EnumerateDeviceExtensionProperties(VkPhysicalDevice /* physicalDevice */, const char* /* pLayerName */,
+                                                                  uint32_t *pCount, VkExtensionProperties *pProperties) {
+    return getProperties<VkExtensionProperties>(0, NULL, pCount, pProperties);
+}
+
+VKAPI_ATTR VkResult VKAPI_CALL nullCreateInstance(const VkInstanceCreateInfo* pCreateInfo,
+                                                  const VkAllocationCallbacks* pAllocator,
+                                                  VkInstance* pInstance) {
+
+    VkLayerInstanceCreateInfo *layerCreateInfo = (VkLayerInstanceCreateInfo *)pCreateInfo->pNext;
+
+    const char* msg = "nullCreateInstance called in nullLayer" xstr(LAYERNAME);
+    ALOGI("%s", msg);
+
+    // Step through the pNext chain until we get to the link function
+    while(layerCreateInfo && (layerCreateInfo->sType != VK_STRUCTURE_TYPE_LOADER_INSTANCE_CREATE_INFO ||
+                              layerCreateInfo->function != VK_LAYER_FUNCTION_LINK)) {
+
+      layerCreateInfo = (VkLayerInstanceCreateInfo *)layerCreateInfo->pNext;
+    }
+
+    if(layerCreateInfo == NULL)
+      return VK_ERROR_INITIALIZATION_FAILED;
+
+    // Grab GIPA for the next layer
+    PFN_vkGetInstanceProcAddr gpa = layerCreateInfo->u.pLayerInfo->pfnNextGetInstanceProcAddr;
+
+    // Track is in our dispatch table
+    g_VulkanDispatchTable.GetInstanceProcAddr = gpa;
+
+    // Advance the chain for next layer
+    layerCreateInfo->u.pLayerInfo = layerCreateInfo->u.pLayerInfo->pNext;
+
+    // Call the next layer
+    PFN_vkCreateInstance createFunc = (PFN_vkCreateInstance)gpa(VK_NULL_HANDLE, "vkCreateInstance");
+    VkResult ret = createFunc(pCreateInfo, pAllocator, pInstance);
+
+    return ret;
+}
+
+VKAPI_ATTR PFN_vkVoidFunction VKAPI_CALL GetDeviceProcAddr(VkDevice /* dev */, const char* /* funcName */) {
+    return nullptr;
+}
+
+VKAPI_ATTR PFN_vkVoidFunction VKAPI_CALL GetInstanceProcAddr(VkInstance instance, const char* funcName) {
+
+    // Our simple layer only intercepts vkCreateInstance
+    const char* targetFunc = "vkCreateInstance";
+    if (!strncmp(targetFunc, funcName, sizeof(&targetFunc)))
+        return (PFN_vkVoidFunction)nullCreateInstance;
+
+    return (PFN_vkVoidFunction)g_VulkanDispatchTable.GetInstanceProcAddr(instance, funcName);
+}
+
+}  // namespace
+
+// loader-layer interface v0, just wrappers since there is only a layer
+
+__attribute((visibility("default"))) VKAPI_ATTR VkResult VKAPI_CALL vkEnumerateInstanceLayerProperties(uint32_t *pCount,
+                                                                  VkLayerProperties *pProperties) {
+    return EnumerateInstanceLayerProperties(pCount, pProperties);
+}
+
+__attribute((visibility("default"))) VKAPI_ATTR VkResult VKAPI_CALL vkEnumerateDeviceLayerProperties(VkPhysicalDevice physicalDevice, uint32_t *pCount,
+                                                                VkLayerProperties *pProperties) {
+    return EnumerateDeviceLayerProperties(physicalDevice, pCount, pProperties);
+}
+
+__attribute((visibility("default"))) VKAPI_ATTR VkResult VKAPI_CALL vkEnumerateInstanceExtensionProperties(const char *pLayerName, uint32_t *pCount,
+                                                                      VkExtensionProperties *pProperties) {
+    return EnumerateInstanceExtensionProperties(pLayerName, pCount, pProperties);
+}
+
+__attribute((visibility("default"))) VKAPI_ATTR VkResult VKAPI_CALL vkEnumerateDeviceExtensionProperties(VkPhysicalDevice physicalDevice,
+                                                                    const char *pLayerName, uint32_t *pCount,
+                                                                    VkExtensionProperties *pProperties) {
+    return EnumerateDeviceExtensionProperties(physicalDevice, pLayerName, pCount, pProperties);
+}
+
+__attribute((visibility("default"))) VKAPI_ATTR PFN_vkVoidFunction VKAPI_CALL vkGetDeviceProcAddr(VkDevice dev, const char *funcName) {
+    return GetDeviceProcAddr(dev, funcName);
+}
+
+__attribute((visibility("default"))) VKAPI_ATTR PFN_vkVoidFunction VKAPI_CALL vkGetInstanceProcAddr(VkInstance instance, const char *funcName) {
+    return GetInstanceProcAddr(instance, funcName);
+}
diff --git a/hostsidetests/gputools/layers/jni/vk_layer_interface.h b/hostsidetests/gputools/layers/jni/vk_layer_interface.h
new file mode 100644
index 0000000..f2a5232
--- /dev/null
+++ b/hostsidetests/gputools/layers/jni/vk_layer_interface.h
@@ -0,0 +1,73 @@
+/*
+ * Copyright (c) 2015-2016 The Khronos Group Inc.
+ * Copyright (c) 2015-2016 Valve Corporation
+ * Copyright (c) 2015-2016 LunarG, Inc.
+ * Copyright (c) 2016 Google Inc.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and/or associated documentation files (the "Materials"), to
+ * deal in the Materials without restriction, including without limitation the
+ * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
+ * sell copies of the Materials, and to permit persons to whom the Materials are
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice(s) and this permission notice shall be included in
+ * all copies or substantial portions of the Materials.
+ *
+ * THE MATERIALS ARE PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
+ *
+ * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM,
+ * DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
+ * OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE MATERIALS OR THE
+ * USE OR OTHER DEALINGS IN THE MATERIALS.
+ *
+ */
+#pragma once
+
+#include <vulkan/vulkan.h>
+
+// ------------------------------------------------------------------------------------------------
+// CreateInstance and CreateDevice support structures
+
+typedef enum VkLayerFunction_ {
+    VK_LAYER_FUNCTION_LINK = 0,
+    VK_LAYER_FUNCTION_DATA_CALLBACK = 1
+} VkLayerFunction;
+
+typedef struct VkLayerInstanceLink_ {
+    struct VkLayerInstanceLink_* pNext;
+    PFN_vkGetInstanceProcAddr pfnNextGetInstanceProcAddr;
+} VkLayerInstanceLink;
+
+typedef VkResult(VKAPI_PTR* PFN_vkSetInstanceLoaderData)(VkInstance instance,
+                                                         void* object);
+typedef VkResult(VKAPI_PTR* PFN_vkSetDeviceLoaderData)(VkDevice device,
+                                                       void* object);
+
+typedef struct {
+    VkStructureType sType;  // VK_STRUCTURE_TYPE_LOADER_INSTANCE_CREATE_INFO
+    const void* pNext;
+    VkLayerFunction function;
+    union {
+        VkLayerInstanceLink* pLayerInfo;
+        PFN_vkSetInstanceLoaderData pfnSetInstanceLoaderData;
+    } u;
+} VkLayerInstanceCreateInfo;
+
+typedef struct VkLayerDeviceLink_ {
+    struct VkLayerDeviceLink_* pNext;
+    PFN_vkGetInstanceProcAddr pfnNextGetInstanceProcAddr;
+    PFN_vkGetDeviceProcAddr pfnNextGetDeviceProcAddr;
+} VkLayerDeviceLink;
+
+typedef struct {
+    VkStructureType sType;  // VK_STRUCTURE_TYPE_LOADER_DEVICE_CREATE_INFO
+    const void* pNext;
+    VkLayerFunction function;
+    union {
+        VkLayerDeviceLink* pLayerInfo;
+        PFN_vkSetDeviceLoaderData pfnSetDeviceLoaderData;
+    } u;
+} VkLayerDeviceCreateInfo;
diff --git a/hostsidetests/gputools/src/android/gputools/cts/CtsRootlessGpuDebugHostTest.java b/hostsidetests/gputools/src/android/gputools/cts/CtsRootlessGpuDebugHostTest.java
new file mode 100644
index 0000000..96096fb
--- /dev/null
+++ b/hostsidetests/gputools/src/android/gputools/cts/CtsRootlessGpuDebugHostTest.java
@@ -0,0 +1,445 @@
+/*
+ * 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.gputools.cts;
+
+import android.platform.test.annotations.Presubmit;
+
+import com.android.tradefed.build.IBuildInfo;
+import com.android.tradefed.device.ITestDevice;
+import com.android.tradefed.testtype.DeviceJUnit4ClassRunner;
+import com.android.tradefed.testtype.IDeviceTest;
+
+import com.android.ddmlib.Log;
+
+import java.util.Scanner;
+
+import org.junit.After;
+import org.junit.Assert;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+/**
+ * Tests that exercise Rootless GPU Debug functionality supported by the loader.
+ */
+@RunWith(DeviceJUnit4ClassRunner.class)
+public class CtsRootlessGpuDebugHostTest implements IDeviceTest {
+
+    public static final String TAG = CtsRootlessGpuDebugHostTest.class.getSimpleName();
+
+    /**
+     * A reference to the device under test.
+     */
+    private ITestDevice mDevice;
+
+    public void setDevice(ITestDevice device) {
+        mDevice = device;
+    }
+
+    @Override
+    public ITestDevice getDevice() {
+        return mDevice;
+    }
+
+    // This test ensures that the Vulkan loader can use Settings to load layer
+    // from the base directory of debuggable applications.  Is also tests several
+    // positive and negative scenarios we want to cover (listed below).
+    //
+    // There are three APKs; DEBUG and RELEASE are practically identical with one
+    // being flagged as debuggable.  The LAYERS APK is mainly a conduit for getting
+    // layers onto the device without affecting the other APKs.
+    //
+    // The RELEASE APK does contain one layer to ensure using Settings to enable
+    // layers does not interfere with legacy methods using system properties.
+    //
+    // The layers themselves are practically null, only enough functionality to
+    // satisfy loader enumerating and loading.  They don't actually chain together.
+    //
+    // Positive tests
+    // - Ensure we can toggle the Enable Setting on and off (testDebugLayerLoadVulkan)
+    // - Ensure we can set the debuggable app (testDebugLayerLoadVulkan)
+    // - Ensure we can set the layer list (testDebugLayerLoadVulkan)
+    // - Ensure we can push a layer to debuggable app (testDebugLayerLoadVulkan)
+    // - Ensure we can specify the app to load layers (testDebugLayerLoadVulkan)
+    // - Ensure we can load a layer from app's data directory (testDebugLayerLoadVulkan)
+    // - Ensure we can load multiple layers, in order, from app's data directory (testDebugLayerLoadVulkan)
+    // - Ensure we can still use system properties if no layers loaded via Settings (testSystemPropertyEnableVulkan)
+    // Negative tests
+    // - Ensure we cannot push a layer to non-debuggable app (testReleaseLayerLoad)
+    // - Ensure non-debuggable app ignores the new Settings (testReleaseLayerLoad)
+    // - Ensure we cannot enumerate layers from debuggable app's data directory if Setting not specified (testDebugNoEnumerateVulkan)
+    // - Ensure we cannot enumerate layers without specifying the debuggable app (testDebugNoEnumerateVulkan)
+    // - Ensure we cannot use system properties when layer is found via Settings with debuggable app (testSystemPropertyIgnoreVulkan)
+
+    private static final String CLASS = "RootlessGpuDebugDeviceActivity";
+    private static final String ACTIVITY = "android.rootlessgpudebug.app.RootlessGpuDebugDeviceActivity";
+    private static final String LAYER_A = "nullLayerA";
+    private static final String LAYER_B = "nullLayerB";
+    private static final String LAYER_C = "nullLayerC";
+    private static final String LAYER_A_LIB = "libVkLayer_" + LAYER_A + ".so";
+    private static final String LAYER_B_LIB = "libVkLayer_" + LAYER_B + ".so";
+    private static final String LAYER_C_LIB = "libVkLayer_" + LAYER_C + ".so";
+    private static final String LAYER_A_NAME = "VK_LAYER_ANDROID_" + LAYER_A;
+    private static final String LAYER_B_NAME = "VK_LAYER_ANDROID_" + LAYER_B;
+    private static final String LAYER_C_NAME = "VK_LAYER_ANDROID_" + LAYER_C;
+    private static final String DEBUG_APP = "android.rootlessgpudebug.DEBUG.app";
+    private static final String RELEASE_APP = "android.rootlessgpudebug.RELEASE.app";
+    private static final String LAYERS_APP = "android.rootlessgpudebug.LAYERS.app";
+
+    // This is how long we'll scan the log for a result before giving up. This limit will only
+    // be reached if something has gone wrong
+    private static final long LOG_SEARCH_TIMEOUT_MS = 5000;
+
+    private String removeWhitespace(String input) {
+        return input.replaceAll(System.getProperty("line.separator"), "").trim();
+    }
+
+    /**
+     * Grab and format the process ID of requested app.
+     */
+    private String getPID(String app) throws Exception {
+        String pid = mDevice.executeAdbCommand("shell", "pidof", app);
+        return removeWhitespace(pid);
+    }
+
+    /**
+     * Extract the requested layer from APK and copy to tmp
+     */
+    private void setupLayer(String layer) throws Exception {
+
+        // We use the LAYERS apk to facilitate getting layers onto the device for mixing and matching
+        String libPath = mDevice.executeAdbCommand("shell", "pm", "path", LAYERS_APP);
+        libPath = libPath.replaceAll("package:", "");
+        libPath = libPath.replaceAll("base.apk", "");
+        libPath = removeWhitespace(libPath);
+        libPath += "lib/";
+
+        // Use find to get the .so so we can ignore ABI
+        String layerPath = mDevice.executeAdbCommand("shell", "find", libPath + " -name " + layer);
+        layerPath = removeWhitespace(layerPath);
+        mDevice.executeAdbCommand("shell", "cp", layerPath + " /data/local/tmp");
+    }
+
+    /**
+     * Simple helper class for returning multiple results
+     */
+    public class LogScanResult {
+        public boolean found;
+        public int lineNumber;
+    }
+
+    private LogScanResult scanLog(String pid, String searchString) throws Exception {
+        return scanLog(pid, searchString, "");
+    }
+
+    /**
+     * Scan the logcat for requested process ID, returning if found and which line
+     */
+    private LogScanResult scanLog(String pid, String searchString, String endString) throws Exception {
+
+        LogScanResult result = new LogScanResult();
+        result.found = false;
+        result.lineNumber = -1;
+
+        // Scan until output from app is found
+        boolean scanComplete= false;
+
+        // Let the test run a reasonable amount of time before moving on
+        long hostStartTime = System.currentTimeMillis();
+
+        while (!scanComplete && ((System.currentTimeMillis() - hostStartTime) < LOG_SEARCH_TIMEOUT_MS)) {
+
+            // Give our activity a chance to run and fill the log
+            Thread.sleep(1000);
+
+            // Pull the logcat for a single process
+            String logcat = mDevice.executeAdbCommand("logcat", "-v", "brief", "-d", "--pid=" + pid, "*:V");
+            int lineNumber = 0;
+            Scanner apkIn = new Scanner(logcat);
+            while (apkIn.hasNextLine()) {
+                lineNumber++;
+                String line = apkIn.nextLine();
+                if (line.contains(searchString) && line.endsWith(endString)) {
+                    result.found = true;
+                    result.lineNumber = lineNumber;
+                }
+                if (line.contains("vkCreateInstance succeeded")) {
+                    // Once we've got output from the app, we've collected what we need
+                    scanComplete= true;
+                }
+            }
+            apkIn.close();
+        }
+
+        return result;
+    }
+
+    /**
+     * Remove any temporary files on the device, clear any settings, kill the apps after each test
+     */
+    @After
+    public void cleanup() throws Exception {
+        mDevice.executeAdbCommand("shell", "am", "force-stop", DEBUG_APP);
+        mDevice.executeAdbCommand("shell", "am", "force-stop", RELEASE_APP);
+        mDevice.executeAdbCommand("shell", "rm", "-f", "/data/local/tmp/" + LAYER_A_LIB);
+        mDevice.executeAdbCommand("shell", "rm", "-f", "/data/local/tmp/" + LAYER_B_LIB);
+        mDevice.executeAdbCommand("shell", "rm", "-f", "/data/local/tmp/" + LAYER_C_LIB);
+        mDevice.executeAdbCommand("shell", "settings", "delete", "global", "enable_gpu_debug_layers");
+        mDevice.executeAdbCommand("shell", "settings", "delete", "global", "gpu_debug_app");
+        mDevice.executeAdbCommand("shell", "settings", "delete", "global", "gpu_debug_layers");
+        mDevice.executeAdbCommand("shell", "setprop", "debug.vulkan.layers", "\'\"\"\'");
+    }
+
+    /**
+     * This is the primary test of the feature. It pushes layers to our debuggable app and ensures they are
+     * loaded in the correct order.
+     */
+    @Test
+    public void testDebugLayerLoadVulkan() throws Exception {
+
+        // Set up layers to be loaded
+        mDevice.executeAdbCommand("shell", "settings", "put", "global", "enable_gpu_debug_layers", "1");
+        mDevice.executeAdbCommand("shell", "settings", "put", "global", "gpu_debug_app", DEBUG_APP);
+        mDevice.executeAdbCommand("shell", "settings", "put", "global", "gpu_debug_layers", LAYER_A_NAME + ":" + LAYER_B_NAME);
+
+        // Copy the layers from our LAYERS APK to tmp
+        setupLayer(LAYER_A_LIB);
+        setupLayer(LAYER_B_LIB);
+
+        // Copy them over to our DEBUG app
+        mDevice.executeAdbCommand("shell", "cat", "/data/local/tmp/" + LAYER_A_LIB, "|", "run-as", DEBUG_APP,
+                                  "sh", "-c", "\'cat", ">", LAYER_A_LIB, ";", "chmod", "700", LAYER_A_LIB + "\'");
+        mDevice.executeAdbCommand("shell", "cat", "/data/local/tmp/" + LAYER_B_LIB, "|", "run-as", DEBUG_APP,
+                                  "sh", "-c", "\'cat", ">", LAYER_B_LIB, ";", "chmod", "700", LAYER_B_LIB + "\'");
+
+        // Kick off our DEBUG app
+        mDevice.executeAdbCommand("shell", "am", "start", "-n", DEBUG_APP + "/" + ACTIVITY);
+
+        // Give it a chance to start, then grab process ID
+        Thread.sleep(1000);
+        String pid = getPID(DEBUG_APP);
+
+        // Check that both layers were loaded, in the correct order
+        String searchStringA = "nullCreateInstance called in " + LAYER_A;
+        LogScanResult resultA = scanLog(pid, searchStringA);
+        Assert.assertTrue("LayerA was not loaded", resultA.found);
+
+        String searchStringB = "nullCreateInstance called in " + LAYER_B;
+        LogScanResult resultB = scanLog(pid, searchStringB);
+        Assert.assertTrue("LayerB was not loaded", resultB.found);
+
+        Assert.assertTrue("LayerA should be loaded before LayerB", resultA.lineNumber < resultB.lineNumber);
+    }
+
+    /**
+     * This test ensures that we cannot push a layer to a non-debuggable app
+     * It also ensures non-debuggable apps ignore Settings and don't enumerate layers in the base directory.
+     */
+    @Test
+    public void testReleaseLayerLoadVulkan() throws Exception {
+
+        // Set up a layers to be loaded for RELEASE app
+        mDevice.executeAdbCommand("shell", "settings", "put", "global", "enable_gpu_debug_layers", "1");
+        mDevice.executeAdbCommand("shell", "settings", "put", "global", "gpu_debug_app", RELEASE_APP);
+        mDevice.executeAdbCommand("shell", "settings", "put", "global", "gpu_debug_layers", LAYER_A_NAME + ":" + LAYER_B_NAME);
+
+        // Copy a layer from our LAYERS APK to tmp
+        setupLayer(LAYER_A_LIB);
+
+        // Attempt to copy them over to our RELEASE app (this should fail)
+        mDevice.executeAdbCommand("shell", "cat", "/data/local/tmp/" + LAYER_A_LIB, "|", "run-as", RELEASE_APP,
+                                   "sh", "-c", "\'cat", ">", LAYER_A_LIB, ";", "chmod", "700", LAYER_A_LIB + "\'", "||", "echo", "run-as", "failed");
+
+        // Kick off our RELEASE app
+        mDevice.executeAdbCommand("shell", "am", "start", "-n", RELEASE_APP + "/" + ACTIVITY);
+
+        // Give it a chance to start, then grab process ID
+        Thread.sleep(1000);
+        String pid = getPID(RELEASE_APP);
+
+        // Ensure we don't load the layer in base dir
+        String searchStringA = LAYER_A_NAME + "loaded";
+        LogScanResult resultA = scanLog(pid, searchStringA);
+        Assert.assertFalse("LayerA was enumerated", resultA.found);
+    }
+
+    /**
+     * This test ensures debuggable apps do not enumerate layers in base
+     * directory if enable_gpu_debug_layers is not enabled.
+     */
+    @Test
+    public void testDebugNotEnabledVulkan() throws Exception {
+
+        // Ensure the global layer enable settings is NOT enabled
+        mDevice.executeAdbCommand("shell", "settings", "put", "global", "enable_gpu_debug_layers", "0");
+        mDevice.executeAdbCommand("shell", "settings", "put", "global", "gpu_debug_app", DEBUG_APP);
+        mDevice.executeAdbCommand("shell", "settings", "put", "global", "gpu_debug_layers", LAYER_A_NAME);
+
+        // Copy a layer from our LAYERS APK to tmp
+        setupLayer(LAYER_A_LIB);
+
+        // Copy it over to our DEBUG app
+        mDevice.executeAdbCommand("shell", "cat", "/data/local/tmp/" + LAYER_A_LIB, "|", "run-as", DEBUG_APP,
+                                  "sh", "-c", "\'cat", ">", LAYER_A_LIB, ";", "chmod", "700", LAYER_A_LIB + "\'");
+
+        // Kick off our DEBUG app
+        mDevice.executeAdbCommand("shell", "am", "start", "-n", DEBUG_APP + "/" + ACTIVITY);
+
+        // Give it a chance to start, then grab process ID
+        Thread.sleep(1000);
+        String pid = getPID(DEBUG_APP);
+
+        // Ensure we don't load the layer in base dir
+        String searchStringA = LAYER_A_NAME + "loaded";
+        LogScanResult resultA = scanLog(pid, searchStringA);
+        Assert.assertFalse("LayerA was enumerated", resultA.found);
+    }
+
+    /**
+     * This test ensures debuggable apps do not enumerate layers in base
+     * directory if gpu_debug_app does not match.
+     */
+    @Test
+    public void testDebugWrongAppVulkan() throws Exception {
+
+        // Ensure the gpu_debug_app does not match what we launch
+        mDevice.executeAdbCommand("shell", "settings", "put", "global", "enable_gpu_debug_layers", "1");
+        mDevice.executeAdbCommand("shell", "settings", "put", "global", "gpu_debug_app", RELEASE_APP);
+        mDevice.executeAdbCommand("shell", "settings", "put", "global", "gpu_debug_layers", LAYER_A_NAME);
+
+        // Copy a layer from our LAYERS APK to tmp
+        setupLayer(LAYER_A_LIB);
+
+        // Copy it over to our DEBUG app
+        mDevice.executeAdbCommand("shell", "cat", "/data/local/tmp/" + LAYER_A_LIB, "|", "run-as", DEBUG_APP,
+                                  "sh", "-c", "\'cat", ">", LAYER_A_LIB, ";", "chmod", "700", LAYER_A_LIB + "\'");
+
+        // Kick off our DEBUG app
+        mDevice.executeAdbCommand("shell", "am", "start", "-n", DEBUG_APP + "/" + ACTIVITY);
+
+        // Give it a chance to start, then grab process ID
+        Thread.sleep(1000);
+        String pid = getPID(DEBUG_APP);
+
+        // Ensure we don't load the layer in base dir
+        String searchStringA = LAYER_A_NAME + "loaded";
+        LogScanResult resultA = scanLog(pid, searchStringA);
+        Assert.assertFalse("LayerA was enumerated", resultA.found);
+    }
+
+    /**
+     * This test ensures debuggable apps do not enumerate layers in base
+     * directory if gpu_debug_layers are not set.
+     */
+    @Test
+    public void testDebugNoLayersEnabledVulkan() throws Exception {
+
+        // Ensure the global layer enable settings is NOT enabled
+        mDevice.executeAdbCommand("shell", "settings", "put", "global", "enable_gpu_debug_layers", "1");
+        mDevice.executeAdbCommand("shell", "settings", "put", "global", "gpu_debug_app", DEBUG_APP);
+        mDevice.executeAdbCommand("shell", "settings", "put", "global", "gpu_debug_layers", "foo");
+
+        // Copy a layer from our LAYERS APK to tmp
+        setupLayer(LAYER_A_LIB);
+
+        // Copy it over to our DEBUG app
+        mDevice.executeAdbCommand("shell", "cat", "/data/local/tmp/" + LAYER_A_LIB, "|", "run-as", DEBUG_APP,
+                                  "sh", "-c", "\'cat", ">", LAYER_A_LIB, ";", "chmod", "700", LAYER_A_LIB + "\'");
+
+        // Kick off our DEBUG app
+        mDevice.executeAdbCommand("shell", "am", "start", "-n", DEBUG_APP + "/" + ACTIVITY);
+
+        // Give it a chance to start, then grab process ID
+        Thread.sleep(1000);
+        String pid = getPID(DEBUG_APP);
+
+        // Ensure layerA is not loaded
+        String searchStringA = "nullCreateInstance called in " + LAYER_A;
+        LogScanResult resultA = scanLog(pid, searchStringA);
+        Assert.assertFalse("LayerA was loaded", resultA.found);
+    }
+
+    /**
+     * This test ensures we can still use properties if no layer found via Settings
+     */
+    @Test
+    public void testSystemPropertyEnableVulkan() throws Exception {
+
+        // Set up layerA to be loaded, but not layerB or layerC
+        mDevice.executeAdbCommand("shell", "settings", "put", "global", "enable_gpu_debug_layers", "1");
+        mDevice.executeAdbCommand("shell", "settings", "put", "global", "gpu_debug_app", RELEASE_APP);
+        mDevice.executeAdbCommand("shell", "settings", "put", "global", "gpu_debug_layers", LAYER_A_NAME);
+
+        // Enable layerC (which is packaged with the RELEASE app) with system properties
+        mDevice.executeAdbCommand("shell", "setprop", "debug.vulkan.layers " + LAYER_C_NAME);
+
+        // Kick off our RELEASE app
+        mDevice.executeAdbCommand("shell", "am", "start", "-n", RELEASE_APP + "/" + ACTIVITY);
+
+        // Give it a chance to start, then grab process ID
+        Thread.sleep(1000);
+        String pid = getPID(RELEASE_APP);
+
+        // Check that both layers were loaded, in the correct order
+        String searchStringA = LAYER_A_NAME + "loaded";
+        LogScanResult resultA = scanLog(pid, searchStringA);
+        Assert.assertFalse("LayerA was enumerated", resultA.found);
+
+        String searchStringC = "nullCreateInstance called in " + LAYER_C;
+        LogScanResult resultC = scanLog(pid, searchStringC);
+        Assert.assertTrue("LayerC was not loaded", resultC.found);
+    }
+
+    /**
+     * This test ensures system properties are ignored if Settings load a layer
+     */
+    @Test
+    public void testSystemPropertyIgnoreVulkan() throws Exception {
+
+        // Set up layerA to be loaded, but not layerB
+        mDevice.executeAdbCommand("shell", "settings", "put", "global", "enable_gpu_debug_layers", "1");
+        mDevice.executeAdbCommand("shell", "settings", "put", "global", "gpu_debug_app", DEBUG_APP);
+        mDevice.executeAdbCommand("shell", "settings", "put", "global", "gpu_debug_layers", LAYER_A_NAME);
+
+        // Copy the layers from our LAYERS APK
+        setupLayer(LAYER_A_LIB);
+        setupLayer(LAYER_B_LIB);
+
+        // Copy them over to our DEBUG app
+        mDevice.executeAdbCommand("shell", "cat", "/data/local/tmp/" + LAYER_A_LIB, "|", "run-as", DEBUG_APP,
+                                 "sh", "-c", "\'cat", ">", LAYER_A_LIB, ";", "chmod", "700", LAYER_A_LIB + "\'");
+        mDevice.executeAdbCommand("shell", "cat", "/data/local/tmp/" + LAYER_B_LIB, "|", "run-as", DEBUG_APP,
+                                 "sh", "-c", "\'cat", ">", LAYER_B_LIB, ";", "chmod", "700", LAYER_B_LIB + "\'");
+
+        // Enable layerB with system properties
+        mDevice.executeAdbCommand("shell", "setprop", "debug.vulkan.layers " + LAYER_B_NAME);
+
+        // Kick off our DEBUG app
+        mDevice.executeAdbCommand("shell", "am", "start", "-n", DEBUG_APP + "/" + ACTIVITY);
+
+        // Give it a chance to start, then grab process ID
+        Thread.sleep(1000);
+        String pid = getPID(DEBUG_APP);
+
+        // Ensure only layerA is loaded
+        String searchStringA = "nullCreateInstance called in " + LAYER_A;
+        LogScanResult resultA = scanLog(pid, searchStringA);
+        Assert.assertTrue("LayerA was not loaded", resultA.found);
+
+        String searchStringB = "nullCreateInstance called in " + LAYER_B;
+        LogScanResult resultB = scanLog(pid, searchStringB);
+        Assert.assertFalse("LayerB was loaded", resultB.found);
+    }
+}
diff --git a/hostsidetests/incident/AndroidTest.xml b/hostsidetests/incident/AndroidTest.xml
index f26092e..641b516 100644
--- a/hostsidetests/incident/AndroidTest.xml
+++ b/hostsidetests/incident/AndroidTest.xml
@@ -14,6 +14,7 @@
      limitations under the License.
 -->
 <configuration description="Config for CTS Incident host test cases">
+    <option name="test-suite-tag" value="cts" />
     <option name="config-descriptor:metadata" key="component" value="metrics" />
     <test class="com.android.compatibility.common.tradefed.testtype.JarHostTest" >
         <option name="jar" value="CtsIncidentHostTestCases.jar" />
diff --git a/hostsidetests/incident/apps/boundwidgetapp/Android.mk b/hostsidetests/incident/apps/boundwidgetapp/Android.mk
index 152414f..5430d47 100644
--- a/hostsidetests/incident/apps/boundwidgetapp/Android.mk
+++ b/hostsidetests/incident/apps/boundwidgetapp/Android.mk
@@ -24,7 +24,7 @@
 
 LOCAL_SRC_FILES := $(call all-java-files-under, src)
 
-LOCAL_JAVA_LIBRARIES := android.test.runner cts-junit
+LOCAL_JAVA_LIBRARIES := android.test.runner cts-junit android.test.base.stubs
 
 LOCAL_STATIC_JAVA_LIBRARIES := \
     ctstestrunner \
diff --git a/hostsidetests/incident/apps/procstatsapp/Android.mk b/hostsidetests/incident/apps/procstatsapp/Android.mk
new file mode 100644
index 0000000..a02c29d
--- /dev/null
+++ b/hostsidetests/incident/apps/procstatsapp/Android.mk
@@ -0,0 +1,39 @@
+# 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_PACKAGE_NAME := CtsProcStatsProtoApp
+
+LOCAL_MODULE_TAGS := optional
+
+LOCAL_MODULE_PATH := $(TARGET_OUT_DATA_APPS)
+
+LOCAL_SRC_FILES := $(call all-java-files-under, src)
+
+LOCAL_JAVA_LIBRARIES := android.test.runner cts-junit
+
+LOCAL_STATIC_JAVA_LIBRARIES := \
+    ctstestrunner \
+    compatibility-device-util \
+    android-support-v4
+
+LOCAL_SDK_VERSION := test_current
+
+# tag this module as a cts test artifact
+LOCAL_COMPATIBILITY_SUITE := cts vts general-tests
+
+include $(BUILD_CTS_PACKAGE)
diff --git a/hostsidetests/incident/apps/procstatsapp/AndroidManifest.xml b/hostsidetests/incident/apps/procstatsapp/AndroidManifest.xml
new file mode 100644
index 0000000..a0cccb4
--- /dev/null
+++ b/hostsidetests/incident/apps/procstatsapp/AndroidManifest.xml
@@ -0,0 +1,27 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2017 The Android Open Source Project
+
+     Licensed under the Apache License, Version 2.0 (the "License");
+     you may not use this file except in compliance with the License.
+     You may obtain a copy of the License at
+
+          http://www.apache.org/licenses/LICENSE-2.0
+
+     Unless required by applicable law or agreed to in writing, software
+     distributed under the License is distributed on an "AS IS" BASIS,
+     WITHOUT 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.server.cts.procstats" >
+
+    <application>
+        <uses-library android:name="android.test.runner" />
+        <activity android:name=".SimpleActivity" android:exported="true" />
+    </application>
+
+    <instrumentation android:name="android.support.test.runner.AndroidJUnitRunner"
+            android:targetPackage="com.android.server.cts.procstats" />
+</manifest>
diff --git a/hostsidetests/incident/apps/procstatsapp/src/com/android/server/cts/procstats/SimpleActivity.java b/hostsidetests/incident/apps/procstatsapp/src/com/android/server/cts/procstats/SimpleActivity.java
new file mode 100644
index 0000000..69ff75e
--- /dev/null
+++ b/hostsidetests/incident/apps/procstatsapp/src/com/android/server/cts/procstats/SimpleActivity.java
@@ -0,0 +1,34 @@
+/*
+ * 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.server.cts.procstats;
+
+import android.app.Activity;
+import android.os.Bundle;
+import android.util.Log;
+
+import java.lang.Override;
+
+public class SimpleActivity extends Activity {
+
+    private static final String TAG = "ProcstatsAppRunningTest";
+
+    @Override
+    public void onCreate(Bundle bundle) {
+        super.onCreate(bundle);
+        Log.i(TAG, "Procstats app is running");
+    }
+}
+
diff --git a/hostsidetests/incident/src/com/android/server/cts/AlarmManagerIncidentTest.java b/hostsidetests/incident/src/com/android/server/cts/AlarmManagerIncidentTest.java
new file mode 100644
index 0000000..a3dc1d6
--- /dev/null
+++ b/hostsidetests/incident/src/com/android/server/cts/AlarmManagerIncidentTest.java
@@ -0,0 +1,205 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.cts;
+
+import com.android.server.AlarmClockMetadataProto;
+import com.android.server.AlarmManagerServiceProto;
+import com.android.server.AlarmProto;
+import com.android.server.BatchProto;
+import com.android.server.BroadcastStatsProto;
+import com.android.server.ConstantsProto;
+import com.android.server.FilterStatsProto;
+import com.android.server.ForceAppStandbyTrackerProto;
+import com.android.server.IdleDispatchEntryProto;
+import com.android.server.InFlightProto;
+import com.android.server.WakeupEventProto;
+import java.util.List;
+
+/**
+ * Test to check that the alarm manager service properly outputs its dump state.
+ */
+public class AlarmManagerIncidentTest extends ProtoDumpTestCase {
+    public void testAlarmManagerServiceDump() throws Exception {
+        final AlarmManagerServiceProto dump =
+                getDump(AlarmManagerServiceProto.parser(), "dumpsys alarm --proto");
+
+        // Times should be positive.
+        assertTrue(0 < dump.getCurrentTime());
+        assertTrue(0 < dump.getElapsedRealtime());
+        assertTrue(0 < dump.getLastTimeChangeClockTime());
+        assertTrue(0 < dump.getLastTimeChangeRealtime());
+
+        // ConstantsProto
+        ConstantsProto settings = dump.getSettings();
+        assertTrue(0 < settings.getMinFuturityDurationMs());
+        assertTrue(0 < settings.getMinIntervalDurationMs());
+        assertTrue(0 < settings.getListenerTimeoutDurationMs());
+        assertTrue(0 < settings.getAllowWhileIdleShortDurationMs());
+        assertTrue(0 < settings.getAllowWhileIdleLongDurationMs());
+        assertTrue(0 < settings.getAllowWhileIdleWhitelistDurationMs());
+
+        // ForceAppStandbyTrackerProto
+        ForceAppStandbyTrackerProto forceAppStandbyTracker = dump.getForceAppStandbyTracker();
+        for (int uid : forceAppStandbyTracker.getForegroundUidsList()) {
+            // 0 is technically a valid UID.
+            assertTrue(0 <= uid);
+        }
+        for (int aid : forceAppStandbyTracker.getPowerSaveWhitelistAppIdsList()) {
+            assertTrue(0 <= aid);
+        }
+        for (int aid : forceAppStandbyTracker.getTempPowerSaveWhitelistAppIdsList()) {
+            assertTrue(0 <= aid);
+        }
+        for (ForceAppStandbyTrackerProto.RunAnyInBackgroundRestrictedPackages r : forceAppStandbyTracker.getRunAnyInBackgroundRestrictedPackagesList()) {
+            assertTrue(0 <= r.getUid());
+        }
+
+        if (!dump.getIsInteractive()) {
+            // These are only valid if is_interactive is false.
+            assertTrue(0 < dump.getTimeSinceNonInteractiveMs());
+            assertTrue(0 < dump.getMaxWakeupDelayMs());
+            assertTrue(0 < dump.getTimeSinceLastDispatchMs());
+            // time_until_next_non_wakeup_delivery_ms could be negative if the delivery time is in the past.
+        }
+
+        assertTrue(0 < dump.getTimeUntilNextNonWakeupAlarmMs());
+        assertTrue(0 < dump.getTimeUntilNextWakeupMs());
+        assertTrue(0 < dump.getTimeSinceLastWakeupMs());
+        assertTrue(0 < dump.getTimeSinceLastWakeupSetMs());
+        assertTrue(0 <= dump.getTimeChangeEventCount());
+
+        for (int aid : dump.getDeviceIdleUserWhitelistAppIdsList()) {
+            assertTrue(0 <= aid);
+        }
+
+        // AlarmClockMetadataProto
+        for (AlarmClockMetadataProto ac : dump.getNextAlarmClockMetadataList()) {
+            assertTrue(0 <= ac.getUser());
+            assertTrue(0 < ac.getTriggerTimeMs());
+        }
+
+        for (BatchProto b : dump.getPendingAlarmBatchesList()) {
+            final long start = b.getStartRealtime();
+            final long end = b.getEndRealtime();
+            assertTrue("Batch start time (" + start+ ") is negative", 0 <= start);
+            assertTrue("Batch end time (" + end + ") is negative", 0 <= end);
+            assertTrue("Batch start time (" + start + ") is after its end time (" + end + ")",
+                start <= end);
+            testAlarmProtoList(b.getAlarmsList());
+        }
+
+        testAlarmProtoList(dump.getPendingUserBlockedBackgroundAlarmsList());
+
+        testAlarmProto(dump.getPendingIdleUntil());
+
+        testAlarmProtoList(dump.getPendingWhileIdleAlarmsList());
+
+        testAlarmProto(dump.getNextWakeFromIdle());
+
+        testAlarmProtoList(dump.getPastDueNonWakeupAlarmsList());
+
+        assertTrue(0 <= dump.getDelayedAlarmCount());
+        assertTrue(0 <= dump.getTotalDelayTimeMs());
+        assertTrue(0 <= dump.getMaxDelayDurationMs());
+        assertTrue(0 <= dump.getMaxNonInteractiveDurationMs());
+
+        assertTrue(0 <= dump.getBroadcastRefCount());
+        assertTrue(0 <= dump.getPendingIntentSendCount());
+        assertTrue(0 <= dump.getPendingIntentFinishCount());
+        assertTrue(0 <= dump.getListenerSendCount());
+        assertTrue(0 <= dump.getListenerFinishCount());
+
+        for (InFlightProto f : dump.getOutstandingDeliveriesList())  {
+            assertTrue(0 <= f.getUid());
+            assertTrue(0 < f.getWhenElapsedMs());
+            testBroadcastStatsProto(f.getBroadcastStats());
+            testFilterStatsProto(f.getFilterStats());
+        }
+
+        long awimds = dump.getAllowWhileIdleMinDurationMs();
+        assertTrue(awimds == settings.getAllowWhileIdleShortDurationMs()
+                || awimds == settings.getAllowWhileIdleLongDurationMs());
+
+        for (AlarmManagerServiceProto.LastAllowWhileIdleDispatch l : dump.getLastAllowWhileIdleDispatchTimesList()) {
+            assertTrue(0 <= l.getUid());
+            assertTrue(0 < l.getTimeMs());
+        }
+
+        for (AlarmManagerServiceProto.TopAlarm ta : dump.getTopAlarmsList()) {
+            assertTrue(0 <= ta.getUid());
+            testFilterStatsProto(ta.getFilter());
+        }
+
+        for (AlarmManagerServiceProto.AlarmStat as : dump.getAlarmStatsList()) {
+            testBroadcastStatsProto(as.getBroadcast());
+            for (FilterStatsProto f : as.getFiltersList()) {
+                testFilterStatsProto(f);
+            }
+        }
+
+        for (IdleDispatchEntryProto id : dump.getAllowWhileIdleDispatchesList()) {
+            assertTrue(0 <= id.getUid());
+            assertTrue(0 <= id.getEntryCreationRealtime());
+            assertTrue(0 <= id.getArgRealtime());
+        }
+
+        for (WakeupEventProto we : dump.getRecentWakeupHistoryList()) {
+            assertTrue(0 <= we.getUid());
+            assertTrue(0 <= we.getWhen());
+        }
+    }
+
+    private void testAlarmProtoList(List<AlarmProto> alarms) throws Exception {
+        for (AlarmProto a : alarms) {
+            testAlarmProto(a);
+        }
+    }
+
+    private void testAlarmProto(AlarmProto alarm) throws Exception {
+        assertNotNull(alarm);
+
+        // alarm.time_until_when_elapsed_ms can be negative if 'when' is in the past.
+        assertTrue(0 <= alarm.getWindowLengthMs());
+        assertTrue(0 <= alarm.getRepeatIntervalMs());
+        assertTrue(0 <= alarm.getCount());
+    }
+
+    private void testBroadcastStatsProto(BroadcastStatsProto broadcast) throws Exception {
+        assertNotNull(broadcast);
+
+        assertTrue(0 <= broadcast.getUid());
+        assertTrue(0 <= broadcast.getTotalFlightDurationMs());
+        assertTrue(0 <= broadcast.getCount());
+        assertTrue(0 <= broadcast.getWakeupCount());
+        assertTrue(0 <= broadcast.getStartTimeRealtime());
+        // Nesting should be non-negative.
+        assertTrue(0 <= broadcast.getNesting());
+    }
+
+    private void testFilterStatsProto(FilterStatsProto filter) throws Exception {
+        assertNotNull(filter);
+
+        assertTrue(0 <= filter.getLastFlightTimeRealtime());
+        assertTrue(0 <= filter.getTotalFlightDurationMs());
+        assertTrue(0 <= filter.getCount());
+        assertTrue(0 <= filter.getWakeupCount());
+        assertTrue(0 <= filter.getStartTimeRealtime());
+        // Nesting should be non-negative.
+        assertTrue(0 <= filter.getNesting());
+    }
+}
+
diff --git a/hostsidetests/incident/src/com/android/server/cts/BatteryIncidentTest.java b/hostsidetests/incident/src/com/android/server/cts/BatteryIncidentTest.java
index 4b83b0a..28000e0 100644
--- a/hostsidetests/incident/src/com/android/server/cts/BatteryIncidentTest.java
+++ b/hostsidetests/incident/src/com/android/server/cts/BatteryIncidentTest.java
@@ -16,6 +16,7 @@
 
 package com.android.server.cts;
 
+import android.os.BatteryManagerProto;
 import android.service.battery.BatteryServiceDumpProto;
 import com.android.tradefed.device.DeviceNotAvailableException;
 
@@ -37,10 +38,10 @@
             return;
         }
 
-        assertTrue(
-                dump.getPlugged()
-                        != BatteryServiceDumpProto.BatteryPlugged.BATTERY_PLUGGED_WIRELESS);
-        assertTrue(dump.getChargeCounter() > 0);
+        assertTrue(dump.getPlugged() != BatteryManagerProto.PlugType.PLUG_TYPE_WIRELESS);
+        assertTrue(dump.getMaxChargingCurrent() >= 0);
+        assertTrue(dump.getMaxChargingVoltage() >= 0);
+        assertTrue(dump.getChargeCounter() >= 0);
         assertTrue(
                 dump.getStatus() != BatteryServiceDumpProto.BatteryStatus.BATTERY_STATUS_INVALID);
         assertTrue(
diff --git a/hostsidetests/incident/src/com/android/server/cts/BatteryStatsIncidentTest.java b/hostsidetests/incident/src/com/android/server/cts/BatteryStatsIncidentTest.java
new file mode 100644
index 0000000..fb971de
--- /dev/null
+++ b/hostsidetests/incident/src/com/android/server/cts/BatteryStatsIncidentTest.java
@@ -0,0 +1,465 @@
+/*
+ * 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.server.cts;
+
+import android.os.BatteryStatsProto;
+import android.os.ControllerActivityProto;
+import android.os.SystemProto;
+import android.os.TimerProto;
+import android.os.UidProto;
+import android.service.batterystats.BatteryStatsServiceDumpProto;
+
+/**
+ * Test to BatteryStats proto dump.
+ */
+public class BatteryStatsIncidentTest extends ProtoDumpTestCase {
+
+    @Override
+    protected void tearDown() throws Exception {
+        batteryOffScreenOn();
+        super.tearDown();
+    }
+
+    protected void batteryOnScreenOff() throws Exception {
+        getDevice().executeShellCommand("dumpsys battery unplug");
+        getDevice().executeShellCommand("dumpsys batterystats enable pretend-screen-off");
+    }
+
+    protected void batteryOffScreenOn() throws Exception {
+        getDevice().executeShellCommand("dumpsys battery reset");
+        getDevice().executeShellCommand("dumpsys batterystats disable pretend-screen-off");
+    }
+
+    /**
+     * Tests that batterystats is dumped to proto with sane values.
+     */
+    public void testBatteryStatsServiceDump() throws Exception {
+        batteryOnScreenOff();
+        Thread.sleep(5000); // Allow some time for battery data to accumulate.
+
+        final BatteryStatsServiceDumpProto dump = getDump(BatteryStatsServiceDumpProto.parser(),
+                "dumpsys batterystats --proto");
+        final BatteryStatsProto bs = dump.getBatterystats();
+        assertNotNull(bs);
+
+        // Proto dumps were finalized when the batterystats report version was ~29 and the parcel
+        // version was ~172.
+        assertTrue(29 <= bs.getReportVersion());
+        assertTrue(172 <= bs.getParcelVersion());
+        assertNotNull(bs.getStartPlatformVersion());
+        assertFalse(bs.getStartPlatformVersion().isEmpty());
+        assertNotNull(bs.getEndPlatformVersion());
+        assertFalse(bs.getEndPlatformVersion().isEmpty());
+
+        for (UidProto u : bs.getUidsList()) {
+            testUidProto(u);
+        }
+
+        testSystemProto(bs.getSystem());
+
+        batteryOffScreenOn();
+    }
+
+    private void testControllerActivityProto(ControllerActivityProto ca) throws Exception {
+        assertNotNull(ca);
+
+        assertTrue(0 <= ca.getIdleDurationMs());
+        assertTrue(0 <= ca.getRxDurationMs());
+        assertTrue(0 <= ca.getPowerMah());
+        for (ControllerActivityProto.TxLevel tx : ca.getTxList()) {
+            assertTrue(0 <= tx.getDurationMs());
+        }
+    }
+
+    private void testBatteryLevelStep(SystemProto.BatteryLevelStep bls) throws Exception {
+        assertNotNull(bls);
+
+        assertTrue(0 < bls.getDurationMs());
+        assertTrue(0 <= bls.getLevel());
+        assertTrue(100 >= bls.getLevel());
+
+        assertTrue(SystemProto.BatteryLevelStep.DisplayState.getDescriptor().getValues()
+                .contains(bls.getDisplayState().getValueDescriptor()));
+        assertTrue(SystemProto.BatteryLevelStep.PowerSaveMode.getDescriptor().getValues()
+                .contains(bls.getPowerSaveMode().getValueDescriptor()));
+        assertTrue(SystemProto.BatteryLevelStep.IdleMode.getDescriptor().getValues()
+                .contains(bls.getIdleMode().getValueDescriptor()));
+    }
+
+    private void testSystemProto(SystemProto s) throws Exception {
+        assertNotNull(s);
+
+        SystemProto.Battery b = s.getBattery();
+        assertTrue(0 < b.getStartClockTimeMs());
+        assertTrue(0 <= b.getStartCount());
+        long totalRealtimeMs = b.getTotalRealtimeMs();
+        long totalUptimeMs = b.getTotalUptimeMs();
+        assertTrue(0 <= totalUptimeMs);
+        assertTrue(totalUptimeMs <= totalRealtimeMs);
+        long batteryRealtimeMs = b.getBatteryRealtimeMs();
+        long batteryUptimeMs = b.getBatteryUptimeMs();
+        assertTrue(0 <= batteryUptimeMs);
+        assertTrue(batteryUptimeMs <= batteryRealtimeMs);
+        assertTrue(batteryRealtimeMs <= totalRealtimeMs);
+        assertTrue(batteryUptimeMs <= totalUptimeMs);
+        long screenOffRealtimeMs = b.getScreenOffRealtimeMs();
+        long screenOffUptimeMs = b.getScreenOffUptimeMs();
+        assertTrue(0 <= screenOffUptimeMs);
+        assertTrue(screenOffUptimeMs <= screenOffRealtimeMs);
+        assertTrue(screenOffRealtimeMs <= totalRealtimeMs);
+        assertTrue(screenOffUptimeMs <= totalUptimeMs);
+        long screenDozeDurationMs = b.getScreenDozeDurationMs();
+        assertTrue(0 <= screenDozeDurationMs);
+        assertTrue(screenDozeDurationMs <= screenOffRealtimeMs);
+        assertTrue(0 < b.getEstimatedBatteryCapacityMah());
+        long minLearnedCapacityUah = b.getMinLearnedBatteryCapacityUah();
+        long maxLearnedCapacityUah = b.getMaxLearnedBatteryCapacityUah();
+        assertTrue(0 <= minLearnedCapacityUah);
+        assertTrue(minLearnedCapacityUah <= maxLearnedCapacityUah);
+
+        SystemProto.BatteryDischarge bd = s.getBatteryDischarge();
+        int lowerBound = bd.getLowerBoundSinceCharge();
+        int upperBound = bd.getUpperBoundSinceCharge();
+        assertTrue(0 <= lowerBound);
+        assertTrue(lowerBound <= upperBound);
+        assertTrue(0 <= bd.getScreenOnSinceCharge());
+        int screenOff = bd.getScreenOffSinceCharge();
+        int screenDoze = bd.getScreenDozeSinceCharge();
+        assertTrue(0 <= screenDoze);
+        assertTrue(screenDoze <= screenOff);
+        long totalMah = bd.getTotalMah();
+        long totalMahScreenOff = bd.getTotalMahScreenOff();
+        long totalMahScreenDoze = bd.getTotalMahScreenDoze();
+        long totalMahLightDoze = bd.getTotalMahLightDoze();
+        long totalMahDeepDoze = bd.getTotalMahDeepDoze();
+        assertTrue(0 <= totalMahScreenDoze);
+        assertTrue(0 <= totalMahLightDoze);
+        assertTrue(0 <= totalMahDeepDoze);
+        assertTrue(totalMahScreenDoze <= totalMahScreenOff);
+        assertTrue(totalMahLightDoze <= totalMahScreenOff);
+        assertTrue(totalMahDeepDoze <= totalMahScreenOff);
+        assertTrue(totalMahScreenOff <= totalMah);
+
+        assertTrue(-1 <= s.getChargeTimeRemainingMs());
+        assertTrue(-1 <= s.getDischargeTimeRemainingMs());
+
+        for (SystemProto.BatteryLevelStep bls : s.getChargeStepList()) {
+            testBatteryLevelStep(bls);
+        }
+        for (SystemProto.BatteryLevelStep bls : s.getDischargeStepList()) {
+            testBatteryLevelStep(bls);
+        }
+
+        for (SystemProto.DataConnection dc : s.getDataConnectionList()) {
+            assertTrue(SystemProto.DataConnection.Name.getDescriptor().getValues()
+                    .contains(dc.getName().getValueDescriptor()));
+            testTimerProto(dc.getTotal());
+        }
+
+        testControllerActivityProto(s.getGlobalBluetoothController());
+        testControllerActivityProto(s.getGlobalModemController());
+        testControllerActivityProto(s.getGlobalWifiController());
+
+        SystemProto.GlobalNetwork gn = s.getGlobalNetwork();
+        assertTrue(0 <= gn.getMobileBytesRx());
+        assertTrue(0 <= gn.getMobileBytesTx());
+        assertTrue(0 <= gn.getWifiBytesRx());
+        assertTrue(0 <= gn.getWifiBytesTx());
+        assertTrue(0 <= gn.getMobilePacketsRx());
+        assertTrue(0 <= gn.getMobilePacketsTx());
+        assertTrue(0 <= gn.getWifiPacketsRx());
+        assertTrue(0 <= gn.getWifiPacketsTx());
+        assertTrue(0 <= gn.getBtBytesRx());
+        assertTrue(0 <= gn.getBtBytesTx());
+
+        SystemProto.GlobalWifi gw = s.getGlobalWifi();
+        assertTrue(0 <= gw.getOnDurationMs());
+        assertTrue(0 <= gw.getRunningDurationMs());
+
+        for (SystemProto.KernelWakelock kw : s.getKernelWakelockList()) {
+            testTimerProto(kw.getTotal());
+        }
+
+        SystemProto.Misc m = s.getMisc();
+        assertTrue(0 <= m.getScreenOnDurationMs());
+        assertTrue(0 <= m.getPhoneOnDurationMs());
+        assertTrue(0 <= m.getFullWakelockTotalDurationMs());
+        assertTrue(0 <= m.getPartialWakelockTotalDurationMs());
+        assertTrue(0 <= m.getMobileRadioActiveDurationMs());
+        assertTrue(0 <= m.getMobileRadioActiveAdjustedTimeMs());
+        assertTrue(0 <= m.getMobileRadioActiveCount());
+        assertTrue(0 <= m.getMobileRadioActiveUnknownDurationMs());
+        assertTrue(0 <= m.getInteractiveDurationMs());
+        assertTrue(0 <= m.getBatterySaverModeEnabledDurationMs());
+        assertTrue(0 <= m.getNumConnectivityChanges());
+        assertTrue(0 <= m.getDeepDozeEnabledDurationMs());
+        assertTrue(0 <= m.getDeepDozeCount());
+        assertTrue(0 <= m.getDeepDozeIdlingDurationMs());
+        assertTrue(0 <= m.getDeepDozeIdlingCount());
+        assertTrue(0 <= m.getLongestDeepDozeDurationMs());
+        assertTrue(0 <= m.getLightDozeEnabledDurationMs());
+        assertTrue(0 <= m.getLightDozeCount());
+        assertTrue(0 <= m.getLightDozeIdlingDurationMs());
+        assertTrue(0 <= m.getLightDozeIdlingCount());
+        assertTrue(0 <= m.getLongestLightDozeDurationMs());
+
+        for (SystemProto.PhoneSignalStrength pss : s.getPhoneSignalStrengthList()) {
+            testTimerProto(pss.getTotal());
+        }
+
+        for (SystemProto.PowerUseItem pui : s.getPowerUseItemList()) {
+            assertTrue(SystemProto.PowerUseItem.Sipper.getDescriptor().getValues()
+                    .contains(pui.getName().getValueDescriptor()));
+            assertTrue(0 <= pui.getUid());
+            assertTrue(0 <= pui.getComputedPowerMah());
+            assertTrue(0 <= pui.getScreenPowerMah());
+            assertTrue(0 <= pui.getProportionalSmearMah());
+        }
+
+        SystemProto.PowerUseSummary pus = s.getPowerUseSummary();
+        assertTrue(0 < pus.getBatteryCapacityMah());
+        assertTrue(0 <= pus.getComputedPowerMah());
+        double minDrained = pus.getMinDrainedPowerMah();
+        double maxDrained = pus.getMaxDrainedPowerMah();
+        assertTrue(0 <= minDrained);
+        assertTrue(minDrained <= maxDrained);
+
+        for (SystemProto.ResourcePowerManager rpm : s.getResourcePowerManagerList()) {
+            assertNotNull(rpm.getName());
+            assertFalse(rpm.getName().isEmpty());
+            testTimerProto(rpm.getTotal());
+            testTimerProto(rpm.getScreenOff());
+        }
+
+        for (SystemProto.ScreenBrightness sb : s.getScreenBrightnessList()) {
+            testTimerProto(sb.getTotal());
+        }
+
+        testTimerProto(s.getSignalScanning());
+
+        for (SystemProto.WakeupReason wr : s.getWakeupReasonList()) {
+            testTimerProto(wr.getTotal());
+        }
+
+        SystemProto.WifiMulticastWakelockTotal wmwl = s.getWifiMulticastWakelockTotal();
+        assertTrue(0 <= wmwl.getDurationMs());
+        assertTrue(0 <= wmwl.getCount());
+
+        for (SystemProto.WifiSignalStrength wss : s.getWifiSignalStrengthList()) {
+            testTimerProto(wss.getTotal());
+        }
+
+        for (SystemProto.WifiState ws : s.getWifiStateList()) {
+            assertTrue(SystemProto.WifiState.Name.getDescriptor().getValues()
+                    .contains(ws.getName().getValueDescriptor()));
+            testTimerProto(ws.getTotal());
+        }
+
+        for (SystemProto.WifiSupplicantState wss : s.getWifiSupplicantStateList()) {
+            assertTrue(SystemProto.WifiSupplicantState.Name.getDescriptor().getValues()
+                    .contains(wss.getName().getValueDescriptor()));
+            testTimerProto(wss.getTotal());
+        }
+    }
+
+    private void testTimerProto(TimerProto t) throws Exception {
+        assertNotNull(t);
+
+        long duration = t.getDurationMs();
+        long curDuration = t.getCurrentDurationMs();
+        long maxDuration = t.getMaxDurationMs();
+        long totalDuration = t.getTotalDurationMs();
+        assertTrue(0 <= duration);
+        assertTrue(0 <= t.getCount());
+        // Not all TimerProtos will have max duration, current duration, or total duration
+        // populated, so must tread carefully. Regardless, they should never be negative.
+        assertTrue(0 <= curDuration);
+        assertTrue(0 <= maxDuration);
+        assertTrue(0 <= totalDuration);
+        if (maxDuration > 0) {
+            assertTrue(curDuration <= maxDuration);
+        }
+        if (totalDuration > 0) {
+            assertTrue(maxDuration <= totalDuration);
+            assertTrue("Duration " + duration + " is greater than totalDuration " + totalDuration,
+                    duration <= totalDuration);
+        }
+    }
+
+    private void testByFrequency(UidProto.Cpu.ByFrequency bf) throws Exception {
+        assertNotNull(bf);
+
+        assertTrue(1 <= bf.getFrequencyIndex());
+        long total = bf.getTotalDurationMs();
+        long screenOff = bf.getScreenOffDurationMs();
+        assertTrue(0 <= screenOff);
+        assertTrue(screenOff <= total);
+    }
+
+    private void testUidProto(UidProto u) throws Exception {
+        assertNotNull(u);
+
+        assertTrue(0 <= u.getUid());
+
+        for (UidProto.Package p : u.getPackagesList()) {
+            assertNotNull(p.getName());
+            assertFalse(p.getName().isEmpty());
+
+            for (UidProto.Package.Service s : p.getServicesList()) {
+                assertNotNull(s.getName());
+                assertFalse(s.getName().isEmpty());
+                assertTrue(0 <= s.getStartDurationMs());
+                assertTrue(0 <= s.getStartCount());
+                assertTrue(0 <= s.getLaunchCount());
+            }
+        }
+
+        testControllerActivityProto(u.getBluetoothController());
+        testControllerActivityProto(u.getModemController());
+        testControllerActivityProto(u.getWifiController());
+
+        UidProto.BluetoothMisc bm = u.getBluetoothMisc();
+        testTimerProto(bm.getApportionedBleScan());
+        testTimerProto(bm.getBackgroundBleScan());
+        testTimerProto(bm.getUnoptimizedBleScan());
+        testTimerProto(bm.getBackgroundUnoptimizedBleScan());
+        assertTrue(0 <= bm.getBleScanResultCount());
+        assertTrue(0 <= bm.getBackgroundBleScanResultCount());
+
+        UidProto.Cpu c = u.getCpu();
+        assertTrue(0 <= c.getUserDurationMs());
+        assertTrue(0 <= c.getSystemDurationMs());
+        for (UidProto.Cpu.ByFrequency bf : c.getByFrequencyList()) {
+            testByFrequency(bf);
+        }
+        for (UidProto.Cpu.ByProcessState bps : c.getByProcessStateList()) {
+            assertTrue(UidProto.Cpu.ProcessState.getDescriptor().getValues()
+                    .contains(bps.getProcessState().getValueDescriptor()));
+            for (UidProto.Cpu.ByFrequency bf : bps.getByFrequencyList()) {
+                testByFrequency(bf);
+            }
+        }
+
+        testTimerProto(u.getAudio());
+        testTimerProto(u.getCamera());
+        testTimerProto(u.getFlashlight());
+        testTimerProto(u.getForegroundActivity());
+        testTimerProto(u.getForegroundService());
+        testTimerProto(u.getVibrator());
+        testTimerProto(u.getVideo());
+
+        for (UidProto.Job j : u.getJobsList()) {
+            assertNotNull(j.getName());
+            assertFalse(j.getName().isEmpty());
+            testTimerProto(j.getTotal());
+            testTimerProto(j.getBackground());
+        }
+
+        for (UidProto.JobCompletion jc : u.getJobCompletionList()) {
+            assertNotNull(jc.getName());
+            assertFalse(jc.getName().isEmpty());
+            for (UidProto.JobCompletion.ReasonCount rc : jc.getReasonCountList()) {
+                assertTrue(0 <= rc.getCount());
+            }
+        }
+
+        UidProto.Network n = u.getNetwork();
+        assertTrue(0 <= n.getMobileBytesRx());
+        assertTrue(0 <= n.getMobileBytesTx());
+        assertTrue(0 <= n.getWifiBytesRx());
+        assertTrue(0 <= n.getWifiBytesTx());
+        assertTrue(0 <= n.getBtBytesRx());
+        assertTrue(0 <= n.getBtBytesTx());
+        assertTrue(0 <= n.getMobilePacketsRx());
+        assertTrue(0 <= n.getMobilePacketsTx());
+        assertTrue(0 <= n.getWifiPacketsRx());
+        assertTrue(0 <= n.getWifiPacketsTx());
+        assertTrue(0 <= n.getMobileActiveDurationMs());
+        assertTrue(0 <= n.getMobileActiveCount());
+        assertTrue(0 <= n.getMobileWakeupCount());
+        assertTrue(0 <= n.getWifiWakeupCount());
+        assertTrue(0 <= n.getMobileBytesBgRx());
+        assertTrue(0 <= n.getMobileBytesBgTx());
+        assertTrue(0 <= n.getWifiBytesBgRx());
+        assertTrue(0 <= n.getWifiBytesBgTx());
+        assertTrue(0 <= n.getMobilePacketsBgRx());
+        assertTrue(0 <= n.getMobilePacketsBgTx());
+        assertTrue(0 <= n.getWifiPacketsBgRx());
+        assertTrue(0 <= n.getWifiPacketsBgTx());
+
+        UidProto.PowerUseItem pui = u.getPowerUseItem();
+        assertTrue(0 <= pui.getComputedPowerMah());
+        assertTrue(0 <= pui.getScreenPowerMah());
+        assertTrue(0 <= pui.getProportionalSmearMah());
+
+        for (UidProto.Process p : u.getProcessList()) {
+            assertNotNull(p.getName());
+            assertFalse(p.getName().isEmpty());
+            assertTrue(0 <= p.getUserDurationMs());
+            assertTrue(0 <= p.getSystemDurationMs());
+            assertTrue(0 <= p.getForegroundDurationMs());
+            assertTrue(0 <= p.getStartCount());
+            assertTrue(0 <= p.getAnrCount());
+            assertTrue(0 <= p.getCrashCount());
+        }
+
+        for (UidProto.StateTime st : u.getStatesList()) {
+            assertTrue(UidProto.StateTime.State.getDescriptor().getValues()
+                    .contains(st.getState().getValueDescriptor()));
+            assertTrue(0 <= st.getDurationMs());
+        }
+
+        for (UidProto.Sensor s : u.getSensorsList()) {
+            testTimerProto(s.getApportioned());
+            testTimerProto(s.getBackground());
+        }
+
+        for (UidProto.Sync s : u.getSyncsList()) {
+            testTimerProto(s.getTotal());
+            testTimerProto(s.getBackground());
+        }
+
+        for (UidProto.UserActivity ua : u.getUserActivityList()) {
+            assertTrue(0 <= ua.getCount());
+        }
+
+        UidProto.AggregatedWakelock aw = u.getAggregatedWakelock();
+        long awPartial = aw.getPartialDurationMs();
+        long awBgPartial = aw.getBackgroundPartialDurationMs();
+        assertTrue(0 <= awBgPartial);
+        assertTrue(awBgPartial <= awPartial);
+
+        for (UidProto.Wakelock w : u.getWakelocksList()) {
+            testTimerProto(w.getFull());
+            testTimerProto(w.getPartial());
+            testTimerProto(w.getBackgroundPartial());
+            testTimerProto(w.getWindow());
+        }
+
+        for (UidProto.WakeupAlarm wa : u.getWakeupAlarmList()) {
+            assertTrue(0 <= wa.getCount());
+        }
+
+        UidProto.Wifi w = u.getWifi();
+        assertTrue(0 <= w.getFullWifiLockDurationMs());
+        assertTrue(0 <= w.getRunningDurationMs());
+        testTimerProto(w.getApportionedScan());
+        testTimerProto(w.getBackgroundScan());
+
+        testTimerProto(u.getWifiMulticastWakelock());
+    }
+}
diff --git a/hostsidetests/incident/src/com/android/server/cts/BatteryStatsValidationTest.java b/hostsidetests/incident/src/com/android/server/cts/BatteryStatsValidationTest.java
index 57d64bb..537a335 100644
--- a/hostsidetests/incident/src/com/android/server/cts/BatteryStatsValidationTest.java
+++ b/hostsidetests/incident/src/com/android/server/cts/BatteryStatsValidationTest.java
@@ -15,14 +15,9 @@
  */
 package com.android.server.cts;
 
-import com.android.ddmlib.IShellOutputReceiver;
 import com.android.tradefed.log.LogUtil;
 
-import com.google.common.base.Charsets;
-
 import java.util.Random;
-import java.util.concurrent.TimeUnit;
-import java.util.concurrent.atomic.AtomicBoolean;
 
 /**
  * Test for "dumpsys batterystats -c
@@ -53,9 +48,9 @@
 
     private static final int STATE_TIME_TOP_INDEX = 4;
     private static final int STATE_TIME_FOREGROUND_SERVICE_INDEX = 5;
-    private static final int STATE_TIME_FOREGROUND_INDEX = 7;
-    private static final int STATE_TIME_BACKGROUND_INDEX = 8;
-    private static final int STATE_TIME_CACHED_INDEX = 9;
+    private static final int STATE_TIME_FOREGROUND_INDEX = 6;
+    private static final int STATE_TIME_BACKGROUND_INDEX = 7;
+    private static final int STATE_TIME_CACHED_INDEX = 10;
 
     private static final long TIME_SPENT_IN_TOP = 2000;
     private static final long TIME_SPENT_IN_FOREGROUND = 2000;
@@ -272,7 +267,7 @@
                 } else if (keyguardStateLines && line.contains("showing=")) {
                     screenAwake &= line.trim().endsWith("false");
                 } else if (keyguardStateLines && line.contains("screenState=")) {
-                    screenAwake &= line.trim().endsWith("2");
+                    screenAwake &= line.trim().endsWith("SCREEN_STATE_ON");
                 }
             }
             Thread.sleep(SCREEN_STATE_POLLING_INTERVAL);
@@ -710,64 +705,6 @@
     }
 
     /**
-    * Runs logcat and waits (for a maximumum of maxTimeMs) until the desired text is displayed with
-    * the given tag.
-    * Logcat is not cleared, so make sure that text is unique (won't get false hits from old data).
-    * Note that, in practice, the actual max wait time seems to be about 10s longer than maxTimeMs.
-    */
-    private void checkLogcatForText(String logcatTag, String text, int maxTimeMs) {
-        IShellOutputReceiver receiver = new IShellOutputReceiver() {
-            private final StringBuilder mOutputBuffer = new StringBuilder();
-            private final AtomicBoolean mIsCanceled = new AtomicBoolean(false);
-
-            @Override
-            public void addOutput(byte[] data, int offset, int length) {
-                if (!isCancelled()) {
-                    synchronized (mOutputBuffer) {
-                        String s = new String(data, offset, length, Charsets.UTF_8);
-                        mOutputBuffer.append(s);
-                        if (checkBufferForText()) {
-                            mIsCanceled.set(true);
-                        }
-                    }
-                }
-            }
-
-            private boolean checkBufferForText() {
-                if (mOutputBuffer.indexOf(text) > -1) {
-                    return true;
-                } else {
-                    // delete all old data (except the last few chars) since they don't contain text
-                    // (presumably large chunks of data will be added at a time, so this is
-                    // sufficiently efficient.)
-                    int newStart = mOutputBuffer.length() - text.length();
-                    if (newStart > 0) {
-                        mOutputBuffer.delete(0, newStart);
-                    }
-                    return false;
-                }
-            }
-
-            @Override
-            public boolean isCancelled() {
-                return mIsCanceled.get();
-            }
-
-            @Override
-            public void flush() {
-            }
-        };
-
-        try {
-            // Wait for at most maxTimeMs for logcat to display the desired text.
-            getDevice().executeShellCommand(String.format("logcat -s %s -e '%s'", logcatTag, text),
-                    receiver, maxTimeMs, TimeUnit.MILLISECONDS, 0);
-        } catch (com.android.tradefed.device.DeviceNotAvailableException e) {
-            System.err.println(e);
-        }
-    }
-
-    /**
      * Returns the bytes downloaded for the wifi transfer download tests.
      * @param requestCode the output of executeForeground() or executeBackground() to identify in
      *                    the logcat the line associated with the desired download information
diff --git a/hostsidetests/incident/src/com/android/server/cts/FingerprintIncidentTest.java b/hostsidetests/incident/src/com/android/server/cts/FingerprintIncidentTest.java
index 9e32e1c..ed87f11 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 android.service.fingerprint.FingerprintActionStatsProto;
-import android.service.fingerprint.FingerprintServiceDumpProto;
-import android.service.fingerprint.FingerprintUserStatsProto;
+import com.android.server.fingerprint.FingerprintServiceDumpProto;
+import com.android.server.fingerprint.FingerprintUserStatsProto;
+import com.android.server.fingerprint.PerformanceStatsProto;
 
 import com.android.tradefed.log.LogUtil.CLog;
 
@@ -27,12 +27,7 @@
  * Test to check that the fingerprint service properly outputs its dump state.
  */
 public class FingerprintIncidentTest extends ProtoDumpTestCase {
-    /**
-     * Test that no fingerprints are registered.
-     *
-     * @throws Exception
-     */
-    public void testNoneRegistered() throws Exception {
+    public void testFingerprintServiceDump() throws Exception {
         // If the device doesn't support fingerprints, then pass.
         if (!getDevice().hasFeature("android.hardware.fingerprint")) {
             CLog.d("Bypass as android.hardware.fingerprint is not supported.");
@@ -42,24 +37,28 @@
         final FingerprintServiceDumpProto dump =
                 getDump(FingerprintServiceDumpProto.parser(), "dumpsys fingerprint --proto");
 
-        // One of them
-        assertEquals(1, dump.getUsersCount());
+        // There should be at least one user.
+        assertTrue(1 <= dump.getUsersCount());
 
-        final FingerprintUserStatsProto userStats = dump.getUsers(0);
-        assertEquals(0, userStats.getUserId());
-        assertEquals(0, userStats.getNumFingerprints());
+        for (int i = 0; i < dump.getUsersCount(); ++i) {
+            final FingerprintUserStatsProto userStats = dump.getUsers(i);
+            assertTrue(0 <= userStats.getUserId());
+            assertTrue(0 <= userStats.getNumFingerprints());
 
-        final FingerprintActionStatsProto normal = userStats.getNormal();
-        assertEquals(0, normal.getAccept());
-        assertEquals(0, normal.getReject());
-        assertEquals(0, normal.getAcquire());
-        assertEquals(0, normal.getLockout());
+            final PerformanceStatsProto normal = userStats.getNormal();
+            assertTrue(0 <= normal.getAccept());
+            assertTrue(0 <= normal.getReject());
+            assertTrue(0 <= normal.getAcquire());
+            assertTrue(0 <= normal.getLockout());
+            assertTrue(0 <= normal.getPermanentLockout());
 
-        final FingerprintActionStatsProto crypto = userStats.getCrypto();
-        assertEquals(0, crypto.getAccept());
-        assertEquals(0, crypto.getReject());
-        assertEquals(0, crypto.getAcquire());
-        assertEquals(0, crypto.getLockout());
+            final PerformanceStatsProto crypto = userStats.getCrypto();
+            assertTrue(0 <= crypto.getAccept());
+            assertTrue(0 <= crypto.getReject());
+            assertTrue(0 <= crypto.getAcquire());
+            assertTrue(0 <= crypto.getLockout());
+            assertTrue(0 <= crypto.getPermanentLockout());
+        }
     }
 }
 
diff --git a/hostsidetests/incident/src/com/android/server/cts/JobSchedulerIncidentTest.java b/hostsidetests/incident/src/com/android/server/cts/JobSchedulerIncidentTest.java
new file mode 100644
index 0000000..fa23d9a
--- /dev/null
+++ b/hostsidetests/incident/src/com/android/server/cts/JobSchedulerIncidentTest.java
@@ -0,0 +1,317 @@
+/*
+ * 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.server.cts;
+
+import android.app.JobParametersProto;
+import android.net.NetworkCapabilitiesProto;
+import android.net.NetworkRequestProto;
+import com.android.server.job.ConstantsProto;
+import com.android.server.job.DataSetProto;
+import com.android.server.job.JobPackageHistoryProto;
+import com.android.server.job.JobPackageTrackerDumpProto;
+import com.android.server.job.JobSchedulerServiceDumpProto;
+import com.android.server.job.JobStatusDumpProto;
+import com.android.server.job.JobStatusShortInfoProto;
+import com.android.server.job.StateControllerProto;
+
+/** Test to check that the jobscheduler service properly outputs its dump state. */
+public class JobSchedulerIncidentTest extends ProtoDumpTestCase {
+    public void testJobSchedulerServiceDump() throws Exception {
+        final JobSchedulerServiceDumpProto dump =
+                getDump(JobSchedulerServiceDumpProto.parser(), "dumpsys jobscheduler --proto");
+
+        testConstantsProto(dump.getSettings());
+
+        for (int u : dump.getStartedUsersList()) {
+            assertTrue(0 <= u);
+        }
+
+        for (JobSchedulerServiceDumpProto.RegisteredJob rj : dump.getRegisteredJobsList()) {
+            testJobStatusShortInfoProto(rj.getInfo());
+            testJobStatusDumpProto(rj.getDump());
+        }
+
+        for (StateControllerProto c : dump.getControllersList()) {
+            testStateControllerProto(c);
+        }
+
+        for (JobSchedulerServiceDumpProto.PriorityOverride po : dump.getPriorityOverridesList()) {
+            assertTrue(0 <= po.getUid());
+        }
+
+        for (int buu : dump.getBackingUpUidsList()) {
+            assertTrue(0 <= buu);
+        }
+
+        testJobPackageHistoryProto(dump.getHistory());
+
+        testJobPackageTrackerDumpProto(dump.getPackageTracker());
+
+        for (JobSchedulerServiceDumpProto.PendingJob pj : dump.getPendingJobsList()) {
+            testJobStatusShortInfoProto(pj.getInfo());
+            testJobStatusDumpProto(pj.getDump());
+            assertTrue(0 <= pj.getEnqueuedDurationMs());
+        }
+
+        for (JobSchedulerServiceDumpProto.ActiveJob aj : dump.getActiveJobsList()) {
+            JobSchedulerServiceDumpProto.ActiveJob.InactiveJob ajIj = aj.getInactive();
+            assertTrue(0 <= ajIj.getTimeSinceStoppedMs());
+
+            JobSchedulerServiceDumpProto.ActiveJob.RunningJob ajRj = aj.getRunning();
+            testJobStatusShortInfoProto(ajRj.getInfo());
+            assertTrue(0 <= ajRj.getRunningDurationMs());
+            assertTrue(0 <= ajRj.getTimeUntilTimeoutMs());
+            testJobStatusDumpProto(ajRj.getDump());
+            assertTrue(0 <= ajRj.getTimeSinceMadeActiveMs());
+            assertTrue(0 <= ajRj.getPendingDurationMs());
+        }
+
+        assertTrue(0 <= dump.getMaxActiveJobs());
+    }
+
+    private void testConstantsProto(ConstantsProto c) throws Exception {
+        assertNotNull(c);
+
+        assertTrue(0 <= c.getMinIdleCount());
+        assertTrue(0 <= c.getMinChargingCount());
+        assertTrue(0 <= c.getMinBatteryNotLowCount());
+        assertTrue(0 <= c.getMinStorageNotLowCount());
+        assertTrue(0 <= c.getMinConnectivityCount());
+        assertTrue(0 <= c.getMinContentCount());
+        assertTrue(0 <= c.getMinReadyJobsCount());
+        assertTrue(0 <= c.getHeavyUseFactor());
+        assertTrue(0 <= c.getModerateUseFactor());
+        assertTrue(0 <= c.getFgJobCount());
+        assertTrue(0 <= c.getBgNormalJobCount());
+        assertTrue(0 <= c.getBgModerateJobCount());
+        assertTrue(0 <= c.getBgLowJobCount());
+        assertTrue(0 <= c.getBgCriticalJobCount());
+        assertTrue(0 <= c.getMaxStandardRescheduleCount());
+        assertTrue(0 <= c.getMaxWorkRescheduleCount());
+        assertTrue(0 <= c.getMinLinearBackoffTimeMs());
+        assertTrue(0 <= c.getMinExpBackoffTimeMs());
+        assertTrue(0 <= c.getStandbyHeartbeatTimeMs());
+        for (int sb : c.getStandbyBeatsList()) {
+            assertTrue(0 <= sb);
+        }
+    }
+
+    private void testDataSetProto(DataSetProto ds) throws Exception {
+        assertNotNull(ds);
+
+        assertTrue(0 <= ds.getStartClockTimeMs());
+        assertTrue(0 <= ds.getElapsedTimeMs());
+        assertTrue(0 <= ds.getPeriodMs());
+
+        for (DataSetProto.PackageEntryProto pe : ds.getPackageEntriesList()) {
+            assertTrue(0 <= pe.getUid());
+
+            assertTrue(0 <= pe.getPendingState().getDurationMs());
+            assertTrue(0 <= pe.getPendingState().getCount());
+            assertTrue(0 <= pe.getActiveState().getDurationMs());
+            assertTrue(0 <= pe.getActiveState().getCount());
+            assertTrue(0 <= pe.getActiveTopState().getDurationMs());
+            assertTrue(0 <= pe.getActiveTopState().getCount());
+
+            for (DataSetProto.PackageEntryProto.StopReasonCount src : pe.getStopReasonsList()) {
+                assertTrue(JobParametersProto.CancelReason.getDescriptor().getValues()
+                        .contains(src.getReason().getValueDescriptor()));
+                assertTrue(0 <= src.getCount());
+            }
+        }
+        assertTrue(0 <= ds.getMaxConcurrency());
+        assertTrue(0 <= ds.getMaxForegroundConcurrency());
+    }
+
+    private void testJobPackageHistoryProto(JobPackageHistoryProto jph) throws Exception {
+        assertNotNull(jph);
+
+        for (JobPackageHistoryProto.HistoryEvent he : jph.getHistoryEventList()) {
+            assertTrue(JobPackageHistoryProto.Event.getDescriptor().getValues()
+                    .contains(he.getEvent().getValueDescriptor()));
+            assertTrue(0 <= he.getTimeSinceEventMs()); // Should be positive.
+            assertTrue(0 <= he.getUid());
+            assertTrue(JobParametersProto.CancelReason.getDescriptor().getValues()
+                    .contains(he.getStopReason().getValueDescriptor()));
+        }
+    }
+
+    private void testJobPackageTrackerDumpProto(JobPackageTrackerDumpProto jptd) throws Exception {
+        assertNotNull(jptd);
+
+        for (DataSetProto ds : jptd.getHistoricalStatsList()) {
+            testDataSetProto(ds);
+        }
+        testDataSetProto(jptd.getCurrentStats());
+    }
+
+    private void testJobStatusShortInfoProto(JobStatusShortInfoProto jssi) throws Exception {
+        assertNotNull(jssi);
+
+        assertTrue(0 <= jssi.getCallingUid());
+    }
+
+    private void testJobStatusDumpProto(JobStatusDumpProto jsd) throws Exception {
+        assertNotNull(jsd);
+
+        assertTrue(0 <= jsd.getCallingUid());
+        assertTrue(0 <= jsd.getSourceUid());
+        assertTrue(0 <= jsd.getSourceUserId());
+
+        JobStatusDumpProto.JobInfo ji = jsd.getJobInfo();
+        if (ji.getIsPeriodic()) {
+            assertTrue(0 <= ji.getPeriodIntervalMs());
+            assertTrue(0 <= ji.getPeriodFlexMs());
+        }
+        assertTrue(0 <= ji.getTriggerContentUpdateDelayMs());
+        assertTrue(0 <= ji.getTriggerContentMaxDelayMs());
+        testNetworkRequestProto(ji.getRequiredNetwork());
+        assertTrue(0 <= ji.getTotalNetworkBytes());
+        assertTrue(0 <= ji.getMinLatencyMs());
+        assertTrue(0 <= ji.getMaxExecutionDelayMs());
+        JobStatusDumpProto.JobInfo.Backoff bp = ji.getBackoffPolicy();
+        assertTrue(JobStatusDumpProto.JobInfo.Backoff.Policy.getDescriptor().getValues()
+                .contains(bp.getPolicy().getValueDescriptor()));
+        assertTrue(0 <= bp.getInitialBackoffMs());
+
+        for (JobStatusDumpProto.Constraint c : jsd.getRequiredConstraintsList()) {
+            assertTrue(JobStatusDumpProto.Constraint.getDescriptor().getValues()
+                    .contains(c.getValueDescriptor()));
+        }
+        for (JobStatusDumpProto.Constraint c : jsd.getSatisfiedConstraintsList()) {
+            assertTrue(JobStatusDumpProto.Constraint.getDescriptor().getValues()
+                    .contains(c.getValueDescriptor()));
+        }
+        for (JobStatusDumpProto.Constraint c : jsd.getUnsatisfiedConstraintsList()) {
+            assertTrue(JobStatusDumpProto.Constraint.getDescriptor().getValues()
+                    .contains(c.getValueDescriptor()));
+        }
+
+        for (JobStatusDumpProto.TrackingController tc : jsd.getTrackingControllersList()) {
+            assertTrue(JobStatusDumpProto.TrackingController.getDescriptor().getValues()
+                    .contains(tc.getValueDescriptor()));
+        }
+
+        for (JobStatusDumpProto.JobWorkItem jwi : jsd.getPendingWorkList()) {
+            assertTrue(0 <= jwi.getDeliveryCount());
+        }
+        for (JobStatusDumpProto.JobWorkItem jwi : jsd.getExecutingWorkList()) {
+            assertTrue(0 <= jwi.getDeliveryCount());
+        }
+
+        assertTrue(JobStatusDumpProto.Bucket.getDescriptor().getValues()
+                .contains(jsd.getStandbyBucket().getValueDescriptor()));
+
+        assertTrue(0 <= jsd.getEnqueueDurationMs());
+
+        assertTrue(0 <= jsd.getNumFailures());
+
+        assertTrue(0 <= jsd.getLastSuccessfulRunTime());
+        assertTrue(0 <= jsd.getLastFailedRunTime());
+    }
+
+    private void testNetworkRequestProto(NetworkRequestProto nr) throws Exception {
+        assertNotNull(nr);
+
+        assertTrue(NetworkRequestProto.Type.getDescriptor().getValues()
+                .contains(nr.getType().getValueDescriptor()));
+        testNetworkCapabilitesProto(nr.getNetworkCapabilities());
+    }
+
+    private void testNetworkCapabilitesProto(NetworkCapabilitiesProto nc) throws Exception {
+        assertNotNull(nc);
+
+        for (NetworkCapabilitiesProto.Transport t : nc.getTransportsList()) {
+            assertTrue(NetworkCapabilitiesProto.Transport.getDescriptor().getValues()
+                .contains(t.getValueDescriptor()));
+        }
+        for (NetworkCapabilitiesProto.NetCapability c : nc.getCapabilitiesList()) {
+            assertTrue(NetworkCapabilitiesProto.NetCapability.getDescriptor().getValues()
+                .contains(c.getValueDescriptor()));
+        }
+
+        assertTrue(0 <= nc.getLinkUpBandwidthKbps());
+        assertTrue(0 <= nc.getLinkDownBandwidthKbps());
+    }
+
+    private void testStateControllerProto(StateControllerProto sc) throws Exception {
+        assertNotNull(sc);
+
+        StateControllerProto.AppIdleController aic = sc.getAppIdle();
+        for (StateControllerProto.AppIdleController.TrackedJob tj : aic.getTrackedJobsList()) {
+            testJobStatusShortInfoProto(tj.getInfo());
+            assertTrue(0 <= tj.getSourceUid());
+        }
+        StateControllerProto.BackgroundJobsController bjc = sc.getBackground();
+        for (StateControllerProto.BackgroundJobsController.TrackedJob tj : bjc.getTrackedJobsList()) {
+            testJobStatusShortInfoProto(tj.getInfo());
+            assertTrue(0 <= tj.getSourceUid());
+        }
+        StateControllerProto.BatteryController bc = sc.getBattery();
+        for (StateControllerProto.BatteryController.TrackedJob tj : bc.getTrackedJobsList()) {
+            testJobStatusShortInfoProto(tj.getInfo());
+            assertTrue(0 <= tj.getSourceUid());
+        }
+        StateControllerProto.ConnectivityController cc = sc.getConnectivity();
+        for (StateControllerProto.ConnectivityController.TrackedJob tj : cc.getTrackedJobsList()) {
+            testJobStatusShortInfoProto(tj.getInfo());
+            assertTrue(0 <= tj.getSourceUid());
+            testNetworkRequestProto(tj.getRequiredNetwork());
+        }
+        StateControllerProto.ContentObserverController coc = sc.getContentObserver();
+        for (StateControllerProto.ContentObserverController.TrackedJob tj : coc.getTrackedJobsList()) {
+            testJobStatusShortInfoProto(tj.getInfo());
+            assertTrue(0 <= tj.getSourceUid());
+        }
+        for (StateControllerProto.ContentObserverController.Observer o : coc.getObserversList()) {
+            assertTrue(0 <= o.getUserId());
+
+            for (StateControllerProto.ContentObserverController.Observer.TriggerContentData tcd : o.getTriggersList()) {
+                for (StateControllerProto.ContentObserverController.Observer.TriggerContentData.JobInstance ji : tcd.getJobsList()) {
+                    testJobStatusShortInfoProto(ji.getInfo());
+
+                    assertTrue(0 <= ji.getSourceUid());
+                    assertTrue(0 <= ji.getTriggerContentUpdateDelayMs());
+                    assertTrue(0 <= ji.getTriggerContentMaxDelayMs());
+                }
+            }
+        }
+        StateControllerProto.DeviceIdleJobsController dijc = sc.getDeviceIdle();
+        for (StateControllerProto.DeviceIdleJobsController.TrackedJob tj : dijc.getTrackedJobsList()) {
+            testJobStatusShortInfoProto(tj.getInfo());
+            assertTrue(0 <= tj.getSourceUid());
+        }
+        StateControllerProto.IdleController ic = sc.getIdle();
+        for (StateControllerProto.IdleController.TrackedJob tj : ic.getTrackedJobsList()) {
+            testJobStatusShortInfoProto(tj.getInfo());
+            assertTrue(0 <= tj.getSourceUid());
+        }
+        StateControllerProto.StorageController scr = sc.getStorage();
+        for (StateControllerProto.StorageController.TrackedJob tj : scr.getTrackedJobsList()) {
+            testJobStatusShortInfoProto(tj.getInfo());
+            assertTrue(0 <= tj.getSourceUid());
+        }
+        StateControllerProto.TimeController tc = sc.getTime();
+        assertTrue(0 <=  tc.getNowElapsedRealtime());
+        assertTrue(0 <= tc.getTimeUntilNextDelayAlarmMs());
+        assertTrue(0 <= tc.getTimeUntilNextDeadlineAlarmMs());
+        for (StateControllerProto.TimeController.TrackedJob tj : tc.getTrackedJobsList()) {
+            testJobStatusShortInfoProto(tj.getInfo());
+            assertTrue(0 <= tj.getSourceUid());
+        }
+    }
+}
diff --git a/hostsidetests/incident/src/com/android/server/cts/MemInfoIncidentTest.java b/hostsidetests/incident/src/com/android/server/cts/MemInfoIncidentTest.java
new file mode 100644
index 0000000..69fe2ac
--- /dev/null
+++ b/hostsidetests/incident/src/com/android/server/cts/MemInfoIncidentTest.java
@@ -0,0 +1,174 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.cts;
+
+import com.android.server.am.proto.MemInfoProto;
+import com.android.server.am.proto.MemInfoProto.AppData;
+import com.android.server.am.proto.MemInfoProto.MemItem;
+import com.android.server.am.proto.MemInfoProto.ProcessMemory;
+
+/** Test to check that ActivityManager properly outputs meminfo data. */
+public class MemInfoIncidentTest extends ProtoDumpTestCase {
+
+    public void testBatteryServiceDump() throws Exception {
+        final MemInfoProto dump =
+                getDump(MemInfoProto.parser(), "dumpsys meminfo -a --proto");
+
+        assertTrue(dump.getUptimeDurationMs() > 0);
+        assertTrue(dump.getElapsedRealtimeMs() >= 0);
+
+        for (ProcessMemory pm : dump.getNativeProcessesList()) {
+            testProcessMemory(pm);
+        }
+
+        for (AppData ad : dump.getAppProcessesList()) {
+            testAppData(ad);
+        }
+
+        for (MemItem mi : dump.getTotalPssByProcessList()) {
+            testMemItem(mi);
+        }
+        for (MemItem mi : dump.getTotalPssByOomAdjustmentList()) {
+            testMemItem(mi);
+        }
+        for (MemItem mi : dump.getTotalPssByCategoryList()) {
+            testMemItem(mi);
+        }
+
+        assertTrue(0 <= dump.getTotalRamKb());
+        assertTrue(0 <= dump.getCachedPssKb());
+        assertTrue(0 <= dump.getCachedKernelKb());
+        assertTrue(0 <= dump.getFreeKb());
+        assertTrue(0 <= dump.getUsedPssKb());
+        assertTrue(0 <= dump.getUsedKernelKb());
+
+        assertTrue(0 <= dump.getLostRamKb());
+
+        assertTrue(0 <= dump.getTotalZramKb());
+        assertTrue(0 <= dump.getZramPhysicalUsedInSwapKb());
+        assertTrue(0 <= dump.getTotalZramSwapKb());
+
+        assertTrue(0 <= dump.getKsmSharingKb());
+        assertTrue(0 <= dump.getKsmSharedKb());
+        assertTrue(0 <= dump.getKsmUnsharedKb());
+        assertTrue(0 <= dump.getKsmVolatileKb());
+
+        assertTrue(0 < dump.getTuningMb());
+        assertTrue(0 < dump.getTuningLargeMb());
+
+        assertTrue(0 <= dump.getOomKb());
+
+        assertTrue(0 < dump.getRestoreLimitKb());
+    }
+
+    private void testProcessMemory(ProcessMemory pm) throws Exception {
+        assertNotNull(pm);
+
+        assertTrue(0 < pm.getPid());
+        // On most Linux machines, the max pid value is 32768 (=2^15), but, it can be set to any
+        // value up to 4194304 (=2^22) if necessary.
+        assertTrue(4194304 >= pm.getPid());
+
+        testHeapInfo(pm.getNativeHeap());
+        testHeapInfo(pm.getDalvikHeap());
+
+        for (ProcessMemory.MemoryInfo mi : pm.getOtherHeapsList()) {
+            testMemoryInfo(mi);
+        }
+        testMemoryInfo(pm.getUnknownHeap());
+        testHeapInfo(pm.getTotalHeap());
+
+        for (ProcessMemory.MemoryInfo mi : pm.getDalvikDetailsList()) {
+            testMemoryInfo(mi);
+        }
+
+        ProcessMemory.AppSummary as = pm.getAppSummary();
+        assertTrue(0 <= as.getJavaHeapPssKb());
+        assertTrue(0 <= as.getNativeHeapPssKb());
+        assertTrue(0 <= as.getCodePssKb());
+        assertTrue(0 <= as.getStackPssKb());
+        assertTrue(0 <= as.getGraphicsPssKb());
+        assertTrue(0 <= as.getPrivateOtherPssKb());
+        assertTrue(0 <= as.getSystemPssKb());
+        assertTrue(0 <= as.getTotalSwapPss());
+        assertTrue(0 <= as.getTotalSwapKb());
+    }
+
+    private void testMemoryInfo(ProcessMemory.MemoryInfo mi) throws Exception {
+        assertNotNull(mi);
+
+        assertTrue(0 <= mi.getTotalPssKb());
+        assertTrue(0 <= mi.getCleanPssKb());
+        assertTrue(0 <= mi.getSharedDirtyKb());
+        assertTrue(0 <= mi.getPrivateDirtyKb());
+        assertTrue(0 <= mi.getSharedCleanKb());
+        assertTrue(0 <= mi.getPrivateCleanKb());
+        assertTrue(0 <= mi.getDirtySwapKb());
+        assertTrue(0 <= mi.getDirtySwapPssKb());
+    }
+
+    private void testHeapInfo(ProcessMemory.HeapInfo hi) throws Exception {
+        assertNotNull(hi);
+
+        testMemoryInfo(hi.getMemInfo());
+        assertTrue(0 <= hi.getHeapSizeKb());
+        assertTrue(0 <= hi.getHeapAllocKb());
+        assertTrue(0 <= hi.getHeapFreeKb());
+    }
+
+    private void testAppData(AppData ad) throws Exception {
+        assertNotNull(ad);
+
+        testProcessMemory(ad.getProcessMemory());
+
+        AppData.ObjectStats os = ad.getObjects();
+        assertTrue(0 <= os.getViewInstanceCount());
+        assertTrue(0 <= os.getViewRootInstanceCount());
+        assertTrue(0 <= os.getAppContextInstanceCount());
+        assertTrue(0 <= os.getActivityInstanceCount());
+        assertTrue(0 <= os.getGlobalAssetCount());
+        assertTrue(0 <= os.getGlobalAssetManagerCount());
+        assertTrue(0 <= os.getLocalBinderObjectCount());
+        assertTrue(0 <= os.getProxyBinderObjectCount());
+        assertTrue(0 <= os.getParcelMemoryKb());
+        assertTrue(0 <= os.getParcelCount());
+        assertTrue(0 <= os.getBinderObjectDeathCount());
+        assertTrue(0 <= os.getOpenSslSocketCount());
+        assertTrue(0 <= os.getWebviewInstanceCount());
+
+        AppData.SqlStats ss = ad.getSql();
+        assertTrue(0 <= ss.getMemoryUsedKb());
+        assertTrue(0 <= ss.getPagecacheOverflowKb());
+        assertTrue(0 <= ss.getMallocSizeKb());
+        for (AppData.SqlStats.Database d : ss.getDatabasesList()) {
+            assertTrue(0 <= d.getPageSize());
+            assertTrue(0 <= d.getDbSize());
+            assertTrue(0 <= d.getLookasideB());
+        }
+    }
+
+    private void testMemItem(MemItem mi) throws Exception {
+        assertNotNull(mi);
+
+        assertTrue(0 <= mi.getPssKb());
+        assertTrue(0 <= mi.getSwapPssKb());
+
+        for (MemItem smi : mi.getSubItemsList()) {
+            testMemItem(smi);
+        }
+    }
+}
diff --git a/hostsidetests/incident/src/com/android/server/cts/NetstatsIncidentTest.java b/hostsidetests/incident/src/com/android/server/cts/NetstatsIncidentTest.java
index f5c13c2..bdf7dcc 100644
--- a/hostsidetests/incident/src/com/android/server/cts/NetstatsIncidentTest.java
+++ b/hostsidetests/incident/src/com/android/server/cts/NetstatsIncidentTest.java
@@ -374,16 +374,14 @@
                 assertNotNegative("TX bytes", bucket.getTxBytes());
                 assertNotNegative("TX packets", bucket.getTxPackets());
 
-// 10 was still too big?                // It should be safe to say # of bytes >= 10 * 10 of packets, due to headers, etc...
-                final long FACTOR = 4;
                 assertTrue(
                         String.format("# of bytes %d too small for # of packets %d",
                                 bucket.getRxBytes(), bucket.getRxPackets()),
-                        bucket.getRxBytes() >= bucket.getRxPackets() * FACTOR);
+                        bucket.getRxBytes() >= bucket.getRxPackets());
                 assertTrue(
                         String.format("# of bytes %d too small for # of packets %d",
                                 bucket.getTxBytes(), bucket.getTxPackets()),
-                        bucket.getTxBytes() >= bucket.getTxPackets() * FACTOR);
+                        bucket.getTxBytes() >= bucket.getTxPackets());
             }
         }
 
diff --git a/hostsidetests/incident/src/com/android/server/cts/NotificationIncidentTest.java b/hostsidetests/incident/src/com/android/server/cts/NotificationIncidentTest.java
new file mode 100644
index 0000000..a96be46
--- /dev/null
+++ b/hostsidetests/incident/src/com/android/server/cts/NotificationIncidentTest.java
@@ -0,0 +1,119 @@
+/*
+ * 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.server.cts;
+
+import android.service.notification.NotificationRecordProto;
+import android.service.notification.NotificationServiceDumpProto;
+import android.service.notification.NotificationRecordProto.State;
+import android.service.notification.RankingHelperProto;
+import android.service.notification.RankingHelperProto.RecordProto;
+import android.service.notification.ZenModeProto;
+import android.service.notification.ZenModeProto.ZenMode;
+
+/**
+ * Test to check that the notification service properly outputs its dump state.
+ *
+ * make -j32 CtsIncidentHostTestCases
+ * cts-tradefed run singleCommand cts-dev -d --module CtsIncidentHostTestCases
+ */
+public class NotificationIncidentTest extends ProtoDumpTestCase {
+    // Constants from android.app.NotificationManager
+    private static final int IMPORTANCE_UNSPECIFIED = -1000;
+    private static final int IMPORTANCE_NONE = 0;
+    private static final int IMPORTANCE_MAX = 5;
+    private static final int VISIBILITY_NO_OVERRIDE = -1000;
+    // Constants from android.app.Notification
+    private static final int PRIORITY_MIN = -2;
+    private static final int PRIORITY_MAX = 2;
+    private static final int VISIBILITY_SECRET = -1;
+    private static final int VISIBILITY_PUBLIC = 1;
+    // These constants are those in PackageManager.
+    public static final String FEATURE_WATCH = "android.hardware.type.watch";
+
+    /**
+     * Tests that at least one notification is posted, and verify its properties are plausible.
+     */
+    public void testNotificationRecords() throws Exception {
+        final NotificationServiceDumpProto dump = getDump(NotificationServiceDumpProto.parser(),
+                "dumpsys notification --proto");
+
+        assertTrue(dump.getRecordsCount() > 0);
+        boolean found = false;
+        for (NotificationRecordProto record : dump.getRecordsList()) {
+            if (record.getKey().contains("android")) {
+                found = true;
+                assertEquals(State.POSTED, record.getState());
+                assertTrue(record.getImportance() > IMPORTANCE_NONE);
+
+                // Ensure these fields exist, at least
+                record.getFlags();
+                record.getChannelId();
+                record.getSound();
+                record.getSoundUsage();
+                record.getCanVibrate();
+                record.getCanShowLight();
+                record.getGroupKey();
+            }
+            assertTrue(State.SNOOZED != record.getState());
+        }
+
+        assertTrue(found);
+    }
+
+    /** Test valid values from the RankingHelper. */
+    public void testRankingConfig() throws Exception {
+        final NotificationServiceDumpProto dump = getDump(NotificationServiceDumpProto.parser(),
+                "dumpsys notification --proto");
+
+        RankingHelperProto rhProto = dump.getRankingConfig();
+        for (RecordProto rp : rhProto.getRecordsList()) {
+            verifyRecordProto(rp);
+        }
+        for (RecordProto rp : rhProto.getRecordsRestoredWithoutUidList()) {
+            verifyRecordProto(rp);
+        }
+    }
+
+    private void verifyRecordProto(RecordProto rp) throws Exception {
+        assertTrue(!rp.getPackage().isEmpty());
+        assertTrue(rp.getUid() == -10000 || rp.getUid() >= 0);
+        assertTrue(rp.getImportance() == IMPORTANCE_UNSPECIFIED ||
+                (rp.getImportance() >= IMPORTANCE_NONE && rp.getImportance() <= IMPORTANCE_MAX));
+        assertTrue(rp.getPriority() >= PRIORITY_MIN && rp.getPriority() <= PRIORITY_MAX);
+        assertTrue(rp.getVisibility() == VISIBILITY_NO_OVERRIDE ||
+                (rp.getVisibility() >= VISIBILITY_SECRET &&
+                 rp.getVisibility() <= VISIBILITY_PUBLIC));
+    }
+
+    // Tests default state: zen mode off, no suppressors
+    public void testZenMode() throws Exception {
+        final NotificationServiceDumpProto dump = getDump(NotificationServiceDumpProto.parser(),
+                "dumpsys notification --proto");
+        ZenModeProto zenProto = dump.getZen();
+
+        assertEquals(ZenMode.ZEN_MODE_OFF, zenProto.getZenMode());
+        assertEquals(0, zenProto.getEnabledActiveConditionsCount());
+
+        // b/64606626 Watches intentionally suppress notifications always
+        if (!getDevice().hasFeature(FEATURE_WATCH)) {
+            assertEquals(0, zenProto.getSuppressedEffects());
+            assertEquals(0, zenProto.getSuppressorsCount());
+        }
+
+        zenProto.getPolicy();
+    }
+}
diff --git a/hostsidetests/incident/src/com/android/server/cts/NotificationTest.java b/hostsidetests/incident/src/com/android/server/cts/NotificationTest.java
deleted file mode 100644
index f91c8a7..0000000
--- a/hostsidetests/incident/src/com/android/server/cts/NotificationTest.java
+++ /dev/null
@@ -1,82 +0,0 @@
-/*
- * Copyright (C) 2017 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.server.cts;
-
-import android.service.notification.NotificationRecordProto;
-import android.service.notification.NotificationServiceDumpProto;
-import android.service.notification.State;
-import android.service.notification.ZenMode;
-import android.service.notification.ZenModeProto;
-
-/**
- * Test to check that the notification service properly outputs its dump state.
- *
- * make -j32 CtsIncidentHostTestCases
- * cts-tradefed run singleCommand cts-dev -d --module CtsIncidentHostTestCases
- */
-public class NotificationTest extends ProtoDumpTestCase {
-    // These constants are those in PackageManager.
-    public static final String FEATURE_WATCH = "android.hardware.type.watch";
-
-    /**
-     * Tests that at least one notification is posted, and verify its properties are plausible.
-     */
-    public void testNotificationRecords() throws Exception {
-        final NotificationServiceDumpProto dump = getDump(NotificationServiceDumpProto.parser(),
-                "dumpsys notification --proto");
-
-        assertTrue(dump.getRecordsCount() > 0);
-        boolean found = false;
-        for (NotificationRecordProto record : dump.getRecordsList()) {
-            if (record.getKey().contains("android")) {
-                found = true;
-                assertEquals(State.POSTED, record.getState());
-                assertTrue(record.getImportance() > 0 /* NotificationManager.IMPORTANCE_NONE */);
-
-                // Ensure these fields exist, at least
-                record.getFlags();
-                record.getChannelId();
-                record.getSound();
-                record.getSoundUsage();
-                record.getCanVibrate();
-                record.getCanShowLight();
-                record.getGroupKey();
-            }
-            assertTrue(State.SNOOZED != record.getState());
-        }
-
-        assertTrue(found);
-    }
-
-    // Tests default state: zen mode off, no suppressors
-    public void testZenMode() throws Exception {
-        final NotificationServiceDumpProto dump = getDump(NotificationServiceDumpProto.parser(),
-                "dumpsys notification --proto");
-        ZenModeProto zenProto = dump.getZen();
-
-        assertEquals(ZenMode.ZEN_MODE_OFF, zenProto.getZenMode());
-        assertEquals(0, zenProto.getEnabledActiveConditionsCount());
-
-        // b/64606626 Watches intentionally suppress notifications always
-        if (!getDevice().hasFeature(FEATURE_WATCH)) {
-            assertEquals(0, zenProto.getSuppressedEffects());
-            assertEquals(0, zenProto.getSuppressorsCount());
-        }
-
-        zenProto.getPolicy();
-    }
-}
diff --git a/hostsidetests/incident/src/com/android/server/cts/PowerIncidentTest.java b/hostsidetests/incident/src/com/android/server/cts/PowerIncidentTest.java
index 77c0163..a0cfd07 100644
--- a/hostsidetests/incident/src/com/android/server/cts/PowerIncidentTest.java
+++ b/hostsidetests/incident/src/com/android/server/cts/PowerIncidentTest.java
@@ -16,28 +16,37 @@
 
 package com.android.server.cts;
 
+import android.app.ProcessState;
+import android.content.IntentProto;
+import android.os.BatteryManagerProto;
 import android.os.LooperProto;
-import android.service.power.PowerServiceDumpProto;
-import android.service.power.PowerServiceSettingsAndConfigurationDumpProto;
+import android.os.PowerManagerInternalProto;
+import android.os.PowerManagerProto;
+import com.android.server.power.PowerManagerServiceDumpProto;
+import com.android.server.power.PowerServiceSettingsAndConfigurationDumpProto;
+import com.android.server.power.WakeLockProto;
 
 /** Test to check that the power manager properly outputs its dump state. */
 public class PowerIncidentTest extends ProtoDumpTestCase {
     private static final int SYSTEM_UID = 1000;
 
     public void testPowerServiceDump() throws Exception {
-        final PowerServiceDumpProto dump =
-                getDump(PowerServiceDumpProto.parser(), "dumpsys power --proto");
+        final PowerManagerServiceDumpProto dump =
+                getDump(PowerManagerServiceDumpProto.parser(), "dumpsys power --proto");
+
+        assertTrue(dump.getBatteryLevel() >= 0);
+        assertTrue(dump.getBatteryLevel() <= 100);
 
         assertTrue(
-                PowerServiceDumpProto.Wakefulness.getDescriptor()
+                PowerManagerInternalProto.Wakefulness.getDescriptor()
                         .getValues()
                         .contains(dump.getWakefulness().getValueDescriptor()));
         assertTrue(
-                PowerServiceDumpProto.PlugType.getDescriptor()
+                BatteryManagerProto.PlugType.getDescriptor()
                         .getValues()
                         .contains(dump.getPlugType().getValueDescriptor()));
         assertTrue(
-                PowerServiceDumpProto.DockState.getDescriptor()
+                IntentProto.DockState.getDescriptor()
                         .getValues()
                         .contains(dump.getDockState().getValueDescriptor()));
 
@@ -47,22 +56,34 @@
         assertTrue(settingsAndConfiguration.getMaximumScreenDimDurationConfigMs() >= 0);
         assertTrue(settingsAndConfiguration.getMaximumScreenDimRatioConfig() > 0);
         assertTrue(settingsAndConfiguration.getScreenOffTimeoutSettingMs() > 0);
+        // Default value is -1.
+        assertTrue(settingsAndConfiguration.getSleepTimeoutSettingMs() >= -1);
         assertTrue(settingsAndConfiguration.getMaximumScreenOffTimeoutFromDeviceAdminMs() > 0);
+        // -1 is used to disable, so is valid.
+        assertTrue(settingsAndConfiguration.getUserActivityTimeoutOverrideFromWindowManagerMs() >= -1);
         final PowerServiceSettingsAndConfigurationDumpProto.ScreenBrightnessSettingLimitsProto
                 brightnessLimits = settingsAndConfiguration.getScreenBrightnessSettingLimits();
-        assertTrue(brightnessLimits.getSettingMaximum() > 0);
+        int settingMax = brightnessLimits.getSettingMaximum();
+        int settingMin = brightnessLimits.getSettingMinimum();
+        assertTrue(settingMin >= 0);
+        assertTrue(settingMax > 0);
+        assertTrue(settingMax >= settingMin);
         assertTrue(brightnessLimits.getSettingDefault() > 0);
         assertTrue(brightnessLimits.getSettingForVrDefault() > 0);
 
-        final PowerServiceDumpProto.UidProto uid = dump.getUids(0);
+        final PowerManagerServiceDumpProto.UidStateProto uid = dump.getUidStates(0);
         assertEquals(uid.getUid(), SYSTEM_UID);
         assertEquals(uid.getUidString(), Integer.toString(SYSTEM_UID));
         assertTrue(uid.getIsActive());
         assertFalse(uid.getIsProcessStateUnknown());
-        assertTrue(
-                PowerServiceDumpProto.UidProto.ProcessState.getDescriptor()
-                        .getValues()
-                        .contains(uid.getProcessState().getValueDescriptor()));
+
+        for (PowerManagerServiceDumpProto.UidStateProto us : dump.getUidStatesList()) {
+            assertTrue(0 <= us.getUid());
+            assertTrue(0 <= us.getNumWakeLocks());
+            assertTrue(ProcessState.getDescriptor()
+                    .getValues()
+                    .contains(us.getProcessState().getValueDescriptor()));
+        }
 
         final LooperProto looper = dump.getLooper();
         assertNotNull(looper.getThreadName());
@@ -70,5 +91,26 @@
         assertTrue(looper.getIdentityHashCode() > 0);
 
         assertTrue(dump.getSuspendBlockersCount() > 0);
+
+        // Check that times/durations are not incorrectly negative.
+        assertTrue(dump.getNotifyLongScheduledMs() >= 0);
+        assertTrue(dump.getNotifyLongDispatchedMs() >= 0);
+        assertTrue(dump.getNotifyLongNextCheckMs() >= 0);
+        assertTrue(dump.getLastWakeTimeMs() >= 0);
+        assertTrue(dump.getLastSleepTimeMs() >= 0);
+        assertTrue(dump.getLastUserActivityTimeMs() >= 0);
+        assertTrue(dump.getLastUserActivityTimeNoChangeLightsMs() >= 0);
+        assertTrue(dump.getLastInteractivePowerHintTimeMs() >= 0);
+        assertTrue(dump.getLastScreenBrightnessBoostTimeMs() >= 0);
+        // -1 is a valid value.
+        assertTrue(dump.getSleepTimeoutMs() >= -1);
+        assertTrue(dump.getScreenOffTimeoutMs() >= 0);
+        assertTrue(dump.getScreenDimDurationMs() >= 0);
+
+        for (WakeLockProto wl : dump.getWakeLocksList()) {
+            assertTrue(0 <= wl.getAcqMs());
+            assertTrue(0 <= wl.getUid());
+            assertTrue(0 <= wl.getPid());
+        }
     }
 }
diff --git a/hostsidetests/incident/src/com/android/server/cts/PrintProtoTest.java b/hostsidetests/incident/src/com/android/server/cts/PrintProtoTest.java
new file mode 100644
index 0000000..24f3d2a
--- /dev/null
+++ b/hostsidetests/incident/src/com/android/server/cts/PrintProtoTest.java
@@ -0,0 +1,49 @@
+/*
+ * 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.server.cts;
+
+import android.service.print.PrintServiceDumpProto;
+import android.service.print.PrintSpoolerStateProto;
+import android.service.print.PrintUserStateProto;
+
+import com.android.tradefed.log.LogUtil;
+
+/**
+ * Test proto dump of print
+ */
+public class PrintProtoTest extends ProtoDumpTestCase {
+    /**
+     * Test that print dump is reasonable
+     *
+     * @throws Exception
+     */
+    public void testDump() throws Exception {
+        // If the device doesn't support printing, then pass.
+        if (!getDevice().hasFeature("android.software.print")) {
+            LogUtil.CLog.d("Bypass as android.software.print is not supported.");
+            return;
+        }
+
+        PrintServiceDumpProto dump = getDump(PrintServiceDumpProto.parser(),
+                "dumpsys print --proto");
+
+        assertTrue(dump.getUserStatesCount() > 0);
+
+        PrintUserStateProto userState = dump.getUserStatesList().get(0);
+        assertEquals(0, userState.getUserId());
+    }
+}
diff --git a/hostsidetests/incident/src/com/android/server/cts/ProcStatsProtoTest.java b/hostsidetests/incident/src/com/android/server/cts/ProcStatsProtoTest.java
new file mode 100644
index 0000000..640da22
--- /dev/null
+++ b/hostsidetests/incident/src/com/android/server/cts/ProcStatsProtoTest.java
@@ -0,0 +1,78 @@
+/*
+ * 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.server.cts;
+
+import android.service.procstats.ProcessStatsProto;
+import android.service.procstats.ProcessStatsServiceDumpProto;
+
+/**
+ * Test proto dump of procstats
+ *
+ * $ make -j32 CtsIncidentHostTestCases
+ * $ cts-tradefed run cts-dev -m CtsIncidentHostTestCases \
+ * -t com.android.server.cts.ProcStatsProtoTest
+ */
+public class ProcStatsProtoTest extends ProtoDumpTestCase {
+
+    private static final String DEVICE_SIDE_TEST_APK = "CtsProcStatsProtoApp.apk";
+    private static final String DEVICE_SIDE_TEST_PACKAGE = "com.android.server.cts.procstats";
+    private static final String TEST_APP_TAG = "ProcstatsAppRunningTest";
+    private static final String TEST_APP_LOG = "Procstats app is running";
+    private static final int WAIT_MS = 1000;
+
+    @Override
+    protected void setUp() throws Exception {
+        super.setUp();
+        getDevice().executeShellCommand("dumpsys procstats --clear");
+    }
+
+    @Override
+    protected void tearDown() throws Exception {
+        getDevice().uninstallPackage(DEVICE_SIDE_TEST_PACKAGE);
+        super.tearDown();
+    }
+
+    /**
+     * Test that procstats dump is reasonable, it installs procstats app and
+     * starts the activity, then asserts the procstats dump contains the package.
+     *
+     * @throws Exception
+     */
+    public void testDump() throws Exception {
+        installPackage(DEVICE_SIDE_TEST_APK, /* grantPermissions= */ true);
+        int retries = 3;
+        do {
+            getDevice().executeShellCommand(
+                "am start -n com.android.server.cts.procstats/.SimpleActivity");
+        } while (!checkLogcatForText(TEST_APP_TAG, TEST_APP_LOG, WAIT_MS) && retries-- > 0);
+
+        final ProcessStatsServiceDumpProto dump = getDump(ProcessStatsServiceDumpProto.parser(),
+                "dumpsys procstats --proto");
+
+        int N = dump.getProcstatsNow().getProcessStatsCount();
+        boolean containsTestApp = false;
+        for (int i = 0; i < N; i++) {
+            ProcessStatsProto ps = dump.getProcstatsNow().getProcessStats(i);
+            if (DEVICE_SIDE_TEST_PACKAGE.equals(ps.getProcess())) {
+                containsTestApp = true;
+            }
+        }
+
+        assertTrue(N > 0);
+        assertTrue(containsTestApp); // found test app in procstats dump
+    }
+}
diff --git a/hostsidetests/incident/src/com/android/server/cts/ProtoDumpTestCase.java b/hostsidetests/incident/src/com/android/server/cts/ProtoDumpTestCase.java
index 5739bf4..d273033 100644
--- a/hostsidetests/incident/src/com/android/server/cts/ProtoDumpTestCase.java
+++ b/hostsidetests/incident/src/com/android/server/cts/ProtoDumpTestCase.java
@@ -17,6 +17,7 @@
 package com.android.server.cts;
 
 import com.android.compatibility.common.tradefed.build.CompatibilityBuildHelper;
+import com.android.ddmlib.IShellOutputReceiver;
 import com.android.ddmlib.testrunner.RemoteAndroidTestRunner;
 import com.android.ddmlib.testrunner.TestIdentifier;
 import com.android.ddmlib.testrunner.TestResult;
@@ -31,11 +32,14 @@
 import com.android.tradefed.testtype.DeviceTestCase;
 import com.android.tradefed.testtype.IBuildReceiver;
 
+import com.google.common.base.Charsets;
 import com.google.protobuf.InvalidProtocolBufferException;
 import com.google.protobuf.MessageLite;
 import com.google.protobuf.Parser;
 
 import java.io.FileNotFoundException;
+import java.util.concurrent.TimeUnit;
+import java.util.concurrent.atomic.AtomicBoolean;
 import java.util.Map;
 import java.util.regex.Matcher;
 import java.util.regex.Pattern;
@@ -176,4 +180,66 @@
         assertTrue("No group found for pattern '" + pattern + "'", matcher.groupCount() > 0);
         return matcher.group(1);
     }
+
+    /**
+     * Runs logcat and waits (for a maximumum of maxTimeMs) until the desired text is displayed with
+     * the given tag.
+     * Logcat is not cleared, so make sure that text is unique (won't get false hits from old data).
+     * Note that, in practice, the actual max wait time seems to be about 10s longer than maxTimeMs.
+     * Returns true means the desired log line is found.
+     */
+    protected boolean checkLogcatForText(String logcatTag, String text, int maxTimeMs) {
+        IShellOutputReceiver receiver = new IShellOutputReceiver() {
+            private final StringBuilder mOutputBuffer = new StringBuilder();
+            private final AtomicBoolean mIsCanceled = new AtomicBoolean(false);
+
+            @Override
+            public void addOutput(byte[] data, int offset, int length) {
+                if (!isCancelled()) {
+                    synchronized (mOutputBuffer) {
+                        String s = new String(data, offset, length, Charsets.UTF_8);
+                        mOutputBuffer.append(s);
+                        if (checkBufferForText()) {
+                            mIsCanceled.set(true);
+                        }
+                    }
+                }
+            }
+
+            private boolean checkBufferForText() {
+                if (mOutputBuffer.indexOf(text) > -1) {
+                    return true;
+                } else {
+                    // delete all old data (except the last few chars) since they don't contain text
+                    // (presumably large chunks of data will be added at a time, so this is
+                    // sufficiently efficient.)
+                    int newStart = mOutputBuffer.length() - text.length();
+                    if (newStart > 0) {
+                        mOutputBuffer.delete(0, newStart);
+                    }
+                    return false;
+                }
+            }
+
+            @Override
+            public boolean isCancelled() {
+                return mIsCanceled.get();
+            }
+
+            @Override
+            public void flush() {
+            }
+        };
+
+        try {
+            // Wait for at most maxTimeMs for logcat to display the desired text.
+            getDevice().executeShellCommand(String.format("logcat -s %s -e '%s'", logcatTag, text),
+                    receiver, maxTimeMs, TimeUnit.MILLISECONDS, 0);
+            return receiver.isCancelled();
+        } catch (com.android.tradefed.device.DeviceNotAvailableException e) {
+            System.err.println(e);
+        }
+        return false;
+    }
+
 }
diff --git a/hostsidetests/incident/src/com/android/server/cts/SettingsIncidentTest.java b/hostsidetests/incident/src/com/android/server/cts/SettingsIncidentTest.java
index 7ea00e1..d9ee469 100644
--- a/hostsidetests/incident/src/com/android/server/cts/SettingsIncidentTest.java
+++ b/hostsidetests/incident/src/com/android/server/cts/SettingsIncidentTest.java
@@ -58,7 +58,7 @@
     private void verifySettings(GeneratedMessage settings) throws Exception {
         verifySettings(getSettingProtos(settings));
 
-        final List<SettingsOperationProto> ops = invoke(settings, "getHistoricalOpList");
+        final List<SettingsOperationProto> ops = invoke(settings, "getHistoricalOperationsList");
         for (SettingsOperationProto op : ops) {
             assertTrue(op.getTimestamp() >= 0);
             assertNotNull(op.getOperation());
diff --git a/hostsidetests/inputmethodservice/common/src/android/inputmethodservice/cts/common/DeviceEventConstants.java b/hostsidetests/inputmethodservice/common/src/android/inputmethodservice/cts/common/DeviceEventConstants.java
index 307693a..1f694d2 100644
--- a/hostsidetests/inputmethodservice/common/src/android/inputmethodservice/cts/common/DeviceEventConstants.java
+++ b/hostsidetests/inputmethodservice/common/src/android/inputmethodservice/cts/common/DeviceEventConstants.java
@@ -50,12 +50,6 @@
     public static final String EXTRA_EVENT_SENDER = "event_sender";
 
     /**
-     * Intent extra key for Event parameters like
-     * {@link DeviceEventTypeParam#ON_START_INPUT_RESTARTING}
-     */
-    public static final String EXTRA_EVENT_PARAMS = "event_params";
-
-    /**
      * Intent extra key for what type a device event is. Values are {@link DeviceEventType#name()}.
      *
      * @see android.content.Intent#putExtra(String,String)
@@ -73,29 +67,6 @@
     public static final String EXTRA_EVENT_TIME = "event_time";
 
     /**
-     * Parameter for {@link DeviceEventType}.
-     */
-    public enum DeviceEventTypeParam {
-
-        /**
-         *  Param for {@link DeviceEventType#ON_START_INPUT}. Represents if IME is restarting.
-         */
-        ON_START_INPUT_RESTARTING(DeviceEventType.ON_START_INPUT, "onStartInput.restarting");
-
-        private final DeviceEventType mType;
-        private final String mName;
-
-        DeviceEventTypeParam(DeviceEventType type, String name) {
-            mType = type;
-            mName = name;
-        }
-
-        public String getName() {
-            return mName;
-        }
-    }
-
-    /**
      * Types of device event, a value of {@link #EXTRA_EVENT_TYPE}.
      */
     public enum DeviceEventType {
@@ -135,15 +106,5 @@
         /** Test start and end event types. */
         TEST_START,
         TEST_END,
-
-        /**
-         * {@link android.view.inputmethod.InputMethod#showSoftInput}
-         */
-        SHOW_SOFT_INPUT,
-
-        /**
-         * {@link android.view.inputmethod.InputMethod#hideSoftInput}
-         */
-        HIDE_SOFT_INPUT,
     }
 }
diff --git a/hostsidetests/inputmethodservice/common/src/android/inputmethodservice/cts/common/EventProviderConstants.java b/hostsidetests/inputmethodservice/common/src/android/inputmethodservice/cts/common/EventProviderConstants.java
index 9288051..172e1a7 100644
--- a/hostsidetests/inputmethodservice/common/src/android/inputmethodservice/cts/common/EventProviderConstants.java
+++ b/hostsidetests/inputmethodservice/common/src/android/inputmethodservice/cts/common/EventProviderConstants.java
@@ -58,9 +58,6 @@
         // This is constants holding class, can't instantiate.
         private EventTableConstants() {}
 
-        /** Column name of the table that holds Event extras in json format. */
-        public static final String EXTRAS = "extras";
-
         /** Name of the table in content provider and database. */
         public static final String NAME = "events";
 
diff --git a/hostsidetests/inputmethodservice/common/src/android/inputmethodservice/cts/common/test/DeviceTestConstants.java b/hostsidetests/inputmethodservice/common/src/android/inputmethodservice/cts/common/test/DeviceTestConstants.java
index aa90e11..4502325 100644
--- a/hostsidetests/inputmethodservice/common/src/android/inputmethodservice/cts/common/test/DeviceTestConstants.java
+++ b/hostsidetests/inputmethodservice/common/src/android/inputmethodservice/cts/common/test/DeviceTestConstants.java
@@ -42,11 +42,4 @@
            "android.inputmethodservice.cts.devicetest.InputMethodServiceDeviceTest";
     public static final String TEST_CREATE_IME1 = "testCreateIme1";
     public static final String TEST_SWITCH_IME1_TO_IME2 = "testSwitchIme1ToIme2";
-    public static final String TEST_IME1_IS_NOT_CURRENT_IME = "testIme1IsNotCurrentIme";
-    public static final String TEST_SEARCH_VIEW_GIVE_FOCUS_SHOW_IME1
-            = "testSearchView_giveFocusShowIme1";
-    public static final String TEST_SEARCH_VIEW_SET_QUERY_HIDE_IME1
-            = "testSearchView_setQueryHideIme1";
-    public static final String TEST_ON_START_INPUT_CALLED_ONCE_IME1
-            = "testOnStartInputCalledOnceIme1";
 }
diff --git a/hostsidetests/inputmethodservice/common/src/android/inputmethodservice/cts/common/test/ShellCommandUtils.java b/hostsidetests/inputmethodservice/common/src/android/inputmethodservice/cts/common/test/ShellCommandUtils.java
index b984aa1..643eb52 100644
--- a/hostsidetests/inputmethodservice/common/src/android/inputmethodservice/cts/common/test/ShellCommandUtils.java
+++ b/hostsidetests/inputmethodservice/common/src/android/inputmethodservice/cts/common/test/ShellCommandUtils.java
@@ -29,11 +29,6 @@
     // Copied from android.content.pm.PackageManager#FEATURE_INPUT_METHODS.
     public static final String FEATURE_INPUT_METHODS = "android.software.input_methods";
 
-    /** Command to check whether system has specified {@code featureName} feature. */
-    public static String hasFeature(final String featureName) {
-        return "cmd package has-feature " + featureName;
-    }
-
     private static final String SETTING_DEFAULT_IME = "secure default_input_method";
 
     /** Command to get ID of current IME. */
@@ -56,6 +51,11 @@
         return "ime disable " + imeId;
     }
 
+    /** Command to reset currently selected/enabled IMEs to the default ones. */
+    public static String resetImes() {
+        return "ime reset";
+    }
+
     /** Command to delete all records of IME event provider. */
     public static String deleteContent(final String contentUri) {
         return "content delete --uri " + contentUri;
diff --git a/hostsidetests/inputmethodservice/deviceside/devicetest/res/layout/activity_inputmethod_test.xml b/hostsidetests/inputmethodservice/deviceside/devicetest/res/layout/activity_inputmethod_test.xml
index f0efb94..94ba557 100644
--- a/hostsidetests/inputmethodservice/deviceside/devicetest/res/layout/activity_inputmethod_test.xml
+++ b/hostsidetests/inputmethodservice/deviceside/devicetest/res/layout/activity_inputmethod_test.xml
@@ -25,14 +25,5 @@
         android:layout_width="fill_parent"
         android:layout_height="wrap_content"
         android:background="@android:drawable/editbox_background"/>
-    <SearchView
-        android:id="@+id/search_view"
-        android:layout_below="@id/text_entry"
-        android:layout_width="match_parent"
-        android:layout_height="wrap_content"
-        android:iconifiedByDefault="false"
-        android:queryHint="hint"
-        android:inputType="textCapCharacters"
-        android:imeOptions="actionDone" />
 
 </RelativeLayout>
diff --git a/hostsidetests/inputmethodservice/deviceside/devicetest/src/android/inputmethodservice/cts/devicetest/InputMethodServiceDeviceTest.java b/hostsidetests/inputmethodservice/deviceside/devicetest/src/android/inputmethodservice/cts/devicetest/InputMethodServiceDeviceTest.java
index e5613ff..b1f16e5 100644
--- a/hostsidetests/inputmethodservice/deviceside/devicetest/src/android/inputmethodservice/cts/devicetest/InputMethodServiceDeviceTest.java
+++ b/hostsidetests/inputmethodservice/deviceside/devicetest/src/android/inputmethodservice/cts/devicetest/InputMethodServiceDeviceTest.java
@@ -16,19 +16,12 @@
 
 package android.inputmethodservice.cts.devicetest;
 
-import static org.junit.Assert.assertEquals;
-import static org.junit.Assert.assertFalse;
-import static org.junit.Assert.assertTrue;
-
 import static android.inputmethodservice.cts.DeviceEvent.isFrom;
 import static android.inputmethodservice.cts.DeviceEvent.isNewerThan;
 import static android.inputmethodservice.cts.DeviceEvent.isType;
-import static android.inputmethodservice.cts.common.DeviceEventConstants.DeviceEventType.HIDE_SOFT_INPUT;
 import static android.inputmethodservice.cts.common.DeviceEventConstants.DeviceEventType.ON_CREATE;
 import static android.inputmethodservice.cts.common.DeviceEventConstants.DeviceEventType.ON_DESTROY;
-import static android.inputmethodservice.cts.common.DeviceEventConstants.DeviceEventType.ON_FINISH_INPUT;
 import static android.inputmethodservice.cts.common.DeviceEventConstants.DeviceEventType.ON_START_INPUT;
-import static android.inputmethodservice.cts.common.DeviceEventConstants.DeviceEventType.SHOW_SOFT_INPUT;
 import static android.inputmethodservice.cts.common.ImeCommandConstants.ACTION_IME_COMMAND;
 import static android.inputmethodservice.cts.common.ImeCommandConstants.COMMAND_SWITCH_INPUT_METHOD;
 import static android.inputmethodservice.cts.common.ImeCommandConstants.EXTRA_ARG_STRING1;
@@ -36,29 +29,24 @@
 import static android.inputmethodservice.cts.devicetest.BusyWaitUtils.pollingCheck;
 import static android.inputmethodservice.cts.devicetest.MoreCollectors.startingFrom;
 
-import android.app.Activity;
 import android.inputmethodservice.cts.DeviceEvent;
 import android.inputmethodservice.cts.common.DeviceEventConstants.DeviceEventType;
-import android.inputmethodservice.cts.common.DeviceEventConstants.DeviceEventTypeParam;
 import android.inputmethodservice.cts.common.Ime1Constants;
 import android.inputmethodservice.cts.common.Ime2Constants;
 import android.inputmethodservice.cts.common.test.DeviceTestConstants;
 import android.inputmethodservice.cts.common.test.ShellCommandUtils;
 import android.inputmethodservice.cts.devicetest.SequenceMatcher.MatchResult;
 import android.os.SystemClock;
-import android.widget.SearchView;
 import android.support.test.runner.AndroidJUnit4;
 
 import org.junit.Test;
 import org.junit.runner.RunWith;
 
 import java.util.Arrays;
-import java.util.List;
 import java.util.concurrent.TimeUnit;
 import java.util.function.IntFunction;
 import java.util.function.Predicate;
 import java.util.stream.Collector;
-import java.util.stream.Collectors;
 
 @RunWith(AndroidJUnit4.class)
 public class InputMethodServiceDeviceTest {
@@ -72,8 +60,7 @@
 
         pollingCheck(() -> helper.queryAllEvents()
                         .collect(startingFrom(helper.isStartOfTest()))
-                        .filter(isFrom(Ime1Constants.CLASS).and(isType(ON_CREATE)))
-                        .findAny().isPresent(),
+                        .anyMatch(isFrom(Ime1Constants.CLASS).and(isType(ON_CREATE))),
                 TIMEOUT, "CtsInputMethod1.onCreate is called");
 
         final long startActivityTime = SystemClock.uptimeMillis();
@@ -81,8 +68,7 @@
 
         pollingCheck(() -> helper.queryAllEvents()
                         .filter(isNewerThan(startActivityTime))
-                        .filter(isFrom(Ime1Constants.CLASS).and(isType(ON_START_INPUT)))
-                        .findAny().isPresent(),
+                        .anyMatch(isFrom(Ime1Constants.CLASS).and(isType(ON_START_INPUT))),
                 TIMEOUT, "CtsInputMethod1.onStartInput is called");
     }
 
@@ -94,8 +80,7 @@
 
         pollingCheck(() -> helper.queryAllEvents()
                         .collect(startingFrom(helper.isStartOfTest()))
-                        .filter(isFrom(Ime1Constants.CLASS).and(isType(ON_CREATE)))
-                        .findAny().isPresent(),
+                        .anyMatch(isFrom(Ime1Constants.CLASS).and(isType(ON_CREATE))),
                 TIMEOUT, "CtsInputMethod1.onCreate is called");
 
         final long startActivityTime = SystemClock.uptimeMillis();
@@ -103,8 +88,7 @@
 
         pollingCheck(() -> helper.queryAllEvents()
                         .filter(isNewerThan(startActivityTime))
-                        .filter(isFrom(Ime1Constants.CLASS).and(isType(ON_START_INPUT)))
-                        .findAny().isPresent(),
+                        .anyMatch(isFrom(Ime1Constants.CLASS).and(isType(ON_START_INPUT))),
                 TIMEOUT, "CtsInputMethod1.onStartInput is called");
 
         helper.findUiObject(R.id.text_entry).click();
@@ -121,8 +105,7 @@
                 TIMEOUT, "CtsInputMethod2 is current IME");
         pollingCheck(() -> helper.queryAllEvents()
                         .filter(isNewerThan(switchImeTime))
-                        .filter(isFrom(Ime1Constants.CLASS).and(isType(ON_DESTROY)))
-                        .findAny().isPresent(),
+                        .anyMatch(isFrom(Ime1Constants.CLASS).and(isType(ON_DESTROY))),
                 TIMEOUT, "CtsInputMethod1.onDestroy is called");
         pollingCheck(() -> helper.queryAllEvents()
                         .filter(isNewerThan(switchImeTime))
@@ -133,97 +116,6 @@
                 "CtsInputMethod2.onCreate and onStartInput are called in sequence");
     }
 
-    /** Test to check CtsInputMethod1 isn't current IME. */
-    @Test
-    public void testIme1IsNotCurrentIme() throws Throwable {
-        final TestHelper helper =
-                new TestHelper(getClass(), DeviceTestConstants.TEST_IME1_IS_NOT_CURRENT_IME);
-
-        helper.launchActivity(DeviceTestConstants.PACKAGE, DeviceTestConstants.TEST_ACTIVITY_CLASS);
-        helper.findUiObject(R.id.text_entry).click();
-
-        pollingCheck(() -> !helper.shell(ShellCommandUtils.getCurrentIme())
-                        .equals(Ime1Constants.IME_ID),
-                TIMEOUT,
-                "CtsInputMethod1 is uninstalled or disabled, and current IME becomes other IME");
-    }
-
-    @Test
-    public void testSearchView_giveFocusShowIme1() throws Throwable {
-        final TestHelper helper = new TestHelper(
-                getClass(), DeviceTestConstants.TEST_SEARCH_VIEW_GIVE_FOCUS_SHOW_IME1);
-
-        helper.launchActivity(DeviceTestConstants.PACKAGE, DeviceTestConstants.TEST_ACTIVITY_CLASS);
-        helper.findUiObject(R.id.search_view).click();
-        pollingCheck(() -> helper.queryAllEvents()
-                        .collect(startingFrom(helper.isStartOfTest()))
-                        .filter(isFrom(Ime1Constants.CLASS).and(isType(SHOW_SOFT_INPUT)))
-                        .findAny().isPresent(),
-                TIMEOUT, "CtsInputMethod1.showSoftInput is called");
-        pollingCheck(() -> helper.queryAllEvents()
-                        .collect(startingFrom(helper.isStartOfTest()))
-                        .filter(isFrom(Ime1Constants.CLASS).and(isType(ON_START_INPUT)))
-                        .findAny().isPresent(),
-                TIMEOUT, "CtsInputMethod1.onStartInput is called");
-    }
-
-    @Test
-    public void testSearchView_setQueryHideIme1() throws Throwable {
-        final TestHelper helper = new TestHelper(
-                getClass(), DeviceTestConstants.TEST_SEARCH_VIEW_SET_QUERY_HIDE_IME1);
-
-        final Activity activity = helper.launchActivitySync(
-                DeviceTestConstants.PACKAGE, DeviceTestConstants.TEST_ACTIVITY_CLASS);
-        final SearchView searchView = (SearchView) activity.findViewById(R.id.search_view);
-        helper.findUiObject(R.id.search_view).click();
-        // test SearchView.onSubmitQuery() closes IME. Alternatively, onCloseClicked() closes IME.
-        // submits the query, should dismiss the inputMethod.
-        activity.runOnUiThread(() -> searchView.setQuery("test", true /* submit */));
-
-        pollingCheck(() -> helper.queryAllEvents()
-                        .collect(startingFrom(helper.isStartOfTest()))
-                        .filter(isFrom(Ime1Constants.CLASS).and(isType(ON_FINISH_INPUT)))
-                        .findAny().isPresent(),
-                TIMEOUT, "CtsInputMethod1.onFinishInput is called");
-        pollingCheck(() -> helper.queryAllEvents()
-                        .collect(startingFrom(helper.isStartOfTest()))
-                        .filter(isFrom(Ime1Constants.CLASS).and(isType(HIDE_SOFT_INPUT)))
-                        .findAny().isPresent(),
-                TIMEOUT, "CtsInputMethod1.hideSoftInput is called");
-    }
-
-    @Test
-    public void testOnStartInputCalledOnceIme1() throws Exception {
-        final TestHelper helper = new TestHelper(
-                getClass(), DeviceTestConstants.TEST_ON_START_INPUT_CALLED_ONCE_IME1);
-
-        helper.launchActivity(DeviceTestConstants.PACKAGE, DeviceTestConstants.TEST_ACTIVITY_CLASS);
-        helper.findUiObject(R.id.text_entry).click();
-
-        // we should've only one onStartInput call.
-        pollingCheck(() -> helper.queryAllEvents()
-                        .collect(startingFrom(helper.isStartOfTest()))
-                        .filter(isFrom(Ime1Constants.CLASS).and(isType(ON_START_INPUT)))
-                        .findAny()
-                        .isPresent(),
-                TIMEOUT, "CtsInputMethod1.onStartInput is called");
-        List<DeviceEvent> startInputEvents = helper.queryAllEvents()
-                .collect(startingFrom(helper.isStartOfTest()))
-                .filter(isFrom(Ime1Constants.CLASS).and(isType(ON_START_INPUT)))
-                .collect(Collectors.toList());
-
-        assertEquals("CtsInputMethod1.onStartInput is called exactly once",
-                startInputEvents.size(),
-                1);
-
-        // check if that single event didn't cause IME restart.
-        final DeviceEvent event = startInputEvents.get(0);
-        Boolean isRestarting = DeviceEvent.getEventParamBoolean(
-                        DeviceEventTypeParam.ON_START_INPUT_RESTARTING, event);
-        assertTrue(isRestarting != null);
-        assertFalse(isRestarting);
-    }
-
     /**
      * Build stream collector of {@link DeviceEvent} collecting sequence that elements have
      * specified types.
diff --git a/hostsidetests/inputmethodservice/deviceside/devicetest/src/android/inputmethodservice/cts/devicetest/MoreCollectors.java b/hostsidetests/inputmethodservice/deviceside/devicetest/src/android/inputmethodservice/cts/devicetest/MoreCollectors.java
index a935f0b..59692a6 100644
--- a/hostsidetests/inputmethodservice/deviceside/devicetest/src/android/inputmethodservice/cts/devicetest/MoreCollectors.java
+++ b/hostsidetests/inputmethodservice/deviceside/devicetest/src/android/inputmethodservice/cts/devicetest/MoreCollectors.java
@@ -1,3 +1,19 @@
+/*
+ * 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.inputmethodservice.cts.devicetest;
 
 import java.util.function.BiConsumer;
diff --git a/hostsidetests/inputmethodservice/deviceside/devicetest/src/android/inputmethodservice/cts/devicetest/SequenceMatcher.java b/hostsidetests/inputmethodservice/deviceside/devicetest/src/android/inputmethodservice/cts/devicetest/SequenceMatcher.java
index 36b711e..996dad9 100644
--- a/hostsidetests/inputmethodservice/deviceside/devicetest/src/android/inputmethodservice/cts/devicetest/SequenceMatcher.java
+++ b/hostsidetests/inputmethodservice/deviceside/devicetest/src/android/inputmethodservice/cts/devicetest/SequenceMatcher.java
@@ -1,3 +1,19 @@
+/*
+ * 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.inputmethodservice.cts.devicetest;
 
 import java.util.ArrayList;
diff --git a/hostsidetests/inputmethodservice/deviceside/devicetest/src/android/inputmethodservice/cts/devicetest/TestHelper.java b/hostsidetests/inputmethodservice/deviceside/devicetest/src/android/inputmethodservice/cts/devicetest/TestHelper.java
index 7f6ba2e..a4b1aae 100644
--- a/hostsidetests/inputmethodservice/deviceside/devicetest/src/android/inputmethodservice/cts/devicetest/TestHelper.java
+++ b/hostsidetests/inputmethodservice/deviceside/devicetest/src/android/inputmethodservice/cts/devicetest/TestHelper.java
@@ -20,12 +20,9 @@
 import static android.inputmethodservice.cts.DeviceEvent.isType;
 import static android.inputmethodservice.cts.common.DeviceEventConstants.DeviceEventType.TEST_START;
 
-import android.app.Instrumentation;
-import android.app.Activity;
 import android.content.ContentResolver;
 import android.content.Context;
 import android.content.Intent;
-import android.content.res.Resources;
 import android.database.Cursor;
 import android.inputmethodservice.cts.DeviceEvent;
 import android.inputmethodservice.cts.common.DeviceEventConstants.DeviceEventType;
@@ -57,7 +54,6 @@
     private final ContentResolver mResolver;
     private final Context mTargetContext;
     private final UiDevice mUiDevice;
-    private final Instrumentation mInstrumentation;
 
     /**
      * Construct a helper object of specified test method.
@@ -70,8 +66,7 @@
         mTestInfo = new TestInfo(testContext.getPackageName(), testClass.getName(), testMethod);
         mResolver = testContext.getContentResolver();
         mTargetContext = InstrumentationRegistry.getTargetContext();
-        mInstrumentation = InstrumentationRegistry.getInstrumentation();
-        mUiDevice = UiDevice.getInstance(mInstrumentation);
+        mUiDevice = UiDevice.getInstance(InstrumentationRegistry.getInstrumentation());
     }
 
     /**
@@ -110,22 +105,6 @@
     }
 
     /**
-     * Launch test activity synchronously.
-     *
-     * @param packageName activity's app package name.
-     * @param className   activity's class name.
-     * @return instance of Activity
-     */
-    Activity launchActivitySync(final String packageName, final String className) {
-        final Intent intent = new Intent()
-                .setAction(Intent.ACTION_MAIN)
-                .setClassName(packageName, className)
-                .addFlags(Intent.FLAG_ACTIVITY_NEW_TASK)
-                .addFlags(Intent.FLAG_ACTIVITY_CLEAR_TASK);
-        return mInstrumentation.startActivitySync(intent);
-    }
-
-    /**
      * Return all device events as {@link Stream}
      * @return {@link Stream<DeviceEvent>} of all device events.
      */
diff --git a/hostsidetests/inputmethodservice/deviceside/lib/Android.mk b/hostsidetests/inputmethodservice/deviceside/lib/Android.mk
index 9fdcec6..f30a85b 100644
--- a/hostsidetests/inputmethodservice/deviceside/lib/Android.mk
+++ b/hostsidetests/inputmethodservice/deviceside/lib/Android.mk
@@ -19,7 +19,6 @@
 LOCAL_SRC_FILES := $(call all-java-files-under, src)
 LOCAL_STATIC_JAVA_LIBRARIES := \
     android-support-annotations \
-    json \
     CtsInputMethodServiceCommon
 
 LOCAL_MODULE_TAGS := tests
diff --git a/hostsidetests/inputmethodservice/deviceside/lib/src/android/inputmethodservice/cts/DeviceEvent.java b/hostsidetests/inputmethodservice/deviceside/lib/src/android/inputmethodservice/cts/DeviceEvent.java
index ff412ef..b6098f9 100644
--- a/hostsidetests/inputmethodservice/deviceside/lib/src/android/inputmethodservice/cts/DeviceEvent.java
+++ b/hostsidetests/inputmethodservice/deviceside/lib/src/android/inputmethodservice/cts/DeviceEvent.java
@@ -17,20 +17,17 @@
 package android.inputmethodservice.cts;
 
 import static android.inputmethodservice.cts.common.DeviceEventConstants.ACTION_DEVICE_EVENT;
-import static android.inputmethodservice.cts.common.DeviceEventConstants.EXTRA_EVENT_PARAMS;
 import static android.inputmethodservice.cts.common.DeviceEventConstants.EXTRA_EVENT_TIME;
 import static android.inputmethodservice.cts.common.DeviceEventConstants.EXTRA_EVENT_TYPE;
 import static android.inputmethodservice.cts.common.DeviceEventConstants.EXTRA_EVENT_SENDER;
 import static android.inputmethodservice.cts.common.DeviceEventConstants.RECEIVER_CLASS;
 import static android.inputmethodservice.cts.common.DeviceEventConstants.RECEIVER_PACKAGE;
 
-
 import android.content.ContentValues;
 import android.content.Intent;
 import android.database.Cursor;
 import android.inputmethodservice.cts.common.DeviceEventConstants;
 import android.inputmethodservice.cts.common.DeviceEventConstants.DeviceEventType;
-import android.inputmethodservice.cts.common.DeviceEventConstants.DeviceEventTypeParam;
 import android.inputmethodservice.cts.common.EventProviderConstants.EventTableConstants;
 import android.inputmethodservice.cts.common.test.TestInfo;
 import android.inputmethodservice.cts.db.Entity;
@@ -38,16 +35,8 @@
 import android.inputmethodservice.cts.db.Table;
 import android.os.SystemClock;
 import android.support.annotation.NonNull;
-import android.support.annotation.Nullable;
-import android.text.TextUtils;
 import android.util.Log;
 
-import com.android.json.stream.JsonReader;
-import com.android.json.stream.JsonWriter;
-
-import java.io.IOException;
-import java.io.StringReader;
-import java.io.StringWriter;
 import java.util.function.Predicate;
 import java.util.stream.Stream;
 
@@ -62,85 +51,21 @@
 
     public static final Table<DeviceEvent> TABLE = new DeviceEventTable(EventTableConstants.NAME);
 
-    public static IntentBuilder builder() {
-        return new IntentBuilder();
-    }
-
     /**
-     * Builder to create an intent to send a device event.
-     * The built intent that has event {@code sender}, {@code type}, {@code paramsString}, time from
-     * {@link SystemClock#uptimeMillis()}, and target component of event receiver.
-     *
+     * Create an intent to send a device event.
+     * @param sender an event sender.
+     * @param type an event type defined at {@link DeviceEventType}.
+     * @return an intent that has event {@code sender}, {@code type}, time from
+     *         {@link SystemClock#uptimeMillis()}, and target component of event receiver.
      */
-    public static final class IntentBuilder {
-        String mSender;
-        DeviceEventType mType;
-        JsonWriter mJsonWriter;
-        StringWriter mStringWriter;
-
-        /**
-         * @param type an event type defined at {@link DeviceEventType}.
-         */
-        public IntentBuilder setType(DeviceEventType type) {
-            mType = type;
-            return this;
-        }
-
-        /**
-         * @param sender an event sender.
-         */
-        public void setSender(String sender) {
-            mSender = sender;
-        }
-
-        public IntentBuilder with(DeviceEventTypeParam eventParam, boolean value) {
-            appendToJson(eventParam, value);
-            return this;
-        }
-
-        public Intent build() {
-            Intent intent = new Intent()
-                    .setAction(ACTION_DEVICE_EVENT)
-                    .setClassName(RECEIVER_PACKAGE, RECEIVER_CLASS)
-                    .putExtra(EXTRA_EVENT_SENDER, mSender)
-                    .putExtra(EXTRA_EVENT_TYPE, mType.name())
-                    .putExtra(EXTRA_EVENT_PARAMS, getJsonString())
-                    .putExtra(EXTRA_EVENT_TIME, SystemClock.uptimeMillis());
-
-            mJsonWriter = null;
-            mStringWriter = null;
-            return intent;
-        }
-
-        private String getJsonString() {
-            if (mJsonWriter == null) {
-                return "";
-            }
-            try {
-                mJsonWriter.endObject();
-                mJsonWriter.flush();
-            } catch (IOException e) {
-                throw new RuntimeException("IntentBuilder.getJsonString() failed.", e);
-            }
-            return mStringWriter.toString();
-        }
-
-        private void appendToJson(DeviceEventTypeParam eventParam, boolean value) {
-            final String key = eventParam.getName();
-            if (TextUtils.isEmpty(key)) {
-                return;
-            }
-            try {
-                if (mJsonWriter == null) {
-                    mStringWriter = new StringWriter();
-                    mJsonWriter = new JsonWriter(mStringWriter);
-                    mJsonWriter.beginObject();
-                }
-                mJsonWriter.name(key).value(value);
-            } catch (IOException e) {
-                throw new RuntimeException("IntentBuilder.appendToJson() failed.", e);
-            }
-        }
+    public static Intent newDeviceEventIntent(@NonNull final String sender,
+            @NonNull final DeviceEventType type) {
+        return new Intent()
+                .setAction(ACTION_DEVICE_EVENT)
+                .setClassName(RECEIVER_PACKAGE, RECEIVER_CLASS)
+                .putExtra(EXTRA_EVENT_SENDER, sender)
+                .putExtra(EXTRA_EVENT_TYPE, type.name())
+                .putExtra(EXTRA_EVENT_TIME, SystemClock.uptimeMillis());
     }
 
     /**
@@ -168,16 +93,7 @@
                     "Intent must have " + EXTRA_EVENT_TIME + ": " + intent);
         }
 
-        String paramsString = intent.getStringExtra(DeviceEventConstants.EXTRA_EVENT_PARAMS);
-        if (paramsString == null) {
-            paramsString = "";
-        }
-
-        return new DeviceEvent(
-                sender,
-                type,
-                paramsString,
-                intent.getLongExtra(EXTRA_EVENT_TIME, 0L));
+        return new DeviceEvent(sender, type, intent.getLongExtra(EXTRA_EVENT_TIME, 0L));
     }
 
     /**
@@ -242,21 +158,13 @@
     public final DeviceEventType type;
 
     /**
-     * Event parameters formatted as json string.
-     * e.g. {@link DeviceEventTypeParam#ON_START_INPUT_RESTARTING}
-     */
-    public final String paramsString;
-
-    /**
      * Event time, value is from {@link SystemClock#uptimeMillis()}.
      */
     public final long time;
 
-    private DeviceEvent(
-            final String sender, final DeviceEventType type, String paramsString, final long time) {
+    private DeviceEvent(final String sender, final DeviceEventType type, final long time) {
         this.sender = sender;
         this.type = type;
-        this.paramsString = paramsString;
         this.time = time;
     }
 
@@ -266,37 +174,6 @@
     }
 
     /**
-     * @param eventParam {@link DeviceEventTypeParam} to look for.
-     * @param event {@link DeviceEvent} to look in.
-     * @return Event parameter for provided key. If key is not found in
-     * {@link DeviceEvent#paramsString}, null is returned.
-     *
-     * TODO: Support other primitive and custom types.
-     */
-    @Nullable
-    public static Boolean getEventParamBoolean(
-            DeviceEventTypeParam eventParam, final DeviceEvent event) {
-        StringReader stringReader = new StringReader(event.paramsString);
-        JsonReader reader = new JsonReader(stringReader);
-
-        try {
-            reader.beginObject();
-            while (reader.hasNext()) {
-                String name = reader.nextName();
-                if (name.equals(eventParam.getName())) {
-                    Boolean value = reader.nextBoolean();
-                    reader.endObject();
-                    return value;
-                }
-            }
-            reader.endObject();
-        } catch (IOException e) {
-            throw new RuntimeException("DeviceEvent.getEventParamBoolean() failed.", e);
-        }
-        return null;
-    }
-
-    /**
      * Abstraction of device event table in database.
      */
     private static final class DeviceEventTable extends Table<DeviceEvent> {
@@ -306,19 +183,16 @@
         private final Field SENDER;
         private final Field TYPE;
         private final Field TIME;
-        private final Field PARAMS;
 
         private DeviceEventTable(final String name) {
             super(name, new Entity.Builder<DeviceEvent>()
                     .addField(EventTableConstants.SENDER, Cursor.FIELD_TYPE_STRING)
                     .addField(EventTableConstants.TYPE, Cursor.FIELD_TYPE_STRING)
                     .addField(EventTableConstants.TIME, Cursor.FIELD_TYPE_INTEGER)
-                    .addField(EventTableConstants.EXTRAS, Cursor.FIELD_TYPE_STRING)
                     .build());
             SENDER = getField(EventTableConstants.SENDER);
             TYPE = getField(EventTableConstants.TYPE);
             TIME = getField(EventTableConstants.TIME);
-            PARAMS = getField(EventTableConstants.EXTRAS);
         }
 
         @Override
@@ -326,7 +200,6 @@
             final ContentValues values = new ContentValues();
             SENDER.putString(values, event.sender);
             TYPE.putString(values, event.type.name());
-            PARAMS.putString(values, event.paramsString);
             TIME.putLong(values, event.time);
             return values;
         }
@@ -341,7 +214,6 @@
                 final DeviceEvent event = new DeviceEvent(
                         SENDER.getString(cursor),
                         DeviceEventType.valueOf(TYPE.getString(cursor)),
-                        PARAMS.getString(cursor),
                         TIME.getLong(cursor));
                 builder.accept(event);
                 if (DEBUG_STREAM) {
diff --git a/hostsidetests/inputmethodservice/deviceside/lib/src/android/inputmethodservice/cts/ime/CtsBaseInputMethod.java b/hostsidetests/inputmethodservice/deviceside/lib/src/android/inputmethodservice/cts/ime/CtsBaseInputMethod.java
index b37873c..6ae3568 100644
--- a/hostsidetests/inputmethodservice/deviceside/lib/src/android/inputmethodservice/cts/ime/CtsBaseInputMethod.java
+++ b/hostsidetests/inputmethodservice/deviceside/lib/src/android/inputmethodservice/cts/ime/CtsBaseInputMethod.java
@@ -16,21 +16,18 @@
 
 package android.inputmethodservice.cts.ime;
 
-import static android.inputmethodservice.cts.common.DeviceEventConstants.DeviceEventType.HIDE_SOFT_INPUT;
 import static android.inputmethodservice.cts.common.DeviceEventConstants.DeviceEventType.ON_CREATE;
 import static android.inputmethodservice.cts.common.DeviceEventConstants.DeviceEventType.ON_DESTROY;
 import static android.inputmethodservice.cts.common.DeviceEventConstants.DeviceEventType.ON_FINISH_INPUT;
 import static android.inputmethodservice.cts.common.DeviceEventConstants.DeviceEventType.ON_FINISH_INPUT_VIEW;
 import static android.inputmethodservice.cts.common.DeviceEventConstants.DeviceEventType.ON_START_INPUT;
 import static android.inputmethodservice.cts.common.DeviceEventConstants.DeviceEventType.ON_START_INPUT_VIEW;
-import static android.inputmethodservice.cts.common.DeviceEventConstants.DeviceEventType.SHOW_SOFT_INPUT;
 
+import android.content.Intent;
 import android.inputmethodservice.InputMethodService;
 import android.inputmethodservice.cts.DeviceEvent;
-import android.inputmethodservice.cts.common.DeviceEventConstants.DeviceEventTypeParam;
+import android.inputmethodservice.cts.common.DeviceEventConstants.DeviceEventType;
 import android.inputmethodservice.cts.ime.ImeCommandReceiver.ImeCommandCallbacks;
-
-import android.os.ResultReceiver;
 import android.util.Log;
 import android.view.inputmethod.EditorInfo;
 import android.view.inputmethod.InputConnection;
@@ -45,33 +42,13 @@
             new ImeCommandReceiver<>();
     private String mLogTag;
 
-    private class CtsInputMethodImpl extends InputMethodImpl {
-        @Override
-        public void showSoftInput(int flags, ResultReceiver resultReceiver) {
-            sendEvent(DeviceEvent.builder().setType(SHOW_SOFT_INPUT));
-            if (DEBUG) {
-                Log.d(mLogTag, "showSoftInput called");
-            }
-            super.showSoftInput(flags, resultReceiver);
-        }
-
-        @Override
-        public void hideSoftInput(int flags, ResultReceiver resultReceiver) {
-            sendEvent(DeviceEvent.builder().setType(HIDE_SOFT_INPUT));
-            if (DEBUG) {
-                Log.d(mLogTag, "hideSoftInput called");
-            }
-            super.hideSoftInput(flags, resultReceiver);
-        }
-    }
-
     @Override
     public void onCreate() {
         mLogTag = getClass().getSimpleName();
         if (DEBUG) {
             Log.d(mLogTag, "onCreate:");
         }
-        sendEvent(DeviceEvent.builder().setType(ON_CREATE));
+        sendEvent(ON_CREATE);
 
         super.onCreate();
 
@@ -85,10 +62,8 @@
                     + " editorInfo=" + editorInfo
                     + " restarting=" + restarting);
         }
+        sendEvent(ON_START_INPUT, editorInfo, restarting);
 
-        sendEvent(DeviceEvent.builder()
-                .setType(ON_START_INPUT)
-                .with(DeviceEventTypeParam.ON_START_INPUT_RESTARTING, restarting));
         super.onStartInput(editorInfo, restarting);
     }
 
@@ -99,8 +74,7 @@
                     + " editorInfo=" + editorInfo
                     + " restarting=" + restarting);
         }
-
-        sendEvent(DeviceEvent.builder().setType(ON_START_INPUT_VIEW));
+        sendEvent(ON_START_INPUT_VIEW, editorInfo, restarting);
 
         super.onStartInputView(editorInfo, restarting);
     }
@@ -110,7 +84,7 @@
         if (DEBUG) {
             Log.d(mLogTag, "onFinishInputView: finishingInput=" + finishingInput);
         }
-        sendEvent(DeviceEvent.builder().setType(ON_FINISH_INPUT_VIEW));
+        sendEvent(ON_FINISH_INPUT_VIEW, finishingInput);
 
         super.onFinishInputView(finishingInput);
     }
@@ -120,7 +94,7 @@
         if (DEBUG) {
             Log.d(mLogTag, "onFinishInput:");
         }
-        sendEvent(DeviceEvent.builder().setType(ON_FINISH_INPUT));
+        sendEvent(ON_FINISH_INPUT);
 
         super.onFinishInput();
     }
@@ -130,23 +104,13 @@
         if (DEBUG) {
             Log.d(mLogTag, "onDestroy:");
         }
-        sendEvent(DeviceEvent.builder().setType(ON_DESTROY));
+        sendEvent(ON_DESTROY);
 
         super.onDestroy();
 
         unregisterReceiver(mImeCommandReceiver);
     }
 
-    @Override
-    public AbstractInputMethodImpl onCreateInputMethodInterface() {
-        final CtsInputMethodImpl inputMethod = new CtsInputMethodImpl();
-        if (DEBUG) {
-            Log.d(mLogTag, "onCreateInputMethodInterface");
-        }
-
-        return inputMethod;
-    }
-
     //
     // Implementations of {@link ImeCommandCallbacks}.
     //
@@ -178,8 +142,10 @@
         }
     }
 
-   private void sendEvent(final DeviceEvent.IntentBuilder intentBuilder) {
-        intentBuilder.setSender(getClass().getName());
-        sendBroadcast(intentBuilder.build());
+    private void sendEvent(final DeviceEventType type, final Object... args) {
+        final String sender = getClass().getName();
+        final Intent intent = DeviceEvent.newDeviceEventIntent(sender, type);
+        // TODO: Send arbitrary {@code args} in {@code intent}.
+        sendBroadcast(intent);
     }
 }
diff --git a/hostsidetests/inputmethodservice/hostside/AndroidTest.xml b/hostsidetests/inputmethodservice/hostside/AndroidTest.xml
index bf7e9a7..0b9dba6 100644
--- a/hostsidetests/inputmethodservice/hostside/AndroidTest.xml
+++ b/hostsidetests/inputmethodservice/hostside/AndroidTest.xml
@@ -16,6 +16,7 @@
 -->
 
 <configuration description="Config for CTS Input Method Service host test cases">
+    <option name="test-suite-tag" value="cts" />
     <option name="config-descriptor:metadata" key="component" value="framework" />
     <target_preparer class="com.android.tradefed.targetprep.RunCommandTargetPreparer">
         <option name="run-command" value="input keyevent KEYCODE_WAKEUP" />
diff --git a/hostsidetests/inputmethodservice/hostside/src/android/inputmethodservice/cts/hostside/InputMethodServiceLifecycleTest.java b/hostsidetests/inputmethodservice/hostside/src/android/inputmethodservice/cts/hostside/InputMethodServiceLifecycleTest.java
index 02c3e55..bdf0983 100644
--- a/hostsidetests/inputmethodservice/hostside/src/android/inputmethodservice/cts/hostside/InputMethodServiceLifecycleTest.java
+++ b/hostsidetests/inputmethodservice/hostside/src/android/inputmethodservice/cts/hostside/InputMethodServiceLifecycleTest.java
@@ -22,7 +22,6 @@
 import static android.inputmethodservice.cts.common.DeviceEventConstants.EXTRA_EVENT_TYPE;
 import static android.inputmethodservice.cts.common.DeviceEventConstants.RECEIVER_COMPONENT;
 
-import static org.junit.Assert.assertEquals;
 import static org.junit.Assert.assertTrue;
 import static org.junit.Assume.assumeTrue;
 
@@ -33,33 +32,34 @@
 import android.inputmethodservice.cts.common.test.ShellCommandUtils;
 import android.inputmethodservice.cts.common.test.TestInfo;
 
-import com.android.compatibility.common.tradefed.testtype.CompatibilityHostTestBase;
 import com.android.tradefed.testtype.DeviceJUnit4ClassRunner;
+import com.android.tradefed.testtype.junit4.BaseHostJUnit4Test;
 
 import org.junit.After;
 import org.junit.Before;
 import org.junit.Test;
 import org.junit.runner.RunWith;
 
-@RunWith(DeviceJUnit4ClassRunner.class)
-public class InputMethodServiceLifecycleTest extends CompatibilityHostTestBase {
+import java.util.concurrent.TimeUnit;
+import java.util.concurrent.TimeoutException;
 
-    private String mDefaultImeId;
+@RunWith(DeviceJUnit4ClassRunner.class)
+public class InputMethodServiceLifecycleTest extends BaseHostJUnit4Test {
+
+    private static final long TIMEOUT = TimeUnit.MICROSECONDS.toMillis(20000);
+    private static final long POLLING_INTERVAL = TimeUnit.MICROSECONDS.toMillis(200);
 
     @Before
     public void setUp() throws Exception {
         // Skip whole tests when DUT has no android.software.input_methods feature.
-        assumeTrue(Boolean.parseBoolean(shell(
-                ShellCommandUtils.hasFeature(ShellCommandUtils.FEATURE_INPUT_METHODS))));
-        mDefaultImeId = shell(ShellCommandUtils.getCurrentIme());
+        assumeTrue(hasDeviceFeature(ShellCommandUtils.FEATURE_INPUT_METHODS));
         cleanUpTestImes();
         shell(ShellCommandUtils.deleteContent(EventTableConstants.CONTENT_URI));
     }
 
     @After
     public void tearDown() throws Exception {
-        shell(ShellCommandUtils.setCurrentIme(mDefaultImeId));
-        cleanUpTestImes();
+        shell(ShellCommandUtils.resetImes());
     }
 
     @Test
@@ -78,69 +78,30 @@
 
     @Test
     public void testUninstallCurrentIme() throws Exception {
-        installAndSetIme1();
-
-        final TestInfo testIme1IsNotCurrentIme = new TestInfo(DeviceTestConstants.PACKAGE,
-                DeviceTestConstants.TEST_CLASS, DeviceTestConstants.TEST_IME1_IS_NOT_CURRENT_IME);
-        sendTestStartEvent(testIme1IsNotCurrentIme);
-        uninstallPackageIfExists(Ime1Constants.PACKAGE);
-        assertTrue(runDeviceTestMethod(testIme1IsNotCurrentIme));
-        assertEquals(shell(ShellCommandUtils.getCurrentIme()), mDefaultImeId);
-    }
-
-    @Test
-    public void testDisableCurrentIme() throws Exception {
-        installAndSetIme1();
-
-        final TestInfo testIme1IsNotCurrentIme = new TestInfo(DeviceTestConstants.PACKAGE,
-                DeviceTestConstants.TEST_CLASS, DeviceTestConstants.TEST_IME1_IS_NOT_CURRENT_IME);
-        sendTestStartEvent(testIme1IsNotCurrentIme);
-        shell(ShellCommandUtils.disableIme(Ime1Constants.IME_ID));
-        assertTrue(runDeviceTestMethod(testIme1IsNotCurrentIme));
-        assertEquals(shell(ShellCommandUtils.getCurrentIme()), mDefaultImeId);
-    }
-
-    @Test
-    public void testSearchView_giveFocusShowIme() throws Exception {
-        installAndSetIme1();
-
-        final TestInfo testGiveFocusShowIme1 = new TestInfo(DeviceTestConstants.PACKAGE,
-                DeviceTestConstants.TEST_CLASS,
-                DeviceTestConstants.TEST_SEARCH_VIEW_GIVE_FOCUS_SHOW_IME1);
-        sendTestStartEvent(testGiveFocusShowIme1);
-        assertTrue(runDeviceTestMethod(testGiveFocusShowIme1));
-    }
-
-    @Test
-    public void testSearchView_setQueryHideIme() throws Exception {
-        installAndSetIme1();
-
-        final TestInfo testSetQueryHideIme1 = new TestInfo(DeviceTestConstants.PACKAGE,
-                DeviceTestConstants.TEST_CLASS,
-                DeviceTestConstants.TEST_SEARCH_VIEW_SET_QUERY_HIDE_IME1);
-        sendTestStartEvent(testSetQueryHideIme1);
-        assertTrue(runDeviceTestMethod(testSetQueryHideIme1));
-    }
-
-    @Test
-    public void testOnStartInputCalledOnce() throws Exception {
-        installAndSetIme1();
-
-        final TestInfo testSetQueryHideIme1 = new TestInfo(DeviceTestConstants.PACKAGE,
-                DeviceTestConstants.TEST_CLASS,
-                DeviceTestConstants.TEST_ON_START_INPUT_CALLED_ONCE_IME1);
-        sendTestStartEvent(testSetQueryHideIme1);
-        assertTrue(runDeviceTestMethod(testSetQueryHideIme1));
-    }
-
-    private void installAndSetIme1() throws Exception {
         final TestInfo testCreateIme1 = new TestInfo(DeviceTestConstants.PACKAGE,
-            DeviceTestConstants.TEST_CLASS, DeviceTestConstants.TEST_CREATE_IME1);
+                DeviceTestConstants.TEST_CLASS, DeviceTestConstants.TEST_CREATE_IME1);
         sendTestStartEvent(testCreateIme1);
         installPackage(Ime1Constants.APK, "-r");
         shell(ShellCommandUtils.enableIme(Ime1Constants.IME_ID));
         shell(ShellCommandUtils.setCurrentIme(Ime1Constants.IME_ID));
         assertTrue(runDeviceTestMethod(testCreateIme1));
+
+        uninstallPackageIfExists(Ime1Constants.PACKAGE);
+        assertImeNotSelectedInSecureSettings(Ime1Constants.IME_ID, TIMEOUT);
+    }
+
+    @Test
+    public void testDisableCurrentIme() throws Exception {
+        final TestInfo testCreateIme1 = new TestInfo(DeviceTestConstants.PACKAGE,
+                DeviceTestConstants.TEST_CLASS, DeviceTestConstants.TEST_CREATE_IME1);
+        sendTestStartEvent(testCreateIme1);
+        installPackage(Ime1Constants.APK, "-r");
+        shell(ShellCommandUtils.enableIme(Ime1Constants.IME_ID));
+        shell(ShellCommandUtils.setCurrentIme(Ime1Constants.IME_ID));
+        assertTrue(runDeviceTestMethod(testCreateIme1));
+
+        shell(ShellCommandUtils.disableIme(Ime1Constants.IME_ID));
+        assertImeNotSelectedInSecureSettings(Ime1Constants.IME_ID, TIMEOUT);
     }
 
     private void sendTestStartEvent(final TestInfo deviceTest) throws Exception {
@@ -166,8 +127,28 @@
     }
 
     private void uninstallPackageIfExists(final String packageName) throws Exception {
-        if (isPackageInstalled(packageName)) {
-            uninstallPackage(packageName);
+        if (isPackageInstalled(getDevice(), packageName)) {
+            uninstallPackage(getDevice(), packageName);
+        }
+    }
+
+    /**
+     * Makes sure that the given IME is not in the stored in the secure settings as the current IME.
+     *
+     * @param imeId IME ID to be monitored
+     * @param timeout timeout in millisecond
+     */
+    private void assertImeNotSelectedInSecureSettings(String imeId, long timeout) throws Exception {
+        while (true) {
+            if (timeout < 0) {
+                throw new TimeoutException(imeId + " is still the current IME even after "
+                        + timeout + " msec.");
+            }
+            if (!imeId.equals(shell(ShellCommandUtils.getCurrentIme()))) {
+                break;
+            }
+            Thread.sleep(POLLING_INTERVAL);
+            timeout -= POLLING_INTERVAL;
         }
     }
 }
diff --git a/hostsidetests/jdwpsecurity/AndroidTest.xml b/hostsidetests/jdwpsecurity/AndroidTest.xml
index 4b23d19..f062508 100644
--- a/hostsidetests/jdwpsecurity/AndroidTest.xml
+++ b/hostsidetests/jdwpsecurity/AndroidTest.xml
@@ -14,6 +14,7 @@
      limitations under the License.
 -->
 <configuration description="Config for the CTS JDWP host test cases">
+    <option name="test-suite-tag" value="cts" />
     <option name="config-descriptor:metadata" key="component" value="art" />
     <test class="com.android.compatibility.common.tradefed.testtype.JarHostTest" >
         <option name="jar" value="CtsJdwpSecurityHostTestCases.jar" />
diff --git a/hostsidetests/jvmti/allocation-tracking/AndroidTest.xml b/hostsidetests/jvmti/allocation-tracking/AndroidTest.xml
index 706bdef..21a5cc7 100644
--- a/hostsidetests/jvmti/allocation-tracking/AndroidTest.xml
+++ b/hostsidetests/jvmti/allocation-tracking/AndroidTest.xml
@@ -14,6 +14,7 @@
      limitations under the License.
 -->
 <configuration description="Config for CTS JVMTI test cases">
+    <option name="test-suite-tag" value="cts" />
     <option name="config-descriptor:metadata" key="component" value="art" />
     <target_preparer class="com.android.tradefed.targetprep.suite.SuiteApkInstaller">
         <option name="cleanup-apks" value="true" />
diff --git a/hostsidetests/jvmti/attaching/host/AndroidTest.xml b/hostsidetests/jvmti/attaching/host/AndroidTest.xml
index 41c161d..710b751 100644
--- a/hostsidetests/jvmti/attaching/host/AndroidTest.xml
+++ b/hostsidetests/jvmti/attaching/host/AndroidTest.xml
@@ -14,6 +14,7 @@
      limitations under the License.
 -->
 <configuration description="Config for CTS JVMTI Attaching test cases">
+    <option name="test-suite-tag" value="cts" />
     <option name="config-descriptor:metadata" key="component" value="art" />
     <target_preparer class="com.android.tradefed.targetprep.suite.SuiteApkInstaller">
         <option name="cleanup-apks" value="true" />
diff --git a/hostsidetests/jvmti/redefining/AndroidTest.xml b/hostsidetests/jvmti/redefining/AndroidTest.xml
index b1f9b01..1d04cb9 100644
--- a/hostsidetests/jvmti/redefining/AndroidTest.xml
+++ b/hostsidetests/jvmti/redefining/AndroidTest.xml
@@ -14,6 +14,7 @@
      limitations under the License.
 -->
 <configuration description="Config for CTS JVMTI test cases">
+    <option name="test-suite-tag" value="cts" />
     <option name="config-descriptor:metadata" key="component" value="art" />
     <target_preparer class="com.android.tradefed.targetprep.suite.SuiteApkInstaller">
         <option name="cleanup-apks" value="true" />
diff --git a/hostsidetests/jvmti/run-tests/test-902/AndroidTest.xml b/hostsidetests/jvmti/run-tests/test-902/AndroidTest.xml
index 69cf790..42e8698 100644
--- a/hostsidetests/jvmti/run-tests/test-902/AndroidTest.xml
+++ b/hostsidetests/jvmti/run-tests/test-902/AndroidTest.xml
@@ -14,6 +14,7 @@
      limitations under the License.
 -->
 <configuration description="Config for CTS JVMTI test cases">
+    <option name="test-suite-tag" value="cts" />
     <option name="config-descriptor:metadata" key="component" value="art" />
     <target_preparer class="com.android.tradefed.targetprep.suite.SuiteApkInstaller">
         <option name="cleanup-apks" value="true" />
diff --git a/hostsidetests/jvmti/run-tests/test-903/AndroidTest.xml b/hostsidetests/jvmti/run-tests/test-903/AndroidTest.xml
index 3bec8b1..4b33b2d 100644
--- a/hostsidetests/jvmti/run-tests/test-903/AndroidTest.xml
+++ b/hostsidetests/jvmti/run-tests/test-903/AndroidTest.xml
@@ -14,6 +14,7 @@
      limitations under the License.
 -->
 <configuration description="Config for CTS JVMTI test cases">
+    <option name="test-suite-tag" value="cts" />
     <option name="config-descriptor:metadata" key="component" value="art" />
     <target_preparer class="com.android.tradefed.targetprep.suite.SuiteApkInstaller">
         <option name="cleanup-apks" value="true" />
diff --git a/hostsidetests/jvmti/run-tests/test-904/AndroidTest.xml b/hostsidetests/jvmti/run-tests/test-904/AndroidTest.xml
index 577c202..57f6027 100644
--- a/hostsidetests/jvmti/run-tests/test-904/AndroidTest.xml
+++ b/hostsidetests/jvmti/run-tests/test-904/AndroidTest.xml
@@ -14,6 +14,7 @@
      limitations under the License.
 -->
 <configuration description="Config for CTS JVMTI test cases">
+    <option name="test-suite-tag" value="cts" />
     <option name="config-descriptor:metadata" key="component" value="art" />
     <target_preparer class="com.android.tradefed.targetprep.suite.SuiteApkInstaller">
         <option name="cleanup-apks" value="true" />
diff --git a/hostsidetests/jvmti/run-tests/test-905/AndroidTest.xml b/hostsidetests/jvmti/run-tests/test-905/AndroidTest.xml
index 96ef377..a0e2e05 100644
--- a/hostsidetests/jvmti/run-tests/test-905/AndroidTest.xml
+++ b/hostsidetests/jvmti/run-tests/test-905/AndroidTest.xml
@@ -14,6 +14,7 @@
      limitations under the License.
 -->
 <configuration description="Config for CTS JVMTI test cases">
+    <option name="test-suite-tag" value="cts" />
     <option name="config-descriptor:metadata" key="component" value="art" />
     <target_preparer class="com.android.tradefed.targetprep.suite.SuiteApkInstaller">
         <option name="cleanup-apks" value="true" />
diff --git a/hostsidetests/jvmti/run-tests/test-906/AndroidTest.xml b/hostsidetests/jvmti/run-tests/test-906/AndroidTest.xml
index 217c127..38d2abe 100644
--- a/hostsidetests/jvmti/run-tests/test-906/AndroidTest.xml
+++ b/hostsidetests/jvmti/run-tests/test-906/AndroidTest.xml
@@ -14,6 +14,7 @@
      limitations under the License.
 -->
 <configuration description="Config for CTS JVMTI test cases">
+    <option name="test-suite-tag" value="cts" />
     <option name="config-descriptor:metadata" key="component" value="art" />
     <target_preparer class="com.android.tradefed.targetprep.suite.SuiteApkInstaller">
         <option name="cleanup-apks" value="true" />
diff --git a/hostsidetests/jvmti/run-tests/test-907/AndroidTest.xml b/hostsidetests/jvmti/run-tests/test-907/AndroidTest.xml
index f6662a9..af587f3 100644
--- a/hostsidetests/jvmti/run-tests/test-907/AndroidTest.xml
+++ b/hostsidetests/jvmti/run-tests/test-907/AndroidTest.xml
@@ -14,6 +14,7 @@
      limitations under the License.
 -->
 <configuration description="Config for CTS JVMTI test cases">
+    <option name="test-suite-tag" value="cts" />
     <option name="config-descriptor:metadata" key="component" value="art" />
     <target_preparer class="com.android.tradefed.targetprep.suite.SuiteApkInstaller">
         <option name="cleanup-apks" value="true" />
diff --git a/hostsidetests/jvmti/run-tests/test-908/AndroidTest.xml b/hostsidetests/jvmti/run-tests/test-908/AndroidTest.xml
index 94cc519..c2feb1a 100644
--- a/hostsidetests/jvmti/run-tests/test-908/AndroidTest.xml
+++ b/hostsidetests/jvmti/run-tests/test-908/AndroidTest.xml
@@ -14,6 +14,7 @@
      limitations under the License.
 -->
 <configuration description="Config for CTS JVMTI test cases">
+    <option name="test-suite-tag" value="cts" />
     <option name="config-descriptor:metadata" key="component" value="art" />
     <target_preparer class="com.android.tradefed.targetprep.suite.SuiteApkInstaller">
         <option name="cleanup-apks" value="true" />
diff --git a/hostsidetests/jvmti/run-tests/test-910/AndroidTest.xml b/hostsidetests/jvmti/run-tests/test-910/AndroidTest.xml
index efefdf6..32f135f 100644
--- a/hostsidetests/jvmti/run-tests/test-910/AndroidTest.xml
+++ b/hostsidetests/jvmti/run-tests/test-910/AndroidTest.xml
@@ -14,6 +14,7 @@
      limitations under the License.
 -->
 <configuration description="Config for CTS JVMTI test cases">
+    <option name="test-suite-tag" value="cts" />
     <option name="config-descriptor:metadata" key="component" value="art" />
     <target_preparer class="com.android.tradefed.targetprep.suite.SuiteApkInstaller">
         <option name="cleanup-apks" value="true" />
diff --git a/hostsidetests/jvmti/run-tests/test-911/AndroidTest.xml b/hostsidetests/jvmti/run-tests/test-911/AndroidTest.xml
index c6a4565..1832662 100644
--- a/hostsidetests/jvmti/run-tests/test-911/AndroidTest.xml
+++ b/hostsidetests/jvmti/run-tests/test-911/AndroidTest.xml
@@ -14,6 +14,7 @@
      limitations under the License.
 -->
 <configuration description="Config for CTS JVMTI test cases">
+    <option name="test-suite-tag" value="cts" />
     <option name="config-descriptor:metadata" key="component" value="art" />
     <target_preparer class="com.android.tradefed.targetprep.suite.SuiteApkInstaller">
         <option name="cleanup-apks" value="true" />
diff --git a/hostsidetests/jvmti/run-tests/test-912/AndroidTest.xml b/hostsidetests/jvmti/run-tests/test-912/AndroidTest.xml
index 7cb82f0..4ba486e 100644
--- a/hostsidetests/jvmti/run-tests/test-912/AndroidTest.xml
+++ b/hostsidetests/jvmti/run-tests/test-912/AndroidTest.xml
@@ -14,6 +14,7 @@
      limitations under the License.
 -->
 <configuration description="Config for CTS JVMTI test cases">
+    <option name="test-suite-tag" value="cts" />
     <option name="config-descriptor:metadata" key="component" value="art" />
     <target_preparer class="com.android.tradefed.targetprep.suite.SuiteApkInstaller">
         <option name="cleanup-apks" value="true" />
diff --git a/hostsidetests/jvmti/run-tests/test-913/AndroidTest.xml b/hostsidetests/jvmti/run-tests/test-913/AndroidTest.xml
index 7dff2cf..a24a927 100644
--- a/hostsidetests/jvmti/run-tests/test-913/AndroidTest.xml
+++ b/hostsidetests/jvmti/run-tests/test-913/AndroidTest.xml
@@ -14,6 +14,7 @@
      limitations under the License.
 -->
 <configuration description="Config for CTS JVMTI test cases">
+    <option name="test-suite-tag" value="cts" />
     <option name="config-descriptor:metadata" key="component" value="art" />
     <target_preparer class="com.android.tradefed.targetprep.suite.SuiteApkInstaller">
         <option name="cleanup-apks" value="true" />
diff --git a/hostsidetests/jvmti/run-tests/test-914/AndroidTest.xml b/hostsidetests/jvmti/run-tests/test-914/AndroidTest.xml
index f6ca1e7..efbc392 100644
--- a/hostsidetests/jvmti/run-tests/test-914/AndroidTest.xml
+++ b/hostsidetests/jvmti/run-tests/test-914/AndroidTest.xml
@@ -14,6 +14,7 @@
      limitations under the License.
 -->
 <configuration description="Config for CTS JVMTI test cases">
+    <option name="test-suite-tag" value="cts" />
     <option name="config-descriptor:metadata" key="component" value="art" />
     <target_preparer class="com.android.tradefed.targetprep.suite.SuiteApkInstaller">
         <option name="cleanup-apks" value="true" />
diff --git a/hostsidetests/jvmti/run-tests/test-915/AndroidTest.xml b/hostsidetests/jvmti/run-tests/test-915/AndroidTest.xml
index b8e03bf..02239d7 100644
--- a/hostsidetests/jvmti/run-tests/test-915/AndroidTest.xml
+++ b/hostsidetests/jvmti/run-tests/test-915/AndroidTest.xml
@@ -14,6 +14,7 @@
      limitations under the License.
 -->
 <configuration description="Config for CTS JVMTI test cases">
+    <option name="test-suite-tag" value="cts" />
     <option name="config-descriptor:metadata" key="component" value="art" />
     <target_preparer class="com.android.tradefed.targetprep.suite.SuiteApkInstaller">
         <option name="cleanup-apks" value="true" />
diff --git a/hostsidetests/jvmti/run-tests/test-917/AndroidTest.xml b/hostsidetests/jvmti/run-tests/test-917/AndroidTest.xml
index 0098aef..5726e10 100644
--- a/hostsidetests/jvmti/run-tests/test-917/AndroidTest.xml
+++ b/hostsidetests/jvmti/run-tests/test-917/AndroidTest.xml
@@ -14,6 +14,7 @@
      limitations under the License.
 -->
 <configuration description="Config for CTS JVMTI test cases">
+    <option name="test-suite-tag" value="cts" />
     <option name="config-descriptor:metadata" key="component" value="art" />
     <target_preparer class="com.android.tradefed.targetprep.suite.SuiteApkInstaller">
         <option name="cleanup-apks" value="true" />
diff --git a/hostsidetests/jvmti/run-tests/test-918/AndroidTest.xml b/hostsidetests/jvmti/run-tests/test-918/AndroidTest.xml
index 78f0927..7d9b72a 100644
--- a/hostsidetests/jvmti/run-tests/test-918/AndroidTest.xml
+++ b/hostsidetests/jvmti/run-tests/test-918/AndroidTest.xml
@@ -14,6 +14,7 @@
      limitations under the License.
 -->
 <configuration description="Config for CTS JVMTI test cases">
+    <option name="test-suite-tag" value="cts" />
     <option name="config-descriptor:metadata" key="component" value="art" />
     <target_preparer class="com.android.tradefed.targetprep.suite.SuiteApkInstaller">
         <option name="cleanup-apks" value="true" />
diff --git a/hostsidetests/jvmti/run-tests/test-919/AndroidTest.xml b/hostsidetests/jvmti/run-tests/test-919/AndroidTest.xml
index d23c3fc..eb22191 100644
--- a/hostsidetests/jvmti/run-tests/test-919/AndroidTest.xml
+++ b/hostsidetests/jvmti/run-tests/test-919/AndroidTest.xml
@@ -14,6 +14,7 @@
      limitations under the License.
 -->
 <configuration description="Config for CTS JVMTI test cases">
+    <option name="test-suite-tag" value="cts" />
     <option name="config-descriptor:metadata" key="component" value="art" />
     <target_preparer class="com.android.tradefed.targetprep.suite.SuiteApkInstaller">
         <option name="cleanup-apks" value="true" />
diff --git a/hostsidetests/jvmti/run-tests/test-920/AndroidTest.xml b/hostsidetests/jvmti/run-tests/test-920/AndroidTest.xml
index ff574cc..9c6d09b 100644
--- a/hostsidetests/jvmti/run-tests/test-920/AndroidTest.xml
+++ b/hostsidetests/jvmti/run-tests/test-920/AndroidTest.xml
@@ -14,6 +14,7 @@
      limitations under the License.
 -->
 <configuration description="Config for CTS JVMTI test cases">
+    <option name="test-suite-tag" value="cts" />
     <option name="config-descriptor:metadata" key="component" value="art" />
     <target_preparer class="com.android.tradefed.targetprep.suite.SuiteApkInstaller">
         <option name="cleanup-apks" value="true" />
diff --git a/hostsidetests/jvmti/run-tests/test-922/AndroidTest.xml b/hostsidetests/jvmti/run-tests/test-922/AndroidTest.xml
index 6c9f7dc..c2e21f1 100644
--- a/hostsidetests/jvmti/run-tests/test-922/AndroidTest.xml
+++ b/hostsidetests/jvmti/run-tests/test-922/AndroidTest.xml
@@ -14,6 +14,7 @@
      limitations under the License.
 -->
 <configuration description="Config for CTS JVMTI test cases">
+    <option name="test-suite-tag" value="cts" />
     <option name="config-descriptor:metadata" key="component" value="art" />
     <target_preparer class="com.android.tradefed.targetprep.suite.SuiteApkInstaller">
         <option name="cleanup-apks" value="true" />
diff --git a/hostsidetests/jvmti/run-tests/test-923/AndroidTest.xml b/hostsidetests/jvmti/run-tests/test-923/AndroidTest.xml
index cae6301..f9c586a 100644
--- a/hostsidetests/jvmti/run-tests/test-923/AndroidTest.xml
+++ b/hostsidetests/jvmti/run-tests/test-923/AndroidTest.xml
@@ -14,6 +14,7 @@
      limitations under the License.
 -->
 <configuration description="Config for CTS JVMTI test cases">
+    <option name="test-suite-tag" value="cts" />
     <option name="config-descriptor:metadata" key="component" value="art" />
     <target_preparer class="com.android.tradefed.targetprep.suite.SuiteApkInstaller">
         <option name="cleanup-apks" value="true" />
diff --git a/hostsidetests/jvmti/run-tests/test-924/AndroidTest.xml b/hostsidetests/jvmti/run-tests/test-924/AndroidTest.xml
index 35bea67..fa355f2 100644
--- a/hostsidetests/jvmti/run-tests/test-924/AndroidTest.xml
+++ b/hostsidetests/jvmti/run-tests/test-924/AndroidTest.xml
@@ -14,6 +14,7 @@
      limitations under the License.
 -->
 <configuration description="Config for CTS JVMTI test cases">
+    <option name="test-suite-tag" value="cts" />
     <option name="config-descriptor:metadata" key="component" value="art" />
     <target_preparer class="com.android.tradefed.targetprep.suite.SuiteApkInstaller">
         <option name="cleanup-apks" value="true" />
diff --git a/hostsidetests/jvmti/run-tests/test-926/AndroidTest.xml b/hostsidetests/jvmti/run-tests/test-926/AndroidTest.xml
index ea30af1..a50a51d 100644
--- a/hostsidetests/jvmti/run-tests/test-926/AndroidTest.xml
+++ b/hostsidetests/jvmti/run-tests/test-926/AndroidTest.xml
@@ -14,6 +14,7 @@
      limitations under the License.
 -->
 <configuration description="Config for CTS JVMTI test cases">
+    <option name="test-suite-tag" value="cts" />
     <option name="config-descriptor:metadata" key="component" value="art" />
     <target_preparer class="com.android.tradefed.targetprep.suite.SuiteApkInstaller">
         <option name="cleanup-apks" value="true" />
diff --git a/hostsidetests/jvmti/run-tests/test-927/AndroidTest.xml b/hostsidetests/jvmti/run-tests/test-927/AndroidTest.xml
index f09082a..5c02f07 100644
--- a/hostsidetests/jvmti/run-tests/test-927/AndroidTest.xml
+++ b/hostsidetests/jvmti/run-tests/test-927/AndroidTest.xml
@@ -14,6 +14,7 @@
      limitations under the License.
 -->
 <configuration description="Config for CTS JVMTI test cases">
+    <option name="test-suite-tag" value="cts" />
     <option name="config-descriptor:metadata" key="component" value="art" />
     <target_preparer class="com.android.tradefed.targetprep.suite.SuiteApkInstaller">
         <option name="cleanup-apks" value="true" />
diff --git a/hostsidetests/jvmti/run-tests/test-928/AndroidTest.xml b/hostsidetests/jvmti/run-tests/test-928/AndroidTest.xml
index 7755ea0..bf6f02c 100644
--- a/hostsidetests/jvmti/run-tests/test-928/AndroidTest.xml
+++ b/hostsidetests/jvmti/run-tests/test-928/AndroidTest.xml
@@ -14,6 +14,7 @@
      limitations under the License.
 -->
 <configuration description="Config for CTS JVMTI test cases">
+    <option name="test-suite-tag" value="cts" />
     <option name="config-descriptor:metadata" key="component" value="art" />
     <target_preparer class="com.android.tradefed.targetprep.suite.SuiteApkInstaller">
         <option name="cleanup-apks" value="true" />
diff --git a/hostsidetests/jvmti/run-tests/test-930/AndroidTest.xml b/hostsidetests/jvmti/run-tests/test-930/AndroidTest.xml
index c2b8745..2599ea5 100644
--- a/hostsidetests/jvmti/run-tests/test-930/AndroidTest.xml
+++ b/hostsidetests/jvmti/run-tests/test-930/AndroidTest.xml
@@ -14,6 +14,7 @@
      limitations under the License.
 -->
 <configuration description="Config for CTS JVMTI test cases">
+    <option name="test-suite-tag" value="cts" />
     <option name="config-descriptor:metadata" key="component" value="art" />
     <target_preparer class="com.android.tradefed.targetprep.suite.SuiteApkInstaller">
         <option name="cleanup-apks" value="true" />
diff --git a/hostsidetests/jvmti/run-tests/test-931/AndroidTest.xml b/hostsidetests/jvmti/run-tests/test-931/AndroidTest.xml
index 349cc80..2f8a15d 100644
--- a/hostsidetests/jvmti/run-tests/test-931/AndroidTest.xml
+++ b/hostsidetests/jvmti/run-tests/test-931/AndroidTest.xml
@@ -14,6 +14,7 @@
      limitations under the License.
 -->
 <configuration description="Config for CTS JVMTI test cases">
+    <option name="test-suite-tag" value="cts" />
     <option name="config-descriptor:metadata" key="component" value="art" />
     <target_preparer class="com.android.tradefed.targetprep.suite.SuiteApkInstaller">
         <option name="cleanup-apks" value="true" />
diff --git a/hostsidetests/jvmti/run-tests/test-932/AndroidTest.xml b/hostsidetests/jvmti/run-tests/test-932/AndroidTest.xml
index c691b40..aa2b9db 100644
--- a/hostsidetests/jvmti/run-tests/test-932/AndroidTest.xml
+++ b/hostsidetests/jvmti/run-tests/test-932/AndroidTest.xml
@@ -14,6 +14,7 @@
      limitations under the License.
 -->
 <configuration description="Config for CTS JVMTI test cases">
+    <option name="test-suite-tag" value="cts" />
     <option name="config-descriptor:metadata" key="component" value="art" />
     <target_preparer class="com.android.tradefed.targetprep.suite.SuiteApkInstaller">
         <option name="cleanup-apks" value="true" />
diff --git a/hostsidetests/jvmti/run-tests/test-940/AndroidTest.xml b/hostsidetests/jvmti/run-tests/test-940/AndroidTest.xml
index fe8f6df..2f35ce7 100644
--- a/hostsidetests/jvmti/run-tests/test-940/AndroidTest.xml
+++ b/hostsidetests/jvmti/run-tests/test-940/AndroidTest.xml
@@ -14,6 +14,7 @@
      limitations under the License.
 -->
 <configuration description="Config for CTS JVMTI test cases">
+    <option name="test-suite-tag" value="cts" />
     <option name="config-descriptor:metadata" key="component" value="art" />
     <target_preparer class="com.android.tradefed.targetprep.suite.SuiteApkInstaller">
         <option name="cleanup-apks" value="true" />
diff --git a/hostsidetests/jvmti/run-tests/test-942/AndroidTest.xml b/hostsidetests/jvmti/run-tests/test-942/AndroidTest.xml
index f200441..bad7710 100644
--- a/hostsidetests/jvmti/run-tests/test-942/AndroidTest.xml
+++ b/hostsidetests/jvmti/run-tests/test-942/AndroidTest.xml
@@ -14,6 +14,7 @@
      limitations under the License.
 -->
 <configuration description="Config for CTS JVMTI test cases">
+    <option name="test-suite-tag" value="cts" />
     <option name="config-descriptor:metadata" key="component" value="art" />
     <target_preparer class="com.android.tradefed.targetprep.suite.SuiteApkInstaller">
         <option name="cleanup-apks" value="true" />
diff --git a/hostsidetests/jvmti/run-tests/test-944/AndroidTest.xml b/hostsidetests/jvmti/run-tests/test-944/AndroidTest.xml
index 97a69cd..d13dbc9 100644
--- a/hostsidetests/jvmti/run-tests/test-944/AndroidTest.xml
+++ b/hostsidetests/jvmti/run-tests/test-944/AndroidTest.xml
@@ -14,6 +14,7 @@
      limitations under the License.
 -->
 <configuration description="Config for CTS JVMTI test cases">
+    <option name="test-suite-tag" value="cts" />
     <option name="config-descriptor:metadata" key="component" value="art" />
     <target_preparer class="com.android.tradefed.targetprep.suite.SuiteApkInstaller">
         <option name="cleanup-apks" value="true" />
diff --git a/hostsidetests/jvmti/run-tests/test-945/AndroidTest.xml b/hostsidetests/jvmti/run-tests/test-945/AndroidTest.xml
index 3c76674..dc7254a 100644
--- a/hostsidetests/jvmti/run-tests/test-945/AndroidTest.xml
+++ b/hostsidetests/jvmti/run-tests/test-945/AndroidTest.xml
@@ -14,6 +14,7 @@
      limitations under the License.
 -->
 <configuration description="Config for CTS JVMTI test cases">
+    <option name="test-suite-tag" value="cts" />
     <option name="config-descriptor:metadata" key="component" value="art" />
     <target_preparer class="com.android.tradefed.targetprep.suite.SuiteApkInstaller">
         <option name="cleanup-apks" value="true" />
diff --git a/hostsidetests/jvmti/run-tests/test-947/AndroidTest.xml b/hostsidetests/jvmti/run-tests/test-947/AndroidTest.xml
index 1f7a004..cd5586e 100644
--- a/hostsidetests/jvmti/run-tests/test-947/AndroidTest.xml
+++ b/hostsidetests/jvmti/run-tests/test-947/AndroidTest.xml
@@ -14,6 +14,7 @@
      limitations under the License.
 -->
 <configuration description="Config for CTS JVMTI test cases">
+    <option name="test-suite-tag" value="cts" />
     <option name="config-descriptor:metadata" key="component" value="art" />
     <target_preparer class="com.android.tradefed.targetprep.suite.SuiteApkInstaller">
         <option name="cleanup-apks" value="true" />
diff --git a/hostsidetests/jvmti/run-tests/test-951/AndroidTest.xml b/hostsidetests/jvmti/run-tests/test-951/AndroidTest.xml
index ef9a6e7..844b5a4 100644
--- a/hostsidetests/jvmti/run-tests/test-951/AndroidTest.xml
+++ b/hostsidetests/jvmti/run-tests/test-951/AndroidTest.xml
@@ -14,6 +14,7 @@
      limitations under the License.
 -->
 <configuration description="Config for CTS JVMTI test cases">
+    <option name="test-suite-tag" value="cts" />
     <option name="config-descriptor:metadata" key="component" value="art" />
     <target_preparer class="com.android.tradefed.targetprep.suite.SuiteApkInstaller">
         <option name="cleanup-apks" value="true" />
diff --git a/hostsidetests/jvmti/run-tests/test-982/AndroidTest.xml b/hostsidetests/jvmti/run-tests/test-982/AndroidTest.xml
index 0470da1..102db35 100644
--- a/hostsidetests/jvmti/run-tests/test-982/AndroidTest.xml
+++ b/hostsidetests/jvmti/run-tests/test-982/AndroidTest.xml
@@ -14,6 +14,7 @@
      limitations under the License.
 -->
 <configuration description="Config for CTS JVMTI test cases">
+    <option name="test-suite-tag" value="cts" />
     <option name="config-descriptor:metadata" key="component" value="art" />
     <target_preparer class="com.android.tradefed.targetprep.suite.SuiteApkInstaller">
         <option name="cleanup-apks" value="true" />
diff --git a/hostsidetests/jvmti/run-tests/test-984/AndroidTest.xml b/hostsidetests/jvmti/run-tests/test-984/AndroidTest.xml
index febd68b..33eedb0 100644
--- a/hostsidetests/jvmti/run-tests/test-984/AndroidTest.xml
+++ b/hostsidetests/jvmti/run-tests/test-984/AndroidTest.xml
@@ -14,6 +14,7 @@
      limitations under the License.
 -->
 <configuration description="Config for CTS JVMTI test cases">
+    <option name="test-suite-tag" value="cts" />
     <option name="config-descriptor:metadata" key="component" value="art" />
     <target_preparer class="com.android.tradefed.targetprep.suite.SuiteApkInstaller">
         <option name="cleanup-apks" value="true" />
diff --git a/hostsidetests/jvmti/run-tests/test-985/AndroidTest.xml b/hostsidetests/jvmti/run-tests/test-985/AndroidTest.xml
index ff85148..684833d 100644
--- a/hostsidetests/jvmti/run-tests/test-985/AndroidTest.xml
+++ b/hostsidetests/jvmti/run-tests/test-985/AndroidTest.xml
@@ -14,6 +14,7 @@
      limitations under the License.
 -->
 <configuration description="Config for CTS JVMTI test cases">
+    <option name="test-suite-tag" value="cts" />
     <option name="config-descriptor:metadata" key="component" value="art" />
     <target_preparer class="com.android.tradefed.targetprep.suite.SuiteApkInstaller">
         <option name="cleanup-apks" value="true" />
diff --git a/hostsidetests/jvmti/run-tests/test-986/AndroidTest.xml b/hostsidetests/jvmti/run-tests/test-986/AndroidTest.xml
index 3205637..170193e 100644
--- a/hostsidetests/jvmti/run-tests/test-986/AndroidTest.xml
+++ b/hostsidetests/jvmti/run-tests/test-986/AndroidTest.xml
@@ -14,6 +14,7 @@
      limitations under the License.
 -->
 <configuration description="Config for CTS JVMTI test cases">
+    <option name="test-suite-tag" value="cts" />
     <option name="config-descriptor:metadata" key="component" value="art" />
     <target_preparer class="com.android.tradefed.targetprep.suite.SuiteApkInstaller">
         <option name="cleanup-apks" value="true" />
diff --git a/hostsidetests/jvmti/tagging/AndroidTest.xml b/hostsidetests/jvmti/tagging/AndroidTest.xml
index 403ca28..be75c08 100644
--- a/hostsidetests/jvmti/tagging/AndroidTest.xml
+++ b/hostsidetests/jvmti/tagging/AndroidTest.xml
@@ -14,6 +14,7 @@
      limitations under the License.
 -->
 <configuration description="Config for CTS JVMTI test cases">
+    <option name="test-suite-tag" value="cts" />
     <option name="config-descriptor:metadata" key="component" value="art" />
     <target_preparer class="com.android.tradefed.targetprep.suite.SuiteApkInstaller">
         <option name="cleanup-apks" value="true" />
diff --git a/hostsidetests/media/AndroidTest.xml b/hostsidetests/media/AndroidTest.xml
index 483d969..e5818b5 100644
--- a/hostsidetests/media/AndroidTest.xml
+++ b/hostsidetests/media/AndroidTest.xml
@@ -14,6 +14,7 @@
      limitations under the License.
 -->
 <configuration description="Config for CTS media host test cases">
+    <option name="test-suite-tag" value="cts" />
     <option name="config-descriptor:metadata" key="component" value="media" />
     <test class="com.android.compatibility.common.tradefed.testtype.JarHostTest" >
         <option name="jar" value="CtsMediaHostTestCases.jar" />
diff --git a/hostsidetests/media/app/MediaSessionTest/Android.mk b/hostsidetests/media/app/MediaSessionTest/Android.mk
index fb0fe40..7303147 100644
--- a/hostsidetests/media/app/MediaSessionTest/Android.mk
+++ b/hostsidetests/media/app/MediaSessionTest/Android.mk
@@ -29,7 +29,10 @@
     $(call all-java-files-under, ../../common)
 
 
-LOCAL_STATIC_JAVA_LIBRARIES := android-support-test compatibility-device-util
+LOCAL_STATIC_JAVA_LIBRARIES := \
+    android-support-test \
+    compatibility-device-util \
+
 
 LOCAL_SDK_VERSION := current
 
diff --git a/hostsidetests/media/app/MediaSessionTest/AndroidManifest.xml b/hostsidetests/media/app/MediaSessionTest/AndroidManifest.xml
index 5ae6f96..18060e1 100644
--- a/hostsidetests/media/app/MediaSessionTest/AndroidManifest.xml
+++ b/hostsidetests/media/app/MediaSessionTest/AndroidManifest.xml
@@ -21,6 +21,8 @@
 
     <application
         android:testOnly="true">
+        <uses-library android:name="android.test.runner" />
+
         <service
             android:name=".MediaSessionManagerTest"
             android:label="MediaSessionManagerTest"
diff --git a/hostsidetests/media/bitstreams/AndroidTest.xml b/hostsidetests/media/bitstreams/AndroidTest.xml
index 6a60a65..1621aa7 100644
--- a/hostsidetests/media/bitstreams/AndroidTest.xml
+++ b/hostsidetests/media/bitstreams/AndroidTest.xml
@@ -14,6 +14,7 @@
      limitations under the License.
 -->
 <configuration description="Config for CTS Sample host test cases">
+    <option name="test-suite-tag" value="cts" />
     <option name="config-descriptor:metadata" key="component" value="media" />
     <target_preparer class="com.android.compatibility.common.tradefed.targetprep.MediaPreparer">
         <option name="media-download-only" value="true" />
diff --git a/hostsidetests/media/bitstreams/src/android/media/cts/bitstreams/ReportProcessor.java b/hostsidetests/media/bitstreams/src/android/media/cts/bitstreams/ReportProcessor.java
index 3c1f14e..e7be900 100644
--- a/hostsidetests/media/bitstreams/src/android/media/cts/bitstreams/ReportProcessor.java
+++ b/hostsidetests/media/bitstreams/src/android/media/cts/bitstreams/ReportProcessor.java
@@ -140,6 +140,8 @@
         instrTest.addIncludeFilter(fullTestName);
         instrTest.setTestTimeout(testTimeout);
         instrTest.setShellTimeout(shellTimeout);
+        // disable rerun mode to avoid collecting tests first then running.
+        instrTest.setRerunMode(false);
         for (Entry<String, String> e : getArgs().entrySet()) {
             instrTest.addInstrumentationArg(e.getKey(), e.getValue());
         }
diff --git a/hostsidetests/monkey/AndroidTest.xml b/hostsidetests/monkey/AndroidTest.xml
index 6479915..ad1507a 100644
--- a/hostsidetests/monkey/AndroidTest.xml
+++ b/hostsidetests/monkey/AndroidTest.xml
@@ -14,6 +14,7 @@
      limitations under the License.
 -->
 <configuration description="Config for the CTS monkey host tests">
+    <option name="test-suite-tag" value="cts" />
     <option name="config-descriptor:metadata" key="component" value="misc" />
     <test class="com.android.compatibility.common.tradefed.testtype.JarHostTest" >
         <option name="jar" value="CtsMonkeyTestCases.jar" />
diff --git a/hostsidetests/multiuser/Android.mk b/hostsidetests/multiuser/Android.mk
index 9827ca7..e4c654d 100644
--- a/hostsidetests/multiuser/Android.mk
+++ b/hostsidetests/multiuser/Android.mk
@@ -22,7 +22,7 @@
 
 LOCAL_SRC_FILES := $(call all-java-files-under, src)
 
-LOCAL_JAVA_LIBRARIES := tools-common-prebuilt cts-tradefed tradefed
+LOCAL_JAVA_LIBRARIES := tools-common-prebuilt cts-tradefed tradefed platform-test-annotations-host
 
 LOCAL_CTS_TEST_PACKAGE := android.host.multiuser
 
diff --git a/hostsidetests/multiuser/AndroidTest.xml b/hostsidetests/multiuser/AndroidTest.xml
index 27a2073..3c359c0 100644
--- a/hostsidetests/multiuser/AndroidTest.xml
+++ b/hostsidetests/multiuser/AndroidTest.xml
@@ -14,6 +14,7 @@
      limitations under the License.
 -->
 <configuration description="Config for the CTS multiuser host tests">
+    <option name="test-suite-tag" value="cts" />
     <option name="config-descriptor:metadata" key="component" value="framework" />
     <test class="com.android.compatibility.common.tradefed.testtype.JarHostTest" >
         <option name="jar" value="CtsMultiUserHostTestCases.jar" />
diff --git a/hostsidetests/multiuser/src/android/host/multiuser/CreateUsersNoAppCrashesTest.java b/hostsidetests/multiuser/src/android/host/multiuser/CreateUsersNoAppCrashesTest.java
index 5d4b89e..0f83f74 100644
--- a/hostsidetests/multiuser/src/android/host/multiuser/CreateUsersNoAppCrashesTest.java
+++ b/hostsidetests/multiuser/src/android/host/multiuser/CreateUsersNoAppCrashesTest.java
@@ -16,8 +16,9 @@
 
 package android.host.multiuser;
 
+import android.platform.test.annotations.Presubmit;
+
 import com.android.tradefed.device.DeviceNotAvailableException;
-import com.android.tradefed.log.LogUtil.CLog;
 
 import java.util.LinkedHashSet;
 import java.util.Scanner;
@@ -25,11 +26,12 @@
 
 /**
  * Test verifies that users can be created/switched to without error dialogs shown to the user
+ * Run: atest CreateUsersNoAppCrashesTest
  */
 public class CreateUsersNoAppCrashesTest extends BaseMultiUserTest {
     private int mInitialUserId;
     private static final long LOGCAT_POLL_INTERVAL_MS = 5000;
-    private static final long BOOT_COMPLETED_TIMEOUT_MS = 120000;
+    private static final long USER_SWITCH_COMPLETE_TIMEOUT_MS = 180000;
 
     @Override
     protected void setUp() throws Exception {
@@ -37,6 +39,7 @@
         mInitialUserId = getDevice().getCurrentUser();
     }
 
+    @Presubmit
     public void testCanCreateGuestUser() throws Exception {
         if (!mSupportsMultiUser) {
             return;
@@ -45,31 +48,68 @@
                 "TestUser_" + System.currentTimeMillis() /* name */,
                 true /* guest */,
                 false /* ephemeral */);
-        getDevice().executeAdbCommand("logcat", "-c"); // Reset log
-        assertTrue("Couldn't switch to user " + userId, getDevice().switchUser(userId));
-        Set<String> appErrors = new LinkedHashSet<>();
-        assertTrue("Didn't receive BOOT_COMPLETED delivered notification. appErrors=" + appErrors,
-                waitForBootCompleted(appErrors, userId));
-        assertTrue("App error dialog(s) are present: " + appErrors, appErrors.isEmpty());
-        assertTrue("Couldn't switch to user " + userId, getDevice().switchUser(mInitialUserId));
+        assertSwitchToNewUser(userId);
+        assertSwitchToUser(userId, mInitialUserId);
     }
 
-    private boolean waitForBootCompleted(Set<String> appErrors, int targetUserId)
-            throws DeviceNotAvailableException, InterruptedException {
+    @Presubmit
+    public void testCanCreateSecondaryUser() throws Exception {
+        if (!mSupportsMultiUser) {
+            return;
+        }
+        int userId = getDevice().createUser(
+                "TestUser_" + System.currentTimeMillis() /* name */,
+                false /* guest */,
+                false /* ephemeral */);
+        assertSwitchToNewUser(userId);
+        assertSwitchToUser(userId, mInitialUserId);
+    }
+
+    private void assertSwitchToNewUser(int toUserId) throws Exception {
+        final String exitString = "Finished processing BOOT_COMPLETED for u" + toUserId;
+        final Set<String> appErrors = new LinkedHashSet<>();
+        getDevice().executeAdbCommand("logcat", "-c"); // Reset log
+        assertTrue("Couldn't switch to user " + toUserId, getDevice().switchUser(toUserId));
+        final boolean result = waitForUserSwitchComplete(appErrors, toUserId, exitString);
+        assertTrue("Didn't receive BOOT_COMPLETED delivered notification. appErrors="
+                + appErrors, result);
+        assertTrue("App error dialog(s) are present: " + appErrors, appErrors.isEmpty());
+    }
+
+    private void assertSwitchToUser(int fromUserId, int toUserId) throws Exception {
+        final String exitString = "Continue user switch oldUser #" + fromUserId + ", newUser #"
+                + toUserId;
+        final Set<String> appErrors = new LinkedHashSet<>();
+        getDevice().executeAdbCommand("logcat", "-c"); // Reset log
+        assertTrue("Couldn't switch to user " + toUserId, getDevice().switchUser(toUserId));
+        final boolean result = waitForUserSwitchComplete(appErrors, toUserId, exitString);
+        assertTrue("Didn't reach \"Continue user switch\" stage. appErrors=" + appErrors, result);
+        assertTrue("App error dialog(s) are present: " + appErrors, appErrors.isEmpty());
+    }
+
+    private boolean waitForUserSwitchComplete(Set<String> appErrors, int targetUserId,
+            String exitString) throws DeviceNotAvailableException, InterruptedException {
+        boolean mExitFound = false;
         long ti = System.currentTimeMillis();
-        while (System.currentTimeMillis() - ti < BOOT_COMPLETED_TIMEOUT_MS) {
-            String logs = getDevice().executeAdbCommand("logcat", "-v", "brief", "-d");
+        while (System.currentTimeMillis() - ti < USER_SWITCH_COMPLETE_TIMEOUT_MS) {
+            String logs = getDevice().executeAdbCommand("logcat", "-v", "brief", "-d",
+                    "ActivityManager:D", "AndroidRuntime:E", "*:S");
             Scanner in = new Scanner(logs);
             while (in.hasNextLine()) {
                 String line = in.nextLine();
                 if (line.contains("Showing crash dialog for package")) {
                     appErrors.add(line);
-                } else if (line.contains("Finished processing BOOT_COMPLETED for u" + targetUserId)) {
-                    return true;
+                } else if (line.contains(exitString)) {
+                    // Parse all logs in case crashes occur as a result of onUserChange callbacks
+                    mExitFound = true;
+                } else if (line.contains("FATAL EXCEPTION IN SYSTEM PROCESS")) {
+                    throw new IllegalStateException("System process crashed - " + line);
                 }
-
             }
             in.close();
+            if (mExitFound) {
+                return true;
+            }
             Thread.sleep(LOGCAT_POLL_INTERVAL_MS);
         }
         return false;
diff --git a/hostsidetests/multiuser/src/android/host/multiuser/CreateUsersPermissionTest.java b/hostsidetests/multiuser/src/android/host/multiuser/CreateUsersPermissionTest.java
index 9a43534..d0c96ec 100644
--- a/hostsidetests/multiuser/src/android/host/multiuser/CreateUsersPermissionTest.java
+++ b/hostsidetests/multiuser/src/android/host/multiuser/CreateUsersPermissionTest.java
@@ -56,8 +56,8 @@
         }
         final String setRestriction = "pm set-user-restriction no_fun ";
         final String output = getDevice().executeShellCommand(setRestriction + "1");
-        final boolean isErrorOutput = output.startsWith("Error")
-                && output.contains("SecurityException");
+        final boolean isErrorOutput = output.contains("SecurityException")
+            && output.contains("You need MANAGE_USERS permission");
         assertTrue("Trying to set user restriction should fail with SecurityException. "
                 + "command output: " + output, isErrorOutput);
     }
diff --git a/hostsidetests/net/AndroidTest.xml b/hostsidetests/net/AndroidTest.xml
index 4a2e2e3..c96fea4 100644
--- a/hostsidetests/net/AndroidTest.xml
+++ b/hostsidetests/net/AndroidTest.xml
@@ -14,6 +14,7 @@
      limitations under the License.
 -->
 <configuration description="Config for CTS net host test cases">
+    <option name="test-suite-tag" value="cts" />
     <option name="config-descriptor:metadata" key="component" value="networking" />
     <target_preparer class="com.android.cts.net.NetPolicyTestsPreparer" />
     <test class="com.android.compatibility.common.tradefed.testtype.JarHostTest" >
diff --git a/hostsidetests/net/app/Android.mk b/hostsidetests/net/app/Android.mk
index f094f3f..c03e70b 100644
--- a/hostsidetests/net/app/Android.mk
+++ b/hostsidetests/net/app/Android.mk
@@ -23,6 +23,8 @@
 LOCAL_STATIC_JAVA_LIBRARIES := compatibility-device-util ctstestrunner ub-uiautomator \
         CtsHostsideNetworkTestsAidl
 
+LOCAL_JAVA_LIBRARIES := android.test.runner.stubs android.test.base.stubs
+
 LOCAL_SRC_FILES := $(call all-java-files-under, src)
 
 LOCAL_PACKAGE_NAME := CtsHostsideNetworkTestsApp
diff --git a/hostsidetests/net/app/src/com/android/cts/net/hostside/AbstractAppIdleTestCase.java b/hostsidetests/net/app/src/com/android/cts/net/hostside/AbstractAppIdleTestCase.java
index d2c0873..133a43b 100644
--- a/hostsidetests/net/app/src/com/android/cts/net/hostside/AbstractAppIdleTestCase.java
+++ b/hostsidetests/net/app/src/com/android/cts/net/hostside/AbstractAppIdleTestCase.java
@@ -127,6 +127,19 @@
         assertBackgroundNetworkAccess(false);
     }
 
+    public void testBackgroundNetworkAccess_tempWhitelisted() throws Exception {
+        if (!isSupported()) return;
+
+        setAppIdle(true);
+        assertBackgroundNetworkAccess(false);
+
+        addTempPowerSaveModeWhitelist(TEST_APP2_PKG, TEMP_POWERSAVE_WHITELIST_DURATION_MS);
+        assertBackgroundNetworkAccess(true);
+        // Wait until the whitelist duration is expired.
+        SystemClock.sleep(TEMP_POWERSAVE_WHITELIST_DURATION_MS);
+        assertBackgroundNetworkAccess(false);
+    }
+
     public void testBackgroundNetworkAccess_disabled() throws Exception {
         if (!isSupported()) return;
 
diff --git a/hostsidetests/net/app/src/com/android/cts/net/hostside/AbstractRestrictBackgroundNetworkTestCase.java b/hostsidetests/net/app/src/com/android/cts/net/hostside/AbstractRestrictBackgroundNetworkTestCase.java
index ce56d25..254b009 100644
--- a/hostsidetests/net/app/src/com/android/cts/net/hostside/AbstractRestrictBackgroundNetworkTestCase.java
+++ b/hostsidetests/net/app/src/com/android/cts/net/hostside/AbstractRestrictBackgroundNetworkTestCase.java
@@ -108,6 +108,8 @@
 
     private static final String APP_NOT_FOREGROUND_ERROR = "app_not_fg";
 
+    protected static final long TEMP_POWERSAVE_WHITELIST_DURATION_MS = 5_000; // 5 sec
+
     protected Context mContext;
     protected Instrumentation mInstrumentation;
     protected ConnectivityManager mCm;
@@ -138,6 +140,7 @@
             enableLocation();
         }
         mSupported = setUpActiveNetworkMeteringState();
+        setAppIdle(false);
 
         Log.i(TAG, "Apps status on " + getName() + ":\n"
                 + "\ttest app: uid=" + mMyUid + ", state=" + getProcessStateByUid(mMyUid) + "\n"
@@ -751,6 +754,12 @@
                 + ". Full list: " + uids);
     }
 
+    protected void addTempPowerSaveModeWhitelist(String packageName, long duration)
+            throws Exception {
+        Log.i(TAG, "Adding pkg " + packageName + " to temp-power-save-mode whitelist");
+        executeShellCommand("dumpsys deviceidle tempwhitelist -d " + duration + " " + packageName);
+    }
+
     protected void assertPowerSaveModeWhitelist(String packageName, boolean expected)
             throws Exception {
         // TODO: currently the power-save mode is behaving like idle, but once it changes, we'll
diff --git a/hostsidetests/net/app/src/com/android/cts/net/hostside/MixedModesTest.java b/hostsidetests/net/app/src/com/android/cts/net/hostside/MixedModesTest.java
index 5248255..76332be 100644
--- a/hostsidetests/net/app/src/com/android/cts/net/hostside/MixedModesTest.java
+++ b/hostsidetests/net/app/src/com/android/cts/net/hostside/MixedModesTest.java
@@ -15,6 +15,7 @@
  */
 package com.android.cts.net.hostside;
 
+import android.os.SystemClock;
 import android.util.Log;
 
 /**
@@ -271,4 +272,55 @@
             setDozeMode(false);
         }
     }
+
+    public void testAppIdleAndDoze_tempPowerSaveWhitelists() throws Exception {
+        if (!isSupported()) {
+            return;
+        }
+        if (!isDozeModeEnabled()) {
+            Log.i(TAG, "Skipping " + getClass() + "." + getName()
+                    + "() because device does not support Doze Mode");
+            return;
+        }
+
+        setDozeMode(true);
+        setAppIdle(true);
+
+        try {
+            assertBackgroundNetworkAccess(false);
+
+            addTempPowerSaveModeWhitelist(TEST_APP2_PKG, TEMP_POWERSAVE_WHITELIST_DURATION_MS);
+            assertBackgroundNetworkAccess(true);
+
+            // Wait until the whitelist duration is expired.
+            SystemClock.sleep(TEMP_POWERSAVE_WHITELIST_DURATION_MS);
+            assertBackgroundNetworkAccess(false);
+        } finally {
+            setAppIdle(false);
+            setDozeMode(false);
+        }
+    }
+
+    public void testAppIdleAndBatterySaver_tempPowerSaveWhitelists() throws Exception {
+        if (!isSupported()) {
+            return;
+        }
+
+        setBatterySaverMode(true);
+        setAppIdle(true);
+
+        try {
+            assertBackgroundNetworkAccess(false);
+
+            addTempPowerSaveModeWhitelist(TEST_APP2_PKG, TEMP_POWERSAVE_WHITELIST_DURATION_MS);
+            assertBackgroundNetworkAccess(true);
+
+            // Wait until the whitelist duration is expired.
+            SystemClock.sleep(TEMP_POWERSAVE_WHITELIST_DURATION_MS);
+            assertBackgroundNetworkAccess(false);
+        } finally {
+            setAppIdle(false);
+            setBatterySaverMode(false);
+        }
+    }
 }
diff --git a/hostsidetests/net/src/com/android/cts/net/HostsideRestrictBackgroundNetworkTests.java b/hostsidetests/net/src/com/android/cts/net/HostsideRestrictBackgroundNetworkTests.java
index bf3fc08..fe9d36c 100644
--- a/hostsidetests/net/src/com/android/cts/net/HostsideRestrictBackgroundNetworkTests.java
+++ b/hostsidetests/net/src/com/android/cts/net/HostsideRestrictBackgroundNetworkTests.java
@@ -146,6 +146,11 @@
                 "testBackgroundNetworkAccess_whitelisted");
     }
 
+    public void testAppIdleMetered_tempWhitelisted() throws Exception {
+        runDeviceTests(TEST_PKG, TEST_PKG + ".AppIdleMeteredTest",
+                "testBackgroundNetworkAccess_tempWhitelisted");
+    }
+
     public void testAppIdleMetered_enabled() throws Exception {
         runDeviceTests(TEST_PKG, TEST_PKG + ".AppIdleMeteredTest",
                 "testBackgroundNetworkAccess_enabled");
@@ -166,6 +171,11 @@
                 "testBackgroundNetworkAccess_whitelisted");
     }
 
+    public void testAppIdleNonMetered_tempWhitelisted() throws Exception {
+        runDeviceTests(TEST_PKG, TEST_PKG + ".AppIdleNonMeteredTest",
+                "testBackgroundNetworkAccess_tempWhitelisted");
+    }
+
     public void testAppIdleNonMetered_enabled() throws Exception {
         runDeviceTests(TEST_PKG, TEST_PKG + ".AppIdleNonMeteredTest",
                 "testBackgroundNetworkAccess_enabled");
@@ -261,6 +271,16 @@
                 "testDozeAndAppIdle_powerSaveWhitelists");
     }
 
+    public void testAppIdleAndDoze_tempPowerSaveWhitelists() throws Exception {
+        runDeviceTests(TEST_PKG, TEST_PKG + ".MixedModesTest",
+                "testAppIdleAndDoze_tempPowerSaveWhitelists");
+    }
+
+    public void testAppIdleAndBatterySaver_tempPowerSaveWhitelists() throws Exception {
+        runDeviceTests(TEST_PKG, TEST_PKG + ".MixedModesTest",
+                "testAppIdleAndBatterySaver_tempPowerSaveWhitelists");
+    }
+
     /*******************
      * Helper methods. *
      *******************/
diff --git a/hostsidetests/numberblocking/AndroidTest.xml b/hostsidetests/numberblocking/AndroidTest.xml
index 9a6d805..6ebc522 100644
--- a/hostsidetests/numberblocking/AndroidTest.xml
+++ b/hostsidetests/numberblocking/AndroidTest.xml
@@ -14,6 +14,7 @@
      limitations under the License.
 -->
 <configuration description="Config for CTS number blocking test cases">
+    <option name="test-suite-tag" value="cts" />
     <option name="config-descriptor:metadata" key="component" value="abuse" />
     <target_preparer class="com.android.tradefed.targetprep.suite.SuiteApkInstaller">
         <option name="cleanup-apks" value="true" />
diff --git a/hostsidetests/numberblocking/app/Android.mk b/hostsidetests/numberblocking/app/Android.mk
index cfbccbe..fc0f4a0 100644
--- a/hostsidetests/numberblocking/app/Android.mk
+++ b/hostsidetests/numberblocking/app/Android.mk
@@ -27,7 +27,9 @@
 
 LOCAL_SRC_FILES := $(call all-java-files-under, src)
 
-LOCAL_STATIC_JAVA_LIBRARIES := ctstestrunner android-support-test legacy-android-test
+LOCAL_STATIC_JAVA_LIBRARIES := ctstestrunner android-support-test
+
+LOCAL_JAVA_LIBRARIES := android.test.base.stubs
 
 # tag this module as a cts test artifact
 LOCAL_COMPATIBILITY_SUITE := cts vts general-tests
diff --git a/hostsidetests/os/AndroidTest.xml b/hostsidetests/os/AndroidTest.xml
index 261c29d..2e61a61 100644
--- a/hostsidetests/os/AndroidTest.xml
+++ b/hostsidetests/os/AndroidTest.xml
@@ -14,6 +14,7 @@
      limitations under the License.
 -->
 <configuration description="Config for the CTS OS host test cases">
+    <option name="test-suite-tag" value="cts" />
     <option name="config-descriptor:metadata" key="component" value="framework" />
     <target_preparer class="com.android.tradefed.targetprep.suite.SuiteApkInstaller">
         <option name="cleanup-apks" value="true" />
diff --git a/hostsidetests/os/test-apps/StaticSharedLibConsumerApp1/AndroidManifest.xml b/hostsidetests/os/test-apps/StaticSharedLibConsumerApp1/AndroidManifest.xml
index cbeb342..a1550a6 100755
--- a/hostsidetests/os/test-apps/StaticSharedLibConsumerApp1/AndroidManifest.xml
+++ b/hostsidetests/os/test-apps/StaticSharedLibConsumerApp1/AndroidManifest.xml
@@ -23,6 +23,8 @@
     <uses-permission android:name="android.permission.REQUEST_INSTALL_PACKAGES"/>
 
     <application>
+        <uses-library android:name="android.test.runner" />
+
         <uses-static-library
                 android:name="foo.bar.lib"
                 android:version="1"
diff --git a/hostsidetests/os/test-apps/StaticSharedLibConsumerApp1/src/android/os/lib/consumer1/UseSharedLibraryTest.java b/hostsidetests/os/test-apps/StaticSharedLibConsumerApp1/src/android/os/lib/consumer1/UseSharedLibraryTest.java
index dca3699..dbadeb9 100644
--- a/hostsidetests/os/test-apps/StaticSharedLibConsumerApp1/src/android/os/lib/consumer1/UseSharedLibraryTest.java
+++ b/hostsidetests/os/test-apps/StaticSharedLibConsumerApp1/src/android/os/lib/consumer1/UseSharedLibraryTest.java
@@ -187,7 +187,7 @@
         // Make sure we see the lib we depend on via getting installed packages
         List<PackageInfo> installedPackages = InstrumentationRegistry.getInstrumentation()
                 .getContext().getPackageManager().getInstalledPackages(0);
-        int usedLibraryVersionCode = -1;
+        long usedLibraryVersionCode = -1;
         for (PackageInfo installedPackage : installedPackages) {
             if (STATIC_LIB_PROVIDER_PKG.equals(installedPackage.packageName)) {
                 if (usedLibraryVersionCode != -1) {
diff --git a/hostsidetests/sample/AndroidTest.xml b/hostsidetests/sample/AndroidTest.xml
index c87969b..777479c 100644
--- a/hostsidetests/sample/AndroidTest.xml
+++ b/hostsidetests/sample/AndroidTest.xml
@@ -14,6 +14,7 @@
      limitations under the License.
 -->
 <configuration description="Config for CTS Sample 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" />
diff --git a/hostsidetests/sample/src/android/sample/cts/SampleHostJUnit4DeviceTest.java b/hostsidetests/sample/src/android/sample/cts/SampleHostJUnit4DeviceTest.java
index f32c523..0fbf3b9 100644
--- a/hostsidetests/sample/src/android/sample/cts/SampleHostJUnit4DeviceTest.java
+++ b/hostsidetests/sample/src/android/sample/cts/SampleHostJUnit4DeviceTest.java
@@ -16,14 +16,14 @@
 
 package android.sample.cts;
 
-import com.android.compatibility.common.tradefed.testtype.CompatibilityHostTestBase;
 import com.android.tradefed.testtype.DeviceJUnit4ClassRunner;
+import com.android.tradefed.testtype.junit4.BaseHostJUnit4Test;
 
 import org.junit.After;
 import org.junit.Assert;
 import org.junit.Before;
-import org.junit.runner.RunWith;
 import org.junit.Test;
+import org.junit.runner.RunWith;
 
 /**
  * Test that collects test results from test package android.sample.cts.app2.
@@ -33,7 +33,7 @@
  * collected from the hostside and reported accordingly.
  */
 @RunWith(DeviceJUnit4ClassRunner.class)
-public class SampleHostJUnit4DeviceTest extends CompatibilityHostTestBase {
+public class SampleHostJUnit4DeviceTest extends BaseHostJUnit4Test {
 
     private static final String TEST_PKG = "android.sample.cts.app2";
     private static final String TEST_CLASS = TEST_PKG + "." + "SampleDeviceTest";
@@ -65,7 +65,7 @@
 
     @After
     public void tearDown() throws Exception {
-        uninstallPackage(TEST_PKG);
+        uninstallPackage(getDevice(), TEST_PKG);
     }
 
 }
diff --git a/hostsidetests/seccomp/Android.mk b/hostsidetests/seccomp/Android.mk
new file mode 100644
index 0000000..2c1c077
--- /dev/null
+++ b/hostsidetests/seccomp/Android.mk
@@ -0,0 +1,32 @@
+# 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_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
+
+LOCAL_MODULE := CtsSeccompHostTestCases
+
+LOCAL_JAVA_LIBRARIES := cts-tradefed tradefed compatibility-host-util
+
+include $(BUILD_HOST_JAVA_LIBRARY)
+
+include $(call all-makefiles-under,$(LOCAL_PATH))
diff --git a/hostsidetests/seccomp/AndroidTest.xml b/hostsidetests/seccomp/AndroidTest.xml
new file mode 100644
index 0000000..cbfd1c4
--- /dev/null
+++ b/hostsidetests/seccomp/AndroidTest.xml
@@ -0,0 +1,26 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2017 The Android Open Source Project
+
+     Licensed under the Apache License, Version 2.0 (the "License");
+     you may not use this file except in compliance with the License.
+     You may obtain a copy of the License at
+
+          http://www.apache.org/licenses/LICENSE-2.0
+
+     Unless required by applicable law or agreed to in writing, software
+     distributed under the License is distributed on an "AS IS" BASIS,
+     WITHOUT 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 Sseccomp 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="CtsSeccompDeviceApp.apk" />
+    </target_preparer>
+    <test class="com.android.compatibility.common.tradefed.testtype.JarHostTest" >
+        <option name="jar" value="CtsSeccompHostTestCases.jar" />
+    </test>
+</configuration>
diff --git a/hostsidetests/seccomp/app/Android.mk b/hostsidetests/seccomp/app/Android.mk
new file mode 100644
index 0000000..726e64b
--- /dev/null
+++ b/hostsidetests/seccomp/app/Android.mk
@@ -0,0 +1,56 @@
+# 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)
+
+# 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)
+
+# Include both the 32 and 64 bit versions
+LOCAL_MULTILIB := both
+
+LOCAL_DEX_PREOPT := false
+
+LOCAL_PROGUARD_ENABLED := disabled
+
+LOCAL_STATIC_JAVA_LIBRARIES := \
+          android-support-test \
+          compatibility-device-util \
+
+LOCAL_JNI_SHARED_LIBRARIES := \
+          libctsseccomp_jni \
+          libcts_jni \
+          libnativehelper_compat_libc++ \
+          libnativehelper \
+          libcutils \
+          libc++ \
+          libpackagelistparser \
+
+LOCAL_SRC_FILES := $(call all-java-files-under, src)
+
+# tag this module as a cts test artifact
+LOCAL_COMPATIBILITY_SUITE := cts vts general-tests
+
+LOCAL_PACKAGE_NAME := CtsSeccompDeviceApp
+
+LOCAL_SDK_VERSION := current
+
+include $(BUILD_PACKAGE)
+
+include $(call all-makefiles-under,$(LOCAL_PATH))
diff --git a/hostsidetests/seccomp/app/AndroidManifest.xml b/hostsidetests/seccomp/app/AndroidManifest.xml
new file mode 100644
index 0000000..b8e97e3
--- /dev/null
+++ b/hostsidetests/seccomp/app/AndroidManifest.xml
@@ -0,0 +1,29 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT 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.seccomp.cts.app">
+
+    <application>
+        <uses-library android:name="android.test.runner" />
+    </application>
+
+    <instrumentation
+        android:name="android.support.test.runner.AndroidJUnitRunner"
+        android:targetPackage="android.seccomp.cts.app" />
+
+</manifest>
diff --git a/hostsidetests/seccomp/app/jni/Android.mk b/hostsidetests/seccomp/app/jni/Android.mk
new file mode 100644
index 0000000..a0604a1
--- /dev/null
+++ b/hostsidetests/seccomp/app/jni/Android.mk
@@ -0,0 +1,41 @@
+# 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 := libctsseccomp_jni
+
+# Don't include this package in any configuration by default.
+LOCAL_MODULE_TAGS := optional
+
+LOCAL_SRC_FILES := \
+		CtsSeccompJniOnLoad.cpp \
+		android_seccomp_cts_app_SeccompDeviceTest.cpp \
+
+LOCAL_SHARED_LIBRARIES := \
+		libnativehelper \
+		liblog \
+		libcutils \
+		libc++ \
+		libpackagelistparser \
+
+
+LOCAL_C_INCLUDES += ndk/sources/cpufeatures
+LOCAL_STATIC_LIBRARIES := cpufeatures
+
+LOCAL_CFLAGS := -Wall -Werror
+
+include $(BUILD_SHARED_LIBRARY)
diff --git a/hostsidetests/seccomp/app/jni/CtsSeccompJniOnLoad.cpp b/hostsidetests/seccomp/app/jni/CtsSeccompJniOnLoad.cpp
new file mode 100644
index 0000000..928b8c5
--- /dev/null
+++ b/hostsidetests/seccomp/app/jni/CtsSeccompJniOnLoad.cpp
@@ -0,0 +1,34 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <jni.h>
+#include <stdio.h>
+
+extern int register_android_seccomp_cts_app_SeccompTest(JNIEnv*);
+
+jint JNI_OnLoad(JavaVM *vm, void * /*reserved*/) {
+    JNIEnv *env = NULL;
+
+    if (vm->GetEnv((void **) &env, JNI_VERSION_1_4) != JNI_OK) {
+        return JNI_ERR;
+    }
+
+    if (register_android_seccomp_cts_app_SeccompTest(env)) {
+        return JNI_ERR;
+    }
+
+    return JNI_VERSION_1_4;
+}
diff --git a/hostsidetests/seccomp/app/jni/android_seccomp_cts_app_SeccompDeviceTest.cpp b/hostsidetests/seccomp/app/jni/android_seccomp_cts_app_SeccompDeviceTest.cpp
new file mode 100644
index 0000000..de82b44
--- /dev/null
+++ b/hostsidetests/seccomp/app/jni/android_seccomp_cts_app_SeccompDeviceTest.cpp
@@ -0,0 +1,81 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <jni.h>
+
+#define LOG_TAG "SeccompTest"
+
+#include <cutils/log.h>
+#include <unistd.h>
+#include <sys/types.h>
+#include <sys/wait.h>
+
+/*
+ * Function: testSyscallBlocked
+ * Purpose: test that the syscall listed is blocked by seccomp
+ * Parameters:
+ *        nr: syscall number
+ * Returns:
+ *        1 if blocked, else 0
+ * Exceptions: None
+ */
+static jboolean testSyscallBlocked(JNIEnv *, jobject, int nr) {
+    int pid = fork();
+    if (pid == 0) {
+        ALOGI("Calling syscall %d", nr);
+        syscall(nr);
+        return false;
+    } else {
+        int status;
+        int ret = waitpid(pid, &status, 0);
+        if (ret != pid) {
+            ALOGE("Unexpected return result from waitpid");
+            return false;
+        }
+
+        if (WIFEXITED(status)) {
+            ALOGE("syscall was not blocked");
+            return false;
+        }
+
+        if (WIFSIGNALED(status)) {
+            int signal = WTERMSIG(status);
+            if (signal == 31) {
+                ALOGI("syscall caused process termination");
+                return true;
+            }
+
+            ALOGE("Unexpected signal");
+            return false;
+        }
+
+        ALOGE("Unexpected status from syscall_exists");
+        return false;
+    }
+}
+
+static JNINativeMethod gMethods[] = {
+    { "testSyscallBlocked", "(I)Z",
+            (void*) testSyscallBlocked },
+};
+
+int register_android_seccomp_cts_app_SeccompTest(JNIEnv* env)
+{
+    jclass clazz = env->FindClass("android/seccomp/cts/app/SeccompDeviceTest");
+
+    return env->RegisterNatives(clazz, gMethods,
+            sizeof(gMethods) / sizeof(JNINativeMethod));
+}
diff --git a/hostsidetests/seccomp/app/src/android/seccomp/cts/app/SeccompDeviceTest.java b/hostsidetests/seccomp/app/src/android/seccomp/cts/app/SeccompDeviceTest.java
new file mode 100644
index 0000000..2a7bcb3
--- /dev/null
+++ b/hostsidetests/seccomp/app/src/android/seccomp/cts/app/SeccompDeviceTest.java
@@ -0,0 +1,109 @@
+/*
+ * 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.seccomp.cts.app;
+
+import org.junit.After;
+import org.junit.Assert;
+import org.junit.Assume;
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+import android.support.test.runner.AndroidJUnit4;
+import com.android.compatibility.common.util.CpuFeatures;
+
+/**
+ * Device-side tests for CtsSeccompHostTestCases
+ */
+@RunWith(AndroidJUnit4.class)
+public class SeccompDeviceTest {
+    static {
+        System.loadLibrary("ctsseccomp_jni");
+    }
+
+    @Test
+    public void testCTSSyscallBlocked() {
+        if (CpuFeatures.isArm64Cpu()) {
+            testBlocked(217); // __NR_add_key
+            testBlocked(219); // __NR_keyctl
+            testAllowed(56); // __NR_openat
+
+            // b/35034743 - do not remove test without reading bug
+            testAllowed(267); // __NR_fstatfs64
+        } else if (CpuFeatures.isArmCpu()) {
+            testBlocked(309); // __NR_add_key
+            testBlocked(311); // __NR_keyctl
+            testAllowed(322); // __NR_openat
+
+            // b/35906875 - do not remove test without reading bug
+            testAllowed(316); // __NR_inotify_init
+        } else if (CpuFeatures.isX86_64Cpu()) {
+            testBlocked(248); // __NR_add_key
+            testBlocked(250); // __NR_keyctl
+            testAllowed(257); // __NR_openat
+        } else if (CpuFeatures.isX86Cpu()) {
+            testBlocked(286); // __NR_add_key
+            testBlocked(288); // __NR_keyctl
+            testAllowed(295); // __NR_openat
+        } else if (CpuFeatures.isMips64Cpu()) {
+            testBlocked(5239); // __NR_add_key
+            testBlocked(5241); // __NR_keyctl
+            testAllowed(5247); // __NR_openat
+        } else if (CpuFeatures.isMipsCpu()) {
+            testBlocked(4280); // __NR_add_key
+            testBlocked(4282); // __NR_keyctl
+            testAllowed(4288); // __NR_openat
+        } else {
+            Assert.fail("Unsupported OS");
+        }
+    }
+
+    @Test
+    public void testCTSSwapOnOffBlocked() {
+        if (CpuFeatures.isArm64Cpu()) {
+            testBlocked(224); // __NR_swapon
+            testBlocked(225); // __NR_swapoff
+        } else if (CpuFeatures.isArmCpu()) {
+            testBlocked(87);  // __NR_swapon
+            testBlocked(115); // __NR_swapoff
+        } else if (CpuFeatures.isX86_64Cpu()) {
+            testBlocked(167); // __NR_swapon
+            testBlocked(168); // __NR_swapoff
+        } else if (CpuFeatures.isX86Cpu()) {
+            testBlocked(87);  // __NR_swapon
+            testBlocked(115); // __NR_swapoff
+        } else if (CpuFeatures.isMips64Cpu()) {
+            testBlocked(5162); // __NR_swapon
+            testBlocked(5163); // __NR_swapoff
+        } else if (CpuFeatures.isMipsCpu()) {
+            testBlocked(4087); // __NR_swapon
+            testBlocked(4115); // __NR_swapoff
+        } else {
+            Assert.fail("Unsupported OS");
+        }
+    }
+
+    private void testBlocked(int nr) {
+        Assert.assertTrue("Syscall " + nr + " not blocked", testSyscallBlocked(nr));
+    }
+
+    private void testAllowed(int nr) {
+        Assert.assertFalse("Syscall " + nr + " blocked", testSyscallBlocked(nr));
+    }
+
+    private static final native boolean testSyscallBlocked(int nr);
+}
diff --git a/hostsidetests/seccomp/src/android/seccomp/cts/SeccompHostJUnit4DeviceTest.java b/hostsidetests/seccomp/src/android/seccomp/cts/SeccompHostJUnit4DeviceTest.java
new file mode 100644
index 0000000..63258e3
--- /dev/null
+++ b/hostsidetests/seccomp/src/android/seccomp/cts/SeccompHostJUnit4DeviceTest.java
@@ -0,0 +1,65 @@
+/*
+ * 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.seccomp.cts;
+
+import com.android.tradefed.testtype.DeviceJUnit4ClassRunner;
+import com.android.tradefed.testtype.junit4.BaseHostJUnit4Test;
+
+import org.junit.After;
+import org.junit.Assert;
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+/**
+ * Test that collects test results from test package android.seccomp.cts.app.
+ *
+ * When this test builds, it also builds a support APK containing
+ * {@link android.seccomp.cts.app.SeccompDeviceTest}, the results of which are
+ * collected from the hostside and reported accordingly.
+ */
+@RunWith(DeviceJUnit4ClassRunner.class)
+public class SeccompHostJUnit4DeviceTest extends BaseHostJUnit4Test {
+
+    private static final String TEST_PKG = "android.seccomp.cts.app";
+    private static final String TEST_CLASS = TEST_PKG + "." + "SeccompDeviceTest";
+    private static final String TEST_APP = "CtsSeccompDeviceApp.apk";
+
+    private static final String TEST_CTS_SYSCALL_BLOCKED = "testCTSSyscallBlocked";
+    private static final String TEST_CTS_SWAP_ON_OFF_BLOCKED = "testCTSSwapOnOffBlocked";
+
+    @Before
+    public void setUp() throws Exception {
+        installPackage(TEST_APP);
+    }
+
+    @Test
+    public void testCTSSyscallBlocked() throws Exception {
+        Assert.assertTrue(runDeviceTests(TEST_PKG, TEST_CLASS, TEST_CTS_SYSCALL_BLOCKED));
+    }
+
+    @Test
+    public void testCTSSwapOnOffBlocked() throws Exception {
+        Assert.assertTrue(runDeviceTests(TEST_PKG, TEST_CLASS, TEST_CTS_SWAP_ON_OFF_BLOCKED));
+    }
+
+    @After
+    public void tearDown() throws Exception {
+        uninstallPackage(getDevice(), TEST_PKG);
+    }
+
+}
diff --git a/hostsidetests/security/Android.mk b/hostsidetests/security/Android.mk
index 00c5742..79325e4 100644
--- a/hostsidetests/security/Android.mk
+++ b/hostsidetests/security/Android.mk
@@ -54,6 +54,7 @@
     $(HOST_OUT_EXECUTABLES)/checkseapp \
     $(HOST_OUT_EXECUTABLES)/checkfc \
     $(HOST_OUT_EXECUTABLES)/property_info_checker \
+    $(HOST_OUT_EXECUTABLES)/searchpolicy \
     $(HOST_OUT_EXECUTABLES)/sepolicy_tests \
     $(HOST_OUT_EXECUTABLES)/treble_sepolicy_tests \
     $(HOST_OUT)/lib64/libsepolwrap.$(SHAREDLIB_EXT) \
diff --git a/hostsidetests/security/AndroidTest.xml b/hostsidetests/security/AndroidTest.xml
index eda596c..1bba585 100644
--- a/hostsidetests/security/AndroidTest.xml
+++ b/hostsidetests/security/AndroidTest.xml
@@ -14,6 +14,7 @@
      limitations under the License.
 -->
 <configuration description="Config for the CTS Security host tests">
+    <option name="test-suite-tag" value="cts" />
     <option name="config-descriptor:metadata" key="component" value="security" />
     <target_preparer class="com.android.compatibility.common.tradefed.targetprep.FilePusher">
         <option name="cleanup" value="true" />
diff --git a/hostsidetests/security/src/android/cts/security/FileSystemPermissionTest.java b/hostsidetests/security/src/android/cts/security/FileSystemPermissionTest.java
index d8b2816..d878342 100644
--- a/hostsidetests/security/src/android/cts/security/FileSystemPermissionTest.java
+++ b/hostsidetests/security/src/android/cts/security/FileSystemPermissionTest.java
@@ -1,12 +1,28 @@
 package android.cts.security;
 
+import static android.security.cts.SELinuxHostTest.copyResourceToTempFile;
+import static android.security.cts.SELinuxHostTest.getDevicePolicyFile;
+import static android.security.cts.SELinuxHostTest.isMac;
+
 import com.android.tradefed.device.ITestDevice;
 import com.android.tradefed.device.DeviceNotAvailableException;
 import com.android.tradefed.testtype.DeviceTestCase;
 
+import java.io.ByteArrayOutputStream;
+import java.io.File;
+import java.io.InputStream;
+import java.io.IOException;
+import java.nio.file.Files;
+import java.util.ArrayList;
 import java.util.Arrays;
 import java.util.HashSet;
+import java.util.List;
 import java.util.Set;
+import java.util.concurrent.Callable;
+import java.util.concurrent.ExecutionException;
+import java.util.concurrent.Executors;
+import java.util.concurrent.ExecutorService;
+import java.util.concurrent.Future;
 
 public class FileSystemPermissionTest extends DeviceTestCase {
 
@@ -52,4 +68,184 @@
     private static String getInsecureDeviceAdbCommand(String path, String type) {
         return String.format(INSECURE_DEVICE_ADB_COMMAND, path, type);
     }
+
+    private static String HW_RNG_DEVICE = "/dev/hw_random";
+
+    public void testDevHwRandomPermissions() throws Exception {
+        // This test asserts that, if present, /dev/hw_random must:
+        // 1. Be owned by UID root and GID system
+        // 2. Have file permissions 0440 (only readable, and only by owner and group). The reason
+        //    for being not readable by all/other is to avoid apps reading from this device.
+        //    Firstly, /dev/hw_random is not public API for apps. Secondly, apps might erroneously
+        //    use the output of Hardware RNG as trusted random output. Android does not trust output
+        //    of /dev/hw_random. HW RNG output is only used for mixing into Linux RNG as untrusted
+        //    input.
+        // 3. Be a character device with major:minor 10:183 -- hwrng kernel driver is using MAJOR 10
+        //    and MINOR 183
+        // 4. Be openable and readable by system_server according to SELinux policy
+
+        if (!mDevice.doesFileExist(HW_RNG_DEVICE)) {
+            // Hardware RNG device is missing. This is OK because it is not required to be exposed
+            // on all devices.
+            return;
+        }
+
+        String command = "ls -l " + HW_RNG_DEVICE;
+        String output = mDevice.executeShellCommand(command).trim();
+        if (!output.endsWith(" " + HW_RNG_DEVICE)) {
+            fail("Unexpected output from " + command + ": \"" + output + "\"");
+        }
+        String[] outputWords = output.split("\\s");
+        assertEquals("Wrong file mode on " + HW_RNG_DEVICE, "cr--r-----", outputWords[0]);
+        assertEquals("Wrong owner of " + HW_RNG_DEVICE, "root", outputWords[2]);
+        assertEquals("Wrong group of " + HW_RNG_DEVICE, "system", outputWords[3]);
+        assertEquals("Wrong device major on " + HW_RNG_DEVICE, "10,", outputWords[4]);
+        assertEquals("Wrong device minor on " + HW_RNG_DEVICE, "183", outputWords[5]);
+
+        command = "ls -Z " + HW_RNG_DEVICE;
+        output = mDevice.executeShellCommand(command).trim();
+        assertEquals(
+                "Wrong SELinux label on " + HW_RNG_DEVICE,
+                "u:object_r:hw_random_device:s0 " + HW_RNG_DEVICE,
+                output);
+
+        File sepolicy = getDevicePolicyFile(mDevice);
+        output =
+                new String(
+                        execSearchPolicy(
+                                "--allow",
+                                "-s", "system_server",
+                                "-t", "hw_random_device",
+                                "-c", "chr_file",
+                                "-p", "open",
+                                sepolicy.getPath()));
+        if (output.trim().isEmpty()) {
+            fail("SELinux policy does not permit system_server to open " + HW_RNG_DEVICE);
+        }
+        output =
+                new String(
+                        execSearchPolicy(
+                                "--allow",
+                                "-s", "system_server",
+                                "-t", "hw_random_device",
+                                "-c", "chr_file",
+                                "-p", "read",
+                                sepolicy.getPath()));
+        if (output.trim().isEmpty()) {
+            fail("SELinux policy does not permit system_server to read " + HW_RNG_DEVICE);
+        }
+    }
+
+    /**
+     * Executes {@code searchpolicy} executable with the provided parameters and returns the
+     * contents of standard output.
+     *
+     * @throws IOException if execution of searchpolicy fails, returns non-zero error code, or
+     *         non-empty stderr
+     */
+    private static byte[] execSearchPolicy(String... args)
+            throws InterruptedException, IOException {
+        File tmpDir = Files.createTempDirectory("searchpolicy").toFile();
+        try {
+            String[] envp;
+            File libsepolwrap;
+            if (isMac()) {
+                libsepolwrap = copyResourceToTempFile("/libsepolwrap.dylib");
+                libsepolwrap =
+                        Files.move(
+                                libsepolwrap.toPath(),
+                                new File(tmpDir, "libsepolwrap.dylib").toPath()).toFile();
+                File libcpp = copyResourceToTempFile("/libc++.dylib");
+                Files.move(libcpp.toPath(), new File(tmpDir, "libc++.dylib").toPath());
+                envp = new String[] {"DYLD_LIBRARY_PATH=" + tmpDir.getAbsolutePath()};
+            } else {
+                libsepolwrap = copyResourceToTempFile("/libsepolwrap.so");
+                libsepolwrap =
+                        Files.move(
+                                libsepolwrap.toPath(),
+                                new File(tmpDir, "libsepolwrap.so").toPath()).toFile();
+                File libcpp = copyResourceToTempFile("/libc++.so");
+                Files.move(libcpp.toPath(), new File(tmpDir, "libc++.so").toPath());
+                envp = new String[] {"LD_LIBRARY_PATH=" + tmpDir.getAbsolutePath()};
+            }
+            File searchpolicy = copyResourceToTempFile("/searchpolicy");
+            searchpolicy =
+                    Files.move(
+                        searchpolicy.toPath(),
+                        new File(tmpDir, "searchpolicy").toPath()).toFile();
+            searchpolicy.setExecutable(true);
+            libsepolwrap.setExecutable(true);
+            List<String> cmd = new ArrayList<>(3 + args.length);
+            cmd.add(searchpolicy.getPath());
+            cmd.add("--libpath");
+            cmd.add(libsepolwrap.getPath());
+            for (String arg : args) {
+                cmd.add(arg);
+            }
+            return execAndCaptureOutput(cmd.toArray(new String[0]), envp);
+        } finally {
+            // Delete tmpDir
+            File[] files = tmpDir.listFiles();
+            if (files == null) {
+                files = new File[0];
+            }
+            for (File f : files) {
+                f.delete();
+            }
+            tmpDir.delete();
+        }
+    }
+
+    /**
+     * Executes the provided command and returns the contents of standard output.
+     *
+     * @throws IOException if execution fails, returns a non-zero error code, or non-empty stderr
+     */
+    private static byte[] execAndCaptureOutput(String[] cmd, String[] envp)
+            throws InterruptedException, IOException {
+        // Start process, read its stdout and stderr in two corresponding background threads, wait
+        // for process to terminate, throw if stderr is not empty or if return code != 0.
+        final Process p = Runtime.getRuntime().exec(cmd, envp);
+        ExecutorService executorService = null;
+        try {
+            executorService = Executors.newFixedThreadPool(2);
+            Future<byte[]> stdoutContentsFuture =
+                    executorService.submit(new DrainCallable(p.getInputStream()));
+            Future<byte[]> stderrContentsFuture =
+                    executorService.submit(new DrainCallable(p.getErrorStream()));
+            int errorCode = p.waitFor();
+            byte[] stderrContents = stderrContentsFuture.get();
+            if ((errorCode != 0)  || (stderrContents.length > 0)) {
+                throw new IOException(
+                        cmd[0] + " failed with error code " + errorCode
+                            + ": " + new String(stderrContents));
+            }
+            return stdoutContentsFuture.get();
+        } catch (ExecutionException e) {
+            throw new IOException("Failed to read stdout or stderr of " + cmd[0], e);
+        } finally {
+            if (executorService != null) {
+                executorService.shutdownNow();
+            }
+        }
+    }
+
+    private static class DrainCallable implements Callable<byte[]> {
+        private final InputStream mIn;
+
+        private DrainCallable(InputStream in) {
+            mIn = in;
+        }
+
+        @Override
+        public byte[] call() throws IOException {
+            ByteArrayOutputStream result = new ByteArrayOutputStream();
+            byte[] buf = new byte[16384];
+            int chunkSize;
+            while ((chunkSize = mIn.read(buf)) != -1) {
+                result.write(buf, 0, chunkSize);
+            }
+            return result.toByteArray();
+        }
+    }
 }
diff --git a/hostsidetests/security/src/android/security/cts/SELinuxHostTest.java b/hostsidetests/security/src/android/security/cts/SELinuxHostTest.java
index dca5076..c005749 100644
--- a/hostsidetests/security/src/android/security/cts/SELinuxHostTest.java
+++ b/hostsidetests/security/src/android/security/cts/SELinuxHostTest.java
@@ -108,8 +108,8 @@
         mDevice = device;
     }
 
-    private File copyResourceToTempFile(String resName) throws IOException {
-        InputStream is = this.getClass().getResourceAsStream(resName);
+    public static File copyResourceToTempFile(String resName) throws IOException {
+        InputStream is = SELinuxHostTest.class.getResourceAsStream(resName);
         File tempFile = File.createTempFile("SELinuxHostTest", ".tmp");
         FileOutputStream os = new FileOutputStream(tempFile);
         byte[] buf = new byte[1024];
@@ -657,7 +657,7 @@
                    + errorString, errorString.length() == 0);
     }
 
-    private boolean isMac() {
+    public static boolean isMac() {
         String os = System.getProperty("os.name").toLowerCase();
         return (os.startsWith("mac") || os.startsWith("darwin"));
     }
diff --git a/hostsidetests/services/activityandwindowmanager/Android.mk b/hostsidetests/services/activityandwindowmanager/Android.mk
deleted file mode 100644
index 178cb8a..0000000
--- a/hostsidetests/services/activityandwindowmanager/Android.mk
+++ /dev/null
@@ -1,17 +0,0 @@
-#
-# Copyright (C) 2015 The Android Open Source Project
-#
-# Licensed under the Apache License, Version 2.0 (the "License");
-# you may not use this file except in compliance with the License.
-# You may obtain a copy of the License at
-#
-#      http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing, software
-# distributed under the License is distributed on an "AS IS" BASIS,
-# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-# See the License for the specific language governing permissions and
-# limitations under the License.
-#
-
-include $(call all-subdir-makefiles)
diff --git a/hostsidetests/services/activityandwindowmanager/activitymanager/Android.mk b/hostsidetests/services/activityandwindowmanager/activitymanager/Android.mk
deleted file mode 100644
index 1761ba6..0000000
--- a/hostsidetests/services/activityandwindowmanager/activitymanager/Android.mk
+++ /dev/null
@@ -1,39 +0,0 @@
-# Copyright (C) 2015 The Android Open Source Project
-#
-# Licensed under the Apache License, Version 2.0 (the "License");
-# you may not use this file except in compliance with the License.
-# You may obtain a copy of the License at
-#
-#      http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing, software
-# distributed under the License is distributed on an "AS IS" BASIS,
-# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-# See the License for the specific language governing permissions and
-# limitations under the License.
-
-LOCAL_PATH := $(call my-dir)
-
-include $(CLEAR_VARS)
-
-LOCAL_MODULE_TAGS := optional
-
-# Must match the package name in CtsTestCaseList.mk
-LOCAL_MODULE := CtsServicesHostTestCases
-
-LOCAL_SRC_FILES := $(call all-java-files-under, src)
-
-LOCAL_JAVA_LIBRARIES := cts-tradefed tradefed
-LOCAL_STATIC_JAVA_LIBRARIES := cts-amwm-util  \
-    cts-display-service-app-util \
-    platform-test-annotations-host
-
-LOCAL_CTS_TEST_PACKAGE := android.server
-
-# Tag this module as a cts test artifact
-LOCAL_COMPATIBILITY_SUITE := cts vts general-tests
-
-include $(BUILD_CTS_HOST_JAVA_LIBRARY)
-
-# Build the test APKs using their own makefiles
-include $(call all-makefiles-under,$(LOCAL_PATH))
diff --git a/hostsidetests/services/activityandwindowmanager/activitymanager/AndroidTest.xml b/hostsidetests/services/activityandwindowmanager/activitymanager/AndroidTest.xml
deleted file mode 100644
index 5ecde6b..0000000
--- a/hostsidetests/services/activityandwindowmanager/activitymanager/AndroidTest.xml
+++ /dev/null
@@ -1,33 +0,0 @@
-<?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 Sample host test cases">
-    <option name="config-descriptor:metadata" key="component" value="framework" />
-    <target_preparer class="com.android.tradefed.targetprep.suite.SuiteApkInstaller">
-        <option name="cleanup-apks" value="true" />
-        <option name="test-file-name" value="CtsDeviceServicesTestApp.apk" />
-        <option name="test-file-name" value="CtsDeviceServicesTestSecondApp.apk" />
-        <option name="test-file-name" value="CtsDeviceServicesTestThirdApp.apk" />
-        <option name="test-file-name" value="CtsDeviceDebuggableApp.apk" />
-        <option name="test-file-name" value="CtsDeviceDisplaySizeApp.apk" />
-        <option name="test-file-name" value="CtsDisplayServiceApp.apk" />
-        <option name="test-file-name" value="CtsDeviceTranslucentTestApp.apk" />
-        <option name="test-file-name" value="CtsDeviceTranslucentTestApp26.apk" />
-    </target_preparer>
-    <test class="com.android.compatibility.common.tradefed.testtype.JarHostTest" >
-        <option name="jar" value="CtsServicesHostTestCases.jar" />
-        <option name="runtime-hint" value="4m44s" />
-    </test>
-</configuration>
diff --git a/hostsidetests/services/activityandwindowmanager/activitymanager/app/AndroidManifest.xml b/hostsidetests/services/activityandwindowmanager/activitymanager/app/AndroidManifest.xml
deleted file mode 100755
index 63f41d1..0000000
--- a/hostsidetests/services/activityandwindowmanager/activitymanager/app/AndroidManifest.xml
+++ /dev/null
@@ -1,413 +0,0 @@
-<?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.
- -->
-
-<manifest xmlns:android="http://schemas.android.com/apk/res/android"
-          xmlns:androidprv="http://schemas.android.com/apk/prv/res/android"
-          package="android.server.cts">
-
-    <!-- virtual display test permissions -->
-    <uses-permission android:name="android.permission.CAPTURE_VIDEO_OUTPUT" />
-    <uses-permission android:name="android.permission.DISABLE_KEYGUARD" />
-    <uses-permission android:name="android.permission.BIND_VOICE_INTERACTION" />
-
-    <application>
-        <activity android:name=".TestActivity"
-                android:resizeableActivity="true"
-                android:supportsPictureInPicture="true"
-                android:exported="true"
-        />
-        <activity android:name=".TestActivityWithSameAffinity"
-                android:resizeableActivity="true"
-                android:supportsPictureInPicture="true"
-                android:exported="true"
-                android:taskAffinity="nobody.but.PipActivitySameAffinity"
-        />
-        <activity android:name=".TranslucentTestActivity"
-                android:resizeableActivity="true"
-                android:supportsPictureInPicture="true"
-                android:configChanges="orientation|screenSize|smallestScreenSize|screenLayout"
-                android:theme="@style/Theme.Transparent" />
-        <activity android:name=".VrTestActivity"
-                android:resizeableActivity="true"
-                android:exported="true"
-        />
-        <activity android:name=".ResumeWhilePausingActivity"
-                android:allowEmbedded="true"
-                android:resumeWhilePausing="true"
-                android:taskAffinity=""
-                android:exported="true"
-        />
-        <activity android:name=".ResizeableActivity"
-                android:resizeableActivity="true"
-                android:allowEmbedded="true"
-                android:exported="true"
-                android:configChanges="orientation|screenSize|smallestScreenSize|screenLayout|colorMode|density"
-        />
-        <activity android:name=".NonResizeableActivity"
-                android:resizeableActivity="false"
-                android:exported="true"
-        />
-        <activity android:name=".DockedActivity"
-                android:resizeableActivity="true"
-                android:exported="true"
-                android:taskAffinity="nobody.but.DockedActivity"
-        />
-        <activity android:name=".TranslucentActivity"
-            android:theme="@android:style/Theme.Translucent.NoTitleBar"
-            android:resizeableActivity="true"
-            android:taskAffinity="nobody.but.TranslucentActivity"
-            android:exported="true"
-        />
-        <activity android:name=".DialogWhenLargeActivity"
-                android:exported="true"
-                android:theme="@android:style/Theme.DeviceDefault.Light.DialogWhenLarge"
-        />
-        <activity android:name=".NoRelaunchActivity"
-                android:resizeableActivity="true"
-                android:configChanges="orientation|screenSize|smallestScreenSize|screenLayout|fontScale"
-                android:exported="true"
-                android:taskAffinity="nobody.but.NoRelaunchActivity"
-        />
-        <activity android:name=".SlowCreateActivity"
-                android:resizeableActivity="true"
-                android:exported="true"
-        />
-        <activity android:name=".LaunchingActivity"
-                android:resizeableActivity="true"
-                android:exported="true"
-                android:taskAffinity="nobody.but.LaunchingActivity"
-        />
-        <activity android:name=".AltLaunchingActivity"
-                android:resizeableActivity="true"
-                android:exported="true"
-                android:taskAffinity="nobody.but.LaunchingActivity"
-        />
-        <activity android:name=".PipActivity"
-                android:resizeableActivity="false"
-                android:supportsPictureInPicture="true"
-                android:configChanges="orientation|screenSize|smallestScreenSize|screenLayout"
-                android:exported="true"
-                android:taskAffinity="nobody.but.PipActivity"
-        />
-        <activity android:name=".PipActivity2"
-                  android:resizeableActivity="false"
-                  android:supportsPictureInPicture="true"
-                  android:configChanges="orientation|screenSize|smallestScreenSize|screenLayout"
-                  android:exported="true"
-                  android:taskAffinity="nobody.but.PipActivity2"
-        />
-        <activity android:name=".PipOnStopActivity"
-                  android:resizeableActivity="false"
-                  android:supportsPictureInPicture="true"
-                  android:configChanges="orientation|screenSize|smallestScreenSize|screenLayout"
-                  android:exported="true"
-                  android:taskAffinity="nobody.but.PipOnStopActivity"
-        />
-        <activity android:name=".PipActivityWithSameAffinity"
-                  android:resizeableActivity="false"
-                  android:supportsPictureInPicture="true"
-                  android:configChanges="orientation|screenSize|smallestScreenSize|screenLayout"
-                  android:exported="true"
-                  android:taskAffinity="nobody.but.PipActivitySameAffinity"
-        />
-        <activity android:name=".AlwaysFocusablePipActivity"
-                  android:theme="@style/Theme.Transparent"
-                  android:resizeableActivity="false"
-                  android:supportsPictureInPicture="true"
-                  androidprv:alwaysFocusable="true"
-                  android:exported="true"
-                  android:taskAffinity="nobody.but.AlwaysFocusablePipActivity"
-        />
-        <activity android:name=".LaunchIntoPinnedStackPipActivity"
-                  android:resizeableActivity="false"
-                  android:supportsPictureInPicture="true"
-                  androidprv:alwaysFocusable="true"
-                  android:configChanges="orientation|screenSize|smallestScreenSize|screenLayout"
-                  android:exported="true"
-        />
-        <activity android:name=".LaunchPipOnPipActivity"
-                  android:resizeableActivity="false"
-                  android:supportsPictureInPicture="true"
-                  android:taskAffinity="nobody.but.LaunchPipOnPipActivity"
-                  android:configChanges="orientation|screenSize|smallestScreenSize|screenLayout"
-                  android:exported="true"
-        />
-        <activity android:name=".LaunchEnterPipActivity"
-                  android:resizeableActivity="false"
-                  android:supportsPictureInPicture="true"
-                  androidprv:alwaysFocusable="true"
-                  android:configChanges="orientation|screenSize|smallestScreenSize|screenLayout"
-                  android:exported="true"
-        />
-        <activity android:name=".FreeformActivity"
-                  android:resizeableActivity="true"
-                  android:taskAffinity="nobody.but.FreeformActivity"
-                  android:exported="true"
-        />
-        <activity android:name=".TopLeftLayoutActivity"
-                  android:resizeableActivity="true"
-                  android:configChanges="orientation|screenSize|smallestScreenSize|screenLayout"
-                  android:exported="true">
-                  <layout android:defaultWidth="240dp"
-                          android:defaultHeight="160dp"
-                          android:gravity="top|left"
-                          android:minWidth="100dp"
-                          android:minHeight="80dp"
-                  />
-        </activity>
-        <activity android:name=".TopRightLayoutActivity"
-                  android:resizeableActivity="true"
-                  android:configChanges="orientation|screenSize|smallestScreenSize|screenLayout"
-                  android:exported="true">
-                  <layout android:defaultWidth="25%"
-                          android:defaultHeight="35%"
-                          android:gravity="top|right"
-                          android:minWidth="90dp"
-                          android:minHeight="80dp"
-                  />
-        </activity>
-        <activity android:name=".BottomLeftLayoutActivity"
-                  android:resizeableActivity="true"
-                  android:configChanges="orientation|screenSize|smallestScreenSize|screenLayout"
-                  android:exported="true">
-                  <layout android:defaultWidth="25%"
-                          android:defaultHeight="35%"
-                          android:gravity="bottom|left"
-                          android:minWidth="90dp"
-                          android:minHeight="80dp"
-                  />
-        </activity>
-        <activity android:name=".BottomRightLayoutActivity"
-                  android:resizeableActivity="true"
-                  android:configChanges="orientation|screenSize|smallestScreenSize|screenLayout"
-                  android:exported="true">
-                  <layout android:defaultWidth="240dp"
-                          android:defaultHeight="160dp"
-                          android:gravity="bottom|right"
-                          android:minWidth="100dp"
-                          android:minHeight="80dp"
-                  />
-        </activity>
-        <activity android:name=".TurnScreenOnActivity"
-                  android:exported="true"
-        />
-        <activity android:name=".TurnScreenOnDismissKeyguardActivity"
-            android:exported="true"
-        />
-        <activity android:name=".SingleTaskActivity"
-            android:exported="true"
-            android:launchMode="singleTask"
-        />
-        <activity android:name=".SingleInstanceActivity"
-            android:exported="true"
-            android:launchMode="singleInstance"
-        />
-        <activity android:name=".TrampolineActivity"
-                  android:exported="true"
-                  android:theme="@android:style/Theme.NoDisplay"
-        />
-        <activity android:name=".BroadcastReceiverActivity"
-                  android:resizeableActivity="true"
-                  android:exported="true"
-        />
-        <activity-alias android:enabled="true"
-                android:exported="true"
-                android:name=".EntryPointAliasActivity"
-                android:targetActivity=".TrampolineActivity" >
-        </activity-alias>
-        <activity android:name=".BottomActivity"
-                  android:exported="true"
-                  android:theme="@style/NoPreview"
-        />
-        <activity android:name=".TopActivity"
-                  android:process=".top_process"
-                  android:exported="true"
-                  android:theme="@style/NoPreview"
-        />
-        <activity android:name=".TranslucentTopActivity"
-                  android:process=".top_process"
-                  android:exported="true"
-                  android:theme="@style/TranslucentTheme"
-        />
-        <!-- An animation test with an explicitly opaque theme, overriding device defaults, as the
-             animation background being tested is not used in translucent activities. -->
-        <activity android:name=".AnimationTestActivity"
-                  android:theme="@style/OpaqueTheme"
-                  android:exported="true"
-        />
-        <activity android:name=".VirtualDisplayActivity"
-                  android:resizeableActivity="true"
-                  android:exported="true"
-                  android:configChanges="orientation|screenSize|smallestScreenSize|screenLayout"
-        />
-        <activity android:name=".ShowWhenLockedActivity"
-                  android:exported="true"
-        />
-        <activity android:name=".ShowWhenLockedWithDialogActivity"
-                  android:exported="true"
-        />
-        <activity android:name=".ShowWhenLockedDialogActivity"
-            android:exported="true"
-            android:theme="@android:style/Theme.Material.Dialog"
-        />
-        <activity android:name=".ShowWhenLockedTranslucentActivity"
-                  android:exported="true"
-                  android:theme="@android:style/Theme.Translucent"
-        />
-        <activity android:name=".DismissKeyguardActivity"
-                  android:exported="true"
-        />
-        <activity android:name=".DismissKeyguardMethodActivity"
-            android:exported="true"
-        />
-        <activity android:name=".WallpaperActivity"
-            android:exported="true"
-            android:theme="@style/WallpaperTheme"
-        />
-        <activity android:name=".KeyguardLockActivity"
-                  android:exported="true"
-        />
-        <activity android:name=".LogConfigurationActivity"
-            android:exported="true"
-            android:configChanges="orientation|screenSize|smallestScreenSize|screenLayout"
-        />
-        <activity android:name=".PortraitOrientationActivity"
-                  android:exported="true"
-                  android:screenOrientation="portrait"
-                  android:documentLaunchMode="always"
-        />
-        <activity android:name=".LandscapeOrientationActivity"
-                  android:exported="true"
-                  android:screenOrientation="landscape"
-                  android:documentLaunchMode="always"
-        />
-        <activity android:name=".MoveTaskToBackActivity"
-                  android:exported="true"
-                  android:launchMode="singleInstance"
-        />
-        <activity android:name=".FinishableActivity"
-                  android:exported="true"
-        />
-        <activity android:name=".NightModeActivity"
-                  android:exported="true"
-                  android:configChanges="uiMode"
-        />
-        <activity android:name=".FontScaleActivity"
-                  android:exported="true"
-        />
-        <activity android:name=".FontScaleNoRelaunchActivity"
-                  android:exported="true"
-                  android:configChanges="fontScale"
-        />
-        <receiver
-            android:name=".LaunchBroadcastReceiver"
-            android:enabled="true"
-            android:exported="true" >
-            <intent-filter>
-                <action android:name="android.server.cts.LAUNCH_BROADCAST_ACTION"/>
-            </intent-filter>
-        </receiver>
-
-        <activity android:name=".AssistantActivity"
-            android:exported="true" />
-        <activity android:name=".TranslucentAssistantActivity"
-            android:exported="true"
-            android:theme="@style/Theme.Transparent" />
-        <activity android:name=".LaunchAssistantActivityFromSession"
-            android:taskAffinity="nobody.but.LaunchAssistantActivityFromSession"
-            android:exported="true" />
-        <activity android:name=".LaunchAssistantActivityIntoAssistantStack"
-            android:taskAffinity="nobody.but.LaunchAssistantActivityIntoAssistantStack"
-            android:exported="true" />
-
-        <service android:name=".AssistantVoiceInteractionService"
-                 android:permission="android.permission.BIND_VOICE_INTERACTION"
-                 android:exported="true">
-            <meta-data android:name="android.voice_interaction"
-                       android:resource="@xml/interaction_service" />
-            <intent-filter>
-                <action android:name="android.service.voice.VoiceInteractionService" />
-            </intent-filter>
-        </service>
-
-        <service android:name=".AssistantVoiceInteractionSessionService"
-                 android:permission="android.permission.BIND_VOICE_INTERACTION"
-                 android:exported="true" />
-
-        <activity android:name=".SplashscreenActivity"
-            android:taskAffinity="nobody.but.SplashscreenActivity"
-            android:theme="@style/SplashscreenTheme"
-            android:exported="true" />
-
-
-        <activity android:name=".SwipeRefreshActivity"
-                  android:exported="true" />
-
-        <activity android:name=".NoHistoryActivity"
-                  android:noHistory="true"
-                  android:exported="true" />
-
-        <activity android:name=".ShowWhenLockedAttrActivity"
-                  android:showWhenLocked="true"
-                  android:exported="true" />
-
-        <activity android:name=".ShowWhenLockedAttrRemoveAttrActivity"
-                  android:showWhenLocked="true"
-                  android:exported="true" />
-
-        <activity android:name=".ShowWhenLockedAttrWithDialogActivity"
-                  android:showWhenLocked="true"
-                  android:exported="true" />
-
-        <activity android:name=".TurnScreenOnAttrActivity"
-                  android:turnScreenOn="true"
-                  android:exported="true" />
-
-        <activity android:name=".TurnScreenOnShowOnLockActivity"
-                  android:showWhenLocked="true"
-                  android:turnScreenOn="true"
-                  android:exported="true" />
-
-        <activity android:name=".TurnScreenOnAttrRemoveAttrActivity"
-                  android:turnScreenOn="true"
-                  android:showWhenLocked="true"
-                  android:exported="true" />
-
-        <activity android:name=".TurnScreenOnSingleTaskActivity"
-                  android:turnScreenOn="true"
-                  android:showWhenLocked="true"
-                  android:exported="true"
-                  android:launchMode="singleTask" />
-
-        <activity android:name=".TurnScreenOnAttrDismissKeyguardActivity"
-                  android:turnScreenOn="true"
-                  android:exported="true"/>
-
-        <activity android:name=".TurnScreenOnWithRelayoutActivity"
-                  android:exported="true"/>
-
-        <service android:name="com.android.cts.verifier.vr.MockVrListenerService"
-                 android:exported="true"
-                 android:enabled="true"
-                 android:permission="android.permission.BIND_VR_LISTENER_SERVICE">
-           <intent-filter>
-               <action android:name="android.service.vr.VrListenerService" />
-           </intent-filter>
-        </service>
-    </application>
-</manifest>
-
diff --git a/hostsidetests/services/activityandwindowmanager/activitymanager/app/res/layout/resizeable_activity.xml b/hostsidetests/services/activityandwindowmanager/activitymanager/app/res/layout/resizeable_activity.xml
deleted file mode 100644
index de52e61..0000000
--- a/hostsidetests/services/activityandwindowmanager/activitymanager/app/res/layout/resizeable_activity.xml
+++ /dev/null
@@ -1,21 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
-  ~ Copyright (C) 2017 The Android Open Source Project
-  ~
-  ~ Licensed under the Apache License, Version 2.0 (the "License");
-  ~ you may not use this file except in compliance with the License.
-  ~ You may obtain a copy of the License at
-  ~
-  ~      http://www.apache.org/licenses/LICENSE-2.0
-  ~
-  ~ Unless required by applicable law or agreed to in writing, software
-  ~ distributed under the License is distributed on an "AS IS" BASIS,
-  ~ WITHOUT 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.server.cts.LifecycleLogView xmlns:android="http://schemas.android.com/apk/res/android"
-                android:layout_width="match_parent"
-                android:layout_height="match_parent">
-</android.server.cts.LifecycleLogView>
\ No newline at end of file
diff --git a/hostsidetests/services/activityandwindowmanager/activitymanager/app/res/xml/interaction_service.xml b/hostsidetests/services/activityandwindowmanager/activitymanager/app/res/xml/interaction_service.xml
deleted file mode 100644
index 7cf92a0..0000000
--- a/hostsidetests/services/activityandwindowmanager/activitymanager/app/res/xml/interaction_service.xml
+++ /dev/null
@@ -1,20 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!-- Copyright (C) 2017 The Android Open Source Project
-
-     Licensed under the Apache License, Version 2.0 (the "License");
-     you may not use this file except in compliance with the License.
-     You may obtain a copy of the License at
-
-          http://www.apache.org/licenses/LICENSE-2.0
-
-     Unless required by applicable law or agreed to in writing, software
-     distributed under the License is distributed on an "AS IS" BASIS,
-     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-     See the License for the specific language governing permissions and
-     limitations under the License.
--->
-
-<voice-interaction-service xmlns:android="http://schemas.android.com/apk/res/android"
-    android:sessionService="android.server.cts.AssistantVoiceInteractionSessionService"
-    android:recognitionService="android.server.cts.AssistantVoiceInteractionSessionService"
-    android:supportsAssist="true" />
diff --git a/hostsidetests/services/activityandwindowmanager/activitymanager/app/src/android/server/cts/AbstractLifecycleLogActivity.java b/hostsidetests/services/activityandwindowmanager/activitymanager/app/src/android/server/cts/AbstractLifecycleLogActivity.java
deleted file mode 100644
index 9d29917..0000000
--- a/hostsidetests/services/activityandwindowmanager/activitymanager/app/src/android/server/cts/AbstractLifecycleLogActivity.java
+++ /dev/null
@@ -1,113 +0,0 @@
-/*
- * Copyright (C) 2016 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License
- */
-
-package android.server.cts;
-
-import android.app.Activity;
-import android.content.res.Configuration;
-import android.graphics.Point;
-import android.os.Bundle;
-import android.util.DisplayMetrics;
-import android.util.Log;
-import android.view.Display;
-import android.view.WindowManager;
-
-public abstract class AbstractLifecycleLogActivity extends Activity {
-
-    @Override
-    protected void onCreate(Bundle icicle) {
-        super.onCreate(icicle);
-        Log.i(getTag(), "onCreate");
-    }
-
-    @Override
-    protected void onStart() {
-        super.onResume();
-        Log.i(getTag(), "onStart");
-    }
-
-    @Override
-    protected void onResume() {
-        super.onResume();
-        Log.i(getTag(), "onResume");
-    }
-
-    @Override
-    public void onConfigurationChanged(Configuration newConfig) {
-        super.onConfigurationChanged(newConfig);
-        Log.i(getTag(), "onConfigurationChanged");
-    }
-
-    @Override
-    public void onMultiWindowModeChanged(boolean isInMultiWindowMode, Configuration newConfig) {
-        super.onMultiWindowModeChanged(isInMultiWindowMode, newConfig);
-        Log.i(getTag(), "onMultiWindowModeChanged");
-    }
-
-    @Override
-    public void onPictureInPictureModeChanged(boolean isInPictureInPictureMode,
-            Configuration newConfig) {
-        super.onPictureInPictureModeChanged(isInPictureInPictureMode, newConfig);
-        Log.i(getTag(), "onPictureInPictureModeChanged");
-    }
-
-    @Override
-    protected void onPause() {
-        super.onPause();
-        Log.i(getTag(), "onPause");
-    }
-
-    @Override
-    protected void onStop() {
-        super.onStop();
-        Log.i(getTag(), "onStop");
-    }
-
-    @Override
-    protected void onDestroy() {
-        super.onDestroy();
-        Log.i(getTag(), "onDestroy");
-    }
-
-    protected abstract String getTag();
-
-    protected void dumpConfiguration(Configuration config) {
-        Log.i(getTag(), "Configuration: " + config);
-    }
-
-    protected void dumpDisplaySize(Configuration config) {
-        // Dump the display size as seen by this Activity.
-        final WindowManager wm = getSystemService(WindowManager.class);
-        final Display display = wm.getDefaultDisplay();
-        final Point point = new Point();
-        display.getSize(point);
-        final DisplayMetrics metrics = getResources().getDisplayMetrics();
-
-        final String line = "config"
-                + " size=" + buildCoordString(config.screenWidthDp, config.screenHeightDp)
-                + " displaySize=" + buildCoordString(point.x, point.y)
-                + " metricsSize=" + buildCoordString(metrics.widthPixels, metrics.heightPixels)
-                + " smallestScreenWidth=" + config.smallestScreenWidthDp
-                + " densityDpi=" + config.densityDpi
-                + " orientation=" + config.orientation;
-
-        Log.i(getTag(), line);
-    }
-
-    protected static String buildCoordString(int x, int y) {
-        return "(" + x + "," + y + ")";
-    }
-}
diff --git a/hostsidetests/services/activityandwindowmanager/activitymanager/app/src/android/server/cts/AltLaunchingActivity.java b/hostsidetests/services/activityandwindowmanager/activitymanager/app/src/android/server/cts/AltLaunchingActivity.java
deleted file mode 100644
index 7bf847e..0000000
--- a/hostsidetests/services/activityandwindowmanager/activitymanager/app/src/android/server/cts/AltLaunchingActivity.java
+++ /dev/null
@@ -1,23 +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.server.cts;
-
-/**
- * An additional launching activity used for alternating between two activities.
- */
-public class AltLaunchingActivity extends LaunchingActivity {
-}
diff --git a/hostsidetests/services/activityandwindowmanager/activitymanager/app/src/android/server/cts/AlwaysFocusablePipActivity.java b/hostsidetests/services/activityandwindowmanager/activitymanager/app/src/android/server/cts/AlwaysFocusablePipActivity.java
deleted file mode 100644
index b6c0667..0000000
--- a/hostsidetests/services/activityandwindowmanager/activitymanager/app/src/android/server/cts/AlwaysFocusablePipActivity.java
+++ /dev/null
@@ -1,43 +0,0 @@
-/*
- * Copyright (C) 2016 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License
- */
-
-package android.server.cts;
-
-import static android.content.Intent.FLAG_ACTIVITY_CLEAR_TASK;
-import static android.content.Intent.FLAG_ACTIVITY_NEW_TASK;
-
-import android.app.Activity;
-import android.app.ActivityOptions;
-import android.content.Intent;
-import android.content.pm.PackageManager;
-import android.graphics.Rect;
-
-public class AlwaysFocusablePipActivity extends Activity {
-
-    static void launchAlwaysFocusablePipActivity(Activity caller, boolean newTask) {
-        final Intent intent = new Intent(caller, AlwaysFocusablePipActivity.class);
-
-        intent.setFlags(FLAG_ACTIVITY_CLEAR_TASK);
-        if (newTask) {
-            intent.addFlags(FLAG_ACTIVITY_NEW_TASK);
-        }
-
-        final ActivityOptions options = ActivityOptions.makeBasic();
-        options.setLaunchBounds(new Rect(0, 0, 500, 500));
-        options.setLaunchStackId(4 /* ActivityManager.StackId.PINNED_STACK_ID */);
-        caller.startActivity(intent, options.toBundle());
-    }
-}
diff --git a/hostsidetests/services/activityandwindowmanager/activitymanager/app/src/android/server/cts/AnimationTestActivity.java b/hostsidetests/services/activityandwindowmanager/activitymanager/app/src/android/server/cts/AnimationTestActivity.java
deleted file mode 100644
index 5ae923e..0000000
--- a/hostsidetests/services/activityandwindowmanager/activitymanager/app/src/android/server/cts/AnimationTestActivity.java
+++ /dev/null
@@ -1,28 +0,0 @@
-/*
- * Copyright (C) 2016 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package android.server.cts;
-
-import android.app.Activity;
-
-public class AnimationTestActivity extends Activity {
-
-    @Override
-    protected void onResume() {
-        super.onResume();
-        overridePendingTransition(R.anim.animation_with_background, -1);
-    }
-}
diff --git a/hostsidetests/services/activityandwindowmanager/activitymanager/app/src/android/server/cts/AssistantActivity.java b/hostsidetests/services/activityandwindowmanager/activitymanager/app/src/android/server/cts/AssistantActivity.java
deleted file mode 100644
index 18f290f..0000000
--- a/hostsidetests/services/activityandwindowmanager/activitymanager/app/src/android/server/cts/AssistantActivity.java
+++ /dev/null
@@ -1,83 +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.server.cts;
-
-import static android.content.Intent.FLAG_ACTIVITY_CLEAR_TASK;
-import static android.content.Intent.FLAG_ACTIVITY_NEW_TASK;
-
-import android.app.Activity;
-import android.app.ActivityOptions;
-import android.content.ComponentName;
-import android.content.Intent;
-import android.os.Bundle;
-
-public class AssistantActivity extends Activity {
-
-    // Launches the given activity in onResume
-    public static final String EXTRA_LAUNCH_NEW_TASK = "launch_new_task";
-    // Finishes this activity in onResume, this happens after EXTRA_LAUNCH_NEW_TASK
-    public static final String EXTRA_FINISH_SELF = "finish_self";
-    // Attempts to enter picture-in-picture in onResume
-    public static final String EXTRA_ENTER_PIP = "enter_pip";
-
-    @Override
-    protected void onCreate(Bundle savedInstanceState) {
-        super.onCreate(savedInstanceState);
-
-        // Set the layout
-        setContentView(R.layout.assistant);
-
-        // Launch the new activity if requested
-        if (getIntent().hasExtra(EXTRA_LAUNCH_NEW_TASK)) {
-            Intent i = new Intent();
-            i.setComponent(new ComponentName(this, getPackageName() + "."
-                    + getIntent().getStringExtra(EXTRA_LAUNCH_NEW_TASK)));
-            i.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_CLEAR_TASK);
-            startActivity(i);
-        }
-
-        // Enter pip if requested
-        if (getIntent().hasExtra(EXTRA_ENTER_PIP)) {
-            try {
-                enterPictureInPictureMode();
-            } catch (IllegalStateException e) {
-                finish();
-                return;
-            }
-        }
-
-        // Finish this activity if requested
-        if (getIntent().hasExtra(EXTRA_FINISH_SELF)) {
-            finish();
-        }
-    }
-
-    /**
-     * Launches a new instance of the AssistantActivity directly into the assistant stack.
-     */
-    static void launchActivityIntoAssistantStack(Activity caller, Bundle extras) {
-        final Intent intent = new Intent(caller, AssistantActivity.class);
-        intent.setFlags(FLAG_ACTIVITY_CLEAR_TASK | FLAG_ACTIVITY_NEW_TASK);
-        if (extras != null) {
-            intent.putExtras(extras);
-        }
-
-        final ActivityOptions options = ActivityOptions.makeBasic();
-        options.setLaunchStackId(6 /* ActivityManager.StackId.ASSISTANT_STACK_ID */);
-        caller.startActivity(intent, options.toBundle());
-    }
-}
diff --git a/hostsidetests/services/activityandwindowmanager/activitymanager/app/src/android/server/cts/AssistantVoiceInteractionService.java b/hostsidetests/services/activityandwindowmanager/activitymanager/app/src/android/server/cts/AssistantVoiceInteractionService.java
deleted file mode 100644
index 51c2348..0000000
--- a/hostsidetests/services/activityandwindowmanager/activitymanager/app/src/android/server/cts/AssistantVoiceInteractionService.java
+++ /dev/null
@@ -1,64 +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.server.cts;
-
-import android.content.ComponentName;
-import android.content.Context;
-import android.content.Intent;
-import android.os.Bundle;
-import android.service.voice.VoiceInteractionService;
-import android.util.Log;
-
-public class AssistantVoiceInteractionService extends VoiceInteractionService {
-
-    private static final String TAG = AssistantVoiceInteractionService.class.getSimpleName();
-
-    private boolean mReady;
-
-    @Override
-    public void onReady() {
-        super.onReady();
-        mReady = true;
-    }
-
-    @Override
-    public int onStartCommand(Intent intent, int flags, int startId) {
-        if (!isActiveService(this, new ComponentName(this, getClass()))) {
-            Log.wtf(TAG, "**** Not starting AssistantVoiceInteractionService because" +
-                    " it is not set as the current voice interaction service");
-            stopSelf();
-            return START_NOT_STICKY;
-        }
-        if (mReady) {
-            Bundle extras = intent.getExtras() != null ? intent.getExtras() : new Bundle();
-            showSession(extras, 0);
-        }
-        return START_NOT_STICKY;
-    }
-
-    /**
-     * Starts the assistant voice interaction service, which initiates a new session that starts
-     * the assistant activity.
-     */
-    public static void launchAssistantActivity(Context context, Bundle extras) {
-        Intent i = new Intent(context, AssistantVoiceInteractionService.class);
-        if (extras != null) {
-            i.putExtras(extras);
-        }
-        context.startService(i);
-    }
-}
diff --git a/hostsidetests/services/activityandwindowmanager/activitymanager/app/src/android/server/cts/AssistantVoiceInteractionSessionService.java b/hostsidetests/services/activityandwindowmanager/activitymanager/app/src/android/server/cts/AssistantVoiceInteractionSessionService.java
deleted file mode 100644
index e711ac4..0000000
--- a/hostsidetests/services/activityandwindowmanager/activitymanager/app/src/android/server/cts/AssistantVoiceInteractionSessionService.java
+++ /dev/null
@@ -1,45 +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.server.cts;
-
-import android.content.Intent;
-import android.os.Bundle;
-import android.service.voice.VoiceInteractionSession;
-import android.service.voice.VoiceInteractionSessionService;
-
-public class AssistantVoiceInteractionSessionService extends VoiceInteractionSessionService {
-
-    @Override
-    public VoiceInteractionSession onNewSession(Bundle args) {
-        return new VoiceInteractionSession(this) {
-            @Override
-            public void onPrepareShow(Bundle args, int showFlags) {
-                setUiEnabled(false);
-            }
-
-            @Override
-            public void onShow(Bundle args, int showFlags) {
-                Intent i = new Intent(AssistantVoiceInteractionSessionService.this,
-                        AssistantActivity.class);
-                if (args != null) {
-                    i.putExtras(args);
-                }
-                startAssistantActivity(i);
-            }
-        };
-    }
-}
diff --git a/hostsidetests/services/activityandwindowmanager/activitymanager/app/src/android/server/cts/BottomActivity.java b/hostsidetests/services/activityandwindowmanager/activitymanager/app/src/android/server/cts/BottomActivity.java
deleted file mode 100644
index 38a71f1..0000000
--- a/hostsidetests/services/activityandwindowmanager/activitymanager/app/src/android/server/cts/BottomActivity.java
+++ /dev/null
@@ -1,102 +0,0 @@
-/*
- * Copyright (C) 2016 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package android.server.cts;
-
-import android.content.Context;
-import android.os.Bundle;
-import android.util.Log;
-import android.view.LayoutInflater;
-import android.view.View;
-import android.view.WindowManager;
-
-public class BottomActivity extends AbstractLifecycleLogActivity {
-
-    private static final String TAG = BottomActivity.class.getSimpleName();
-
-    private int mStopDelay;
-    private View mFloatingWindow;
-
-    @Override
-    protected String getTag() {
-        return TAG;
-    }
-
-    @Override
-    protected void onCreate(Bundle savedInstanceState) {
-        super.onCreate(savedInstanceState);
-
-        final boolean useWallpaper = getIntent().getBooleanExtra("USE_WALLPAPER", false);
-        if (useWallpaper) {
-            setTheme(R.style.WallpaperTheme);
-        }
-        setContentView(R.layout.main);
-
-        // Delayed stop is for simulating a case where resume happens before
-        // activityStopped() is received by AM, and the transition starts without
-        // going through fully stopped state (see b/30255354).
-        // If enabled, we stall onStop() of BottomActivity, open TopActivity but make
-        // it finish before onStop() ends. This will cause BottomActivity to resume before
-        // it notifies AM of activityStopped(). We also add a second window of
-        // TYPE_BASE_APPLICATION, so that the transition animation could start earlier.
-        // Otherwise the main window has to relayout to visible first and the error won't occur.
-        // Note that if the test fails, we shouldn't try to change the app here to make
-        // it pass. The test app is artificially made to simulate an failure case, but
-        // it's not doing anything wrong.
-        mStopDelay = getIntent().getIntExtra("STOP_DELAY", 0);
-        if (mStopDelay > 0) {
-            LayoutInflater inflater = getLayoutInflater();
-            mFloatingWindow = inflater.inflate(R.layout.floating, null);
-
-            WindowManager.LayoutParams params = new WindowManager.LayoutParams();
-            params.height = WindowManager.LayoutParams.WRAP_CONTENT;
-            params.width = WindowManager.LayoutParams.WRAP_CONTENT;
-            params.type = WindowManager.LayoutParams.TYPE_BASE_APPLICATION;
-            params.setTitle("Floating");
-            getWindowManager().addView(mFloatingWindow, params);
-        }
-    }
-
-    @Override
-    public void onResume() {
-        Log.d(TAG, "onResume() E");
-        super.onResume();
-
-        if (mStopDelay > 0) {
-            // Refresh floating window
-            Log.d(TAG, "Scheuling invalidate Floating Window in onResume()");
-            mFloatingWindow.invalidate();
-        }
-
-        Log.d(TAG, "onResume() X");
-    }
-
-    @Override
-    protected void onStop() {
-        super.onStop();
-
-        if (mStopDelay > 0) {
-            try {
-                Log.d(TAG, "Stalling onStop() by " + mStopDelay + " ms...");
-                Thread.sleep(mStopDelay);
-            } catch(InterruptedException e) {}
-
-            // Refresh floating window
-            Log.d(TAG, "Scheuling invalidate Floating Window in onStop()");
-            mFloatingWindow.invalidate();
-        }
-    }
-}
diff --git a/hostsidetests/services/activityandwindowmanager/activitymanager/app/src/android/server/cts/BottomLeftLayoutActivity.java b/hostsidetests/services/activityandwindowmanager/activitymanager/app/src/android/server/cts/BottomLeftLayoutActivity.java
deleted file mode 100644
index 8de1cda..0000000
--- a/hostsidetests/services/activityandwindowmanager/activitymanager/app/src/android/server/cts/BottomLeftLayoutActivity.java
+++ /dev/null
@@ -1,22 +0,0 @@
-/*
- * Copyright (C) 2016 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package android.server.cts;
-
-import android.app.Activity;
-
-public class BottomLeftLayoutActivity extends Activity {
-}
diff --git a/hostsidetests/services/activityandwindowmanager/activitymanager/app/src/android/server/cts/BottomRightLayoutActivity.java b/hostsidetests/services/activityandwindowmanager/activitymanager/app/src/android/server/cts/BottomRightLayoutActivity.java
deleted file mode 100644
index 24fa2fc..0000000
--- a/hostsidetests/services/activityandwindowmanager/activitymanager/app/src/android/server/cts/BottomRightLayoutActivity.java
+++ /dev/null
@@ -1,22 +0,0 @@
-/*
- * Copyright (C) 2016 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package android.server.cts;
-
-import android.app.Activity;
-
-public class BottomRightLayoutActivity extends Activity {
-}
diff --git a/hostsidetests/services/activityandwindowmanager/activitymanager/app/src/android/server/cts/BroadcastReceiverActivity.java b/hostsidetests/services/activityandwindowmanager/activitymanager/app/src/android/server/cts/BroadcastReceiverActivity.java
deleted file mode 100644
index 24be43a..0000000
--- a/hostsidetests/services/activityandwindowmanager/activitymanager/app/src/android/server/cts/BroadcastReceiverActivity.java
+++ /dev/null
@@ -1,86 +0,0 @@
-/*
- * Copyright (C) 2016 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License
- */
-
-package android.server.cts;
-
-import static android.view.WindowManager.LayoutParams.FLAG_DISMISS_KEYGUARD;
-
-import android.app.Activity;
-import android.app.KeyguardManager;
-import android.content.BroadcastReceiver;
-import android.content.Context;
-import android.content.Intent;
-import android.content.IntentFilter;
-import android.os.Bundle;
-import android.server.cts.tools.ActivityLauncher;
-import android.util.Log;
-
-/**
- * Activity that registers broadcast receiver .
- */
-public class BroadcastReceiverActivity extends Activity {
-
-    public static final String ACTION_TRIGGER_BROADCAST = "trigger_broadcast";
-    private static final String TAG = BroadcastReceiverActivity.class.getSimpleName();
-
-    private TestBroadcastReceiver mBroadcastReceiver = new TestBroadcastReceiver();
-
-    @Override
-    protected void onCreate(Bundle icicle) {
-        super.onCreate(icicle);
-
-        IntentFilter broadcastFilter = new IntentFilter(ACTION_TRIGGER_BROADCAST);
-
-        registerReceiver(mBroadcastReceiver, broadcastFilter);
-    }
-
-    @Override
-    protected void onDestroy() {
-        super.onDestroy();
-
-        unregisterReceiver(mBroadcastReceiver);
-    }
-
-    public class TestBroadcastReceiver extends BroadcastReceiver {
-        @Override
-        public void onReceive(Context context, Intent intent) {
-            final Bundle extras = intent.getExtras();
-            Log.i(TAG, "onReceive: extras=" + extras);
-
-            if (extras == null) {
-                return;
-            }
-            if (extras.getBoolean("finish")) {
-                finish();
-            }
-            if (extras.getBoolean("moveToBack")) {
-                moveTaskToBack(true);
-            }
-            if (extras.containsKey("orientation")) {
-                setRequestedOrientation(extras.getInt("orientation"));
-            }
-            if (extras.getBoolean("dismissKeyguard")) {
-                getWindow().addFlags(FLAG_DISMISS_KEYGUARD);
-            }
-            if (extras.getBoolean("dismissKeyguardMethod")) {
-                getSystemService(KeyguardManager.class).requestDismissKeyguard(
-                        BroadcastReceiverActivity.this, new KeyguardDismissLoggerCallback(context));
-            }
-
-            ActivityLauncher.launchActivityFromExtras(BroadcastReceiverActivity.this, extras);
-        }
-    }
-}
diff --git a/hostsidetests/services/activityandwindowmanager/activitymanager/app/src/android/server/cts/DialogWhenLargeActivity.java b/hostsidetests/services/activityandwindowmanager/activitymanager/app/src/android/server/cts/DialogWhenLargeActivity.java
deleted file mode 100644
index 139c648..0000000
--- a/hostsidetests/services/activityandwindowmanager/activitymanager/app/src/android/server/cts/DialogWhenLargeActivity.java
+++ /dev/null
@@ -1,28 +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.server.cts;
-
-/**
- * Activity with DialogWhenLarge Theme.
- */
-public class DialogWhenLargeActivity extends AbstractLifecycleLogActivity {
-    private static final String TAG = DialogWhenLargeActivity.class.getSimpleName();
-    @Override
-    protected String getTag() {
-        return TAG;
-    }
-}
diff --git a/hostsidetests/services/activityandwindowmanager/activitymanager/app/src/android/server/cts/DismissKeyguardActivity.java b/hostsidetests/services/activityandwindowmanager/activitymanager/app/src/android/server/cts/DismissKeyguardActivity.java
deleted file mode 100644
index 726a756..0000000
--- a/hostsidetests/services/activityandwindowmanager/activitymanager/app/src/android/server/cts/DismissKeyguardActivity.java
+++ /dev/null
@@ -1,30 +0,0 @@
-/*
- * Copyright (C) 2016 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License
- */
-
-package android.server.cts;
-
-import android.app.Activity;
-import android.os.Bundle;
-import android.view.WindowManager;
-
-public class DismissKeyguardActivity extends Activity {
-
-    @Override
-    protected void onCreate(Bundle savedInstanceState) {
-        super.onCreate(savedInstanceState);
-        getWindow().addFlags(WindowManager.LayoutParams.FLAG_DISMISS_KEYGUARD);
-    }
-}
diff --git a/hostsidetests/services/activityandwindowmanager/activitymanager/app/src/android/server/cts/DismissKeyguardMethodActivity.java b/hostsidetests/services/activityandwindowmanager/activitymanager/app/src/android/server/cts/DismissKeyguardMethodActivity.java
deleted file mode 100644
index c833eb2..0000000
--- a/hostsidetests/services/activityandwindowmanager/activitymanager/app/src/android/server/cts/DismissKeyguardMethodActivity.java
+++ /dev/null
@@ -1,31 +0,0 @@
-/*
- * Copyright (C) 2016 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License
- */
-
-package android.server.cts;
-
-import android.app.Activity;
-import android.app.KeyguardManager;
-import android.os.Bundle;
-
-public class DismissKeyguardMethodActivity extends Activity {
-
-    @Override
-    protected void onCreate(Bundle savedInstanceState) {
-        super.onCreate(savedInstanceState);
-        getSystemService(KeyguardManager.class).requestDismissKeyguard(this,
-                new KeyguardDismissLoggerCallback(this));
-    }
-}
diff --git a/hostsidetests/services/activityandwindowmanager/activitymanager/app/src/android/server/cts/DockedActivity.java b/hostsidetests/services/activityandwindowmanager/activitymanager/app/src/android/server/cts/DockedActivity.java
deleted file mode 100644
index 007df5f..0000000
--- a/hostsidetests/services/activityandwindowmanager/activitymanager/app/src/android/server/cts/DockedActivity.java
+++ /dev/null
@@ -1,22 +0,0 @@
-/*
- * Copyright (C) 2015 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package android.server.cts;
-
-import android.app.Activity;
-
-public class DockedActivity extends Activity {
-}
diff --git a/hostsidetests/services/activityandwindowmanager/activitymanager/app/src/android/server/cts/FinishableActivity.java b/hostsidetests/services/activityandwindowmanager/activitymanager/app/src/android/server/cts/FinishableActivity.java
deleted file mode 100644
index d64b930..0000000
--- a/hostsidetests/services/activityandwindowmanager/activitymanager/app/src/android/server/cts/FinishableActivity.java
+++ /dev/null
@@ -1,61 +0,0 @@
-/*
- * Copyright (C) 2016 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License
- */
-
-package android.server.cts;
-
-import android.content.Context;
-import android.content.Intent;
-import android.content.IntentFilter;
-import android.content.BroadcastReceiver;
-import android.os.Bundle;
-
-/**
- * This activity finishes when you send a broadcast with the following action from adb shell
- *  am broadcast -a 'android.server.cts.FinishableActivity.finish'
- */
-public class FinishableActivity extends AbstractLifecycleLogActivity {
-
-    private static final String TAG = FinishableActivity.class.getSimpleName();
-    private static final String ACTION_FINISH = "android.server.cts.FinishableActivity.finish";
-
-    private BroadcastReceiver mReceiver = new BroadcastReceiver() {
-        @Override
-        public void onReceive(Context context, Intent intent) {
-            if (intent != null && intent.getAction().equals(ACTION_FINISH)) {
-                finish();
-            }
-        }
-    };
-
-    @Override
-    protected void onCreate(Bundle savedInstanceState) {
-        super.onCreate(savedInstanceState);
-
-        IntentFilter intentFilter = new IntentFilter(ACTION_FINISH);
-        registerReceiver(mReceiver, intentFilter);
-    }
-
-    @Override
-    protected void onDestroy() {
-        unregisterReceiver(mReceiver);
-        super.onDestroy();
-    }
-
-    @Override
-    protected String getTag() {
-        return TAG;
-    }
-}
diff --git a/hostsidetests/services/activityandwindowmanager/activitymanager/app/src/android/server/cts/FontScaleActivity.java b/hostsidetests/services/activityandwindowmanager/activitymanager/app/src/android/server/cts/FontScaleActivity.java
deleted file mode 100644
index a5620c1..0000000
--- a/hostsidetests/services/activityandwindowmanager/activitymanager/app/src/android/server/cts/FontScaleActivity.java
+++ /dev/null
@@ -1,70 +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.server.cts;
-
-import android.content.res.Configuration;
-import android.content.res.TypedArray;
-import android.content.res.XmlResourceParser;
-import android.os.Bundle;
-import android.util.AttributeSet;
-import android.util.Log;
-import android.util.Xml;
-
-import org.xmlpull.v1.XmlPullParser;
-import org.xmlpull.v1.XmlPullParserException;
-
-import java.io.IOException;
-
-public class FontScaleActivity extends AbstractLifecycleLogActivity {
-    private static final String TAG = FontScaleActivity.class.getSimpleName();
-
-    @Override
-    protected void onCreate(Bundle icicle) {
-        super.onCreate(icicle);
-        dumpFontSize();
-    }
-
-    // We're basically ensuring that no matter what happens to the resources underneath the
-    // Activity, any TypedArrays obtained from the pool have the correct DisplayMetrics.
-    protected void dumpFontSize() {
-        try (XmlResourceParser parser = getResources().getXml(R.layout.font_scale)) {
-            //noinspection StatementWithEmptyBody
-            while (parser.next() != XmlPullParser.START_TAG) { }
-
-            final AttributeSet attrs = Xml.asAttributeSet(parser);
-            TypedArray ta = getTheme().obtainStyledAttributes(attrs,
-                    new int[] { android.R.attr.textSize }, 0, 0);
-            try {
-                final int fontPixelSize = ta.getDimensionPixelSize(0, -1);
-                if (fontPixelSize == -1) {
-                    throw new AssertionError("android:attr/textSize not found");
-                }
-
-                Log.i(getTag(), "fontPixelSize=" + fontPixelSize);
-            } finally {
-                ta.recycle();
-            }
-        } catch (XmlPullParserException | IOException e) {
-            throw new RuntimeException(e);
-        }
-    }
-
-    @Override
-    protected String getTag() {
-        return TAG;
-    }
-
-}
diff --git a/hostsidetests/services/activityandwindowmanager/activitymanager/app/src/android/server/cts/FontScaleNoRelaunchActivity.java b/hostsidetests/services/activityandwindowmanager/activitymanager/app/src/android/server/cts/FontScaleNoRelaunchActivity.java
deleted file mode 100644
index 8789f68..0000000
--- a/hostsidetests/services/activityandwindowmanager/activitymanager/app/src/android/server/cts/FontScaleNoRelaunchActivity.java
+++ /dev/null
@@ -1,31 +0,0 @@
-/*
- * Copyright (C) 2017 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License"); you may not
- * use this file except in compliance with the License. You may obtain a copy
- * of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
- * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
- * License for the specific language governing permissions and limitations
- * under the License.
- */
-package android.server.cts;
-
-import android.content.res.Configuration;
-
-public class FontScaleNoRelaunchActivity extends FontScaleActivity {
-    @Override
-    public void onConfigurationChanged(Configuration newConfig) {
-        super.onConfigurationChanged(newConfig);
-        dumpFontSize();
-    }
-
-    @Override
-    protected String getTag() {
-        return FontScaleNoRelaunchActivity.class.getSimpleName();
-    }
-}
diff --git a/hostsidetests/services/activityandwindowmanager/activitymanager/app/src/android/server/cts/FreeformActivity.java b/hostsidetests/services/activityandwindowmanager/activitymanager/app/src/android/server/cts/FreeformActivity.java
deleted file mode 100644
index f8c6d0c..0000000
--- a/hostsidetests/services/activityandwindowmanager/activitymanager/app/src/android/server/cts/FreeformActivity.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.server.cts;
-
-import static android.content.Intent.FLAG_ACTIVITY_CLEAR_TASK;
-import static android.content.Intent.FLAG_ACTIVITY_NEW_TASK;
-
-import android.app.Activity;
-import android.app.ActivityOptions;
-import android.content.Intent;
-import android.graphics.Rect;
-
-public class FreeformActivity extends Activity {
-
-    @Override
-    public void onResume() {
-        super.onResume();
-        final Intent intent = new Intent(this, TestActivity.class);
-        intent.setFlags(FLAG_ACTIVITY_CLEAR_TASK | FLAG_ACTIVITY_NEW_TASK);
-
-        final ActivityOptions options = ActivityOptions.makeBasic();
-        options.setLaunchBounds(new Rect(0, 0, 900, 900));
-        this.startActivity(intent, options.toBundle());
-    }
-}
diff --git a/hostsidetests/services/activityandwindowmanager/activitymanager/app/src/android/server/cts/KeyguardDismissLoggerCallback.java b/hostsidetests/services/activityandwindowmanager/activitymanager/app/src/android/server/cts/KeyguardDismissLoggerCallback.java
deleted file mode 100644
index 860f2ae..0000000
--- a/hostsidetests/services/activityandwindowmanager/activitymanager/app/src/android/server/cts/KeyguardDismissLoggerCallback.java
+++ /dev/null
@@ -1,54 +0,0 @@
-/*
- * Copyright (C) 2016 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License
- */
-
-package android.server.cts;
-
-import android.app.KeyguardManager;
-import android.app.KeyguardManager.KeyguardDismissCallback;
-import android.content.Context;
-import android.util.Log;
-
-public class KeyguardDismissLoggerCallback extends KeyguardDismissCallback {
-
-    private final String TAG = "KeyguardDismissLoggerCallback";
-
-    private final Context mContext;
-
-    public KeyguardDismissLoggerCallback(Context context) {
-        mContext = context;
-    }
-
-    @Override
-    public void onDismissError() {
-        Log.i(TAG, "onDismissError");
-    }
-
-    @Override
-    public void onDismissSucceeded() {
-        if (mContext.getSystemService(KeyguardManager.class).isDeviceLocked()) {
-            // Device is still locked? What a fail. Don't print "onDismissSucceded" such that the
-            // log fails.
-            Log.i(TAG, "dismiss succedded was called but device is still locked.");
-        } else {
-            Log.i(TAG, "onDismissSucceeded");
-        }
-    }
-
-    @Override
-    public void onDismissCancelled() {
-        Log.i(TAG, "onDismissCancelled");
-    }
-}
diff --git a/hostsidetests/services/activityandwindowmanager/activitymanager/app/src/android/server/cts/KeyguardLockActivity.java b/hostsidetests/services/activityandwindowmanager/activitymanager/app/src/android/server/cts/KeyguardLockActivity.java
deleted file mode 100644
index 352cf04..0000000
--- a/hostsidetests/services/activityandwindowmanager/activitymanager/app/src/android/server/cts/KeyguardLockActivity.java
+++ /dev/null
@@ -1,38 +0,0 @@
-/*
- * Copyright (C) 2016 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License
- */
-
-package android.server.cts;
-
-import android.app.KeyguardManager;
-import android.os.Bundle;
-
-public class KeyguardLockActivity extends BroadcastReceiverActivity {
-
-    private KeyguardManager.KeyguardLock mKeyguardLock;
-
-    @Override
-    protected void onCreate(Bundle savedInstanceState) {
-        super.onCreate(savedInstanceState);
-        mKeyguardLock = getSystemService(KeyguardManager.class).newKeyguardLock("test");
-        mKeyguardLock.disableKeyguard();
-    }
-
-    @Override
-    protected void onDestroy() {
-        mKeyguardLock.reenableKeyguard();
-        super.onDestroy();
-    }
-}
diff --git a/hostsidetests/services/activityandwindowmanager/activitymanager/app/src/android/server/cts/LandscapeOrientationActivity.java b/hostsidetests/services/activityandwindowmanager/activitymanager/app/src/android/server/cts/LandscapeOrientationActivity.java
deleted file mode 100644
index ffd5aee..0000000
--- a/hostsidetests/services/activityandwindowmanager/activitymanager/app/src/android/server/cts/LandscapeOrientationActivity.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.server.cts;
-
-import android.content.res.Configuration;
-
-public class LandscapeOrientationActivity extends AbstractLifecycleLogActivity {
-    @Override
-    protected String getTag() {
-        return "LandscapeOrientationActivity";
-    }
-
-    @Override
-    protected void onResume() {
-        super.onResume();
-        final Configuration config = getResources().getConfiguration();
-        dumpDisplaySize(config);
-    }
-
-    @Override
-    public void onConfigurationChanged(Configuration newConfig) {
-        super.onConfigurationChanged(newConfig);
-        dumpDisplaySize(newConfig);
-    }
-}
diff --git a/hostsidetests/services/activityandwindowmanager/activitymanager/app/src/android/server/cts/LaunchAssistantActivityFromSession.java b/hostsidetests/services/activityandwindowmanager/activitymanager/app/src/android/server/cts/LaunchAssistantActivityFromSession.java
deleted file mode 100644
index 2d562ff..0000000
--- a/hostsidetests/services/activityandwindowmanager/activitymanager/app/src/android/server/cts/LaunchAssistantActivityFromSession.java
+++ /dev/null
@@ -1,28 +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.server.cts;
-
-import android.app.Activity;
-
-public class LaunchAssistantActivityFromSession extends Activity {
-    @Override
-    protected void onResume() {
-        super.onResume();
-        AssistantVoiceInteractionService.launchAssistantActivity(this, getIntent().getExtras());
-        finishAndRemoveTask();
-    }
-}
diff --git a/hostsidetests/services/activityandwindowmanager/activitymanager/app/src/android/server/cts/LaunchAssistantActivityIntoAssistantStack.java b/hostsidetests/services/activityandwindowmanager/activitymanager/app/src/android/server/cts/LaunchAssistantActivityIntoAssistantStack.java
deleted file mode 100644
index 62839a4..0000000
--- a/hostsidetests/services/activityandwindowmanager/activitymanager/app/src/android/server/cts/LaunchAssistantActivityIntoAssistantStack.java
+++ /dev/null
@@ -1,39 +0,0 @@
-/*
- * Copyright (C) 2017 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License
- */
-
-package android.server.cts;
-
-import android.app.Activity;
-
-public class LaunchAssistantActivityIntoAssistantStack extends Activity {
-
-    // Launches the translucent assist activity
-    public static final String EXTRA_IS_TRANSLUCENT = "is_translucent";
-
-    @Override
-    protected void onResume() {
-        super.onResume();
-
-        if (getIntent().hasExtra(EXTRA_IS_TRANSLUCENT) &&
-                Boolean.valueOf(getIntent().getStringExtra(EXTRA_IS_TRANSLUCENT))) {
-            TranslucentAssistantActivity.launchActivityIntoAssistantStack(this,
-                    getIntent().getExtras());
-        } else {
-            AssistantActivity.launchActivityIntoAssistantStack(this, getIntent().getExtras());
-        }
-        finishAndRemoveTask();
-    }
-}
diff --git a/hostsidetests/services/activityandwindowmanager/activitymanager/app/src/android/server/cts/LaunchBroadcastReceiver.java b/hostsidetests/services/activityandwindowmanager/activitymanager/app/src/android/server/cts/LaunchBroadcastReceiver.java
deleted file mode 100644
index 6e713ec..0000000
--- a/hostsidetests/services/activityandwindowmanager/activitymanager/app/src/android/server/cts/LaunchBroadcastReceiver.java
+++ /dev/null
@@ -1,37 +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.server.cts;
-
-import android.content.BroadcastReceiver;
-import android.content.Context;
-import android.content.Intent;
-import android.server.cts.tools.ActivityLauncher;
-import android.util.Log;
-
-/** Broadcast receiver that can launch activities. */
-public class LaunchBroadcastReceiver extends BroadcastReceiver {
-    private static final String TAG = LaunchBroadcastReceiver.class.getSimpleName();
-
-    @Override
-    public void onReceive(Context context, Intent intent) {
-        try {
-            ActivityLauncher.launchActivityFromExtras(context, intent.getExtras());
-        } catch (SecurityException e) {
-            Log.e(TAG, "SecurityException launching activity");
-        }
-    }
-}
diff --git a/hostsidetests/services/activityandwindowmanager/activitymanager/app/src/android/server/cts/LaunchEnterPipActivity.java b/hostsidetests/services/activityandwindowmanager/activitymanager/app/src/android/server/cts/LaunchEnterPipActivity.java
deleted file mode 100644
index e2b4786..0000000
--- a/hostsidetests/services/activityandwindowmanager/activitymanager/app/src/android/server/cts/LaunchEnterPipActivity.java
+++ /dev/null
@@ -1,29 +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.server.cts;
-
-import android.app.Activity;
-import android.graphics.Rect;
-import android.os.Bundle;
-
-public class LaunchEnterPipActivity extends Activity {
-    @Override
-    protected void onCreate(Bundle bundle) {
-        super.onCreate(bundle);
-        PipActivity.launchEnterPipActivity(this);
-    }
-}
diff --git a/hostsidetests/services/activityandwindowmanager/activitymanager/app/src/android/server/cts/LaunchIntoPinnedStackPipActivity.java b/hostsidetests/services/activityandwindowmanager/activitymanager/app/src/android/server/cts/LaunchIntoPinnedStackPipActivity.java
deleted file mode 100644
index 86c4834..0000000
--- a/hostsidetests/services/activityandwindowmanager/activitymanager/app/src/android/server/cts/LaunchIntoPinnedStackPipActivity.java
+++ /dev/null
@@ -1,27 +0,0 @@
-/*
- * Copyright (C) 2016 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License
- */
-
-package android.server.cts;
-
-import android.app.Activity;
-
-public class LaunchIntoPinnedStackPipActivity extends Activity {
-    @Override
-    protected void onResume() {
-        super.onResume();
-        AlwaysFocusablePipActivity.launchAlwaysFocusablePipActivity(this, true /* newTask */);
-    }
-}
diff --git a/hostsidetests/services/activityandwindowmanager/activitymanager/app/src/android/server/cts/LaunchPipOnPipActivity.java b/hostsidetests/services/activityandwindowmanager/activitymanager/app/src/android/server/cts/LaunchPipOnPipActivity.java
deleted file mode 100644
index d0b47b0..0000000
--- a/hostsidetests/services/activityandwindowmanager/activitymanager/app/src/android/server/cts/LaunchPipOnPipActivity.java
+++ /dev/null
@@ -1,29 +0,0 @@
-/*
- * Copyright (C) 2016 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License
- */
-
-package android.server.cts;
-
-import android.app.Activity;
-import android.content.pm.PackageManager;
-
-public class LaunchPipOnPipActivity extends Activity {
-    @Override
-    public void onPictureInPictureModeChanged(boolean isInPictureInPictureMode) {
-        super.onPictureInPictureModeChanged(isInPictureInPictureMode);
-        AlwaysFocusablePipActivity.launchAlwaysFocusablePipActivity(this,
-            getPackageManager().hasSystemFeature(PackageManager.FEATURE_LEANBACK));
-    }
-}
diff --git a/hostsidetests/services/activityandwindowmanager/activitymanager/app/src/android/server/cts/LaunchingActivity.java b/hostsidetests/services/activityandwindowmanager/activitymanager/app/src/android/server/cts/LaunchingActivity.java
deleted file mode 100644
index b312c97..0000000
--- a/hostsidetests/services/activityandwindowmanager/activitymanager/app/src/android/server/cts/LaunchingActivity.java
+++ /dev/null
@@ -1,32 +0,0 @@
-/*
- * Copyright (C) 2016 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License
- */
-
-package android.server.cts;
-
-import android.app.Activity;
-import android.content.Intent;
-import android.server.cts.tools.ActivityLauncher;
-
-/**
- * Activity that launches another activities when new intent is received.
- */
-public class LaunchingActivity extends Activity {
-    @Override
-    protected void onNewIntent(Intent intent) {
-        super.onNewIntent(intent);
-        ActivityLauncher.launchActivityFromExtras(this, intent.getExtras());
-    }
-}
diff --git a/hostsidetests/services/activityandwindowmanager/activitymanager/app/src/android/server/cts/LifecycleLogView.java b/hostsidetests/services/activityandwindowmanager/activitymanager/app/src/android/server/cts/LifecycleLogView.java
deleted file mode 100644
index 66e0cf2..0000000
--- a/hostsidetests/services/activityandwindowmanager/activitymanager/app/src/android/server/cts/LifecycleLogView.java
+++ /dev/null
@@ -1,50 +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.server.cts;
-
-import android.content.Context;
-import android.content.res.Configuration;
-import android.util.AttributeSet;
-import android.util.Log;
-import android.view.View;
-
-public class LifecycleLogView extends View {
-    private final String TAG = "LifecycleLogView";
-
-    public LifecycleLogView(Context context) {
-        super(context);
-    }
-
-    public LifecycleLogView(Context context, AttributeSet attrs) {
-        this(context, attrs, 0);
-    }
-
-    public LifecycleLogView(Context context, AttributeSet attrs, int defStyleAttr) {
-        this(context, attrs, defStyleAttr, 0);
-    }
-
-    public LifecycleLogView(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes)
-    {
-        super(context, attrs, defStyleAttr, defStyleRes);
-    }
-
-    @Override
-    protected void onConfigurationChanged(Configuration newConfig) {
-        super.onConfigurationChanged(newConfig);
-        Log.i(TAG, "onConfigurationChanged");
-    }
-}
diff --git a/hostsidetests/services/activityandwindowmanager/activitymanager/app/src/android/server/cts/LogConfigurationActivity.java b/hostsidetests/services/activityandwindowmanager/activitymanager/app/src/android/server/cts/LogConfigurationActivity.java
deleted file mode 100644
index 61eb78c..0000000
--- a/hostsidetests/services/activityandwindowmanager/activitymanager/app/src/android/server/cts/LogConfigurationActivity.java
+++ /dev/null
@@ -1,36 +0,0 @@
-/*
- * Copyright (C) 2016 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License
- */
-
-package android.server.cts;
-
-import android.app.Activity;
-import android.content.res.Configuration;
-import android.util.Log;
-
-/**
- * Activity that logs configuration changes.
- */
-public class LogConfigurationActivity extends Activity {
-
-    private static final String TAG = "LogConfigurationActivity";
-
-    @Override
-    public void onConfigurationChanged(Configuration newConfig) {
-        super.onConfigurationChanged(newConfig);
-        Log.i(TAG, "Configuration changed: " + newConfig.screenWidthDp + ","
-                + newConfig.screenHeightDp);
-    }
-}
diff --git a/hostsidetests/services/activityandwindowmanager/activitymanager/app/src/android/server/cts/MoveTaskToBackActivity.java b/hostsidetests/services/activityandwindowmanager/activitymanager/app/src/android/server/cts/MoveTaskToBackActivity.java
deleted file mode 100644
index 6c47fb7..0000000
--- a/hostsidetests/services/activityandwindowmanager/activitymanager/app/src/android/server/cts/MoveTaskToBackActivity.java
+++ /dev/null
@@ -1,62 +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.server.cts;
-
-import android.content.Intent;
-import android.os.Bundle;
-
-/**
- * Activity that finishes itself using "moveTaskToBack".
- */
-public class MoveTaskToBackActivity extends AbstractLifecycleLogActivity {
-
-    private static final String TAG = MoveTaskToBackActivity.class.getSimpleName();
-
-    private String mFinishPoint;
-
-    @Override
-    protected void onCreate(Bundle icicle) {
-        super.onCreate(icicle);
-
-        final Intent intent = getIntent();
-        mFinishPoint = intent.getExtras().getString("finish_point");
-    }
-
-    @Override
-    protected void onPause() {
-        super.onPause();
-
-        if (mFinishPoint.equals("on_pause")) {
-            moveTaskToBack(true /* nonRoot */);
-        }
-    }
-
-    @Override
-    protected void onStop() {
-        super.onStop();
-
-        if (mFinishPoint.equals("on_stop")) {
-            moveTaskToBack(true /* nonRoot */);
-        }
-    }
-
-    @Override
-    protected String getTag() {
-        return TAG;
-    }
-
-}
diff --git a/hostsidetests/services/activityandwindowmanager/activitymanager/app/src/android/server/cts/NightModeActivity.java b/hostsidetests/services/activityandwindowmanager/activitymanager/app/src/android/server/cts/NightModeActivity.java
deleted file mode 100644
index e33141b..0000000
--- a/hostsidetests/services/activityandwindowmanager/activitymanager/app/src/android/server/cts/NightModeActivity.java
+++ /dev/null
@@ -1,42 +0,0 @@
-/*
- * Copyright (C) 2017 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License
- */
-
-package android.server.cts;
-
-import android.app.UiModeManager;
-import android.content.Context;
-import android.os.Bundle;
-
-/** Activity that changes UI mode on creation and handles corresponding configuration change. */
-public class NightModeActivity extends AbstractLifecycleLogActivity {
-
-    private static final String TAG = NightModeActivity.class.getSimpleName();
-
-    @Override
-    protected String getTag() {
-        return TAG;
-    }
-
-    @Override
-    protected void onCreate(Bundle icicle) {
-        super.onCreate(icicle);
-
-        UiModeManager uiManager = (UiModeManager) getSystemService(Context.UI_MODE_SERVICE);
-        // Switch the mode two times to make sure it is independent of the current setting.
-        uiManager.setNightMode(UiModeManager.MODE_NIGHT_YES);
-        uiManager.setNightMode(UiModeManager.MODE_NIGHT_NO);
-    }
-}
diff --git a/hostsidetests/services/activityandwindowmanager/activitymanager/app/src/android/server/cts/NoHistoryActivity.java b/hostsidetests/services/activityandwindowmanager/activitymanager/app/src/android/server/cts/NoHistoryActivity.java
deleted file mode 100644
index 6a84602..0000000
--- a/hostsidetests/services/activityandwindowmanager/activitymanager/app/src/android/server/cts/NoHistoryActivity.java
+++ /dev/null
@@ -1,29 +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.server.cts;
-
-/**
- * An activity that has the noHistory flag set.
- */
-public class NoHistoryActivity extends AbstractLifecycleLogActivity {
-    private static final String TAG = NoHistoryActivity.class.getSimpleName();
-
-    @Override
-    protected String getTag() {
-        return TAG;
-    }
-}
diff --git a/hostsidetests/services/activityandwindowmanager/activitymanager/app/src/android/server/cts/NoRelaunchActivity.java b/hostsidetests/services/activityandwindowmanager/activitymanager/app/src/android/server/cts/NoRelaunchActivity.java
deleted file mode 100644
index 9cc3b9d..0000000
--- a/hostsidetests/services/activityandwindowmanager/activitymanager/app/src/android/server/cts/NoRelaunchActivity.java
+++ /dev/null
@@ -1,27 +0,0 @@
-/*
- * Copyright (C) 2016 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package android.server.cts;
-
-public class NoRelaunchActivity extends AbstractLifecycleLogActivity {
-
-    private static final String TAG = NoRelaunchActivity.class.getSimpleName();
-
-    @Override
-    protected String getTag() {
-        return TAG;
-    }
-}
diff --git a/hostsidetests/services/activityandwindowmanager/activitymanager/app/src/android/server/cts/NonResizeableActivity.java b/hostsidetests/services/activityandwindowmanager/activitymanager/app/src/android/server/cts/NonResizeableActivity.java
deleted file mode 100644
index b871a8d..0000000
--- a/hostsidetests/services/activityandwindowmanager/activitymanager/app/src/android/server/cts/NonResizeableActivity.java
+++ /dev/null
@@ -1,27 +0,0 @@
-/*
- * Copyright (C) 2016 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package android.server.cts;
-
-public class NonResizeableActivity extends AbstractLifecycleLogActivity {
-
-     private static final String TAG = NonResizeableActivity.class.getSimpleName();
-
-    @Override
-    protected String getTag() {
-        return TAG;
-    }
-}
diff --git a/hostsidetests/services/activityandwindowmanager/activitymanager/app/src/android/server/cts/PipActivity.java b/hostsidetests/services/activityandwindowmanager/activitymanager/app/src/android/server/cts/PipActivity.java
deleted file mode 100644
index f968970..0000000
--- a/hostsidetests/services/activityandwindowmanager/activitymanager/app/src/android/server/cts/PipActivity.java
+++ /dev/null
@@ -1,338 +0,0 @@
-/*
- * Copyright (C) 2015 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License
- */
-
-package android.server.cts;
-
-import static android.content.Intent.FLAG_ACTIVITY_CLEAR_TASK;
-import static android.content.Intent.FLAG_ACTIVITY_NEW_TASK;
-
-import android.app.Activity;
-import android.app.ActivityOptions;
-import android.app.PictureInPictureParams;
-import android.content.res.Configuration;
-import android.content.BroadcastReceiver;
-import android.content.ComponentName;
-import android.content.Context;
-import android.content.Intent;
-import android.content.IntentFilter;
-import android.graphics.Rect;
-import android.os.Bundle;
-import android.os.Handler;
-import android.os.SystemClock;
-import android.util.Log;
-import android.util.Rational;
-import android.view.WindowManager;
-
-public class PipActivity extends AbstractLifecycleLogActivity {
-
-    private static final String TAG = "PipActivity";
-
-    // Intent action that this activity dynamically registers to enter picture-in-picture
-    private static final String ACTION_ENTER_PIP = "android.server.cts.PipActivity.enter_pip";
-    // Intent action that this activity dynamically registers to move itself to the back
-    private static final String ACTION_MOVE_TO_BACK = "android.server.cts.PipActivity.move_to_back";
-    // Intent action that this activity dynamically registers to expand itself.
-    // If EXTRA_SET_ASPECT_RATIO_WITH_DELAY is set, it will also attempt to apply the aspect ratio
-    // after a short delay.
-    private static final String ACTION_EXPAND_PIP = "android.server.cts.PipActivity.expand_pip";
-    // Intent action that this activity dynamically registers to set requested orientation.
-    // Will apply the oriention to the value set in the EXTRA_FIXED_ORIENTATION extra.
-    private static final String ACTION_SET_REQUESTED_ORIENTATION =
-            "android.server.cts.PipActivity.set_requested_orientation";
-    // Intent action that will finish this activity
-    private static final String ACTION_FINISH = "android.server.cts.PipActivity.finish";
-
-    // Sets the fixed orientation (can be one of {@link ActivityInfo.ScreenOrientation}
-    private static final String EXTRA_FIXED_ORIENTATION = "fixed_orientation";
-    // Calls enterPictureInPicture() on creation
-    private static final String EXTRA_ENTER_PIP = "enter_pip";
-    // Used with EXTRA_AUTO_ENTER_PIP, value specifies the aspect ratio to enter PIP with
-    private static final String EXTRA_ENTER_PIP_ASPECT_RATIO_NUMERATOR =
-            "enter_pip_aspect_ratio_numerator";
-    // Used with EXTRA_AUTO_ENTER_PIP, value specifies the aspect ratio to enter PIP with
-    private static final String EXTRA_ENTER_PIP_ASPECT_RATIO_DENOMINATOR =
-            "enter_pip_aspect_ratio_denominator";
-    // Calls setPictureInPictureAspectRatio with the aspect ratio specified in the value
-    private static final String EXTRA_SET_ASPECT_RATIO_NUMERATOR = "set_aspect_ratio_numerator";
-    // Calls setPictureInPictureAspectRatio with the aspect ratio specified in the value
-    private static final String EXTRA_SET_ASPECT_RATIO_DENOMINATOR = "set_aspect_ratio_denominator";
-    // Calls setPictureInPictureAspectRatio with the aspect ratio specified in the value with a
-    // fixed delay
-    private static final String EXTRA_SET_ASPECT_RATIO_WITH_DELAY_NUMERATOR =
-            "set_aspect_ratio_with_delay_numerator";
-    // Calls setPictureInPictureAspectRatio with the aspect ratio specified in the value with a
-    // fixed delay
-    private static final String EXTRA_SET_ASPECT_RATIO_WITH_DELAY_DENOMINATOR =
-            "set_aspect_ratio_with_delay_denominator";
-    // Adds a click listener to finish this activity when it is clicked
-    private static final String EXTRA_TAP_TO_FINISH = "tap_to_finish";
-    // Calls requestAutoEnterPictureInPicture() with the value provided
-    private static final String EXTRA_ENTER_PIP_ON_PAUSE = "enter_pip_on_pause";
-    // Starts the activity (component name) provided by the value at the end of onCreate
-    private static final String EXTRA_START_ACTIVITY = "start_activity";
-    // Finishes the activity at the end of onResume (after EXTRA_START_ACTIVITY is handled)
-    private static final String EXTRA_FINISH_SELF_ON_RESUME = "finish_self_on_resume";
-    // Calls enterPictureInPicture() again after onPictureInPictureModeChanged(false) is called
-    private static final String EXTRA_REENTER_PIP_ON_EXIT = "reenter_pip_on_exit";
-    // Shows this activity over the keyguard
-    private static final String EXTRA_SHOW_OVER_KEYGUARD = "show_over_keyguard";
-    // Adds an assertion that we do not ever get onStop() before we enter picture in picture
-    private static final String EXTRA_ASSERT_NO_ON_STOP_BEFORE_PIP = "assert_no_on_stop_before_pip";
-    // The amount to delay to artificially introduce in onPause() (before EXTRA_ENTER_PIP_ON_PAUSE
-    // is processed)
-    private static final String EXTRA_ON_PAUSE_DELAY = "on_pause_delay";
-
-    private boolean mEnteredPictureInPicture;
-
-    private Handler mHandler = new Handler();
-    private BroadcastReceiver mReceiver = new BroadcastReceiver() {
-        @Override
-        public void onReceive(Context context, Intent intent) {
-            if (intent != null) {
-                switch (intent.getAction()) {
-                    case ACTION_ENTER_PIP:
-                        enterPictureInPictureMode();
-                        break;
-                    case ACTION_MOVE_TO_BACK:
-                        moveTaskToBack(false /* nonRoot */);
-                        break;
-                    case ACTION_EXPAND_PIP:
-                        // Trigger the activity to expand
-                        Intent startIntent = new Intent(PipActivity.this, PipActivity.class);
-                        startIntent.setFlags(Intent.FLAG_ACTIVITY_REORDER_TO_FRONT);
-                        startActivity(startIntent);
-
-                        if (intent.hasExtra(EXTRA_SET_ASPECT_RATIO_WITH_DELAY_NUMERATOR)
-                                && intent.hasExtra(EXTRA_SET_ASPECT_RATIO_WITH_DELAY_DENOMINATOR)) {
-                            // Ugly, but required to wait for the startActivity to actually start
-                            // the activity...
-                            mHandler.postDelayed(() -> {
-                                final PictureInPictureParams.Builder builder =
-                                        new PictureInPictureParams.Builder();
-                                builder.setAspectRatio(getAspectRatio(intent,
-                                        EXTRA_SET_ASPECT_RATIO_WITH_DELAY_NUMERATOR,
-                                        EXTRA_SET_ASPECT_RATIO_WITH_DELAY_DENOMINATOR));
-                                setPictureInPictureParams(builder.build());
-                            }, 100);
-                        }
-                        break;
-                    case ACTION_SET_REQUESTED_ORIENTATION:
-                        setRequestedOrientation(Integer.parseInt(intent.getStringExtra(
-                                EXTRA_FIXED_ORIENTATION)));
-                        break;
-                    case ACTION_FINISH:
-                        finish();
-                        break;
-                }
-            }
-        }
-    };
-
-    @Override
-    protected void onCreate(Bundle savedInstanceState) {
-        super.onCreate(savedInstanceState);
-
-        // Set the fixed orientation if requested
-        if (getIntent().hasExtra(EXTRA_FIXED_ORIENTATION)) {
-            final int ori = Integer.parseInt(getIntent().getStringExtra(EXTRA_FIXED_ORIENTATION));
-            setRequestedOrientation(ori);
-        }
-
-        // Set the window flag to show over the keyguard
-        if (getIntent().hasExtra(EXTRA_SHOW_OVER_KEYGUARD)) {
-            getWindow().addFlags(WindowManager.LayoutParams.FLAG_SHOW_WHEN_LOCKED);
-        }
-
-        // Enter picture in picture with the given aspect ratio if provided
-        if (getIntent().hasExtra(EXTRA_ENTER_PIP)) {
-            if (getIntent().hasExtra(EXTRA_ENTER_PIP_ASPECT_RATIO_NUMERATOR)
-                    && getIntent().hasExtra(EXTRA_ENTER_PIP_ASPECT_RATIO_DENOMINATOR)) {
-                try {
-                    final PictureInPictureParams.Builder builder =
-                            new PictureInPictureParams.Builder();
-                    builder.setAspectRatio(getAspectRatio(getIntent(),
-                            EXTRA_ENTER_PIP_ASPECT_RATIO_NUMERATOR,
-                            EXTRA_ENTER_PIP_ASPECT_RATIO_DENOMINATOR));
-                    enterPictureInPictureMode(builder.build());
-                } catch (Exception e) {
-                    // This call can fail intentionally if the aspect ratio is too extreme
-                }
-            } else {
-                enterPictureInPictureMode(new PictureInPictureParams.Builder().build());
-            }
-        }
-
-        // We need to wait for either enterPictureInPicture() or requestAutoEnterPictureInPicture()
-        // to be called before setting the aspect ratio
-        if (getIntent().hasExtra(EXTRA_SET_ASPECT_RATIO_NUMERATOR)
-                && getIntent().hasExtra(EXTRA_SET_ASPECT_RATIO_DENOMINATOR)) {
-            final PictureInPictureParams.Builder builder =
-                    new PictureInPictureParams.Builder();
-            builder.setAspectRatio(getAspectRatio(getIntent(),
-                    EXTRA_SET_ASPECT_RATIO_NUMERATOR, EXTRA_SET_ASPECT_RATIO_DENOMINATOR));
-            try {
-                setPictureInPictureParams(builder.build());
-            } catch (Exception e) {
-                // This call can fail intentionally if the aspect ratio is too extreme
-            }
-        }
-
-        // Enable tap to finish if necessary
-        if (getIntent().hasExtra(EXTRA_TAP_TO_FINISH)) {
-            setContentView(R.layout.tap_to_finish_pip_layout);
-            findViewById(R.id.content).setOnClickListener(v -> {
-                finish();
-            });
-        }
-
-        // Launch a new activity if requested
-        String launchActivityComponent = getIntent().getStringExtra(EXTRA_START_ACTIVITY);
-        if (launchActivityComponent != null) {
-            Intent launchIntent = new Intent();
-            launchIntent.setComponent(ComponentName.unflattenFromString(launchActivityComponent));
-            startActivity(launchIntent);
-        }
-
-        // Register the broadcast receiver
-        IntentFilter filter = new IntentFilter();
-        filter.addAction(ACTION_ENTER_PIP);
-        filter.addAction(ACTION_MOVE_TO_BACK);
-        filter.addAction(ACTION_EXPAND_PIP);
-        filter.addAction(ACTION_SET_REQUESTED_ORIENTATION);
-        filter.addAction(ACTION_FINISH);
-        registerReceiver(mReceiver, filter);
-
-        // Dump applied display metrics.
-        Configuration config = getResources().getConfiguration();
-        dumpDisplaySize(config);
-        dumpConfiguration(config);
-    }
-
-    @Override
-    protected void onResume() {
-        super.onResume();
-
-        // Finish self if requested
-        if (getIntent().hasExtra(EXTRA_FINISH_SELF_ON_RESUME)) {
-            finish();
-        }
-    }
-
-    @Override
-    protected void onPause() {
-        super.onPause();
-
-        // Pause if requested
-        if (getIntent().hasExtra(EXTRA_ON_PAUSE_DELAY)) {
-            SystemClock.sleep(Long.valueOf(getIntent().getStringExtra(EXTRA_ON_PAUSE_DELAY)));
-        }
-
-        // Enter PIP on move to background
-        if (getIntent().hasExtra(EXTRA_ENTER_PIP_ON_PAUSE)) {
-            enterPictureInPictureMode(new PictureInPictureParams.Builder().build());
-        }
-    }
-
-    @Override
-    protected void onStop() {
-        super.onStop();
-
-        if (getIntent().hasExtra(EXTRA_ASSERT_NO_ON_STOP_BEFORE_PIP) && !mEnteredPictureInPicture) {
-            Log.w(TAG, "Unexpected onStop() called before entering picture-in-picture");
-            finish();
-        }
-    }
-
-    @Override
-    protected void onDestroy() {
-        super.onDestroy();
-
-        unregisterReceiver(mReceiver);
-    }
-
-    @Override
-    public void onPictureInPictureModeChanged(boolean isInPictureInPictureMode) {
-        super.onPictureInPictureModeChanged(isInPictureInPictureMode);
-
-        // Fail early if the activity state does not match the dispatched state
-        if (isInPictureInPictureMode() != isInPictureInPictureMode) {
-            Log.w(TAG, "Received onPictureInPictureModeChanged mode=" + isInPictureInPictureMode
-                    + " activityState=" + isInPictureInPictureMode());
-            finish();
-        }
-
-        // Mark that we've entered picture-in-picture so that we can stop checking for
-        // EXTRA_ASSERT_NO_ON_STOP_BEFORE_PIP
-        if (isInPictureInPictureMode) {
-            mEnteredPictureInPicture = true;
-        }
-
-        if (!isInPictureInPictureMode && getIntent().hasExtra(EXTRA_REENTER_PIP_ON_EXIT)) {
-            // This call to re-enter PIP can happen too quickly (host side tests can have difficulty
-            // checking that the stacks ever changed). Therefor, we need to delay here slightly to
-            // allow the tests to verify that the stacks have changed before re-entering.
-            mHandler.postDelayed(() -> {
-                enterPictureInPictureMode(new PictureInPictureParams.Builder().build());
-            }, 1000);
-        }
-    }
-
-    @Override
-    public void onConfigurationChanged(Configuration newConfig) {
-        super.onConfigurationChanged(newConfig);
-        dumpDisplaySize(newConfig);
-        dumpConfiguration(newConfig);
-    }
-
-    /**
-     * Launches a new instance of the PipActivity directly into the pinned stack.
-     */
-    static void launchActivityIntoPinnedStack(Activity caller, Rect bounds) {
-        final Intent intent = new Intent(caller, PipActivity.class);
-        intent.setFlags(FLAG_ACTIVITY_CLEAR_TASK | FLAG_ACTIVITY_NEW_TASK);
-        intent.putExtra(EXTRA_ASSERT_NO_ON_STOP_BEFORE_PIP, "true");
-
-        final ActivityOptions options = ActivityOptions.makeBasic();
-        options.setLaunchBounds(bounds);
-        options.setLaunchStackId(4 /* ActivityManager.StackId.PINNED_STACK_ID */);
-        caller.startActivity(intent, options.toBundle());
-    }
-
-    /**
-     * Launches a new instance of the PipActivity in the same task that will automatically enter
-     * PiP.
-     */
-    static void launchEnterPipActivity(Activity caller) {
-        final Intent intent = new Intent(caller, PipActivity.class);
-        intent.putExtra(EXTRA_ENTER_PIP, "true");
-        intent.putExtra(EXTRA_ASSERT_NO_ON_STOP_BEFORE_PIP, "true");
-        caller.startActivity(intent);
-    }
-
-    @Override
-    protected String getTag() {
-        return TAG;
-    }
-
-    /**
-     * @return a {@link Rational} aspect ratio from the given intent and extras.
-     */
-    private Rational getAspectRatio(Intent intent, String extraNum, String extraDenom) {
-        return new Rational(
-                Integer.valueOf(intent.getStringExtra(extraNum)),
-                Integer.valueOf(intent.getStringExtra(extraDenom)));
-    }
-}
diff --git a/hostsidetests/services/activityandwindowmanager/activitymanager/app/src/android/server/cts/PipActivity2.java b/hostsidetests/services/activityandwindowmanager/activitymanager/app/src/android/server/cts/PipActivity2.java
deleted file mode 100644
index 53b4f75..0000000
--- a/hostsidetests/services/activityandwindowmanager/activitymanager/app/src/android/server/cts/PipActivity2.java
+++ /dev/null
@@ -1,23 +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.server.cts;
-
-/**
- * A secondary activity that has the same behavior as {@link PipActivity}.
- */
-public class PipActivity2 extends PipActivity {
-}
diff --git a/hostsidetests/services/activityandwindowmanager/activitymanager/app/src/android/server/cts/PipActivityWithSameAffinity.java b/hostsidetests/services/activityandwindowmanager/activitymanager/app/src/android/server/cts/PipActivityWithSameAffinity.java
deleted file mode 100644
index e97dc0e..0000000
--- a/hostsidetests/services/activityandwindowmanager/activitymanager/app/src/android/server/cts/PipActivityWithSameAffinity.java
+++ /dev/null
@@ -1,34 +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.server.cts;
-
-import android.app.Activity;
-import android.app.PictureInPictureParams;
-
-/**
- * An activity that can enter PiP with a fixed affinity to match
- * {@link TestActivityWithSameAffinity}.
- */
-public class PipActivityWithSameAffinity extends Activity {
-
-    @Override
-    protected void onResume() {
-        super.onResume();
-
-        enterPictureInPictureMode(new PictureInPictureParams.Builder().build());
-    }
-}
diff --git a/hostsidetests/services/activityandwindowmanager/activitymanager/app/src/android/server/cts/PipOnStopActivity.java b/hostsidetests/services/activityandwindowmanager/activitymanager/app/src/android/server/cts/PipOnStopActivity.java
deleted file mode 100644
index 1abc696..0000000
--- a/hostsidetests/services/activityandwindowmanager/activitymanager/app/src/android/server/cts/PipOnStopActivity.java
+++ /dev/null
@@ -1,52 +0,0 @@
-/*
- * Copyright (C) 2016 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License
- */
-
-package android.server.cts;
-
-import android.app.Activity;
-import android.content.Intent;
-import android.os.Bundle;
-
-/**
- * This activity will try and enter picture in picture when it is stopped.
- */
-public class PipOnStopActivity extends Activity {
-
-    @Override
-    protected void onCreate(Bundle savedInstanceState) {
-        super.onCreate(savedInstanceState);
-
-        setContentView(R.layout.tap_to_finish_pip_layout);
-    }
-
-    @Override
-    protected void onResume() {
-        super.onResume();
-
-        startActivity(new Intent(this, PipActivity.class));
-    }
-
-    @Override
-    protected void onStop() {
-        super.onStop();
-
-        try {
-            enterPictureInPictureMode();
-        } catch (RuntimeException e) {
-            // Known failure, we expect this call to throw an exception
-        }
-    }
-}
diff --git a/hostsidetests/services/activityandwindowmanager/activitymanager/app/src/android/server/cts/PortraitOrientationActivity.java b/hostsidetests/services/activityandwindowmanager/activitymanager/app/src/android/server/cts/PortraitOrientationActivity.java
deleted file mode 100644
index 5fa1fc8..0000000
--- a/hostsidetests/services/activityandwindowmanager/activitymanager/app/src/android/server/cts/PortraitOrientationActivity.java
+++ /dev/null
@@ -1,40 +0,0 @@
-/*
- * Copyright (C) 2016 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License
- */
-
-package android.server.cts;
-
-import android.content.res.Configuration;
-
-public class PortraitOrientationActivity extends AbstractLifecycleLogActivity {
-
-    @Override
-    protected String getTag() {
-        return "PortraitOrientationActivity";
-    }
-
-    @Override
-    protected void onResume() {
-        super.onResume();
-        final Configuration config = getResources().getConfiguration();
-        dumpDisplaySize(config);
-    }
-
-    @Override
-    public void onConfigurationChanged(Configuration newConfig) {
-        super.onConfigurationChanged(newConfig);
-        dumpDisplaySize(newConfig);
-    }
-}
diff --git a/hostsidetests/services/activityandwindowmanager/activitymanager/app/src/android/server/cts/ResizeableActivity.java b/hostsidetests/services/activityandwindowmanager/activitymanager/app/src/android/server/cts/ResizeableActivity.java
deleted file mode 100644
index aad874b..0000000
--- a/hostsidetests/services/activityandwindowmanager/activitymanager/app/src/android/server/cts/ResizeableActivity.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.server.cts;
-
-import android.content.res.Configuration;
-import android.os.Bundle;
-
-public class ResizeableActivity extends AbstractLifecycleLogActivity {
-    @Override
-    protected String getTag() {
-        return "ResizeableActivity";
-    }
-
-    @Override
-    protected void onCreate(Bundle icicle) {
-        super.onCreate(icicle);
-        setContentView(R.layout.resizeable_activity);
-        dumpDisplaySize(getResources().getConfiguration());
-    }
-
-    @Override
-    public void onConfigurationChanged(Configuration newConfig) {
-        super.onConfigurationChanged(newConfig);
-        dumpDisplaySize(newConfig);
-    }
-}
diff --git a/hostsidetests/services/activityandwindowmanager/activitymanager/app/src/android/server/cts/ResumeWhilePausingActivity.java b/hostsidetests/services/activityandwindowmanager/activitymanager/app/src/android/server/cts/ResumeWhilePausingActivity.java
deleted file mode 100644
index 578d7e5..0000000
--- a/hostsidetests/services/activityandwindowmanager/activitymanager/app/src/android/server/cts/ResumeWhilePausingActivity.java
+++ /dev/null
@@ -1,23 +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.server.cts;
-
-import android.app.Activity;
-
-public class ResumeWhilePausingActivity extends Activity {
-    // Empty
-}
diff --git a/hostsidetests/services/activityandwindowmanager/activitymanager/app/src/android/server/cts/ShowWhenLockedActivity.java b/hostsidetests/services/activityandwindowmanager/activitymanager/app/src/android/server/cts/ShowWhenLockedActivity.java
deleted file mode 100644
index 6e3348a..0000000
--- a/hostsidetests/services/activityandwindowmanager/activitymanager/app/src/android/server/cts/ShowWhenLockedActivity.java
+++ /dev/null
@@ -1,30 +0,0 @@
-/*
- * Copyright (C) 2016 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License
- */
-
-package android.server.cts;
-
-import android.app.Activity;
-import android.os.Bundle;
-import android.view.WindowManager;
-
-public class ShowWhenLockedActivity extends BroadcastReceiverActivity {
-
-    @Override
-    protected void onCreate(Bundle savedInstanceState) {
-        super.onCreate(savedInstanceState);
-        getWindow().addFlags(WindowManager.LayoutParams.FLAG_SHOW_WHEN_LOCKED);
-    }
-}
diff --git a/hostsidetests/services/activityandwindowmanager/activitymanager/app/src/android/server/cts/ShowWhenLockedAttrActivity.java b/hostsidetests/services/activityandwindowmanager/activitymanager/app/src/android/server/cts/ShowWhenLockedAttrActivity.java
deleted file mode 100644
index c53c485..0000000
--- a/hostsidetests/services/activityandwindowmanager/activitymanager/app/src/android/server/cts/ShowWhenLockedAttrActivity.java
+++ /dev/null
@@ -1,27 +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.server.cts;
-
-public class ShowWhenLockedAttrActivity extends AbstractLifecycleLogActivity {
-    private static final String TAG = ShowWhenLockedAttrActivity.class.getSimpleName();
-
-    @Override
-    protected String getTag() {
-        return TAG;
-    }
-
-}
diff --git a/hostsidetests/services/activityandwindowmanager/activitymanager/app/src/android/server/cts/ShowWhenLockedAttrRemoveAttrActivity.java b/hostsidetests/services/activityandwindowmanager/activitymanager/app/src/android/server/cts/ShowWhenLockedAttrRemoveAttrActivity.java
deleted file mode 100644
index dbad34d..0000000
--- a/hostsidetests/services/activityandwindowmanager/activitymanager/app/src/android/server/cts/ShowWhenLockedAttrRemoveAttrActivity.java
+++ /dev/null
@@ -1,35 +0,0 @@
-/*
- * Copyright (C) 2017 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License
- */
-
-package android.server.cts;
-
-import android.os.Bundle;
-
-public class ShowWhenLockedAttrRemoveAttrActivity extends AbstractLifecycleLogActivity {
-    private static final String TAG = ShowWhenLockedAttrRemoveAttrActivity.class.getSimpleName();
-
-    @Override
-    protected void onStop() {
-        super.onStop();
-        setShowWhenLocked(false);
-    }
-
-    @Override
-    protected String getTag() {
-        return TAG;
-    }
-
-}
diff --git a/hostsidetests/services/activityandwindowmanager/activitymanager/app/src/android/server/cts/ShowWhenLockedAttrWithDialogActivity.java b/hostsidetests/services/activityandwindowmanager/activitymanager/app/src/android/server/cts/ShowWhenLockedAttrWithDialogActivity.java
deleted file mode 100644
index 136706a..0000000
--- a/hostsidetests/services/activityandwindowmanager/activitymanager/app/src/android/server/cts/ShowWhenLockedAttrWithDialogActivity.java
+++ /dev/null
@@ -1,33 +0,0 @@
-/*
- * Copyright (C) 2017 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License
- */
-
-package android.server.cts;
-
-import android.app.Activity;
-import android.app.AlertDialog;
-import android.os.Bundle;
-import android.view.WindowManager;
-
-public class ShowWhenLockedAttrWithDialogActivity extends Activity {
-
-    @Override
-    protected void onCreate(Bundle savedInstanceState) {
-        super.onCreate(savedInstanceState);
-        new AlertDialog.Builder(this)
-                .setTitle("Dialog")
-                .show();
-    }
-}
diff --git a/hostsidetests/services/activityandwindowmanager/activitymanager/app/src/android/server/cts/ShowWhenLockedDialogActivity.java b/hostsidetests/services/activityandwindowmanager/activitymanager/app/src/android/server/cts/ShowWhenLockedDialogActivity.java
deleted file mode 100644
index 5b60139..0000000
--- a/hostsidetests/services/activityandwindowmanager/activitymanager/app/src/android/server/cts/ShowWhenLockedDialogActivity.java
+++ /dev/null
@@ -1,30 +0,0 @@
-/*
- * Copyright (C) 2016 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License
- */
-
-package android.server.cts;
-
-import android.app.Activity;
-import android.os.Bundle;
-import android.view.WindowManager;
-
-public class ShowWhenLockedDialogActivity extends Activity {
-
-    @Override
-    protected void onCreate(Bundle savedInstanceState) {
-        super.onCreate(savedInstanceState);
-        getWindow().addFlags(WindowManager.LayoutParams.FLAG_SHOW_WHEN_LOCKED);
-    }
-}
diff --git a/hostsidetests/services/activityandwindowmanager/activitymanager/app/src/android/server/cts/ShowWhenLockedTranslucentActivity.java b/hostsidetests/services/activityandwindowmanager/activitymanager/app/src/android/server/cts/ShowWhenLockedTranslucentActivity.java
deleted file mode 100644
index 929c9f7..0000000
--- a/hostsidetests/services/activityandwindowmanager/activitymanager/app/src/android/server/cts/ShowWhenLockedTranslucentActivity.java
+++ /dev/null
@@ -1,31 +0,0 @@
-/*
- * Copyright (C) 2016 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License
- */
-
-package android.server.cts;
-
-import android.app.Activity;
-import android.os.Bundle;
-import android.view.WindowManager;
-
-public class ShowWhenLockedTranslucentActivity extends Activity {
-
-    @Override
-    protected void onCreate(Bundle savedInstanceState) {
-        super.onCreate(savedInstanceState);
-        getWindow().addFlags(WindowManager.LayoutParams.FLAG_SHOW_WHEN_LOCKED);
-        setContentView(R.layout.translucent);
-    }
-}
diff --git a/hostsidetests/services/activityandwindowmanager/activitymanager/app/src/android/server/cts/ShowWhenLockedWithDialogActivity.java b/hostsidetests/services/activityandwindowmanager/activitymanager/app/src/android/server/cts/ShowWhenLockedWithDialogActivity.java
deleted file mode 100644
index 58d5ac7..0000000
--- a/hostsidetests/services/activityandwindowmanager/activitymanager/app/src/android/server/cts/ShowWhenLockedWithDialogActivity.java
+++ /dev/null
@@ -1,34 +0,0 @@
-/*
- * Copyright (C) 2016 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License
- */
-
-package android.server.cts;
-
-import android.app.Activity;
-import android.app.AlertDialog;
-import android.os.Bundle;
-import android.view.WindowManager;
-
-public class ShowWhenLockedWithDialogActivity extends Activity {
-
-    @Override
-    protected void onCreate(Bundle savedInstanceState) {
-        super.onCreate(savedInstanceState);
-        getWindow().addFlags(WindowManager.LayoutParams.FLAG_SHOW_WHEN_LOCKED);
-        new AlertDialog.Builder(this)
-                .setTitle("Dialog")
-                .show();
-    }
-}
diff --git a/hostsidetests/services/activityandwindowmanager/activitymanager/app/src/android/server/cts/SingleInstanceActivity.java b/hostsidetests/services/activityandwindowmanager/activitymanager/app/src/android/server/cts/SingleInstanceActivity.java
deleted file mode 100644
index e0eaecf..0000000
--- a/hostsidetests/services/activityandwindowmanager/activitymanager/app/src/android/server/cts/SingleInstanceActivity.java
+++ /dev/null
@@ -1,22 +0,0 @@
-/*
- * Copyright (C) 2016 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License
- */
-
-package android.server.cts;
-
-import android.app.Activity;
-
-public class SingleInstanceActivity extends Activity {
-}
diff --git a/hostsidetests/services/activityandwindowmanager/activitymanager/app/src/android/server/cts/SingleTaskActivity.java b/hostsidetests/services/activityandwindowmanager/activitymanager/app/src/android/server/cts/SingleTaskActivity.java
deleted file mode 100644
index 7ffde13..0000000
--- a/hostsidetests/services/activityandwindowmanager/activitymanager/app/src/android/server/cts/SingleTaskActivity.java
+++ /dev/null
@@ -1,22 +0,0 @@
-/*
- * Copyright (C) 2016 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License
- */
-
-package android.server.cts;
-
-import android.app.Activity;
-
-public class SingleTaskActivity extends Activity {
-}
diff --git a/hostsidetests/services/activityandwindowmanager/activitymanager/app/src/android/server/cts/SlowCreateActivity.java b/hostsidetests/services/activityandwindowmanager/activitymanager/app/src/android/server/cts/SlowCreateActivity.java
deleted file mode 100644
index a757165..0000000
--- a/hostsidetests/services/activityandwindowmanager/activitymanager/app/src/android/server/cts/SlowCreateActivity.java
+++ /dev/null
@@ -1,30 +0,0 @@
-/*
- * Copyright (C) 2016 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package android.server.cts;
-
-import android.app.Activity;
-import android.os.Bundle;
-
-public class SlowCreateActivity extends Activity {
-    @Override
-    protected void onCreate(Bundle savedInstanceState) {
-        try {
-            Thread.sleep(2000);
-        } catch(InterruptedException e) {}
-        super.onCreate(savedInstanceState);
-    }
-}
diff --git a/hostsidetests/services/activityandwindowmanager/activitymanager/app/src/android/server/cts/SplashscreenActivity.java b/hostsidetests/services/activityandwindowmanager/activitymanager/app/src/android/server/cts/SplashscreenActivity.java
deleted file mode 100644
index 22391bd..0000000
--- a/hostsidetests/services/activityandwindowmanager/activitymanager/app/src/android/server/cts/SplashscreenActivity.java
+++ /dev/null
@@ -1,36 +0,0 @@
-/*
- * Copyright (C) 2017 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License
- */
-
-package android.server.cts;
-
-import android.app.Activity;
-import android.os.Bundle;
-import android.os.SystemClock;
-
-/**
- * Activity that shows a custom splashscreen when being launched.
- */
-public class SplashscreenActivity extends Activity {
-
-    @Override
-    protected void onCreate(Bundle savedInstanceState) {
-        super.onCreate(savedInstanceState);
-        // Make sure splash screen is visible. The test won't take 5 seconds because the condition
-        // such that we can dump the state will trigger much earlier and then the test will just
-        // kill us.
-        SystemClock.sleep(5000);
-    }
-}
diff --git a/hostsidetests/services/activityandwindowmanager/activitymanager/app/src/android/server/cts/SwipeRefreshActivity.java b/hostsidetests/services/activityandwindowmanager/activitymanager/app/src/android/server/cts/SwipeRefreshActivity.java
deleted file mode 100644
index 5f36abc..0000000
--- a/hostsidetests/services/activityandwindowmanager/activitymanager/app/src/android/server/cts/SwipeRefreshActivity.java
+++ /dev/null
@@ -1,40 +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.server.cts;
-
-import android.app.Activity;
-import android.os.Bundle;
-import android.server.cts.SwipeRefreshLayout;
-
-
-/**
- * An activity containing a SwipeRefreshLayout which prevents activity idle.
- */
-public class SwipeRefreshActivity extends AbstractLifecycleLogActivity {
-    private static final String TAG = SwipeRefreshActivity.class.getSimpleName();
-
-    @Override
-    protected void onCreate(Bundle savedInstanceState) {
-        super.onCreate(savedInstanceState);
-        setContentView(new SwipeRefreshLayout(this));
-    }
-
-    @Override
-    protected String getTag() {
-        return TAG;
-    }
-}
diff --git a/hostsidetests/services/activityandwindowmanager/activitymanager/app/src/android/server/cts/SwipeRefreshLayout.java b/hostsidetests/services/activityandwindowmanager/activitymanager/app/src/android/server/cts/SwipeRefreshLayout.java
deleted file mode 100644
index a061d9e..0000000
--- a/hostsidetests/services/activityandwindowmanager/activitymanager/app/src/android/server/cts/SwipeRefreshLayout.java
+++ /dev/null
@@ -1,40 +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.server.cts;
-
-import android.content.Context;
-import android.util.AttributeSet;
-
-/**
- * An extension of {@link android.support.v4.widget.SwipeRefreshLayout} that calls
- * {@link #setRefreshing} during construction, preventing activity idle.
- */
-public class SwipeRefreshLayout extends android.support.v4.widget.SwipeRefreshLayout {
-    public SwipeRefreshLayout(Context context) {
-        super(context);
-        initialize();
-    }
-
-    public SwipeRefreshLayout(Context context, AttributeSet attrs) {
-        super(context, attrs);
-        initialize();
-    }
-
-    private void initialize() {
-        setRefreshing(true);
-    }
-}
diff --git a/hostsidetests/services/activityandwindowmanager/activitymanager/app/src/android/server/cts/TestActivity.java b/hostsidetests/services/activityandwindowmanager/activitymanager/app/src/android/server/cts/TestActivity.java
deleted file mode 100644
index f9a86c6..0000000
--- a/hostsidetests/services/activityandwindowmanager/activitymanager/app/src/android/server/cts/TestActivity.java
+++ /dev/null
@@ -1,88 +0,0 @@
-/*
- * Copyright (C) 2015 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package android.server.cts;
-
-import android.content.BroadcastReceiver;
-import android.content.Context;
-import android.content.Intent;
-import android.content.IntentFilter;
-import android.content.res.Configuration;
-import android.os.Bundle;
-import android.util.Log;
-
-public class TestActivity extends AbstractLifecycleLogActivity {
-
-    private static final String TAG = TestActivity.class.getSimpleName();
-
-    // Sets the fixed orientation (can be one of {@link ActivityInfo.ScreenOrientation}
-    private static final String EXTRA_FIXED_ORIENTATION = "fixed_orientation";
-
-    // Finishes the activity
-    private static final String ACTION_FINISH_SELF = "android.server.cts.TestActivity.finish_self";
-
-    private BroadcastReceiver mReceiver = new BroadcastReceiver() {
-        @Override
-        public void onReceive(Context context, Intent intent) {
-            if (intent != null && intent.getAction().equals(ACTION_FINISH_SELF)) {
-                finish();
-            }
-        }
-    };
-
-    @Override
-    protected void onCreate(Bundle icicle) {
-        super.onCreate(icicle);
-
-        // Set the fixed orientation if requested
-        if (getIntent().hasExtra(EXTRA_FIXED_ORIENTATION)) {
-            final int ori = Integer.parseInt(getIntent().getStringExtra(EXTRA_FIXED_ORIENTATION));
-            setRequestedOrientation(ori);
-        }
-    }
-
-    @Override
-    protected void onStart() {
-        super.onStart();
-        registerReceiver(mReceiver, new IntentFilter(ACTION_FINISH_SELF));
-    }
-
-    @Override
-    protected void onResume() {
-        super.onResume();
-        final Configuration config = getResources().getConfiguration();
-        dumpDisplaySize(config);
-        dumpConfiguration(config);
-    }
-
-    @Override
-    protected void onStop() {
-        super.onStop();
-        unregisterReceiver(mReceiver);
-    }
-
-    @Override
-    public void onConfigurationChanged(Configuration newConfig) {
-        super.onConfigurationChanged(newConfig);
-        dumpDisplaySize(newConfig);
-        dumpConfiguration(newConfig);
-    }
-
-    @Override
-    protected String getTag() {
-        return TAG;
-    }
-}
diff --git a/hostsidetests/services/activityandwindowmanager/activitymanager/app/src/android/server/cts/TestActivityWithSameAffinity.java b/hostsidetests/services/activityandwindowmanager/activitymanager/app/src/android/server/cts/TestActivityWithSameAffinity.java
deleted file mode 100644
index db75dee..0000000
--- a/hostsidetests/services/activityandwindowmanager/activitymanager/app/src/android/server/cts/TestActivityWithSameAffinity.java
+++ /dev/null
@@ -1,72 +0,0 @@
-/*
- * Copyright (C) 2015 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package android.server.cts;
-
-import android.app.PictureInPictureParams;
-import android.content.BroadcastReceiver;
-import android.content.ComponentName;
-import android.content.Context;
-import android.content.Intent;
-import android.content.IntentFilter;
-import android.content.res.Configuration;
-import android.os.Bundle;
-import android.util.Log;
-
-public class TestActivityWithSameAffinity extends TestActivity {
-
-    private static final String TAG = TestActivityWithSameAffinity.class.getSimpleName();
-
-    // Calls enterPictureInPicture() on creation
-    private static final String EXTRA_ENTER_PIP = "enter_pip";
-    // Starts the activity (component name) provided by the value at the end of onCreate
-    private static final String EXTRA_START_ACTIVITY = "start_activity";
-    // Finishes the activity at the end of onResume (after EXTRA_START_ACTIVITY is handled)
-    private static final String EXTRA_FINISH_SELF_ON_RESUME = "finish_self_on_resume";
-
-    @Override
-    protected void onCreate(Bundle icicle) {
-        super.onCreate(icicle);
-
-        // Enter picture in picture if requested
-        if (getIntent().hasExtra(EXTRA_ENTER_PIP)) {
-            enterPictureInPictureMode(new PictureInPictureParams.Builder().build());
-        }
-
-        // Launch a new activity if requested
-        String launchActivityComponent = getIntent().getStringExtra(EXTRA_START_ACTIVITY);
-        if (launchActivityComponent != null) {
-            Intent launchIntent = new Intent();
-            launchIntent.setComponent(ComponentName.unflattenFromString(launchActivityComponent));
-            startActivity(launchIntent);
-        }
-    }
-
-    @Override
-    protected void onResume() {
-        super.onResume();
-
-        // Finish self if requested
-        if (getIntent().hasExtra(EXTRA_FINISH_SELF_ON_RESUME)) {
-            finish();
-        }
-    }
-
-    @Override
-    protected String getTag() {
-        return TAG;
-    }
-}
diff --git a/hostsidetests/services/activityandwindowmanager/activitymanager/app/src/android/server/cts/TopActivity.java b/hostsidetests/services/activityandwindowmanager/activitymanager/app/src/android/server/cts/TopActivity.java
deleted file mode 100644
index 6684999..0000000
--- a/hostsidetests/services/activityandwindowmanager/activitymanager/app/src/android/server/cts/TopActivity.java
+++ /dev/null
@@ -1,53 +0,0 @@
-/*
- * Copyright (C) 2016 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package android.server.cts;
-
-import android.os.Handler;
-import android.os.Bundle;
-import android.util.Log;
-
-public class TopActivity extends AbstractLifecycleLogActivity {
-
-    private static final String TAG = TopActivity.class.getSimpleName();
-
-    @Override
-    protected String getTag() {
-        return TAG;
-    }
-
-    @Override
-    protected void onCreate(Bundle savedInstanceState) {
-        super.onCreate(savedInstanceState);
-
-        final boolean useWallpaper = getIntent().getBooleanExtra("USE_WALLPAPER", false);
-        if (useWallpaper) {
-            setTheme(R.style.WallpaperTheme);
-        }
-
-        final int finishDelay = getIntent().getIntExtra("FINISH_DELAY", 0);
-        if (finishDelay > 0) {
-            Handler handler = new Handler();
-            handler.postDelayed(new Runnable() {
-                @Override
-                public void run() {
-                    Log.d(TAG, "Calling finish()");
-                    finish();
-                }
-            }, finishDelay);
-        }
-    }
-}
diff --git a/hostsidetests/services/activityandwindowmanager/activitymanager/app/src/android/server/cts/TopLeftLayoutActivity.java b/hostsidetests/services/activityandwindowmanager/activitymanager/app/src/android/server/cts/TopLeftLayoutActivity.java
deleted file mode 100644
index 2305582..0000000
--- a/hostsidetests/services/activityandwindowmanager/activitymanager/app/src/android/server/cts/TopLeftLayoutActivity.java
+++ /dev/null
@@ -1,22 +0,0 @@
-/*
- * Copyright (C) 2016 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package android.server.cts;
-
-import android.app.Activity;
-
-public class TopLeftLayoutActivity extends Activity {
-}
diff --git a/hostsidetests/services/activityandwindowmanager/activitymanager/app/src/android/server/cts/TopRightLayoutActivity.java b/hostsidetests/services/activityandwindowmanager/activitymanager/app/src/android/server/cts/TopRightLayoutActivity.java
deleted file mode 100644
index 701d3db..0000000
--- a/hostsidetests/services/activityandwindowmanager/activitymanager/app/src/android/server/cts/TopRightLayoutActivity.java
+++ /dev/null
@@ -1,22 +0,0 @@
-/*
- * Copyright (C) 2016 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package android.server.cts;
-
-import android.app.Activity;
-
-public class TopRightLayoutActivity extends Activity {
-}
diff --git a/hostsidetests/services/activityandwindowmanager/activitymanager/app/src/android/server/cts/TrampolineActivity.java b/hostsidetests/services/activityandwindowmanager/activitymanager/app/src/android/server/cts/TrampolineActivity.java
deleted file mode 100644
index b3f9444..0000000
--- a/hostsidetests/services/activityandwindowmanager/activitymanager/app/src/android/server/cts/TrampolineActivity.java
+++ /dev/null
@@ -1,50 +0,0 @@
-/*
- * Copyright (C) 2016 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package android.server.cts;
-
-import android.os.Bundle;
-
-import static android.content.Intent.FLAG_ACTIVITY_CLEAR_TOP;
-import static android.content.Intent.FLAG_ACTIVITY_NEW_TASK;
-
-import android.content.Intent;
-
-public class TrampolineActivity extends AbstractLifecycleLogActivity {
-
-    private static final String TAG = TrampolineActivity.class.getSimpleName();
-
-    @Override
-    protected void onCreate(Bundle savedInstanceState) {
-        super.onCreate(savedInstanceState);
-        // Add a delay here to expose more easily a failure case where the real target
-        // activity is visible before it's launched, because its task is being brought
-        // to foreground. We need to verify that 'am start' is unblocked correctly.
-        try {
-            Thread.sleep(2000);
-        } catch(InterruptedException e) {}
-        Intent intent = new Intent(this, SingleTaskActivity.class);
-        intent.setFlags(FLAG_ACTIVITY_CLEAR_TOP | FLAG_ACTIVITY_NEW_TASK);
-
-        startActivity(intent);
-        finish();
-    }
-
-    @Override
-    protected String getTag() {
-        return TAG;
-    }
-}
diff --git a/hostsidetests/services/activityandwindowmanager/activitymanager/app/src/android/server/cts/TranslucentActivity.java b/hostsidetests/services/activityandwindowmanager/activitymanager/app/src/android/server/cts/TranslucentActivity.java
deleted file mode 100644
index 96697aa..0000000
--- a/hostsidetests/services/activityandwindowmanager/activitymanager/app/src/android/server/cts/TranslucentActivity.java
+++ /dev/null
@@ -1,23 +0,0 @@
-/*
- * Copyright (C) 2016 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License
- */
-
-package android.server.cts;
-
-import android.app.Activity;
-
-public class TranslucentActivity extends Activity {
-
-}
diff --git a/hostsidetests/services/activityandwindowmanager/activitymanager/app/src/android/server/cts/TranslucentAssistantActivity.java b/hostsidetests/services/activityandwindowmanager/activitymanager/app/src/android/server/cts/TranslucentAssistantActivity.java
deleted file mode 100644
index e979712..0000000
--- a/hostsidetests/services/activityandwindowmanager/activitymanager/app/src/android/server/cts/TranslucentAssistantActivity.java
+++ /dev/null
@@ -1,44 +0,0 @@
-/*
- * Copyright (C) 2017 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License
- */
-
-package android.server.cts;
-
-import static android.content.Intent.FLAG_ACTIVITY_CLEAR_TASK;
-import static android.content.Intent.FLAG_ACTIVITY_NEW_TASK;
-
-import android.app.Activity;
-import android.app.ActivityOptions;
-import android.content.Intent;
-import android.os.Bundle;
-
-public class TranslucentAssistantActivity extends AssistantActivity {
-
-    /**
-     * Launches a new instance of the TranslucentAssistantActivity directly into the assistant
-     * stack.
-     */
-    static void launchActivityIntoAssistantStack(Activity caller, Bundle extras) {
-        final Intent intent = new Intent(caller, TranslucentAssistantActivity.class);
-        intent.setFlags(FLAG_ACTIVITY_CLEAR_TASK | FLAG_ACTIVITY_NEW_TASK);
-        if (extras != null) {
-            intent.putExtras(extras);
-        }
-
-        final ActivityOptions options = ActivityOptions.makeBasic();
-        options.setLaunchStackId(6 /* ActivityManager.StackId.ASSISTANT_STACK_ID */);
-        caller.startActivity(intent, options.toBundle());
-    }
-}
diff --git a/hostsidetests/services/activityandwindowmanager/activitymanager/app/src/android/server/cts/TranslucentTestActivity.java b/hostsidetests/services/activityandwindowmanager/activitymanager/app/src/android/server/cts/TranslucentTestActivity.java
deleted file mode 100644
index 1d10a51..0000000
--- a/hostsidetests/services/activityandwindowmanager/activitymanager/app/src/android/server/cts/TranslucentTestActivity.java
+++ /dev/null
@@ -1,36 +0,0 @@
-/*
- * Copyright (C) 2015 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package android.server.cts;
-
-import android.os.Bundle;
-
-public class TranslucentTestActivity extends TestActivity {
-
-    private static final String TAG = TranslucentTestActivity.class.getSimpleName();
-
-    @Override
-    protected void onCreate(Bundle icicle) {
-        super.onCreate(icicle);
-
-        setContentView(R.layout.task_overlay);
-    }
-
-    @Override
-    protected String getTag() {
-        return TAG;
-    }
-}
diff --git a/hostsidetests/services/activityandwindowmanager/activitymanager/app/src/android/server/cts/TranslucentTopActivity.java b/hostsidetests/services/activityandwindowmanager/activitymanager/app/src/android/server/cts/TranslucentTopActivity.java
deleted file mode 100644
index 9d96898..0000000
--- a/hostsidetests/services/activityandwindowmanager/activitymanager/app/src/android/server/cts/TranslucentTopActivity.java
+++ /dev/null
@@ -1,53 +0,0 @@
-/*
- * Copyright (C) 2016 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package android.server.cts;
-
-import android.os.Handler;
-import android.os.Bundle;
-import android.util.Log;
-
-public class TranslucentTopActivity extends AbstractLifecycleLogActivity {
-
-    private static final String TAG = TranslucentTopActivity.class.getSimpleName();
-
-    @Override
-    protected String getTag() {
-        return TAG;
-    }
-
-    @Override
-    protected void onCreate(Bundle savedInstanceState) {
-        super.onCreate(savedInstanceState);
-
-        final boolean useWallpaper = getIntent().getBooleanExtra("USE_WALLPAPER", false);
-        if (useWallpaper) {
-            setTheme(R.style.TranslucentWallpaperTheme);
-        }
-
-        final int finishDelay = getIntent().getIntExtra("FINISH_DELAY", 0);
-        if (finishDelay > 0) {
-            Handler handler = new Handler();
-            handler.postDelayed(new Runnable() {
-                @Override
-                public void run() {
-                    Log.d(TAG, "Calling finish()");
-                    finish();
-                }
-            }, finishDelay);
-        }
-    }
-}
diff --git a/hostsidetests/services/activityandwindowmanager/activitymanager/app/src/android/server/cts/TurnScreenOnActivity.java b/hostsidetests/services/activityandwindowmanager/activitymanager/app/src/android/server/cts/TurnScreenOnActivity.java
deleted file mode 100644
index 79e31b3..0000000
--- a/hostsidetests/services/activityandwindowmanager/activitymanager/app/src/android/server/cts/TurnScreenOnActivity.java
+++ /dev/null
@@ -1,32 +0,0 @@
-/*
- * Copyright (C) 2016 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License
- */
-
-package android.server.cts;
-
-import android.app.Activity;
-import android.os.Bundle;
-import android.view.WindowManager;
-
-public class TurnScreenOnActivity extends Activity {
-    @Override
-    protected void onCreate(Bundle savedInstanceState) {
-        getWindow().addFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON
-                | WindowManager.LayoutParams.FLAG_SHOW_WHEN_LOCKED
-                | WindowManager.LayoutParams.FLAG_DISMISS_KEYGUARD
-                | WindowManager.LayoutParams.FLAG_TURN_SCREEN_ON);
-        super.onCreate(savedInstanceState);
-    }
-}
diff --git a/hostsidetests/services/activityandwindowmanager/activitymanager/app/src/android/server/cts/TurnScreenOnAttrActivity.java b/hostsidetests/services/activityandwindowmanager/activitymanager/app/src/android/server/cts/TurnScreenOnAttrActivity.java
deleted file mode 100644
index 5e1f5d1..0000000
--- a/hostsidetests/services/activityandwindowmanager/activitymanager/app/src/android/server/cts/TurnScreenOnAttrActivity.java
+++ /dev/null
@@ -1,26 +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.server.cts;
-
-public class TurnScreenOnAttrActivity extends AbstractLifecycleLogActivity {
-    private static final String TAG = TurnScreenOnAttrActivity.class.getSimpleName();
-
-    @Override
-    protected String getTag() {
-        return TAG;
-    }
-}
diff --git a/hostsidetests/services/activityandwindowmanager/activitymanager/app/src/android/server/cts/TurnScreenOnAttrDismissKeyguardActivity.java b/hostsidetests/services/activityandwindowmanager/activitymanager/app/src/android/server/cts/TurnScreenOnAttrDismissKeyguardActivity.java
deleted file mode 100644
index b1b860f..0000000
--- a/hostsidetests/services/activityandwindowmanager/activitymanager/app/src/android/server/cts/TurnScreenOnAttrDismissKeyguardActivity.java
+++ /dev/null
@@ -1,36 +0,0 @@
-/*
- * Copyright (C) 2017 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License
- */
-
-package android.server.cts;
-
-import android.app.KeyguardManager;
-import android.os.Bundle;
-
-public class TurnScreenOnAttrDismissKeyguardActivity extends AbstractLifecycleLogActivity {
-    private static final String TAG = TurnScreenOnAttrDismissKeyguardActivity.class.getSimpleName();
-
-    @Override
-    protected void onCreate(Bundle icicle) {
-        super.onCreate(icicle);
-        ((KeyguardManager) getSystemService(KEYGUARD_SERVICE))
-                .requestDismissKeyguard(this, new KeyguardDismissLoggerCallback(this));
-    }
-
-    @Override
-    protected String getTag() {
-        return TAG;
-    }
-}
diff --git a/hostsidetests/services/activityandwindowmanager/activitymanager/app/src/android/server/cts/TurnScreenOnAttrRemoveAttrActivity.java b/hostsidetests/services/activityandwindowmanager/activitymanager/app/src/android/server/cts/TurnScreenOnAttrRemoveAttrActivity.java
deleted file mode 100644
index 29911fe..0000000
--- a/hostsidetests/services/activityandwindowmanager/activitymanager/app/src/android/server/cts/TurnScreenOnAttrRemoveAttrActivity.java
+++ /dev/null
@@ -1,32 +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.server.cts;
-
-public class TurnScreenOnAttrRemoveAttrActivity extends AbstractLifecycleLogActivity {
-    private static final String TAG = TurnScreenOnAttrRemoveAttrActivity.class.getSimpleName();
-
-    @Override
-    protected void onStop() {
-        super.onStop();
-        setTurnScreenOn(false);
-    }
-
-    @Override
-    protected String getTag() {
-        return TAG;
-    }
-}
diff --git a/hostsidetests/services/activityandwindowmanager/activitymanager/app/src/android/server/cts/TurnScreenOnDismissKeyguardActivity.java b/hostsidetests/services/activityandwindowmanager/activitymanager/app/src/android/server/cts/TurnScreenOnDismissKeyguardActivity.java
deleted file mode 100644
index 487f785..0000000
--- a/hostsidetests/services/activityandwindowmanager/activitymanager/app/src/android/server/cts/TurnScreenOnDismissKeyguardActivity.java
+++ /dev/null
@@ -1,33 +0,0 @@
-/*
- * Copyright (C) 2016 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License
- */
-
-package android.server.cts;
-
-import android.app.Activity;
-import android.app.KeyguardManager;
-import android.os.Bundle;
-import android.view.WindowManager;
-
-public class TurnScreenOnDismissKeyguardActivity extends Activity {
-    @Override
-    protected void onCreate(Bundle savedInstanceState) {
-        super.onCreate(savedInstanceState);
-        getWindow().addFlags(WindowManager.LayoutParams.FLAG_SHOW_WHEN_LOCKED
-                | WindowManager.LayoutParams.FLAG_TURN_SCREEN_ON);
-        getSystemService(KeyguardManager.class).requestDismissKeyguard(this,
-                new KeyguardDismissLoggerCallback(this));
-    }
-}
diff --git a/hostsidetests/services/activityandwindowmanager/activitymanager/app/src/android/server/cts/TurnScreenOnShowOnLockActivity.java b/hostsidetests/services/activityandwindowmanager/activitymanager/app/src/android/server/cts/TurnScreenOnShowOnLockActivity.java
deleted file mode 100644
index 57ff4fb..0000000
--- a/hostsidetests/services/activityandwindowmanager/activitymanager/app/src/android/server/cts/TurnScreenOnShowOnLockActivity.java
+++ /dev/null
@@ -1,26 +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.server.cts;
-
-public class TurnScreenOnShowOnLockActivity extends AbstractLifecycleLogActivity {
-    private static final String TAG = TurnScreenOnShowOnLockActivity.class.getSimpleName();
-
-    @Override
-    protected String getTag() {
-        return TAG;
-    }
-}
diff --git a/hostsidetests/services/activityandwindowmanager/activitymanager/app/src/android/server/cts/TurnScreenOnSingleTaskActivity.java b/hostsidetests/services/activityandwindowmanager/activitymanager/app/src/android/server/cts/TurnScreenOnSingleTaskActivity.java
deleted file mode 100644
index 29c71d0..0000000
--- a/hostsidetests/services/activityandwindowmanager/activitymanager/app/src/android/server/cts/TurnScreenOnSingleTaskActivity.java
+++ /dev/null
@@ -1,26 +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.server.cts;
-
-public class TurnScreenOnSingleTaskActivity extends AbstractLifecycleLogActivity {
-    private static final String TAG = TurnScreenOnSingleTaskActivity.class.getSimpleName();
-
-    @Override
-    protected String getTag() {
-        return TAG;
-    }
-}
diff --git a/hostsidetests/services/activityandwindowmanager/activitymanager/app/src/android/server/cts/TurnScreenOnWithRelayoutActivity.java b/hostsidetests/services/activityandwindowmanager/activitymanager/app/src/android/server/cts/TurnScreenOnWithRelayoutActivity.java
deleted file mode 100644
index 14c2801..0000000
--- a/hostsidetests/services/activityandwindowmanager/activitymanager/app/src/android/server/cts/TurnScreenOnWithRelayoutActivity.java
+++ /dev/null
@@ -1,43 +0,0 @@
-/*
- * Copyright (C) 2017 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License
- */
-
-package android.server.cts;
-import android.view.WindowManager;
-
-public class TurnScreenOnWithRelayoutActivity extends AbstractLifecycleLogActivity {
-    private static final String TAG = TurnScreenOnWithRelayoutActivity.class.getSimpleName();
-
-    @Override
-    protected String getTag() {
-        return TAG;
-    }
-
-    @Override
-    protected void onStart() {
-        super.onStart();
-        getWindow().addFlags(WindowManager.LayoutParams.FLAG_SHOW_WHEN_LOCKED
-                | WindowManager.LayoutParams.FLAG_TURN_SCREEN_ON);
-    }
-
-    @Override
-    protected void onStop() {
-        super.onStop();
-
-        // This is to force a relayout, specifically with insets changed. When the insets are
-        // changed, it will trigger a performShow that could turn the screen on.
-        getWindow().addFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN);
-    }
-}
diff --git a/hostsidetests/services/activityandwindowmanager/activitymanager/app/src/android/server/cts/VirtualDisplayActivity.java b/hostsidetests/services/activityandwindowmanager/activitymanager/app/src/android/server/cts/VirtualDisplayActivity.java
deleted file mode 100644
index dd12acb..0000000
--- a/hostsidetests/services/activityandwindowmanager/activitymanager/app/src/android/server/cts/VirtualDisplayActivity.java
+++ /dev/null
@@ -1,235 +0,0 @@
-/*
- * Copyright (C) 2016 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License
- */
-
-package android.server.cts;
-
-import static android.hardware.display.DisplayManager.VIRTUAL_DISPLAY_FLAG_OWN_CONTENT_ONLY;
-import static android.hardware.display.DisplayManager.VIRTUAL_DISPLAY_FLAG_PUBLIC;
-
-import android.app.Activity;
-import android.content.Intent;
-import android.hardware.display.DisplayManager;
-import android.hardware.display.VirtualDisplay;
-import android.os.Bundle;
-import android.server.cts.tools.ActivityLauncher;
-import android.util.Log;
-import android.view.Surface;
-import android.view.SurfaceHolder;
-import android.view.SurfaceView;
-import android.view.ViewGroup;
-
-import java.util.HashMap;
-
-/**
- * Activity that is able to create and destroy a virtual display.
- */
-public class VirtualDisplayActivity extends Activity implements SurfaceHolder.Callback {
-    private static final String TAG = "VirtualDisplayActivity";
-
-    private static final int DEFAULT_DENSITY_DPI = 160;
-    private static final String KEY_DENSITY_DPI = "density_dpi";
-    private static final String KEY_CAN_SHOW_WITH_INSECURE_KEYGUARD
-            = "can_show_with_insecure_keyguard";
-    private static final String KEY_PUBLIC_DISPLAY = "public_display";
-    private static final String KEY_RESIZE_DISPLAY = "resize_display";
-    private static final String KEY_LAUNCH_TARGET_ACTIVITY = "launch_target_activity";
-    private static final String KEY_COUNT = "count";
-
-    private DisplayManager mDisplayManager;
-
-    // Container for details about a pending virtual display creation request.
-    private static class VirtualDisplayRequest {
-        public final SurfaceView surfaceView;
-        public final Bundle extras;
-
-        public VirtualDisplayRequest(SurfaceView surfaceView, Bundle extras) {
-            this.surfaceView = surfaceView;
-            this.extras = extras;
-        }
-    }
-
-    // Container to hold association between an active virtual display and surface view.
-    private static class VirtualDisplayEntry {
-        public final VirtualDisplay display;
-        public final SurfaceView surfaceView;
-        public final boolean resizeDisplay;
-        public final int density;
-
-        public VirtualDisplayEntry(VirtualDisplay display, SurfaceView surfaceView, int density,
-                boolean resizeDisplay) {
-            this.display = display;
-            this.surfaceView = surfaceView;
-            this.density = density;
-            this.resizeDisplay = resizeDisplay;
-        }
-    }
-
-    private HashMap<Surface, VirtualDisplayRequest> mPendingDisplayRequests;
-    private HashMap<Surface, VirtualDisplayEntry> mVirtualDisplays;
-
-    @Override
-    protected void onCreate(Bundle savedInstanceState) {
-        super.onCreate(savedInstanceState);
-        setContentView(R.layout.virtual_display_layout);
-
-        mVirtualDisplays = new HashMap<>();
-        mPendingDisplayRequests = new HashMap<>();
-        mDisplayManager = (DisplayManager) getSystemService(DISPLAY_SERVICE);
-    }
-
-    @Override
-    protected void onNewIntent(Intent intent) {
-        super.onNewIntent(intent);
-        final Bundle extras = intent.getExtras();
-        if (extras == null) {
-            return;
-        }
-
-        String command = extras.getString("command");
-        switch (command) {
-            case "create_display":
-                createVirtualDisplay(extras);
-                break;
-            case "destroy_display":
-                destroyVirtualDisplays();
-                break;
-            case "resize_display":
-                resizeDisplay();
-                break;
-        }
-    }
-
-    @Override
-    protected void onDestroy() {
-        super.onDestroy();
-        destroyVirtualDisplays();
-    }
-
-    private void createVirtualDisplay(Bundle extras) {
-        final int requestedCount = extras.getInt(KEY_COUNT, 1);
-        Log.d(TAG, "createVirtualDisplays. requested count:" + requestedCount);
-
-        for (int displayCount = 0; displayCount < requestedCount; ++displayCount) {
-            final ViewGroup root = (ViewGroup) findViewById(android.R.id.content);
-            final SurfaceView surfaceView = new SurfaceView(this);
-            surfaceView.setLayoutParams(new ViewGroup.LayoutParams(
-                    ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.MATCH_PARENT));
-            surfaceView.getHolder().addCallback(this);
-            mPendingDisplayRequests.put(surfaceView.getHolder().getSurface(),
-                    new VirtualDisplayRequest(surfaceView, extras));
-            root.addView(surfaceView);
-        }
-    }
-
-    private void destroyVirtualDisplays() {
-        Log.d(TAG, "destroyVirtualDisplays");
-        final ViewGroup root = (ViewGroup) findViewById(android.R.id.content);
-
-        for (VirtualDisplayEntry entry : mVirtualDisplays.values()) {
-            Log.d(TAG, "destroying:" + entry.display);
-            entry.display.release();
-            root.removeView(entry.surfaceView);
-        }
-
-        mPendingDisplayRequests.clear();
-        mVirtualDisplays.clear();
-    }
-
-    @Override
-    public void surfaceCreated(SurfaceHolder surfaceHolder) {
-        final VirtualDisplayRequest entry =
-                mPendingDisplayRequests.remove(surfaceHolder.getSurface());
-
-        if (entry == null) {
-            return;
-        }
-
-        final int densityDpi = entry.extras.getInt(KEY_DENSITY_DPI, DEFAULT_DENSITY_DPI);
-        final boolean resizeDisplay = entry.extras.getBoolean(KEY_RESIZE_DISPLAY);
-        final String launchActivityName = entry.extras.getString(KEY_LAUNCH_TARGET_ACTIVITY);
-        final Surface surface = surfaceHolder.getSurface();
-
-        // Initially, the surface will not have a set width or height so rely on the parent.
-        // This should be accurate with match parent on both params.
-        final int width = surfaceHolder.getSurfaceFrame().width();
-        final int height = surfaceHolder.getSurfaceFrame().height();
-
-        int flags = 0;
-
-        final boolean canShowWithInsecureKeyguard
-                = entry.extras.getBoolean(KEY_CAN_SHOW_WITH_INSECURE_KEYGUARD);
-        if (canShowWithInsecureKeyguard) {
-            flags |= 1 << 5; // VIRTUAL_DISPLAY_FLAG_CAN_SHOW_WITH_INSECURE_KEYGUARD
-        }
-
-        final boolean publicDisplay = entry.extras.getBoolean(KEY_PUBLIC_DISPLAY);
-        if (publicDisplay) {
-            flags |= VIRTUAL_DISPLAY_FLAG_PUBLIC | VIRTUAL_DISPLAY_FLAG_OWN_CONTENT_ONLY;
-        }
-
-        Log.d(TAG, "createVirtualDisplay: " + width + "x" + height + ", dpi: "
-                + densityDpi + ", canShowWithInsecureKeyguard=" + canShowWithInsecureKeyguard
-                + ", publicDisplay=" + publicDisplay);
-        try {
-            VirtualDisplay virtualDisplay = mDisplayManager.createVirtualDisplay(
-                    "VirtualDisplay" + mVirtualDisplays.size(), width,
-                    height, densityDpi, surface, flags);
-            mVirtualDisplays.put(surface,
-                    new VirtualDisplayEntry(virtualDisplay, entry.surfaceView, densityDpi,
-                            resizeDisplay));
-            if (launchActivityName != null) {
-                final int displayId = virtualDisplay.getDisplay().getDisplayId();
-                Log.d(TAG, "Launch activity after display created: activityName="
-                        + launchActivityName + ", displayId=" + displayId);
-                launchActivity(launchActivityName, displayId);
-            }
-        } catch (IllegalArgumentException e) {
-            final ViewGroup root = (ViewGroup) findViewById(android.R.id.content);
-            // This is expected when trying to create show-when-locked public display.
-            root.removeView(entry.surfaceView);
-        }
-
-    }
-
-    @Override
-    public void surfaceChanged(SurfaceHolder surfaceHolder, int format, int width, int height) {
-        final VirtualDisplayEntry entry = mVirtualDisplays.get(surfaceHolder.getSurface());
-
-        if (entry != null && entry.resizeDisplay) {
-            entry.display.resize(width, height, entry.density);
-        }
-    }
-
-    @Override
-    public void surfaceDestroyed(SurfaceHolder surfaceHolder) {
-    }
-
-    /** Resize virtual display to half of the surface frame size. */
-    private void resizeDisplay() {
-        final VirtualDisplayEntry vd = (VirtualDisplayEntry) mVirtualDisplays.values().toArray()[0];
-        final SurfaceHolder surfaceHolder = vd.surfaceView.getHolder();
-        vd.display.resize(surfaceHolder.getSurfaceFrame().width() / 2,
-                surfaceHolder.getSurfaceFrame().height() / 2, vd.density);
-    }
-
-    private void launchActivity(String activityName, int displayId) {
-        final Bundle extras = new Bundle();
-        extras.putBoolean("launch_activity", true);
-        extras.putString("target_activity", activityName);
-        extras.putInt("display_id", displayId);
-        ActivityLauncher.launchActivityFromExtras(this, extras);
-    }
-}
diff --git a/hostsidetests/services/activityandwindowmanager/activitymanager/app/src/android/server/cts/VrTestActivity.java b/hostsidetests/services/activityandwindowmanager/activitymanager/app/src/android/server/cts/VrTestActivity.java
deleted file mode 100644
index 6ef25e2..0000000
--- a/hostsidetests/services/activityandwindowmanager/activitymanager/app/src/android/server/cts/VrTestActivity.java
+++ /dev/null
@@ -1,62 +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.server.cts;
-
-import android.app.Activity;
-import android.content.ComponentName;
-import android.content.pm.PackageManager;
-import android.os.Bundle;
-import android.util.Log;
-
-import com.android.cts.verifier.vr.MockVrListenerService;
-
-/**
- * Activity that is able to create and destroy a virtual display.
- */
-public class VrTestActivity extends Activity {
-    private static final String TAG = "VrTestActivity";
-    private static final boolean DEBUG = false;
-
-    @Override
-    protected void onCreate(Bundle savedInstanceState) {
-        if (DEBUG) Log.i(TAG, "onCreate called.");
-        super.onCreate(savedInstanceState);
-        try {
-            setVrModeEnabled(true, new ComponentName(this, MockVrListenerService.class));
-        } catch (PackageManager.NameNotFoundException e) {
-            Log.e(TAG, "Could not set VR mode: " + e);
-        }
-    }
-
-    @Override
-    protected void onResume() {
-        if (DEBUG) Log.i(TAG, "onResume called.");
-        super.onResume();
-    }
-
-    @Override
-    public void onWindowFocusChanged(boolean hasFocus) {
-        if (DEBUG) Log.i(TAG, "onWindowFocusChanged called with " + hasFocus);
-        super.onWindowFocusChanged(hasFocus);
-    }
-
-    @Override
-    protected void onPause() {
-        if (DEBUG) Log.i(TAG, "onPause called.");
-        super.onPause();
-    }
-}
diff --git a/hostsidetests/services/activityandwindowmanager/activitymanager/app/src/android/server/cts/WallpaperActivity.java b/hostsidetests/services/activityandwindowmanager/activitymanager/app/src/android/server/cts/WallpaperActivity.java
deleted file mode 100644
index 63e11d5..0000000
--- a/hostsidetests/services/activityandwindowmanager/activitymanager/app/src/android/server/cts/WallpaperActivity.java
+++ /dev/null
@@ -1,22 +0,0 @@
-/*
- * Copyright (C) 2016 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License
- */
-
-package android.server.cts;
-
-import android.app.Activity;
-
-public class WallpaperActivity extends Activity {
-}
diff --git a/hostsidetests/services/activityandwindowmanager/activitymanager/app/src/android/server/cts/tools/ActivityLauncher.java b/hostsidetests/services/activityandwindowmanager/activitymanager/app/src/android/server/cts/tools/ActivityLauncher.java
deleted file mode 100644
index 15b55f5..0000000
--- a/hostsidetests/services/activityandwindowmanager/activitymanager/app/src/android/server/cts/tools/ActivityLauncher.java
+++ /dev/null
@@ -1,90 +0,0 @@
-/*
- * Copyright (C) 2017 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License
- */
-
-package android.server.cts.tools;
-
-import static android.content.Intent.FLAG_ACTIVITY_LAUNCH_ADJACENT;
-import static android.content.Intent.FLAG_ACTIVITY_MULTIPLE_TASK;
-import static android.content.Intent.FLAG_ACTIVITY_NEW_TASK;
-import static android.content.Intent.FLAG_ACTIVITY_REORDER_TO_FRONT;
-
-import android.app.ActivityOptions;
-import android.content.Intent;
-import android.content.ComponentName;
-import android.content.Context;
-import android.net.Uri;
-import android.os.Bundle;
-import android.server.cts.TestActivity;
-import android.util.Log;
-
-/** Utility class which contains common code for launching activities. */
-public class ActivityLauncher {
-    private static final String TAG = ActivityLauncher.class.getSimpleName();
-
-    public static void launchActivityFromExtras(final Context context, Bundle extras) {
-        if (extras == null || !extras.getBoolean("launch_activity")) {
-            return;
-        }
-
-        Log.i(TAG, "launchActivityFromExtras: extras=" + extras);
-
-        final Intent newIntent = new Intent();
-        final String targetActivity = extras.getString("target_activity");
-        if (targetActivity != null) {
-            final String extraPackageName = extras.getString("package_name");
-            final String packageName = extraPackageName != null ? extraPackageName
-                    : context.getApplicationContext().getPackageName();
-            newIntent.setComponent(new ComponentName(packageName,
-                    packageName + "." + targetActivity));
-        } else {
-            newIntent.setClass(context, TestActivity.class);
-        }
-
-        if (extras.getBoolean("launch_to_the_side")) {
-            newIntent.addFlags(FLAG_ACTIVITY_NEW_TASK | FLAG_ACTIVITY_LAUNCH_ADJACENT);
-            if (extras.getBoolean("random_data")) {
-                final Uri data = new Uri.Builder()
-                        .path(String.valueOf(System.currentTimeMillis()))
-                        .build();
-                newIntent.setData(data);
-            }
-        }
-        if (extras.getBoolean("multiple_task")) {
-            newIntent.addFlags(FLAG_ACTIVITY_MULTIPLE_TASK);
-        }
-        if (extras.getBoolean("new_task")) {
-            newIntent.addFlags(FLAG_ACTIVITY_NEW_TASK);
-        }
-
-        if (extras.getBoolean("reorder_to_front")) {
-            newIntent.addFlags(FLAG_ACTIVITY_REORDER_TO_FRONT);
-        }
-
-        ActivityOptions options = null;
-        final int displayId = extras.getInt("display_id", -1);
-        if (displayId != -1) {
-            options = ActivityOptions.makeBasic();
-            options.setLaunchDisplayId(displayId);
-            newIntent.addFlags(FLAG_ACTIVITY_NEW_TASK | FLAG_ACTIVITY_MULTIPLE_TASK);
-        }
-
-        try {
-            context.startActivity(newIntent, options != null ? options.toBundle() : null);
-        } catch (SecurityException e) {
-            Log.e(TAG, "SecurityException launching activity");
-        }
-    }
-}
diff --git a/hostsidetests/services/activityandwindowmanager/activitymanager/appDebuggable/AndroidManifest.xml b/hostsidetests/services/activityandwindowmanager/activitymanager/appDebuggable/AndroidManifest.xml
deleted file mode 100644
index c00f2b6..0000000
--- a/hostsidetests/services/activityandwindowmanager/activitymanager/appDebuggable/AndroidManifest.xml
+++ /dev/null
@@ -1,31 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
-     Copyright (C) 2017 The Android Open Source Project
-
-     Licensed under the Apache License, Version 2.0 (the "License");
-     you may not use this file except in compliance with the License.
-     You may obtain a copy of the License at
-
-          http://www.apache.org/licenses/LICENSE-2.0
-
-     Unless required by applicable law or agreed to in writing, software
-     distributed under the License is distributed on an "AS IS" BASIS,
-     WITHOUT 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.server.cts.debuggable">
-
-    <!--
-     * Security policy requires that only debuggable processes can be profiled
-     * which is tested by ActivityManagerAmProfileTests.
-     -->
-    <application android:debuggable="true">
-        <activity android:name=".DebuggableAppActivity"
-                  android:resizeableActivity="true"
-                  android:exported="true" />
-    </application>
-
-</manifest>
diff --git a/hostsidetests/services/activityandwindowmanager/activitymanager/appDebuggable/src/android/server/cts/debuggable/DebuggableAppActivity.java b/hostsidetests/services/activityandwindowmanager/activitymanager/appDebuggable/src/android/server/cts/debuggable/DebuggableAppActivity.java
deleted file mode 100644
index 504ffcf..0000000
--- a/hostsidetests/services/activityandwindowmanager/activitymanager/appDebuggable/src/android/server/cts/debuggable/DebuggableAppActivity.java
+++ /dev/null
@@ -1,22 +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.server.cts.debuggable;
-
-import android.app.Activity;
-
-public class DebuggableAppActivity extends Activity {
-}
diff --git a/hostsidetests/services/activityandwindowmanager/activitymanager/appDisplaySize/AndroidManifest.xml b/hostsidetests/services/activityandwindowmanager/activitymanager/appDisplaySize/AndroidManifest.xml
deleted file mode 100644
index 7727759..0000000
--- a/hostsidetests/services/activityandwindowmanager/activitymanager/appDisplaySize/AndroidManifest.xml
+++ /dev/null
@@ -1,32 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
-     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.
--->
-
-<manifest xmlns:android="http://schemas.android.com/apk/res/android"
-          package="android.displaysize.app">
-
-    <uses-permission android:name="android.permission.DISABLE_KEYGUARD" />
-
-    <!-- Set a ridiculously high value for required smallest width. Hopefully
-         we never have to run CTS on Times Square display. -->
-    <supports-screens android:requiresSmallestWidthDp="100000" />
-
-    <application>
-        <activity android:name=".SmallestWidthActivity"
-                  android:exported="true" />
-    </application>
-
-</manifest>
diff --git a/hostsidetests/services/activityandwindowmanager/activitymanager/appDisplaySize/src/android/displaysize/app/SmallestWidthActivity.java b/hostsidetests/services/activityandwindowmanager/activitymanager/appDisplaySize/src/android/displaysize/app/SmallestWidthActivity.java
deleted file mode 100644
index 5da44c7..0000000
--- a/hostsidetests/services/activityandwindowmanager/activitymanager/appDisplaySize/src/android/displaysize/app/SmallestWidthActivity.java
+++ /dev/null
@@ -1,38 +0,0 @@
-/*
- * Copyright (C) 2016 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package android.displaysize.app;
-
-import android.app.Activity;
-import android.content.ComponentName;
-import android.content.Intent;
-import android.os.Bundle;
-
-public class SmallestWidthActivity extends Activity {
-
-    @Override
-    protected void onNewIntent(Intent intent) {
-        super.onNewIntent(intent);
-
-        final Bundle extras = intent.getExtras();
-        if (extras != null && extras.getBoolean("launch_another_activity")) {
-            Intent startIntent = new Intent();
-            startIntent.setComponent(
-                    new ComponentName("android.server.cts", "android.server.cts.TestActivity"));
-            startActivity(startIntent);
-        }
-    }
-}
diff --git a/hostsidetests/services/activityandwindowmanager/activitymanager/appSecondUid/AndroidManifest.xml b/hostsidetests/services/activityandwindowmanager/activitymanager/appSecondUid/AndroidManifest.xml
deleted file mode 100644
index 53aa2d4..0000000
--- a/hostsidetests/services/activityandwindowmanager/activitymanager/appSecondUid/AndroidManifest.xml
+++ /dev/null
@@ -1,42 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
-     Copyright (C) 2017 The Android Open Source Project
-
-     Licensed under the Apache License, Version 2.0 (the "License");
-     you may not use this file except in compliance with the License.
-     You may obtain a copy of the License at
-
-          http://www.apache.org/licenses/LICENSE-2.0
-
-     Unless required by applicable law or agreed to in writing, software
-     distributed under the License is distributed on an "AS IS" BASIS,
-     WITHOUT 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.server.cts.second">
-
-    <application>
-        <activity
-            android:name=".SecondActivity"
-            android:resizeableActivity="true"
-            android:allowEmbedded="true"
-            android:exported="true" />
-        <activity
-            android:name=".SecondActivityNoEmbedding"
-            android:resizeableActivity="true"
-            android:allowEmbedded="false"
-            android:exported="true" />
-        <receiver
-            android:name=".LaunchBroadcastReceiver"
-            android:enabled="true"
-            android:exported="true" >
-            <intent-filter>
-                <action android:name="android.server.cts.second.LAUNCH_BROADCAST_ACTION"/>
-            </intent-filter>
-        </receiver>
-    </application>
-
-</manifest>
diff --git a/hostsidetests/services/activityandwindowmanager/activitymanager/appSecondUid/src/android/server/cts/second/LaunchBroadcastReceiver.java b/hostsidetests/services/activityandwindowmanager/activitymanager/appSecondUid/src/android/server/cts/second/LaunchBroadcastReceiver.java
deleted file mode 100644
index a8ebb86..0000000
--- a/hostsidetests/services/activityandwindowmanager/activitymanager/appSecondUid/src/android/server/cts/second/LaunchBroadcastReceiver.java
+++ /dev/null
@@ -1,60 +0,0 @@
-/*
- * Copyright (C) 2017 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License
- */
-
-package android.server.cts.second;
-
-import android.app.ActivityOptions;
-import android.content.BroadcastReceiver;
-import android.content.ComponentName;
-import android.content.Context;
-import android.content.Intent;
-import android.os.Bundle;
-import android.util.Log;
-
-/** Broadcast receiver that can launch activities. */
-public class LaunchBroadcastReceiver extends BroadcastReceiver {
-    private static final String TAG = LaunchBroadcastReceiver.class.getSimpleName();
-
-    @Override
-    public void onReceive(Context context, Intent intent) {
-        final Bundle extras = intent.getExtras();
-        final Intent newIntent = new Intent();
-
-        final String targetActivity = extras != null ? extras.getString("target_activity") : null;
-        if (targetActivity != null) {
-            String packageName = extras.getString("package_name");
-            newIntent.setComponent(new ComponentName(packageName,
-                    packageName + "." + targetActivity));
-        } else {
-            newIntent.setClass(context, SecondActivity.class);
-        }
-
-        ActivityOptions options = ActivityOptions.makeBasic();
-        int displayId = extras.getInt("display_id", -1);
-        if (displayId != -1) {
-            options.setLaunchDisplayId(displayId);
-            newIntent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_MULTIPLE_TASK);
-        } else {
-            newIntent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
-        }
-
-        try {
-            context.startActivity(newIntent, options.toBundle());
-        } catch (SecurityException e) {
-            Log.e(TAG, "SecurityException launching activity");
-        }
-    }
-}
diff --git a/hostsidetests/services/activityandwindowmanager/activitymanager/appSecondUid/src/android/server/cts/second/SecondActivity.java b/hostsidetests/services/activityandwindowmanager/activitymanager/appSecondUid/src/android/server/cts/second/SecondActivity.java
deleted file mode 100644
index cea9ed5..0000000
--- a/hostsidetests/services/activityandwindowmanager/activitymanager/appSecondUid/src/android/server/cts/second/SecondActivity.java
+++ /dev/null
@@ -1,22 +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.server.cts.second;
-
-import android.app.Activity;
-
-public class SecondActivity extends Activity {
-}
diff --git a/hostsidetests/services/activityandwindowmanager/activitymanager/appSecondUid/src/android/server/cts/second/SecondActivityNoEmbedding.java b/hostsidetests/services/activityandwindowmanager/activitymanager/appSecondUid/src/android/server/cts/second/SecondActivityNoEmbedding.java
deleted file mode 100644
index 543e008..0000000
--- a/hostsidetests/services/activityandwindowmanager/activitymanager/appSecondUid/src/android/server/cts/second/SecondActivityNoEmbedding.java
+++ /dev/null
@@ -1,22 +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.server.cts.second;
-
-import android.app.Activity;
-
-public class SecondActivityNoEmbedding extends Activity {
-}
diff --git a/hostsidetests/services/activityandwindowmanager/activitymanager/appThirdUid/AndroidManifest.xml b/hostsidetests/services/activityandwindowmanager/activitymanager/appThirdUid/AndroidManifest.xml
deleted file mode 100644
index aed24c7..0000000
--- a/hostsidetests/services/activityandwindowmanager/activitymanager/appThirdUid/AndroidManifest.xml
+++ /dev/null
@@ -1,28 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
-     Copyright (C) 2017 The Android Open Source Project
-
-     Licensed under the Apache License, Version 2.0 (the "License");
-     you may not use this file except in compliance with the License.
-     You may obtain a copy of the License at
-
-          http://www.apache.org/licenses/LICENSE-2.0
-
-     Unless required by applicable law or agreed to in writing, software
-     distributed under the License is distributed on an "AS IS" BASIS,
-     WITHOUT 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.server.cts.third">
-
-    <application>
-        <activity android:name=".ThirdActivity"
-                  android:resizeableActivity="true"
-                  android:allowEmbedded="true"
-                  android:exported="true" />
-    </application>
-
-</manifest>
diff --git a/hostsidetests/services/activityandwindowmanager/activitymanager/appThirdUid/src/android/server/cts/third/ThirdActivity.java b/hostsidetests/services/activityandwindowmanager/activitymanager/appThirdUid/src/android/server/cts/third/ThirdActivity.java
deleted file mode 100644
index eeddc3d..0000000
--- a/hostsidetests/services/activityandwindowmanager/activitymanager/appThirdUid/src/android/server/cts/third/ThirdActivity.java
+++ /dev/null
@@ -1,22 +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.server.cts.third;
-
-import android.app.Activity;
-
-public class ThirdActivity extends Activity {
-}
diff --git a/hostsidetests/services/activityandwindowmanager/activitymanager/src/android/server/cts/ActivityAndWindowManagerOverrideConfigTests.java b/hostsidetests/services/activityandwindowmanager/activitymanager/src/android/server/cts/ActivityAndWindowManagerOverrideConfigTests.java
deleted file mode 100644
index add7e42..0000000
--- a/hostsidetests/services/activityandwindowmanager/activitymanager/src/android/server/cts/ActivityAndWindowManagerOverrideConfigTests.java
+++ /dev/null
@@ -1,91 +0,0 @@
-/*
- * Copyright (C) 2016 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package android.server.cts;
-
-import com.android.ddmlib.Log.LogLevel;
-import com.android.tradefed.device.CollectingOutputReceiver;
-import com.android.tradefed.device.DeviceNotAvailableException;
-import com.android.tradefed.log.LogUtil.CLog;
-
-import java.util.regex.Matcher;
-import java.util.regex.Pattern;
-
-import static android.server.cts.StateLogger.log;
-
-/**
- * Build: mmma -j32 cts/hostsidetests/services
- * Run: cts/hostsidetests/services/activityandwindowmanager/util/run-test CtsServicesHostTestCases android.server.cts.ActivityAndWindowManagerOverrideConfigTests
- */
-public class ActivityAndWindowManagerOverrideConfigTests extends ActivityManagerTestBase {
-    private static final String TEST_ACTIVITY_NAME = "LogConfigurationActivity";
-
-    private class ConfigurationChangeObserver {
-        private final Pattern mConfigurationChangedPattern =
-            Pattern.compile("(.+)Configuration changed: (\\d+),(\\d+)");
-
-        private ConfigurationChangeObserver() {
-        }
-
-        private boolean findConfigurationChange(String activityName, String logSeparator)
-                throws DeviceNotAvailableException, InterruptedException {
-            int tries = 0;
-            boolean observedChange = false;
-            while (tries < 5 && !observedChange) {
-                final String[] lines = getDeviceLogsForComponent(activityName, logSeparator);
-                log("Looking at logcat");
-                for (int i = lines.length - 1; i >= 0; i--) {
-                    final String line = lines[i].trim();
-                    log(line);
-                    Matcher matcher = mConfigurationChangedPattern.matcher(line);
-                    if (matcher.matches()) {
-                        observedChange = true;
-                        break;
-                    }
-                }
-                tries++;
-                Thread.sleep(500);
-            }
-            return observedChange;
-        }
-    }
-
-    public void testReceiveOverrideConfigFromRelayout() throws Exception {
-        if (!supportsFreeform()) {
-            CLog.logAndDisplay(LogLevel.INFO, "Device doesn't support freeform. Skipping test.");
-            return;
-        }
-
-        launchActivityInStack(TEST_ACTIVITY_NAME, FREEFORM_WORKSPACE_STACK_ID);
-
-        setDeviceRotation(0);
-        String logSeparator = clearLogcat();
-        resizeActivityTask(TEST_ACTIVITY_NAME, 0, 0, 100, 100);
-        ConfigurationChangeObserver c = new ConfigurationChangeObserver();
-        final boolean reportedSizeAfterResize = c.findConfigurationChange(TEST_ACTIVITY_NAME,
-                logSeparator);
-        assertTrue("Expected to observe configuration change when resizing",
-                reportedSizeAfterResize);
-
-        logSeparator = clearLogcat();
-        setDeviceRotation(2);
-        final boolean reportedSizeAfterRotation = c.findConfigurationChange(TEST_ACTIVITY_NAME,
-                logSeparator);
-        assertFalse("Not expected to observe configuration change after flip rotation",
-                reportedSizeAfterRotation);
-    }
-}
-
diff --git a/hostsidetests/services/activityandwindowmanager/activitymanager/src/android/server/cts/ActivityManagerActivityVisibilityTests.java b/hostsidetests/services/activityandwindowmanager/activitymanager/src/android/server/cts/ActivityManagerActivityVisibilityTests.java
deleted file mode 100644
index c51f24a..0000000
--- a/hostsidetests/services/activityandwindowmanager/activitymanager/src/android/server/cts/ActivityManagerActivityVisibilityTests.java
+++ /dev/null
@@ -1,404 +0,0 @@
-/*
- * Copyright (C) 2016 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License
- */
-
-package android.server.cts;
-
-import static android.server.cts.ActivityManagerState.STATE_RESUMED;
-import static android.server.cts.StateLogger.logE;
-
-import android.platform.test.annotations.Presubmit;
-
-import java.lang.Exception;
-import java.lang.String;
-
-import static com.android.ddmlib.Log.LogLevel.INFO;
-
-import com.android.tradefed.log.LogUtil.CLog;
-import com.android.tradefed.device.DeviceNotAvailableException;
-
-/**
- * Build: mmma -j32 cts/hostsidetests/services
- * Run: cts/hostsidetests/services/activityandwindowmanager/util/run-test CtsServicesHostTestCases android.server.cts.ActivityManagerActivityVisibilityTests
- */
-public class ActivityManagerActivityVisibilityTests extends ActivityManagerTestBase {
-    private static final String TRANSLUCENT_ACTIVITY = "AlwaysFocusablePipActivity";
-    private static final String PIP_ON_PIP_ACTIVITY = "LaunchPipOnPipActivity";
-    private static final String TEST_ACTIVITY_NAME = "TestActivity";
-    private static final String TRANSLUCENT_ACTIVITY_NAME = "TranslucentActivity";
-    private static final String DOCKED_ACTIVITY_NAME = "DockedActivity";
-    private static final String TURN_SCREEN_ON_ACTIVITY_NAME = "TurnScreenOnActivity";
-    private static final String MOVE_TASK_TO_BACK_ACTIVITY_NAME = "MoveTaskToBackActivity";
-    private static final String SWIPE_REFRESH_ACTIVITY = "SwipeRefreshActivity";
-
-    private static final String NOHISTORY_ACTIVITY = "NoHistoryActivity";
-    private static final String TURN_SCREEN_ON_ATTR_ACTIVITY_NAME = "TurnScreenOnAttrActivity";
-    private static final String TURN_SCREEN_ON_SHOW_ON_LOCK_ACTIVITY_NAME = "TurnScreenOnShowOnLockActivity";
-    private static final String TURN_SCREEN_ON_ATTR_REMOVE_ATTR_ACTIVITY_NAME = "TurnScreenOnAttrRemoveAttrActivity";
-    private static final String TURN_SCREEN_ON_SINGLE_TASK_ACTIVITY_NAME = "TurnScreenOnSingleTaskActivity";
-    private static final String TURN_SCREEN_ON_WITH_RELAYOUT_ACTIVITY =
-            "TurnScreenOnWithRelayoutActivity";
-
-    @Override
-    protected void tearDown() throws Exception {
-        super.tearDown();
-        tearDownLockCredentials();
-    }
-
-    public void testTranslucentActivityOnTopOfPinnedStack() throws Exception {
-        if (!supportsPip()) {
-            return;
-        }
-
-        executeShellCommand(getAmStartCmdOverHome(PIP_ON_PIP_ACTIVITY));
-        mAmWmState.waitForValidState(mDevice, PIP_ON_PIP_ACTIVITY);
-        // NOTE: moving to pinned stack will trigger the pip-on-pip activity to launch the
-        // translucent activity.
-        executeShellCommand(AM_MOVE_TOP_ACTIVITY_TO_PINNED_STACK_COMMAND);
-
-        mAmWmState.computeState(mDevice, new String[] {PIP_ON_PIP_ACTIVITY, TRANSLUCENT_ACTIVITY});
-        mAmWmState.assertFrontStack("Pinned stack must be the front stack.", PINNED_STACK_ID);
-        mAmWmState.assertVisibility(PIP_ON_PIP_ACTIVITY, true);
-        mAmWmState.assertVisibility(TRANSLUCENT_ACTIVITY, true);
-    }
-
-    /**
-     * Asserts that the home activity is visible when a translucent activity is launched in the
-     * fullscreen stack over the home activity.
-     */
-    public void testTranslucentActivityOnTopOfHome() throws Exception {
-        if (noHomeScreen()) {
-            return;
-        }
-
-        launchHomeActivity();
-        launchActivity(TRANSLUCENT_ACTIVITY);
-
-        mAmWmState.computeState(mDevice, new String[]{TRANSLUCENT_ACTIVITY});
-        mAmWmState.assertFrontStack(
-                "Fullscreen stack must be the front stack.", FULLSCREEN_WORKSPACE_STACK_ID);
-        mAmWmState.assertVisibility(TRANSLUCENT_ACTIVITY, true);
-        mAmWmState.assertHomeActivityVisible(true);
-    }
-
-    /**
-     * Assert that the home activity is visible if a task that was launched from home is pinned
-     * and also assert the next task in the fullscreen stack isn't visible.
-     */
-    public void testHomeVisibleOnActivityTaskPinned() throws Exception {
-        if (!supportsPip()) {
-            return;
-        }
-
-        launchHomeActivity();
-        launchActivity(TEST_ACTIVITY_NAME);
-        launchHomeActivity();
-        launchActivity(TRANSLUCENT_ACTIVITY);
-        executeShellCommand(AM_MOVE_TOP_ACTIVITY_TO_PINNED_STACK_COMMAND);
-
-        mAmWmState.computeState(mDevice, new String[]{TRANSLUCENT_ACTIVITY});
-
-        mAmWmState.assertVisibility(TRANSLUCENT_ACTIVITY, true);
-        mAmWmState.assertVisibility(TEST_ACTIVITY_NAME, false);
-        mAmWmState.assertHomeActivityVisible(true);
-    }
-
-    public void testTranslucentActivityOverDockedStack() throws Exception {
-        if (!supportsSplitScreenMultiWindow()) {
-            CLog.logAndDisplay(INFO, "Skipping test: no multi-window support");
-            return;
-        }
-
-        launchActivityInDockStack(DOCKED_ACTIVITY_NAME);
-        mAmWmState.computeState(mDevice, new String[] {DOCKED_ACTIVITY_NAME});
-        launchActivityInStack(TEST_ACTIVITY_NAME, FULLSCREEN_WORKSPACE_STACK_ID);
-        mAmWmState.computeState(mDevice, new String[] {DOCKED_ACTIVITY_NAME, TEST_ACTIVITY_NAME});
-        launchActivityInStack(TRANSLUCENT_ACTIVITY_NAME, DOCKED_STACK_ID);
-        mAmWmState.computeState(mDevice, new String[] {TEST_ACTIVITY_NAME, DOCKED_ACTIVITY_NAME,
-                TRANSLUCENT_ACTIVITY_NAME}, false /* compareTaskAndStackBounds */);
-        mAmWmState.assertContainsStack("Must contain docked stack", DOCKED_STACK_ID);
-        mAmWmState.assertContainsStack("Must contain fullscreen stack",
-                FULLSCREEN_WORKSPACE_STACK_ID);
-        mAmWmState.assertVisibility(DOCKED_ACTIVITY_NAME, true);
-        mAmWmState.assertVisibility(TEST_ACTIVITY_NAME, true);
-        mAmWmState.assertVisibility(TRANSLUCENT_ACTIVITY_NAME, true);
-    }
-
-    @Presubmit
-    public void testTurnScreenOnActivity() throws Exception {
-        sleepDevice();
-        launchActivity(TURN_SCREEN_ON_ACTIVITY_NAME);
-        mAmWmState.computeState(mDevice, new String[] { TURN_SCREEN_ON_ACTIVITY_NAME });
-        mAmWmState.assertVisibility(TURN_SCREEN_ON_ACTIVITY_NAME, true);
-    }
-
-    public void testFinishActivityInNonFocusedStack() throws Exception {
-        if (!supportsSplitScreenMultiWindow()) {
-            CLog.logAndDisplay(INFO, "Skipping test: no multi-window support");
-            return;
-        }
-
-        // Launch two activities in docked stack.
-        launchActivityInDockStack(LAUNCHING_ACTIVITY);
-        getLaunchActivityBuilder().setTargetActivityName(BROADCAST_RECEIVER_ACTIVITY).execute();
-        mAmWmState.computeState(mDevice, new String[] { BROADCAST_RECEIVER_ACTIVITY });
-        mAmWmState.assertVisibility(BROADCAST_RECEIVER_ACTIVITY, true);
-        // Launch something to fullscreen stack to make it focused.
-        launchActivityInStack(TEST_ACTIVITY_NAME, 1);
-        mAmWmState.computeState(mDevice, new String[] { TEST_ACTIVITY_NAME });
-        mAmWmState.assertVisibility(TEST_ACTIVITY_NAME, true);
-        // Finish activity in non-focused (docked) stack.
-        executeShellCommand(FINISH_ACTIVITY_BROADCAST);
-        mAmWmState.computeState(mDevice, new String[] { LAUNCHING_ACTIVITY });
-        mAmWmState.assertVisibility(LAUNCHING_ACTIVITY, true);
-        mAmWmState.assertVisibility(BROADCAST_RECEIVER_ACTIVITY, false);
-    }
-
-    public void testFinishActivityWithMoveTaskToBackAfterPause() throws Exception {
-        performFinishActivityWithMoveTaskToBack("on_pause");
-    }
-
-    public void testFinishActivityWithMoveTaskToBackAfterStop() throws Exception {
-        performFinishActivityWithMoveTaskToBack("on_stop");
-    }
-
-    private void performFinishActivityWithMoveTaskToBack(String finishPoint) throws Exception {
-        // Make sure home activity is visible.
-        launchHomeActivity();
-        if (!noHomeScreen()) {
-            mAmWmState.assertHomeActivityVisible(true /* visible */);
-        }
-
-        // Launch an activity that calls "moveTaskToBack" to finish itself.
-        launchActivity(MOVE_TASK_TO_BACK_ACTIVITY_NAME, "finish_point", finishPoint);
-        mAmWmState.waitForValidState(mDevice, MOVE_TASK_TO_BACK_ACTIVITY_NAME);
-        mAmWmState.assertVisibility(MOVE_TASK_TO_BACK_ACTIVITY_NAME, true);
-
-        // Launch a different activity on top.
-        launchActivity(BROADCAST_RECEIVER_ACTIVITY);
-        mAmWmState.waitForActivityState(mDevice, BROADCAST_RECEIVER_ACTIVITY, STATE_RESUMED);
-        mAmWmState.assertVisibility(MOVE_TASK_TO_BACK_ACTIVITY_NAME, false);
-        mAmWmState.assertVisibility(BROADCAST_RECEIVER_ACTIVITY, true);
-
-        // Finish the top-most activity.
-        executeShellCommand(FINISH_ACTIVITY_BROADCAST);
-
-        // Home must be visible.
-        if (!noHomeScreen()) {
-            mAmWmState.waitForHomeActivityVisible(mDevice);
-            mAmWmState.assertHomeActivityVisible(true /* visible */);
-        }
-    }
-
-    /**
-     * Asserts that launching between reorder to front activities exhibits the correct backstack
-     * behavior.
-     */
-    public void testReorderToFrontBackstack() throws Exception {
-        // Start with home on top
-        launchHomeActivity();
-        if (!noHomeScreen()) {
-            mAmWmState.assertHomeActivityVisible(true /* visible */);
-        }
-
-        // Launch the launching activity to the foreground
-        launchActivity(LAUNCHING_ACTIVITY);
-
-        // Launch the alternate launching activity from launching activity with reorder to front.
-        getLaunchActivityBuilder().setTargetActivityName(ALT_LAUNCHING_ACTIVITY)
-                .setReorderToFront(true).execute();
-
-        // Launch the launching activity from the alternate launching activity with reorder to
-        // front.
-        getLaunchActivityBuilder().setTargetActivityName(LAUNCHING_ACTIVITY)
-                .setLaunchingActivityName(ALT_LAUNCHING_ACTIVITY).setReorderToFront(true)
-                .execute();
-
-        // Press back
-        pressBackButton();
-
-        mAmWmState.waitForValidState(mDevice, ALT_LAUNCHING_ACTIVITY);
-
-        // Ensure the alternate launching activity is in focus
-        mAmWmState.assertFocusedActivity("Alt Launching Activity must be focused",
-                ALT_LAUNCHING_ACTIVITY);
-    }
-
-    /**
-     * Asserts that the activity focus and history is preserved moving between the activity and
-     * home stack.
-     */
-    public void testReorderToFrontChangingStack() throws Exception {
-        // Start with home on top
-        launchHomeActivity();
-        if (!noHomeScreen()) {
-            mAmWmState.assertHomeActivityVisible(true /* visible */);
-        }
-
-        // Launch the launching activity to the foreground
-        launchActivity(LAUNCHING_ACTIVITY);
-
-        // Launch the alternate launching activity from launching activity with reorder to front.
-        getLaunchActivityBuilder().setTargetActivityName(ALT_LAUNCHING_ACTIVITY)
-                .setReorderToFront(true).execute();
-
-        // Return home
-        launchHomeActivity();
-        if (!noHomeScreen()) {
-            mAmWmState.assertHomeActivityVisible(true /* visible */);
-        }
-        // Launch the launching activity from the alternate launching activity with reorder to
-        // front.
-
-        // Bring launching activity back to the foreground
-        launchActivity(LAUNCHING_ACTIVITY);
-        mAmWmState.waitForValidState(mDevice, LAUNCHING_ACTIVITY);
-
-        // Ensure the alternate launching activity is still in focus.
-        mAmWmState.assertFocusedActivity("Alt Launching Activity must be focused",
-                ALT_LAUNCHING_ACTIVITY);
-
-        pressBackButton();
-
-        mAmWmState.waitForValidState(mDevice, LAUNCHING_ACTIVITY);
-
-        // Ensure launching activity was brought forward.
-        mAmWmState.assertFocusedActivity("Launching Activity must be focused",
-                LAUNCHING_ACTIVITY);
-    }
-
-    /**
-     * Asserts that a nohistory activity is stopped and removed immediately after a resumed activity
-     * above becomes visible and does not idle.
-     */
-    public void testNoHistoryActivityFinishedResumedActivityNotIdle() throws Exception {
-        if (noHomeScreen()) {
-            return;
-        }
-
-        // Start with home on top
-        launchHomeActivity();
-
-        // Launch no history activity
-        launchActivity(NOHISTORY_ACTIVITY);
-
-        // Launch an activity with a swipe refresh layout configured to prevent idle.
-        launchActivity(SWIPE_REFRESH_ACTIVITY);
-
-        pressBackButton();
-        mAmWmState.waitForHomeActivityVisible(mDevice);
-        mAmWmState.assertHomeActivityVisible(true);
-    }
-
-    public void testTurnScreenOnAttrNoLockScreen() throws Exception {
-        wakeUpAndRemoveLock();
-        sleepDevice();
-        final String logSeparator = clearLogcat();
-        launchActivity(TURN_SCREEN_ON_ATTR_ACTIVITY_NAME);
-        mAmWmState.computeState(mDevice, new String[] { TURN_SCREEN_ON_ATTR_ACTIVITY_NAME });
-        mAmWmState.assertVisibility(TURN_SCREEN_ON_ATTR_ACTIVITY_NAME, true);
-        assertTrue(isDisplayOn());
-        assertSingleLaunch(TURN_SCREEN_ON_ATTR_ACTIVITY_NAME, logSeparator);
-    }
-
-    public void testTurnScreenOnAttrWithLockScreen() throws Exception {
-        if (!isHandheld()) {
-            // This test requires the ability to have a lock screen.
-            return;
-        }
-
-        setLockCredential();
-        sleepDevice();
-        final String logSeparator = clearLogcat();
-        launchActivity(TURN_SCREEN_ON_ATTR_ACTIVITY_NAME);
-        mAmWmState.computeState(mDevice, new String[] { TURN_SCREEN_ON_ATTR_ACTIVITY_NAME });
-        assertFalse(isDisplayOn());
-        assertSingleLaunchAndStop(TURN_SCREEN_ON_ATTR_ACTIVITY_NAME, logSeparator);
-    }
-
-    public void testTurnScreenOnShowOnLockAttr() throws Exception {
-        sleepDevice();
-        mAmWmState.waitForAllStoppedActivities(mDevice);
-        final String logSeparator = clearLogcat();
-        launchActivity(TURN_SCREEN_ON_SHOW_ON_LOCK_ACTIVITY_NAME);
-        mAmWmState.computeState(mDevice, new String[] { TURN_SCREEN_ON_SHOW_ON_LOCK_ACTIVITY_NAME });
-        mAmWmState.assertVisibility(TURN_SCREEN_ON_SHOW_ON_LOCK_ACTIVITY_NAME, true);
-        assertTrue(isDisplayOn());
-        assertSingleLaunch(TURN_SCREEN_ON_SHOW_ON_LOCK_ACTIVITY_NAME, logSeparator);
-    }
-
-    public void testTurnScreenOnAttrRemove() throws Exception {
-        sleepDevice();
-        mAmWmState.waitForAllStoppedActivities(mDevice);
-        String logSeparator = clearLogcat();
-        launchActivity(TURN_SCREEN_ON_ATTR_REMOVE_ATTR_ACTIVITY_NAME);
-        mAmWmState.computeState(mDevice, new String[] {
-                TURN_SCREEN_ON_ATTR_REMOVE_ATTR_ACTIVITY_NAME});
-        assertTrue(isDisplayOn());
-        assertSingleLaunch(TURN_SCREEN_ON_ATTR_REMOVE_ATTR_ACTIVITY_NAME, logSeparator);
-
-        sleepDevice();
-        mAmWmState.waitForAllStoppedActivities(mDevice);
-        logSeparator = clearLogcat();
-        launchActivity(TURN_SCREEN_ON_ATTR_REMOVE_ATTR_ACTIVITY_NAME);
-        assertFalse(isDisplayOn());
-        assertSingleStartAndStop(TURN_SCREEN_ON_ATTR_REMOVE_ATTR_ACTIVITY_NAME, logSeparator);
-    }
-
-    public void testTurnScreenOnSingleTask() throws Exception {
-        sleepDevice();
-        String logSeparator = clearLogcat();
-        launchActivity(TURN_SCREEN_ON_SINGLE_TASK_ACTIVITY_NAME);
-        mAmWmState.computeState(mDevice, new String[] { TURN_SCREEN_ON_SINGLE_TASK_ACTIVITY_NAME });
-        mAmWmState.assertVisibility(TURN_SCREEN_ON_SINGLE_TASK_ACTIVITY_NAME, true);
-        assertTrue(isDisplayOn());
-        assertSingleLaunch(TURN_SCREEN_ON_SINGLE_TASK_ACTIVITY_NAME, logSeparator);
-
-        sleepDevice();
-        logSeparator = clearLogcat();
-        launchActivity(TURN_SCREEN_ON_SINGLE_TASK_ACTIVITY_NAME);
-        mAmWmState.computeState(mDevice, new String[] { TURN_SCREEN_ON_SINGLE_TASK_ACTIVITY_NAME });
-        mAmWmState.assertVisibility(TURN_SCREEN_ON_SINGLE_TASK_ACTIVITY_NAME, true);
-        assertTrue(isDisplayOn());
-        assertSingleStart(TURN_SCREEN_ON_SINGLE_TASK_ACTIVITY_NAME, logSeparator);
-    }
-
-    public void testTurnScreenOnActivity_withRelayout() throws Exception {
-        sleepDevice();
-        launchActivity(TURN_SCREEN_ON_WITH_RELAYOUT_ACTIVITY);
-        mAmWmState.computeState(mDevice, new String[] { TURN_SCREEN_ON_WITH_RELAYOUT_ACTIVITY });
-        mAmWmState.assertVisibility(TURN_SCREEN_ON_WITH_RELAYOUT_ACTIVITY, true);
-
-        String logSeparator = clearLogcat();
-        sleepDevice();
-        mAmWmState.waitFor("Waiting for stopped state", () ->
-                lifecycleStopOccurred(TURN_SCREEN_ON_WITH_RELAYOUT_ACTIVITY, logSeparator));
-
-        // Ensure there was an actual stop if the waitFor timed out.
-        assertTrue(lifecycleStopOccurred(TURN_SCREEN_ON_WITH_RELAYOUT_ACTIVITY, logSeparator));
-        assertFalse(isDisplayOn());
-    }
-
-    private boolean lifecycleStopOccurred(String activityName, String logSeparator) {
-        try {
-            ActivityLifecycleCounts lifecycleCounts = new ActivityLifecycleCounts(activityName,
-                    logSeparator);
-            return lifecycleCounts.mStopCount > 0;
-        } catch (DeviceNotAvailableException e) {
-            logE(e.toString());
-            return false;
-        }
-    }
-}
diff --git a/hostsidetests/services/activityandwindowmanager/activitymanager/src/android/server/cts/ActivityManagerAmProfileTests.java b/hostsidetests/services/activityandwindowmanager/activitymanager/src/android/server/cts/ActivityManagerAmProfileTests.java
deleted file mode 100644
index f462c60..0000000
--- a/hostsidetests/services/activityandwindowmanager/activitymanager/src/android/server/cts/ActivityManagerAmProfileTests.java
+++ /dev/null
@@ -1,166 +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.server.cts;
-
-import com.google.common.io.Files;
-
-import com.android.tradefed.device.DeviceNotAvailableException;
-import com.android.tradefed.device.ITestDevice;
-import com.android.tradefed.util.FileUtil;
-
-import java.io.File;
-import java.lang.StringBuilder;
-
-/**
- * Build: mmma -j32 cts/hostsidetests/services
- * Run: cts/hostsidetests/services/activityandwindowmanager/util/run-test CtsServicesHostTestCases android.server.cts.ActivityManagerAmProfileTests
- *
- * Please talk to Android Studio team first if you want to modify or delete these tests.
- */
-public class ActivityManagerAmProfileTests extends ActivityManagerTestBase {
-
-    private static final String TEST_PACKAGE_NAME = "android.server.cts.debuggable";
-    private static final String TEST_ACTIVITY_NAME = "DebuggableAppActivity";
-    private static final String OUTPUT_FILE_PATH = "/data/local/tmp/profile.trace";
-    private static final String FIRST_WORD_NO_STREAMING = "*version\n";
-    private static final String FIRST_WORD_STREAMING = "SLOW";  // Magic word set by runtime.
-
-    private ITestDevice mDevice;
-
-    @Override
-    protected void setUp() throws Exception {
-        super.setUp();
-        mDevice = getDevice();
-        setComponentName(TEST_PACKAGE_NAME);
-    }
-
-    /**
-     * Test am profile functionality with the following 3 configurable options:
-     *    starting the activity before start profiling? yes;
-     *    sampling-based profiling? no;
-     *    using streaming output mode? no.
-     */
-    public void testAmProfileStartNoSamplingNoStreaming() throws Exception {
-        // am profile start ... , and the same to the following 3 test methods.
-        testProfile(true, false, false);
-    }
-
-    /**
-     * The following tests are similar to testAmProfileStartNoSamplingNoStreaming(),
-     * only different in the three configuration options.
-     */
-    public void testAmProfileStartNoSamplingStreaming() throws Exception {
-        testProfile(true, false, true);
-    }
-    public void testAmProfileStartSamplingNoStreaming() throws Exception {
-        testProfile(true, true, false);
-    }
-    public void testAmProfileStartSamplingStreaming() throws Exception {
-        testProfile(true, true, true);
-    }
-    public void testAmStartStartProfilerNoSamplingNoStreaming() throws Exception {
-        // am start --start-profiler ..., and the same to the following 3 test methods.
-        testProfile(false, false, false);
-    }
-    public void testAmStartStartProfilerNoSamplingStreaming() throws Exception {
-        testProfile(false, false, true);
-    }
-    public void testAmStartStartProfilerSamplingNoStreaming() throws Exception {
-        testProfile(false, true, false);
-    }
-    public void testAmStartStartProfilerSamplingStreaming() throws Exception {
-        testProfile(false, true, true);
-    }
-
-    private void testProfile(boolean startActivityFirst,
-                             boolean sampling, boolean streaming) throws Exception {
-        if (startActivityFirst) {
-            launchActivity(TEST_ACTIVITY_NAME);
-        }
-
-        String cmd = getStartCmd(TEST_ACTIVITY_NAME, startActivityFirst, sampling, streaming);
-        executeShellCommand(cmd);
-        // Go to home screen and then warm start the activity to generate some interesting trace.
-        pressHomeButton();
-        launchActivity(TEST_ACTIVITY_NAME);
-
-        cmd = "am profile stop " + componentName;
-        executeShellCommand(cmd);
-        // Sleep for 0.1 second (100 milliseconds) so the generation of the profiling
-        // file is complete.
-        try {
-            Thread.sleep(100);
-        } catch (InterruptedException e) {
-            //ignored
-        }
-        verifyOutputFileFormat(streaming);
-    }
-
-    private String getStartCmd(String activityName, boolean activityAlreadyStarted,
-                                        boolean sampling, boolean streaming) {
-        StringBuilder builder = new StringBuilder("am");
-        if (activityAlreadyStarted) {
-            builder.append(" profile start");
-        } else {
-            builder.append(String.format(" start -n %s/.%s -W -S --start-profiler %s",
-                                         componentName, activityName, OUTPUT_FILE_PATH));
-        }
-        if (sampling) {
-            builder.append(" --sampling 1000");
-        }
-        if (streaming) {
-            builder.append(" --streaming");
-        }
-        if (activityAlreadyStarted) {
-            builder.append(String.format(" %s %s", componentName, OUTPUT_FILE_PATH));
-        } else {
-
-        }
-        return builder.toString();
-    }
-
-    private void verifyOutputFileFormat(boolean streaming) throws Exception {
-        String expectedFirstWord = streaming ? FIRST_WORD_STREAMING : FIRST_WORD_NO_STREAMING;
-        byte[] data = readFileOnClient(OUTPUT_FILE_PATH);
-        assertTrue("data size=" + data.length, data.length >= expectedFirstWord.length());
-        String actualFirstWord = new String(data, 0, expectedFirstWord.length());
-        assertTrue("Unexpected first word: '" + actualFirstWord + "'",
-                   actualFirstWord.equals(expectedFirstWord));
-        // Clean up.
-        executeShellCommand("rm -f " + OUTPUT_FILE_PATH);
-    }
-
-    private byte[] readFileOnClient(String clientPath) throws Exception {
-        assertTrue("File not found on client: " + clientPath,
-                mDevice.doesFileExist(clientPath));
-        File copyOnHost = File.createTempFile("host", "copy");
-        try {
-            executeAdbCommand("pull", clientPath, copyOnHost.getPath());
-            return Files.toByteArray(copyOnHost);
-        } finally {
-            FileUtil.deleteFile(copyOnHost);
-        }
-    }
-
-    private String[] executeAdbCommand(String... command) throws DeviceNotAvailableException {
-        String output = mDevice.executeAdbCommand(command);
-        // "".split() returns { "" }, but we want an empty array
-        String[] lines = output.equals("") ? new String[0] : output.split("\n");
-        return lines;
-    }
-
-}
diff --git a/hostsidetests/services/activityandwindowmanager/activitymanager/src/android/server/cts/ActivityManagerAmStartOptionsTests.java b/hostsidetests/services/activityandwindowmanager/activitymanager/src/android/server/cts/ActivityManagerAmStartOptionsTests.java
deleted file mode 100644
index f6df4a8..0000000
--- a/hostsidetests/services/activityandwindowmanager/activitymanager/src/android/server/cts/ActivityManagerAmStartOptionsTests.java
+++ /dev/null
@@ -1,185 +0,0 @@
-/*
- * Copyright (C) 2016 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package android.server.cts;
-
-import com.android.tradefed.device.DeviceNotAvailableException;
-
-import java.util.regex.Pattern;
-import java.util.regex.Matcher;
-
-/**
- * Build: mmma -j32 cts/hostsidetests/services
- * Run: cts/hostsidetests/services/activityandwindowmanager/util/run-test CtsServicesHostTestCases android.server.cts.ActivityManagerAmStartOptionsTests
- */
-public class ActivityManagerAmStartOptionsTests extends ActivityManagerTestBase {
-
-    private static final String TEST_ACTIVITY_NAME = "TestActivity";
-    private static final String ENTRYPOINT_ACTIVITY_NAME = "EntryPointAliasActivity";
-    private static final String SINGLE_TASK_ACTIVITY_NAME = "SingleTaskActivity";
-
-    public void testDashD() throws Exception {
-        final String activityComponentName =
-                ActivityManagerTestBase.getActivityComponentName(TEST_ACTIVITY_NAME);
-
-        final String[] waitForActivityRecords = new String[] {activityComponentName};
-
-        // Run at least 2 rounds to verify that -D works with an existing process.
-        // -D could fail in this case if the force stop of process is broken.
-        int prevProcId = -1;
-        for (int i = 0; i < 2; i++) {
-            executeShellCommand(getAmStartCmd(TEST_ACTIVITY_NAME) + " -D");
-
-            mAmWmState.waitForDebuggerWindowVisible(mDevice, waitForActivityRecords);
-            int procId = mAmWmState.getAmState().getActivityProcId(activityComponentName);
-
-            assertTrue("Invalid ProcId.", procId >= 0);
-            if (i > 0) {
-                assertTrue("Run " + i + " didn't start new proc.", prevProcId != procId);
-            }
-            prevProcId = procId;
-        }
-    }
-
-    public void testDashW_Direct() throws Exception {
-        testDashW(SINGLE_TASK_ACTIVITY_NAME, SINGLE_TASK_ACTIVITY_NAME);
-    }
-
-    public void testDashW_Indirect() throws Exception {
-        testDashW(ENTRYPOINT_ACTIVITY_NAME, SINGLE_TASK_ACTIVITY_NAME);
-    }
-
-    private void testDashW(final String entryActivity, final String actualActivity)
-            throws Exception {
-        // Test cold start
-        startActivityAndVerifyResult(entryActivity, actualActivity, true);
-
-        // Test warm start
-        pressHomeButton();
-        startActivityAndVerifyResult(entryActivity, actualActivity, false);
-
-        // Test "hot" start (app already in front)
-        startActivityAndVerifyResult(entryActivity, actualActivity, false);
-    }
-
-    private void startActivityAndVerifyResult(final String entryActivity,
-            final String actualActivity, boolean shouldStart) throws Exception {
-        // See TODO below
-        // final String logSeparator = clearLogcat();
-
-        // Pass in different data only when cold starting. This is to make the intent
-        // different in subsequent warm/hot launches, so that the entrypoint alias
-        // activity is always started, but the actual activity is not started again
-        // because of the NEW_TASK and singleTask flags.
-        final String result = executeShellCommand(getAmStartCmd(entryActivity) + " -W"
-                + (shouldStart ? " -d about:blank" : ""));
-
-        // Verify shell command return value
-        verifyShellOutput(result, actualActivity, shouldStart);
-
-        // TODO: Disable logcat check for now.
-        // Logcat of WM or AM tag could be lost (eg. chatty if earlier events generated
-        // too many lines), and make the test look flaky. We need to either use event
-        // log or swith to other mechanisms. Only verify shell output for now, it should
-        // still catch most failures.
-
-        // Verify adb logcat log
-        //verifyLogcat(actualActivity, shouldStart, logSeparator);
-    }
-
-    private static final Pattern sNotStartedWarningPattern = Pattern.compile(
-            "Warning: Activity not started(.*)");
-    private static final Pattern sStatusPattern = Pattern.compile(
-            "Status: (.*)");
-    private static final Pattern sActivityPattern = Pattern.compile(
-            "Activity: (.*)");
-    private static final String sStatusOk = "ok";
-
-    private void verifyShellOutput(
-            final String result, final String activity, boolean shouldStart) {
-        boolean warningFound = false;
-        String status = null;
-        String reportedActivity = null;
-        String componentActivityName = getActivityComponentName(activity);
-
-        final String[] lines = result.split("\\n");
-        // Going from the end of logs to beginning in case if some other activity is started first.
-        for (int i = lines.length - 1; i >= 0; i--) {
-            final String line = lines[i].trim();
-            Matcher matcher = sNotStartedWarningPattern.matcher(line);
-            if (matcher.matches()) {
-                warningFound = true;
-                continue;
-            }
-            matcher = sStatusPattern.matcher(line);
-            if (matcher.matches()) {
-                status = matcher.group(1);
-                continue;
-            }
-            matcher = sActivityPattern.matcher(line);
-            if (matcher.matches()) {
-                reportedActivity = matcher.group(1);
-                continue;
-            }
-        }
-
-        assertTrue("Status " + status + " is not ok", sStatusOk.equals(status));
-        assertTrue("Reported activity is " + reportedActivity + " not " + componentActivityName,
-                componentActivityName.equals(reportedActivity));
-
-        if (shouldStart && warningFound) {
-            fail("Should start new activity but brought something to front.");
-        } else if (!shouldStart && !warningFound){
-            fail("Should bring existing activity to front but started new activity.");
-        }
-    }
-
-    private static final Pattern sDisplayTimePattern =
-            Pattern.compile("(.+): Displayed (.*): (\\+{0,1})([0-9]+)ms(.*)");
-
-    void verifyLogcat(String actualActivityName, boolean shouldStart, String logSeparator)
-            throws DeviceNotAvailableException {
-        int displayCount = 0;
-        String activityName = null;
-
-        for (String line : getDeviceLogsForComponent("ActivityManager", logSeparator)) {
-            line = line.trim();
-
-            Matcher matcher = sDisplayTimePattern.matcher(line);
-            if (matcher.matches()) {
-                activityName = matcher.group(2);
-                // Ignore activitiy displays from other packages, we don't
-                // want some random activity starts to ruin our test.
-                if (!activityName.startsWith("android.server.cts")) {
-                    continue;
-                }
-                if (!shouldStart) {
-                    fail("Shouldn't display anything but displayed " + activityName);
-                }
-                displayCount++;
-            }
-        }
-        final String expectedActivityName = getActivityComponentName(actualActivityName);
-        if (shouldStart) {
-            if (displayCount != 1) {
-                fail("Should display exactly one activity but displayed " + displayCount);
-            } else if (!expectedActivityName.equals(activityName)) {
-                fail("Should display " + expectedActivityName +
-                        " but displayed " + activityName);
-            }
-        }
-    }
-}
diff --git a/hostsidetests/services/activityandwindowmanager/activitymanager/src/android/server/cts/ActivityManagerAppConfigurationTests.java b/hostsidetests/services/activityandwindowmanager/activitymanager/src/android/server/cts/ActivityManagerAppConfigurationTests.java
deleted file mode 100644
index ccecd1d..0000000
--- a/hostsidetests/services/activityandwindowmanager/activitymanager/src/android/server/cts/ActivityManagerAppConfigurationTests.java
+++ /dev/null
@@ -1,588 +0,0 @@
-/*
- * Copyright (C) 2016 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License"); you may not
- * use this file except in compliance with the License. You may obtain a copy of
- * the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
- * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
- * License for the specific language governing permissions and limitations under
- * the License.
- */
-package android.server.cts;
-
-import static android.server.cts.ActivityManagerState.STATE_RESUMED;
-
-import android.platform.test.annotations.Presubmit;
-
-import android.server.cts.ActivityManagerState.ActivityStack;
-import android.server.cts.ActivityManagerState.ActivityTask;
-import com.android.ddmlib.Log.LogLevel;
-import com.android.tradefed.log.LogUtil.CLog;
-
-import java.awt.Rectangle;
-import java.util.ArrayList;
-import java.util.List;
-
-import static android.server.cts.ActivityAndWindowManagersState.dpToPx;
-
-/**
- * Build: mmma -j32 cts/hostsidetests/services
- * Run: cts/hostsidetests/services/activityandwindowmanager/util/run-test CtsServicesHostTestCases android.server.cts.ActivityManagerAppConfigurationTests
- */
-public class ActivityManagerAppConfigurationTests extends ActivityManagerTestBase {
-    private static final String RESIZEABLE_ACTIVITY_NAME = "ResizeableActivity";
-    private static final String TEST_ACTIVITY_NAME = "TestActivity";
-    private static final String PORTRAIT_ACTIVITY_NAME = "PortraitOrientationActivity";
-    private static final String LANDSCAPE_ACTIVITY_NAME = "LandscapeOrientationActivity";
-    private static final String NIGHT_MODE_ACTIVITY = "NightModeActivity";
-    private static final String DIALOG_WHEN_LARGE_ACTIVITY = "DialogWhenLargeActivity";
-
-    private static final String TRANSLUCENT_ACTIVITY =
-            "android.server.translucentapp.TranslucentLandscapeActivity";
-    private static final String TRANSLUCENT_SDK_26_PACKAGE = "android.server.translucentapp26";
-    private static final String TRANSLUCENT_CURRENT_PACKAGE = "android.server.translucentapp";
-
-    private static final String EXTRA_LAUNCH_NEW_TASK = "launch_new_task";
-
-    private static final int SMALL_WIDTH_DP = 426;
-    private static final int SMALL_HEIGHT_DP = 320;
-
-    /**
-     * Tests that the WindowManager#getDefaultDisplay() and the Configuration of the Activity
-     * has an updated size when the Activity is resized from fullscreen to docked state.
-     *
-     * The Activity handles configuration changes, so it will not be restarted between resizes.
-     * On Configuration changes, the Activity logs the Display size and Configuration width
-     * and heights. The values reported in fullscreen should be larger than those reported in
-     * docked state.
-     */
-    public void testConfigurationUpdatesWhenResizedFromFullscreen() throws Exception {
-        if (!supportsSplitScreenMultiWindow()) {
-            CLog.logAndDisplay(LogLevel.INFO, "Skipping test: no multi-window support");
-            return;
-        }
-
-        String logSeparator = clearLogcat();
-        launchActivityInStack(RESIZEABLE_ACTIVITY_NAME, FULLSCREEN_WORKSPACE_STACK_ID);
-        final ReportedSizes fullscreenSizes = getActivityDisplaySize(RESIZEABLE_ACTIVITY_NAME,
-                logSeparator);
-
-        logSeparator = clearLogcat();
-        moveActivityToStack(RESIZEABLE_ACTIVITY_NAME, DOCKED_STACK_ID);
-        final ReportedSizes dockedSizes = getActivityDisplaySize(RESIZEABLE_ACTIVITY_NAME,
-                logSeparator);
-
-        assertSizesAreSane(fullscreenSizes, dockedSizes);
-    }
-
-    /**
-     * Same as {@link #testConfigurationUpdatesWhenResizedFromFullscreen()} but resizing
-     * from docked state to fullscreen (reverse).
-     */
-    // TODO: Flaky, add to presubmit when b/63404575 is fixed.
-    public void testConfigurationUpdatesWhenResizedFromDockedStack() throws Exception {
-        if (!supportsSplitScreenMultiWindow()) {
-            CLog.logAndDisplay(LogLevel.INFO, "Skipping test: no multi-window support");
-            return;
-        }
-
-        String logSeparator = clearLogcat();
-        launchActivityInStack(RESIZEABLE_ACTIVITY_NAME, DOCKED_STACK_ID);
-        final ReportedSizes dockedSizes = getActivityDisplaySize(RESIZEABLE_ACTIVITY_NAME,
-                logSeparator);
-
-        logSeparator = clearLogcat();
-        moveActivityToStack(RESIZEABLE_ACTIVITY_NAME, FULLSCREEN_WORKSPACE_STACK_ID);
-        final ReportedSizes fullscreenSizes = getActivityDisplaySize(RESIZEABLE_ACTIVITY_NAME,
-                logSeparator);
-
-        assertSizesAreSane(fullscreenSizes, dockedSizes);
-    }
-
-    /**
-     * Tests whether the Display sizes change when rotating the device.
-     */
-    public void testConfigurationUpdatesWhenRotatingWhileFullscreen() throws Exception {
-        if (!supportsRotation()) {
-            CLog.logAndDisplay(LogLevel.INFO, "Skipping test: no rotation support");
-            return;
-        }
-        setDeviceRotation(0);
-        final String logSeparator = clearLogcat();
-        launchActivityInStack(RESIZEABLE_ACTIVITY_NAME, FULLSCREEN_WORKSPACE_STACK_ID);
-        final ReportedSizes initialSizes = getActivityDisplaySize(RESIZEABLE_ACTIVITY_NAME,
-                logSeparator);
-
-        rotateAndCheckSizes(initialSizes);
-    }
-
-    /**
-     * Same as {@link #testConfigurationUpdatesWhenRotatingWhileFullscreen()} but when the Activity
-     * is in the docked stack.
-     */
-    // TODO: Flaky, add to presubmit when b/63404575 is fixed.
-    public void testConfigurationUpdatesWhenRotatingWhileDocked() throws Exception {
-        if (!supportsSplitScreenMultiWindow()) {
-            CLog.logAndDisplay(LogLevel.INFO, "Skipping test: no multi-window support");
-            return;
-        }
-
-        setDeviceRotation(0);
-        final String logSeparator = clearLogcat();
-        launchActivityInDockStack(LAUNCHING_ACTIVITY);
-        // Launch our own activity to side in case Recents (or other activity to side) doesn't
-        // support rotation.
-        getLaunchActivityBuilder().setToSide(true).setTargetActivityName(TEST_ACTIVITY_NAME)
-                .execute();
-        // Launch target activity in docked stack.
-        getLaunchActivityBuilder().setTargetActivityName(RESIZEABLE_ACTIVITY_NAME).execute();
-        final ReportedSizes initialSizes = getActivityDisplaySize(RESIZEABLE_ACTIVITY_NAME,
-                logSeparator);
-
-        rotateAndCheckSizes(initialSizes);
-    }
-
-    /**
-     * Same as {@link #testConfigurationUpdatesWhenRotatingWhileDocked()} but when the Activity
-     * is launched to side from docked stack.
-     */
-    public void testConfigurationUpdatesWhenRotatingToSideFromDocked() throws Exception {
-        if (!supportsSplitScreenMultiWindow()) {
-            CLog.logAndDisplay(LogLevel.INFO, "Skipping test: no multi-window support");
-            return;
-        }
-
-        setDeviceRotation(0);
-
-        final String logSeparator = clearLogcat();
-        launchActivityInDockStack(LAUNCHING_ACTIVITY);
-
-        getLaunchActivityBuilder().setToSide(true).setTargetActivityName(RESIZEABLE_ACTIVITY_NAME)
-                .execute();
-        final ReportedSizes initialSizes = getActivityDisplaySize(RESIZEABLE_ACTIVITY_NAME,
-                logSeparator);
-
-        rotateAndCheckSizes(initialSizes);
-    }
-
-    private void rotateAndCheckSizes(ReportedSizes prevSizes) throws Exception {
-        for (int rotation = 3; rotation >= 0; --rotation) {
-            final String logSeparator = clearLogcat();
-            final int actualStackId = mAmWmState.getAmState().getTaskByActivityName(
-                    RESIZEABLE_ACTIVITY_NAME).mStackId;
-            final int displayId = mAmWmState.getAmState().getStackById(actualStackId).mDisplayId;
-            setDeviceRotation(rotation);
-            final int newDeviceRotation = getDeviceRotation(displayId);
-            if (newDeviceRotation == INVALID_DEVICE_ROTATION) {
-                CLog.logAndDisplay(LogLevel.WARN, "Got an invalid device rotation value. "
-                        + "Continuing the test despite of that, but it is likely to fail.");
-            } else if (rotation != newDeviceRotation) {
-                CLog.logAndDisplay(LogLevel.INFO, "This device doesn't support locked user "
-                        + "rotation mode. Not continuing the rotation checks.");
-                return;
-            }
-
-            final ReportedSizes rotatedSizes = getActivityDisplaySize(RESIZEABLE_ACTIVITY_NAME,
-                    logSeparator);
-            assertSizesRotate(prevSizes, rotatedSizes);
-            prevSizes = rotatedSizes;
-        }
-    }
-
-    /**
-     * Tests when activity moved from fullscreen stack to docked and back. Activity will be
-     * relaunched twice and it should have same config as initial one.
-     */
-    public void testSameConfigurationFullSplitFullRelaunch() throws Exception {
-        moveActivityFullSplitFull(TEST_ACTIVITY_NAME);
-    }
-
-    /**
-     * Same as {@link #testSameConfigurationFullSplitFullRelaunch} but without relaunch.
-     */
-    @Presubmit
-    public void testSameConfigurationFullSplitFullNoRelaunch() throws Exception {
-        moveActivityFullSplitFull(RESIZEABLE_ACTIVITY_NAME);
-    }
-
-    /**
-     * Launches activity in fullscreen stack, moves to docked stack and back to fullscreen stack.
-     * Last operation is done in a way which simulates split-screen divider movement maximizing
-     * docked stack size and then moving task to fullscreen stack - the same way it is done when
-     * user long-presses overview/recents button to exit split-screen.
-     * Asserts that initial and final reported sizes in fullscreen stack are the same.
-     */
-    private void moveActivityFullSplitFull(String activityName) throws Exception {
-        if (!supportsSplitScreenMultiWindow()) {
-            CLog.logAndDisplay(LogLevel.INFO, "Skipping test: no multi-window support");
-            return;
-        }
-
-        // Launch to fullscreen stack and record size.
-        String logSeparator = clearLogcat();
-        launchActivityInStack(activityName, FULLSCREEN_WORKSPACE_STACK_ID);
-        final ReportedSizes initialFullscreenSizes = getActivityDisplaySize(activityName,
-                logSeparator);
-        final Rectangle displayRect = getDisplayRect(activityName);
-
-        // Move to docked stack.
-        logSeparator = clearLogcat();
-        moveActivityToStack(activityName, DOCKED_STACK_ID);
-        final ReportedSizes dockedSizes = getActivityDisplaySize(activityName, logSeparator);
-        assertSizesAreSane(initialFullscreenSizes, dockedSizes);
-        // Make sure docked stack is focused. This way when we dismiss it later fullscreen stack
-        // will come up.
-        launchActivityInStack(activityName, DOCKED_STACK_ID);
-        mAmWmState.computeState(mDevice, new String[] { activityName },
-                false /* compareTaskAndStackBounds */);
-
-        // Resize docked stack to fullscreen size. This will trigger activity relaunch with
-        // non-empty override configuration corresponding to fullscreen size.
-        logSeparator = clearLogcat();
-        runCommandAndPrintOutput("am stack resize " + DOCKED_STACK_ID + " 0 0 "
-                + displayRect.width + " " + displayRect.height);
-        // Move activity back to fullscreen stack.
-        moveActivityToStack(activityName, FULLSCREEN_WORKSPACE_STACK_ID);
-        final ReportedSizes finalFullscreenSizes = getActivityDisplaySize(activityName,
-                logSeparator);
-
-        // After activity configuration was changed twice it must report same size as original one.
-        assertSizesAreSame(initialFullscreenSizes, finalFullscreenSizes);
-    }
-
-    /**
-     * Tests when activity moved from docked stack to fullscreen and back. Activity will be
-     * relaunched twice and it should have same config as initial one.
-     */
-    public void testSameConfigurationSplitFullSplitRelaunch() throws Exception {
-        moveActivitySplitFullSplit(TEST_ACTIVITY_NAME);
-    }
-
-    /**
-     * Same as {@link #testSameConfigurationSplitFullSplitRelaunch} but without relaunch.
-     */
-    public void testSameConfigurationSplitFullSplitNoRelaunch() throws Exception {
-        moveActivitySplitFullSplit(RESIZEABLE_ACTIVITY_NAME);
-    }
-
-    /**
-     * Tests that an activity with the DialogWhenLarge theme can transform properly when in split
-     * screen.
-     */
-    @Presubmit
-    public void testDialogWhenLargeSplitSmall() throws Exception {
-        if (!supportsSplitScreenMultiWindow()) {
-            CLog.logAndDisplay(LogLevel.INFO, "Skipping test: no multi-window support");
-            return;
-        }
-
-        launchActivityInStack(DIALOG_WHEN_LARGE_ACTIVITY, DOCKED_STACK_ID);
-        final ActivityManagerState.ActivityStack stack = mAmWmState.getAmState()
-                .getStackById(DOCKED_STACK_ID);
-        final WindowManagerState.Display display =
-                mAmWmState.getWmState().getDisplay(stack.mDisplayId);
-        final int density = display.getDpi();
-        final int smallWidthPx = dpToPx(SMALL_WIDTH_DP, density);
-        final int smallHeightPx = dpToPx(SMALL_HEIGHT_DP, density);
-
-        runCommandAndPrintOutput("am stack resize " + DOCKED_STACK_ID + " 0 0 "
-                + smallWidthPx + " " + smallHeightPx);
-        mAmWmState.waitForValidState(mDevice, DIALOG_WHEN_LARGE_ACTIVITY, DOCKED_STACK_ID);
-    }
-
-    /**
-     * Test that device handles consequent requested orientations and displays the activities.
-     */
-    @Presubmit
-    public void testFullscreenAppOrientationRequests() throws Exception {
-        launchActivity(PORTRAIT_ACTIVITY_NAME);
-        mAmWmState.assertVisibility(PORTRAIT_ACTIVITY_NAME, true /* visible */);
-        assertEquals("Fullscreen app requested portrait orientation",
-                1 /* portrait */, mAmWmState.getWmState().getLastOrientation());
-
-        launchActivity(LANDSCAPE_ACTIVITY_NAME);
-        mAmWmState.assertVisibility(LANDSCAPE_ACTIVITY_NAME, true /* visible */);
-        assertEquals("Fullscreen app requested landscape orientation",
-                0 /* landscape */, mAmWmState.getWmState().getLastOrientation());
-
-        launchActivity(PORTRAIT_ACTIVITY_NAME);
-        mAmWmState.assertVisibility(PORTRAIT_ACTIVITY_NAME, true /* visible */);
-        assertEquals("Fullscreen app requested portrait orientation",
-                1 /* portrait */, mAmWmState.getWmState().getLastOrientation());
-    }
-
-    public void testNonfullscreenAppOrientationRequests() throws Exception {
-        String logSeparator = clearLogcat();
-        launchActivity(PORTRAIT_ACTIVITY_NAME);
-        final ReportedSizes initialReportedSizes =
-                getLastReportedSizesForActivity(PORTRAIT_ACTIVITY_NAME, logSeparator);
-        assertEquals("portrait activity should be in portrait",
-                1 /* portrait */, initialReportedSizes.orientation);
-        logSeparator = clearLogcat();
-
-        launchActivityInComponent(TRANSLUCENT_SDK_26_PACKAGE, TRANSLUCENT_ACTIVITY);
-
-        assertEquals("Legacy non-fullscreen activity requested landscape orientation",
-                0 /* landscape */, mAmWmState.getWmState().getLastOrientation());
-
-        // TODO(b/36897968): uncomment once we can suppress unsupported configurations
-        // final ReportedSizes updatedReportedSizes =
-        //      getLastReportedSizesForActivity(PORTRAIT_ACTIVITY_NAME, logSeparator);
-        // assertEquals("portrait activity should not have moved from portrait",
-        //         1 /* portrait */, updatedReportedSizes.orientation);
-    }
-
-    public void testNonFullscreenActivityPermitted() throws Exception {
-        setComponentName(TRANSLUCENT_CURRENT_PACKAGE);
-        setDeviceRotation(0);
-
-        launchActivity(TRANSLUCENT_ACTIVITY);
-        mAmWmState.assertResumedActivity(
-                "target SDK non-fullscreen activity should be allowed to launch",
-                TRANSLUCENT_ACTIVITY);
-        assertEquals("non-fullscreen activity requested landscape orientation",
-                0 /* landscape */, mAmWmState.getWmState().getLastOrientation());
-    }
-
-    /**
-     * Test that device handles moving between two tasks with different orientations.
-     */
-    public void testTaskCloseRestoreOrientation() throws Exception {
-        // Start landscape activity.
-        launchActivity(LANDSCAPE_ACTIVITY_NAME);
-        mAmWmState.assertVisibility(LANDSCAPE_ACTIVITY_NAME, true /* visible */);
-        assertEquals("Fullscreen app requested landscape orientation",
-                0 /* landscape */, mAmWmState.getWmState().getLastOrientation());
-
-        // Start another activity in a different task.
-        launchActivityInNewTask(BROADCAST_RECEIVER_ACTIVITY);
-
-        // Request portrait
-        executeShellCommand(getOrientationBroadcast(1 /*portrait*/));
-        mAmWmState.waitForRotation(mDevice, 1);
-
-        // Finish activity
-        executeShellCommand(FINISH_ACTIVITY_BROADCAST);
-
-        // Verify that activity brought to front is in originally requested orientation.
-        mAmWmState.computeState(mDevice, new String[]{LANDSCAPE_ACTIVITY_NAME});
-        assertEquals("Should return to app in landscape orientation",
-                0 /* landscape */, mAmWmState.getWmState().getLastOrientation());
-    }
-
-    /**
-     * Test that device handles moving between two tasks with different orientations.
-     */
-    public void testTaskMoveToBackOrientation() throws Exception {
-        // Start landscape activity.
-        launchActivity(LANDSCAPE_ACTIVITY_NAME);
-        mAmWmState.assertVisibility(LANDSCAPE_ACTIVITY_NAME, true /* visible */);
-        assertEquals("Fullscreen app requested landscape orientation",
-                0 /* landscape */, mAmWmState.getWmState().getLastOrientation());
-
-        // Start another activity in a different task.
-        launchActivityInNewTask(BROADCAST_RECEIVER_ACTIVITY);
-
-        // Request portrait
-        executeShellCommand(getOrientationBroadcast(1 /*portrait*/));
-        mAmWmState.waitForRotation(mDevice, 1);
-
-        // Finish activity
-        executeShellCommand(MOVE_TASK_TO_BACK_BROADCAST);
-
-        // Verify that activity brought to front is in originally requested orientation.
-        mAmWmState.waitForValidState(mDevice, LANDSCAPE_ACTIVITY_NAME);
-        assertEquals("Should return to app in landscape orientation",
-                0 /* landscape */, mAmWmState.getWmState().getLastOrientation());
-    }
-
-    /**
-     * Test that device doesn't change device orientation by app request while in multi-window.
-     */
-    public void testSplitscreenPortraitAppOrientationRequests() throws Exception {
-        if (!supportsSplitScreenMultiWindow()) {
-          CLog.logAndDisplay(LogLevel.INFO, "Skipping test: no multi-window support");
-          return;
-        }
-        requestOrientationInSplitScreen(1 /* portrait */, LANDSCAPE_ACTIVITY_NAME);
-    }
-
-    /**
-     * Test that device doesn't change device orientation by app request while in multi-window.
-     */
-    public void testSplitscreenLandscapeAppOrientationRequests() throws Exception {
-        if (!supportsSplitScreenMultiWindow()) {
-          CLog.logAndDisplay(LogLevel.INFO, "Skipping test: no multi-window support");
-          return;
-        }
-        requestOrientationInSplitScreen(0 /* landscape */, PORTRAIT_ACTIVITY_NAME);
-    }
-
-    /**
-     * Rotate the device and launch specified activity in split-screen, checking if orientation
-     * didn't change.
-     */
-    private void requestOrientationInSplitScreen(int orientation, String activity)
-            throws Exception {
-        // Set initial orientation.
-        setDeviceRotation(orientation);
-
-        // Launch activities that request orientations and check that device doesn't rotate.
-        launchActivityInDockStack(LAUNCHING_ACTIVITY);
-
-        getLaunchActivityBuilder().setToSide(true).setMultipleTask(true)
-                .setTargetActivityName(activity).execute();
-        mAmWmState.computeState(mDevice, new String[] {activity});
-        mAmWmState.assertVisibility(activity, true /* visible */);
-        assertEquals("Split-screen apps shouldn't influence device orientation",
-                orientation, mAmWmState.getWmState().getRotation());
-
-        getLaunchActivityBuilder().setMultipleTask(true).setTargetActivityName(activity).execute();
-        mAmWmState.computeState(mDevice, new String[] {activity});
-        mAmWmState.assertVisibility(activity, true /* visible */);
-        assertEquals("Split-screen apps shouldn't influence device orientation",
-                orientation, mAmWmState.getWmState().getRotation());
-    }
-
-    /**
-     * Launches activity in docked stack, moves to fullscreen stack and back to docked stack.
-     * Asserts that initial and final reported sizes in docked stack are the same.
-     */
-    private void moveActivitySplitFullSplit(String activityName) throws Exception {
-        if (!supportsSplitScreenMultiWindow()) {
-            CLog.logAndDisplay(LogLevel.INFO, "Skipping test: no multi-window support");
-            return;
-        }
-
-        // Launch to docked stack and record size.
-        String logSeparator = clearLogcat();
-        launchActivityInStack(activityName, DOCKED_STACK_ID);
-        final ReportedSizes initialDockedSizes = getActivityDisplaySize(activityName, logSeparator);
-        // Make sure docked stack is focused. This way when we dismiss it later fullscreen stack
-        // will come up.
-        launchActivityInStack(activityName, DOCKED_STACK_ID);
-        mAmWmState.computeState(mDevice, new String[] { activityName },
-                false /* compareTaskAndStackBounds */);
-
-        // Move to fullscreen stack.
-        logSeparator = clearLogcat();
-        moveActivityToStack(activityName, FULLSCREEN_WORKSPACE_STACK_ID);
-        final ReportedSizes fullscreenSizes = getActivityDisplaySize(activityName, logSeparator);
-        assertSizesAreSane(fullscreenSizes, initialDockedSizes);
-
-        // Move activity back to docked stack.
-        logSeparator = clearLogcat();
-        moveActivityToStack(activityName, DOCKED_STACK_ID);
-        final ReportedSizes finalDockedSizes = getActivityDisplaySize(activityName, logSeparator);
-
-        // After activity configuration was changed twice it must report same size as original one.
-        assertSizesAreSame(initialDockedSizes, finalDockedSizes);
-    }
-
-    /**
-     * Asserts that after rotation, the aspect ratios of display size, metrics, and configuration
-     * have flipped.
-     */
-    private static void assertSizesRotate(ReportedSizes rotationA, ReportedSizes rotationB)
-            throws Exception {
-        assertEquals(rotationA.displayWidth, rotationA.metricsWidth);
-        assertEquals(rotationA.displayHeight, rotationA.metricsHeight);
-        assertEquals(rotationB.displayWidth, rotationB.metricsWidth);
-        assertEquals(rotationB.displayHeight, rotationB.metricsHeight);
-
-        final boolean beforePortrait = rotationA.displayWidth < rotationA.displayHeight;
-        final boolean afterPortrait = rotationB.displayWidth < rotationB.displayHeight;
-        assertFalse(beforePortrait == afterPortrait);
-
-        final boolean beforeConfigPortrait = rotationA.widthDp < rotationA.heightDp;
-        final boolean afterConfigPortrait = rotationB.widthDp < rotationB.heightDp;
-        assertEquals(beforePortrait, beforeConfigPortrait);
-        assertEquals(afterPortrait, afterConfigPortrait);
-
-        assertEquals(rotationA.smallestWidthDp, rotationB.smallestWidthDp);
-    }
-
-    /**
-     * Throws an AssertionError if fullscreenSizes has widths/heights (depending on aspect ratio)
-     * that are smaller than the dockedSizes.
-     */
-    private static void assertSizesAreSane(ReportedSizes fullscreenSizes, ReportedSizes dockedSizes)
-            throws Exception {
-        final boolean portrait = fullscreenSizes.displayWidth < fullscreenSizes.displayHeight;
-        if (portrait) {
-            assertTrue(dockedSizes.displayHeight < fullscreenSizes.displayHeight);
-            assertTrue(dockedSizes.heightDp < fullscreenSizes.heightDp);
-            assertTrue(dockedSizes.metricsHeight < fullscreenSizes.metricsHeight);
-        } else {
-            assertTrue(dockedSizes.displayWidth < fullscreenSizes.displayWidth);
-            assertTrue(dockedSizes.widthDp < fullscreenSizes.widthDp);
-            assertTrue(dockedSizes.metricsWidth < fullscreenSizes.metricsWidth);
-        }
-    }
-
-    /**
-     * Throws an AssertionError if sizes are different.
-     */
-    private static void assertSizesAreSame(ReportedSizes firstSize, ReportedSizes secondSize)
-            throws Exception {
-        assertEquals(firstSize.widthDp, secondSize.widthDp);
-        assertEquals(firstSize.heightDp, secondSize.heightDp);
-        assertEquals(firstSize.displayWidth, secondSize.displayWidth);
-        assertEquals(firstSize.displayHeight, secondSize.displayHeight);
-        assertEquals(firstSize.metricsWidth, secondSize.metricsWidth);
-        assertEquals(firstSize.metricsHeight, secondSize.metricsHeight);
-        assertEquals(firstSize.smallestWidthDp, secondSize.smallestWidthDp);
-    }
-
-    private ReportedSizes getActivityDisplaySize(String activityName, String logSeparator)
-            throws Exception {
-        mAmWmState.computeState(mDevice, new String[] { activityName },
-                false /* compareTaskAndStackBounds */);
-        final ReportedSizes details = getLastReportedSizesForActivity(activityName, logSeparator);
-        assertNotNull(details);
-        return details;
-    }
-
-    private Rectangle getDisplayRect(String activityName)
-            throws Exception {
-        final String windowName = getWindowName(activityName);
-
-        mAmWmState.computeState(mDevice, new String[] {activityName});
-        mAmWmState.assertFocusedWindow("Test window must be the front window.", windowName);
-
-        final List<WindowManagerState.WindowState> tempWindowList = new ArrayList<>();
-        mAmWmState.getWmState().getMatchingVisibleWindowState(windowName, tempWindowList);
-
-        assertEquals("Should have exactly one window state for the activity.", 1,
-                tempWindowList.size());
-
-        WindowManagerState.WindowState windowState = tempWindowList.get(0);
-        assertNotNull("Should have a valid window", windowState);
-
-        WindowManagerState.Display display = mAmWmState.getWmState()
-                .getDisplay(windowState.getDisplayId());
-        assertNotNull("Should be on a display", display);
-
-        return display.getDisplayRect();
-    }
-
-    /**
-     * Test launching an activity which requests specific UI mode during creation.
-     */
-    public void testLaunchWithUiModeChange() throws Exception {
-        // Launch activity that changes UI mode and handles this configuration change.
-        launchActivity(NIGHT_MODE_ACTIVITY);
-        mAmWmState.waitForActivityState(mDevice, NIGHT_MODE_ACTIVITY, STATE_RESUMED);
-
-        // Check if activity is launched successfully.
-        mAmWmState.assertVisibility(NIGHT_MODE_ACTIVITY, true /* visible */);
-        mAmWmState.assertFocusedActivity("Launched activity should be focused",
-                NIGHT_MODE_ACTIVITY);
-        mAmWmState.assertResumedActivity("Launched activity must be resumed", NIGHT_MODE_ACTIVITY);
-    }
-}
diff --git a/hostsidetests/services/activityandwindowmanager/activitymanager/src/android/server/cts/ActivityManagerAssistantStackTests.java b/hostsidetests/services/activityandwindowmanager/activitymanager/src/android/server/cts/ActivityManagerAssistantStackTests.java
deleted file mode 100644
index be2af68..0000000
--- a/hostsidetests/services/activityandwindowmanager/activitymanager/src/android/server/cts/ActivityManagerAssistantStackTests.java
+++ /dev/null
@@ -1,291 +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.server.cts;
-
-import static android.server.cts.ActivityManagerState.STATE_RESUMED;
-
-/**
- * Build: mmma -j32 cts/hostsidetests/services
- * Run: cts/hostsidetests/services/activityandwindowmanager/util/run-test android.server.cts.ActivityManagerAssistantStackTests
- */
-public class ActivityManagerAssistantStackTests extends ActivityManagerTestBase {
-
-    private static final String VOICE_INTERACTION_SERVICE = "AssistantVoiceInteractionService";
-
-    private static final String TEST_ACTIVITY = "TestActivity";
-    private static final String ANIMATION_TEST_ACTIVITY = "AnimationTestActivity";
-    private static final String DOCKED_ACTIVITY = "DockedActivity";
-    private static final String ASSISTANT_ACTIVITY = "AssistantActivity";
-    private static final String TRANSLUCENT_ASSISTANT_ACTIVITY = "TranslucentAssistantActivity";
-    private static final String LAUNCH_ASSISTANT_ACTIVITY_FROM_SESSION =
-            "LaunchAssistantActivityFromSession";
-    private static final String LAUNCH_ASSISTANT_ACTIVITY_INTO_STACK =
-            "LaunchAssistantActivityIntoAssistantStack";
-    private static final String PIP_ACTIVITY = "PipActivity";
-
-    private static final String EXTRA_ENTER_PIP = "enter_pip";
-    private static final String EXTRA_LAUNCH_NEW_TASK = "launch_new_task";
-    private static final String EXTRA_FINISH_SELF = "finish_self";
-    public static final String EXTRA_IS_TRANSLUCENT = "is_translucent";
-
-    private static final String TEST_ACTIVITY_ACTION_FINISH_SELF =
-            "android.server.cts.TestActivity.finish_self";
-
-    public void testLaunchingAssistantActivityIntoAssistantStack() throws Exception {
-        // Enable the assistant and launch an assistant activity
-        enableAssistant();
-        launchActivity(LAUNCH_ASSISTANT_ACTIVITY_FROM_SESSION);
-        mAmWmState.waitForValidState(mDevice, ASSISTANT_ACTIVITY, ASSISTANT_STACK_ID);
-
-        // Ensure that the activity launched in the fullscreen assistant stack
-        assertAssistantStackExists();
-        assertTrue("Expected assistant stack to be fullscreen",
-                mAmWmState.getAmState().getStackById(ASSISTANT_STACK_ID).isFullscreen());
-
-        disableAssistant();
-    }
-
-    public void testAssistantStackZOrder() throws Exception {
-        if (!supportsPip() || !supportsSplitScreenMultiWindow()) return;
-        // Launch a pinned stack task
-        launchActivity(PIP_ACTIVITY, EXTRA_ENTER_PIP, "true");
-        mAmWmState.waitForValidState(mDevice, PIP_ACTIVITY, PINNED_STACK_ID);
-        mAmWmState.assertContainsStack("Must contain pinned stack.", PINNED_STACK_ID);
-
-        // Dock a task
-        launchActivity(TEST_ACTIVITY);
-        launchActivityInDockStack(DOCKED_ACTIVITY);
-        mAmWmState.assertContainsStack("Must contain fullscreen stack.",
-                FULLSCREEN_WORKSPACE_STACK_ID);
-        mAmWmState.assertContainsStack("Must contain docked stack.", DOCKED_STACK_ID);
-
-        // Enable the assistant and launch an assistant activity, ensure it is on top
-        enableAssistant();
-        launchActivity(LAUNCH_ASSISTANT_ACTIVITY_FROM_SESSION);
-        mAmWmState.waitForValidState(mDevice, ASSISTANT_ACTIVITY, ASSISTANT_STACK_ID);
-        assertAssistantStackExists();
-
-        mAmWmState.assertFrontStack("Pinned stack should be on top.", PINNED_STACK_ID);
-        mAmWmState.assertFocusedStack("Assistant stack should be focused.", ASSISTANT_STACK_ID);
-
-        disableAssistant();
-    }
-
-    public void testAssistantStackLaunchNewTask() throws Exception {
-        enableAssistant();
-        assertAssistantStackCanLaunchAndReturnFromNewTask();
-        disableAssistant();
-    }
-
-    public void testAssistantStackLaunchNewTaskWithDockedStack() throws Exception {
-        if (!supportsSplitScreenMultiWindow()) return;
-        // Dock a task
-        launchActivity(TEST_ACTIVITY);
-        launchActivityInDockStack(DOCKED_ACTIVITY);
-        mAmWmState.assertContainsStack("Must contain fullscreen stack.",
-                FULLSCREEN_WORKSPACE_STACK_ID);
-        mAmWmState.assertContainsStack("Must contain docked stack.", DOCKED_STACK_ID);
-
-        enableAssistant();
-        assertAssistantStackCanLaunchAndReturnFromNewTask();
-        disableAssistant();
-    }
-
-    private void assertAssistantStackCanLaunchAndReturnFromNewTask() throws Exception {
-        // Enable the assistant and launch an assistant activity which will launch a new task
-        enableAssistant();
-        launchActivity(LAUNCH_ASSISTANT_ACTIVITY_INTO_STACK,
-                EXTRA_LAUNCH_NEW_TASK, TEST_ACTIVITY);
-        disableAssistant();
-
-        // Ensure that the fullscreen stack is on top and the test activity is now visible
-        mAmWmState.waitForValidState(mDevice, TEST_ACTIVITY, FULLSCREEN_WORKSPACE_STACK_ID);
-        mAmWmState.assertFocusedActivity("TestActivity should be resumed", TEST_ACTIVITY);
-        mAmWmState.assertFrontStack("Fullscreen stack should be on top.",
-                FULLSCREEN_WORKSPACE_STACK_ID);
-        mAmWmState.assertFocusedStack("Fullscreen stack should be focused.",
-                FULLSCREEN_WORKSPACE_STACK_ID);
-
-        // 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);
-        mAmWmState.waitForFocusedStack(mDevice, ASSISTANT_STACK_ID);
-        mAmWmState.assertFrontStack("Assistant stack should be on top.", ASSISTANT_STACK_ID);
-        mAmWmState.assertFocusedStack("Assistant stack should be focused.", ASSISTANT_STACK_ID);
-    }
-
-    public void testAssistantStackFinishToPreviousApp() throws Exception {
-        // Launch an assistant activity on top of an existing fullscreen activity, and ensure that
-        // the fullscreen activity is still visible and on top after the assistant activity finishes
-        launchActivity(TEST_ACTIVITY);
-        enableAssistant();
-        launchActivity(LAUNCH_ASSISTANT_ACTIVITY_INTO_STACK,
-                EXTRA_FINISH_SELF, "true");
-        disableAssistant();
-        mAmWmState.waitForValidState(mDevice, TEST_ACTIVITY, FULLSCREEN_WORKSPACE_STACK_ID);
-        mAmWmState.waitForActivityState(mDevice, TEST_ACTIVITY, STATE_RESUMED);
-        mAmWmState.assertFocusedActivity("TestActivity should be resumed", TEST_ACTIVITY);
-        mAmWmState.assertFrontStack("Fullscreen stack should be on top.",
-                FULLSCREEN_WORKSPACE_STACK_ID);
-        mAmWmState.assertFocusedStack("Fullscreen stack should be focused.",
-                FULLSCREEN_WORKSPACE_STACK_ID);
-    }
-
-    public void testDisallowEnterPiPFromAssistantStack() throws Exception {
-        enableAssistant();
-        launchActivity(LAUNCH_ASSISTANT_ACTIVITY_INTO_STACK,
-                EXTRA_ENTER_PIP, "true");
-        disableAssistant();
-        mAmWmState.waitForValidState(mDevice, ASSISTANT_ACTIVITY, ASSISTANT_STACK_ID);
-        mAmWmState.assertDoesNotContainStack("Must not contain pinned stack.", PINNED_STACK_ID);
-    }
-
-    public void testTranslucentAssistantActivityStackVisibility() throws Exception {
-        enableAssistant();
-        // Go home, launch the assistant and check to see that home is visible
-        removeStacks(FULLSCREEN_WORKSPACE_STACK_ID);
-        launchHomeActivity();
-        launchActivity(LAUNCH_ASSISTANT_ACTIVITY_INTO_STACK,
-                EXTRA_IS_TRANSLUCENT, String.valueOf(true));
-        mAmWmState.waitForValidState(mDevice, TRANSLUCENT_ASSISTANT_ACTIVITY, ASSISTANT_STACK_ID);
-        assertAssistantStackExists();
-        if (!noHomeScreen()) {
-            mAmWmState.assertHomeActivityVisible(true);
-        }
-
-        // Launch a fullscreen app and then launch the assistant and check to see that it is
-        // also visible
-        removeStacks(ASSISTANT_STACK_ID);
-        launchActivity(TEST_ACTIVITY);
-        launchActivity(LAUNCH_ASSISTANT_ACTIVITY_INTO_STACK,
-                EXTRA_IS_TRANSLUCENT, String.valueOf(true));
-        mAmWmState.waitForValidState(mDevice, TRANSLUCENT_ASSISTANT_ACTIVITY, ASSISTANT_STACK_ID);
-        assertAssistantStackExists();
-        mAmWmState.assertVisibility(TEST_ACTIVITY, true);
-
-        // Go home, launch assistant, launch app into fullscreen with activity present, and go back.
-        // Ensure home is visible.
-        removeStacks(ASSISTANT_STACK_ID);
-        launchHomeActivity();
-        launchActivity(LAUNCH_ASSISTANT_ACTIVITY_INTO_STACK,
-                EXTRA_IS_TRANSLUCENT, String.valueOf(true), EXTRA_LAUNCH_NEW_TASK,
-                TEST_ACTIVITY);
-        mAmWmState.waitForValidState(mDevice, TEST_ACTIVITY, FULLSCREEN_WORKSPACE_STACK_ID);
-        mAmWmState.assertHomeActivityVisible(false);
-        pressBackButton();
-        mAmWmState.waitForFocusedStack(mDevice, ASSISTANT_STACK_ID);
-        assertAssistantStackExists();
-        if (!noHomeScreen()) {
-            mAmWmState.assertHomeActivityVisible(true);
-        }
-
-        // Launch a fullscreen and docked app and then launch the assistant and check to see that it
-        // is also visible
-        if (supportsSplitScreenMultiWindow()) {
-            removeStacks(ASSISTANT_STACK_ID);
-            launchActivityInDockStack(DOCKED_ACTIVITY);
-            launchActivity(TEST_ACTIVITY);
-            mAmWmState.assertContainsStack("Must contain docked stack.", DOCKED_STACK_ID);
-            launchActivity(LAUNCH_ASSISTANT_ACTIVITY_INTO_STACK,
-                    EXTRA_IS_TRANSLUCENT, String.valueOf(true));
-            mAmWmState.waitForValidState(mDevice, TRANSLUCENT_ASSISTANT_ACTIVITY, ASSISTANT_STACK_ID);
-            assertAssistantStackExists();
-            mAmWmState.assertVisibility(DOCKED_ACTIVITY, true);
-            mAmWmState.assertVisibility(TEST_ACTIVITY, true);
-        }
-        disableAssistant();
-    }
-
-    public void testLaunchIntoSameTask() throws Exception {
-        enableAssistant();
-
-        // Launch the assistant
-        launchActivity(LAUNCH_ASSISTANT_ACTIVITY_FROM_SESSION);
-        assertAssistantStackExists();
-        mAmWmState.assertVisibility(ASSISTANT_ACTIVITY, true);
-        mAmWmState.assertFocusedStack("Expected assistant stack focused", ASSISTANT_STACK_ID);
-        assertEquals(1, mAmWmState.getAmState().getStackById(ASSISTANT_STACK_ID).getTasks().size());
-        final int taskId = mAmWmState.getAmState().getTaskByActivityName(ASSISTANT_ACTIVITY)
-                .mTaskId;
-
-        // Launch a new fullscreen activity
-        // Using Animation Test Activity because it is opaque on all devices.
-        launchActivity(ANIMATION_TEST_ACTIVITY);
-        mAmWmState.assertVisibility(ASSISTANT_ACTIVITY, false);
-
-        // Launch the assistant again and ensure that it goes into the same task
-        launchActivity(LAUNCH_ASSISTANT_ACTIVITY_FROM_SESSION);
-        assertAssistantStackExists();
-        mAmWmState.assertVisibility(ASSISTANT_ACTIVITY, true);
-        mAmWmState.assertFocusedStack("Expected assistant stack focused", ASSISTANT_STACK_ID);
-        assertEquals(1, mAmWmState.getAmState().getStackById(ASSISTANT_STACK_ID).getTasks().size());
-        assertEquals(taskId,
-                mAmWmState.getAmState().getTaskByActivityName(ASSISTANT_ACTIVITY).mTaskId);
-
-        disableAssistant();
-    }
-
-    public void testPinnedStackWithAssistant() throws Exception {
-        if (!supportsPip() || !supportsSplitScreenMultiWindow()) return;
-
-        enableAssistant();
-
-        // Launch a fullscreen activity and a PIP activity, then launch the assistant, and ensure
-        // that the test activity is still visible
-        launchActivity(TEST_ACTIVITY);
-        launchActivity(PIP_ACTIVITY, EXTRA_ENTER_PIP, "true");
-        launchActivity(LAUNCH_ASSISTANT_ACTIVITY_INTO_STACK,
-                EXTRA_IS_TRANSLUCENT, String.valueOf(true));
-        mAmWmState.waitForValidState(mDevice, TRANSLUCENT_ASSISTANT_ACTIVITY, ASSISTANT_STACK_ID);
-        assertAssistantStackExists();
-        mAmWmState.assertVisibility(TRANSLUCENT_ASSISTANT_ACTIVITY, true);
-        mAmWmState.assertVisibility(PIP_ACTIVITY, true);
-        mAmWmState.assertVisibility(TEST_ACTIVITY, true);
-
-        disableAssistant();
-    }
-
-    /**
-     * Asserts that the assistant stack exists.
-     */
-    private void assertAssistantStackExists() throws Exception {
-        mAmWmState.assertContainsStack("Must contain assistant stack.", ASSISTANT_STACK_ID);
-    }
-
-    /**
-     * Asserts that the assistant stack does not exist.
-     */
-    private void assertAssistantStackDoesNotExist() throws Exception {
-        mAmWmState.assertDoesNotContainStack("Must not contain assistant stack.",
-                ASSISTANT_STACK_ID);
-    }
-
-    /**
-     * Sets the system voice interaction service.
-     */
-    private void enableAssistant() throws Exception {
-        executeShellCommand("settings put secure voice_interaction_service " +
-                getActivityComponentName(VOICE_INTERACTION_SERVICE));
-    }
-
-    /**
-     * Resets the system voice interaction service.
-     */
-    private void disableAssistant() throws Exception {
-        executeShellCommand("settings delete secure voice_interaction_service " +
-                getActivityComponentName(VOICE_INTERACTION_SERVICE));
-    }
-}
diff --git a/hostsidetests/services/activityandwindowmanager/activitymanager/src/android/server/cts/ActivityManagerConfigChangeTests.java b/hostsidetests/services/activityandwindowmanager/activitymanager/src/android/server/cts/ActivityManagerConfigChangeTests.java
deleted file mode 100644
index 4640d19..0000000
--- a/hostsidetests/services/activityandwindowmanager/activitymanager/src/android/server/cts/ActivityManagerConfigChangeTests.java
+++ /dev/null
@@ -1,237 +0,0 @@
-/*
- * Copyright (C) 2016 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package android.server.cts;
-
-import android.platform.test.annotations.Presubmit;
-
-import static android.server.cts.ActivityManagerState.STATE_RESUMED;
-import static android.server.cts.StateLogger.logE;
-
-import com.android.ddmlib.Log.LogLevel;
-import com.android.tradefed.log.LogUtil.CLog;
-
-import java.util.regex.Matcher;
-import java.util.regex.Pattern;
-
-/**
- * Build: mmma -j32 cts/hostsidetests/services
- * Run: cts/hostsidetests/services/activityandwindowmanager/util/run-test CtsServicesHostTestCases android.server.cts.ActivityManagerConfigChangeTests
- */
-public class ActivityManagerConfigChangeTests extends ActivityManagerTestBase {
-    private static final String TEST_ACTIVITY_NAME = "TestActivity";
-    private static final String NO_RELAUNCH_ACTIVITY_NAME = "NoRelaunchActivity";
-
-    private static final String FONT_SCALE_ACTIVITY_NAME = "FontScaleActivity";
-    private static final String FONT_SCALE_NO_RELAUNCH_ACTIVITY_NAME =
-            "FontScaleNoRelaunchActivity";
-
-    private static final float EXPECTED_FONT_SIZE_SP = 10.0f;
-
-    public void testRotation90Relaunch() throws Exception{
-        // Should relaunch on every rotation and receive no onConfigurationChanged()
-        testRotation(TEST_ACTIVITY_NAME, 1, 1, 0);
-    }
-
-    public void testRotation90NoRelaunch() throws Exception {
-        // Should receive onConfigurationChanged() on every rotation and no relaunch
-        testRotation(NO_RELAUNCH_ACTIVITY_NAME, 1, 0, 1);
-    }
-
-    public void testRotation180Relaunch() throws Exception {
-        // Should receive nothing
-        testRotation(TEST_ACTIVITY_NAME, 2, 0, 0);
-    }
-
-    public void testRotation180NoRelaunch() throws Exception {
-        // Should receive nothing
-        testRotation(NO_RELAUNCH_ACTIVITY_NAME, 2, 0, 0);
-    }
-
-    @Presubmit
-    public void testChangeFontScaleRelaunch() throws Exception {
-        // Should relaunch and receive no onConfigurationChanged()
-        testChangeFontScale(FONT_SCALE_ACTIVITY_NAME, true /* relaunch */);
-    }
-
-    @Presubmit
-    public void testChangeFontScaleNoRelaunch() throws Exception {
-        // Should receive onConfigurationChanged() and no relaunch
-        testChangeFontScale(FONT_SCALE_NO_RELAUNCH_ACTIVITY_NAME, false /* relaunch */);
-    }
-
-    private void testRotation(
-            String activityName, int rotationStep, int numRelaunch, int numConfigChange)
-                    throws Exception {
-        launchActivity(activityName);
-
-        final String[] waitForActivitiesVisible = new String[] {activityName};
-        mAmWmState.computeState(mDevice, waitForActivitiesVisible);
-
-        final int initialRotation = 4 - rotationStep;
-        setDeviceRotation(initialRotation);
-        mAmWmState.computeState(mDevice, waitForActivitiesVisible);
-        final int actualStackId = mAmWmState.getAmState().getTaskByActivityName(
-                activityName).mStackId;
-        final int displayId = mAmWmState.getAmState().getStackById(actualStackId).mDisplayId;
-        final int newDeviceRotation = getDeviceRotation(displayId);
-        if (newDeviceRotation == INVALID_DEVICE_ROTATION) {
-            CLog.logAndDisplay(LogLevel.WARN, "Got an invalid device rotation value. "
-                    + "Continuing the test despite of that, but it is likely to fail.");
-        } else if (newDeviceRotation != initialRotation) {
-            CLog.logAndDisplay(LogLevel.INFO, "This device doesn't support user rotation "
-                    + "mode. Not continuing the rotation checks.");
-            return;
-        }
-
-        for (int rotation = 0; rotation < 4; rotation += rotationStep) {
-            final String logSeparator = clearLogcat();
-            setDeviceRotation(rotation);
-            mAmWmState.computeState(mDevice, waitForActivitiesVisible);
-            assertRelaunchOrConfigChanged(activityName, numRelaunch, numConfigChange, logSeparator);
-        }
-    }
-
-    private void testChangeFontScale(
-            String activityName, boolean relaunch) throws Exception {
-        launchActivity(activityName);
-        final String[] waitForActivitiesVisible = new String[] {activityName};
-        mAmWmState.computeState(mDevice, waitForActivitiesVisible);
-
-        setFontScale(1.0f);
-        mAmWmState.computeState(mDevice, waitForActivitiesVisible);
-
-        final int densityDpi = getGlobalDensityDpi();
-
-        for (float fontScale = 0.85f; fontScale <= 1.3f; fontScale += 0.15f) {
-            final String logSeparator = clearLogcat();
-            setFontScale(fontScale);
-            mAmWmState.computeState(mDevice, waitForActivitiesVisible);
-            assertRelaunchOrConfigChanged(activityName, relaunch ? 1 : 0, relaunch ? 0 : 1,
-                    logSeparator);
-
-            // Verify that the display metrics are updated, and therefore the text size is also
-            // updated accordingly.
-            assertExpectedFontPixelSize(activityName,
-                    scaledPixelsToPixels(EXPECTED_FONT_SIZE_SP, fontScale, densityDpi),
-                    logSeparator);
-        }
-    }
-
-    /**
-     * Test updating application info when app is running. An activity with matching package name
-     * must be recreated and its asset sequence number must be incremented.
-     */
-    public void testUpdateApplicationInfo() throws Exception {
-        final String firstLogSeparator = clearLogcat();
-
-        // Launch an activity that prints applied config.
-        launchActivity(TEST_ACTIVITY_NAME);
-        final int assetSeq = readAssetSeqNumber(TEST_ACTIVITY_NAME, firstLogSeparator);
-
-        final String logSeparator = clearLogcat();
-        // Update package info.
-        executeShellCommand("am update-appinfo all " + componentName);
-        mAmWmState.waitForWithAmState(mDevice, (amState) -> {
-            // Wait for activity to be resumed and asset seq number to be updated.
-            try {
-                return readAssetSeqNumber(TEST_ACTIVITY_NAME, logSeparator) == assetSeq + 1
-                        && amState.hasActivityState(TEST_ACTIVITY_NAME, STATE_RESUMED);
-            } catch (Exception e) {
-                logE("Error waiting for valid state: " + e.getMessage());
-                return false;
-            }
-        }, "Waiting asset sequence number to be updated and for activity to be resumed.");
-
-        // Check if activity is relaunched and asset seq is updated.
-        assertRelaunchOrConfigChanged(TEST_ACTIVITY_NAME, 1 /* numRelaunch */,
-                0 /* numConfigChange */, logSeparator);
-        final int newAssetSeq = readAssetSeqNumber(TEST_ACTIVITY_NAME, logSeparator);
-        assertEquals("Asset sequence number must be incremented.", assetSeq + 1, newAssetSeq);
-    }
-
-    private static final Pattern sConfigurationPattern = Pattern.compile(
-            "(.+): Configuration: \\{(.*) as.(\\d+)(.*)\\}");
-
-    /** Read asset sequence number in last applied configuration from logs. */
-    private int readAssetSeqNumber(String activityName, String logSeparator) throws Exception {
-        final String[] lines = getDeviceLogsForComponent(activityName, logSeparator);
-        for (int i = lines.length - 1; i >= 0; i--) {
-            final String line = lines[i].trim();
-            final Matcher matcher = sConfigurationPattern.matcher(line);
-            if (matcher.matches()) {
-                final String assetSeqNumber = matcher.group(3);
-                try {
-                    return Integer.valueOf(assetSeqNumber);
-                } catch (NumberFormatException e) {
-                    // Ignore, asset seq number is not printed when not set.
-                }
-            }
-        }
-        return 0;
-    }
-
-    // Calculate the scaled pixel size just like the device is supposed to.
-    private static int scaledPixelsToPixels(float sp, float fontScale, int densityDpi) {
-        final int DEFAULT_DENSITY = 160;
-        float f = densityDpi * (1.0f / DEFAULT_DENSITY) * fontScale * sp;
-        return (int) ((f >= 0) ? (f + 0.5f) : (f - 0.5f));
-    }
-
-    private static Pattern sDeviceDensityPattern =
-            Pattern.compile(".*?-(l|m|tv|h|xh|xxh|xxxh|\\d+)dpi-.*?");
-
-    private int getGlobalDensityDpi() throws Exception {
-        final String result = getDevice().executeShellCommand("am get-config");
-        final String[] lines = result.split("\n");
-        if (lines.length < 1) {
-            throw new IllegalStateException("Invalid config returned from device: " + result);
-        }
-
-        final Matcher matcher = sDeviceDensityPattern.matcher(lines[0]);
-        if (!matcher.matches()) {
-            throw new IllegalStateException("Invalid config returned from device: " + lines[0]);
-        }
-        switch (matcher.group(1)) {
-            case "l": return 120;
-            case "m": return 160;
-            case "tv": return 213;
-            case "h": return 240;
-            case "xh": return 320;
-            case "xxh": return 480;
-            case "xxxh": return 640;
-        }
-        return Integer.parseInt(matcher.group(1));
-    }
-
-    private static final Pattern sFontSizePattern = Pattern.compile("^(.+): fontPixelSize=(.+)$");
-
-    /** Read the font size in the last log line. */
-    private void assertExpectedFontPixelSize(String activityName, int fontPixelSize,
-            String logSeparator) throws Exception {
-        final String[] lines = getDeviceLogsForComponent(activityName, logSeparator);
-        for (int i = lines.length - 1; i >= 0; i--) {
-            final String line = lines[i].trim();
-            final Matcher matcher = sFontSizePattern.matcher(line);
-            if (matcher.matches()) {
-                assertEquals("Expected font pixel size does not match", fontPixelSize,
-                        Integer.parseInt(matcher.group(2)));
-                return;
-            }
-        }
-        fail("No fontPixelSize reported from activity " + activityName);
-    }
-}
diff --git a/hostsidetests/services/activityandwindowmanager/activitymanager/src/android/server/cts/ActivityManagerDisplayLockedKeyguardTests.java b/hostsidetests/services/activityandwindowmanager/activitymanager/src/android/server/cts/ActivityManagerDisplayLockedKeyguardTests.java
deleted file mode 100644
index 1e8c5a1..0000000
--- a/hostsidetests/services/activityandwindowmanager/activitymanager/src/android/server/cts/ActivityManagerDisplayLockedKeyguardTests.java
+++ /dev/null
@@ -1,78 +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.server.cts;
-
-import com.android.tradefed.device.DeviceNotAvailableException;
-
-import static android.server.cts.ActivityManagerState.STATE_RESUMED;
-import static android.server.cts.ActivityManagerState.STATE_STOPPED;
-import static android.server.cts.StateLogger.logE;
-
-/**
- * Display tests that require a locked keyguard.
- *
- * Build: mmma -j32 cts/hostsidetests/services
- * Run: cts/hostsidetests/services/activityandwindowmanager/util/run-test CtsServicesHostTestCases android.server.cts.ActivityManagerDisplayLockedKeyguardTests
- */
-public class ActivityManagerDisplayLockedKeyguardTests extends ActivityManagerDisplayTestBase {
-
-    private static final String TEST_ACTIVITY_NAME = "TestActivity";
-    private static final String VIRTUAL_DISPLAY_ACTIVITY = "VirtualDisplayActivity";
-
-    @Override
-    protected void setUp() throws Exception {
-        super.setUp();
-        setLockCredential();
-    }
-
-    @Override
-    protected void tearDown() throws Exception {
-        try {
-            tearDownLockCredentials();
-        } catch (DeviceNotAvailableException e) {
-            logE(e.getMessage());
-        }
-        super.tearDown();
-    }
-
-    /**
-     * Test that virtual display content is hidden when device is locked.
-     */
-    public void testVirtualDisplayHidesContentWhenLocked() throws Exception {
-        if (!supportsMultiDisplay() || !isHandheld()) { return; }
-
-        // Create new usual virtual display.
-        final DisplayState newDisplay = new VirtualDisplayBuilder(this).build();
-        mAmWmState.assertVisibility(VIRTUAL_DISPLAY_ACTIVITY, true /* visible */);
-
-        // Launch activity on new secondary display.
-        launchActivityOnDisplay(TEST_ACTIVITY_NAME, newDisplay.mDisplayId);
-        mAmWmState.assertVisibility(TEST_ACTIVITY_NAME, true /* visible */);
-
-        // Lock the device.
-        gotoKeyguard();
-        mAmWmState.waitForKeyguardShowingAndNotOccluded(mDevice);
-        mAmWmState.waitForActivityState(mDevice, TEST_ACTIVITY_NAME, STATE_STOPPED);
-        mAmWmState.assertVisibility(TEST_ACTIVITY_NAME, false /* visible */);
-
-        // Unlock and check if visibility is back.
-        unlockDeviceWithCredential();
-        mAmWmState.waitForKeyguardGone(mDevice);
-        mAmWmState.waitForActivityState(mDevice, TEST_ACTIVITY_NAME, STATE_RESUMED);
-        mAmWmState.assertVisibility(TEST_ACTIVITY_NAME, true /* visible */);
-    }
-}
diff --git a/hostsidetests/services/activityandwindowmanager/activitymanager/src/android/server/cts/ActivityManagerDisplayTestBase.java b/hostsidetests/services/activityandwindowmanager/activitymanager/src/android/server/cts/ActivityManagerDisplayTestBase.java
deleted file mode 100644
index cc0a257..0000000
--- a/hostsidetests/services/activityandwindowmanager/activitymanager/src/android/server/cts/ActivityManagerDisplayTestBase.java
+++ /dev/null
@@ -1,508 +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.server.cts;
-
-import com.android.tradefed.device.CollectingOutputReceiver;
-import com.android.tradefed.device.DeviceNotAvailableException;
-
-import static android.server.cts.ActivityAndWindowManagersState.DEFAULT_DISPLAY_ID;
-import static android.server.cts.StateLogger.log;
-import static android.server.cts.StateLogger.logE;
-
-import java.util.ArrayList;
-import java.util.Collections;
-import java.util.HashMap;
-import java.util.LinkedList;
-import java.util.List;
-import java.util.Map;
-import java.util.regex.Matcher;
-import java.util.regex.Pattern;
-
-/**
- * Base class for ActivityManager display tests.
- *
- * @see ActivityManagerDisplayTests
- * @see ActivityManagerDisplayLockedKeyguardTests
- */
-public class ActivityManagerDisplayTestBase extends ActivityManagerTestBase {
-
-    static final int CUSTOM_DENSITY_DPI = 222;
-
-    private static final String DUMPSYS_ACTIVITY_PROCESSES = "dumpsys activity processes";
-    private static final String DUMPSYS_DISPLAY = "dumpsys display";
-    private static final String VIRTUAL_DISPLAY_ACTIVITY = "VirtualDisplayActivity";
-    private static final int INVALID_DENSITY_DPI = -1;
-
-    private boolean mVirtualDisplayCreated;
-    private boolean mDisplaySimulated;
-
-    /** Temp storage used for parsing. */
-    final LinkedList<String> mDumpLines = new LinkedList<>();
-    final LinkedList<String> mAltDumpLines = new LinkedList<>();
-
-    @Override
-    protected void tearDown() throws Exception {
-        try {
-            destroyVirtualDisplays();
-            destroySimulatedDisplays();
-        } catch (DeviceNotAvailableException e) {
-            logE(e.getMessage());
-        }
-        super.tearDown();
-    }
-
-    /** Contains the configurations applied to attached displays. */
-    static final class DisplayState {
-        int mDisplayId;
-        String mOverrideConfig;
-        String mDisplayViewportId;
-
-        private DisplayState(int displayId, String overrideConfig, String uniqueId) {
-            mDisplayId = displayId;
-            mOverrideConfig = overrideConfig;
-            mDisplayViewportId = uniqueId;
-        }
-
-        private int getWidth() {
-            final String[] configParts = mOverrideConfig.split(" ");
-            for (String part : configParts) {
-                if (part.endsWith("dp") && part.startsWith("w")) {
-                    final String widthString = part.substring(1, part.length() - 3);
-                    return Integer.parseInt(widthString);
-                }
-            }
-
-            return -1;
-        }
-
-        private int getHeight() {
-            final String[] configParts = mOverrideConfig.split(" ");
-            for (String part : configParts) {
-                if (part.endsWith("dp") && part.startsWith("h")) {
-                    final String heightString = part.substring(1, part.length() - 3);
-                    return Integer.parseInt(heightString);
-                }
-            }
-
-            return -1;
-        }
-
-        int getDpi() {
-            final String[] configParts = mOverrideConfig.split(" ");
-            for (String part : configParts) {
-                if (part.endsWith("dpi")) {
-                    final String densityDpiString = part.substring(0, part.length() - 3);
-                    return Integer.parseInt(densityDpiString);
-                }
-            }
-
-            return -1;
-        }
-
-        String getDisplayViewPortId() {
-            return mDisplayViewportId;
-        }
-    }
-
-    /** Contains the configurations applied to attached displays. */
-    static final class ReportedDisplays {
-        private static final Pattern sGlobalConfigurationPattern =
-                Pattern.compile("mGlobalConfiguration: (\\{.*\\})");
-        private static final Pattern sDisplayOverrideConfigurationsPattern =
-                Pattern.compile("Display override configurations:");
-        private static final Pattern sDisplayConfigPattern =
-                Pattern.compile("(\\d+): (\\{.*\\})");
-        private static final Pattern sVirtualTouchViewportPattern =
-                Pattern.compile("mVirtualTouchViewports.*displayId=(\\d+), uniqueId='([^']+)', .*");
-
-        String mGlobalConfig;
-        private Map<Integer, DisplayState> mDisplayStates = new HashMap<>();
-
-        static ReportedDisplays create(LinkedList<String> activityProcessDump,
-                LinkedList<String> displayDump) {
-            final ReportedDisplays result = new ReportedDisplays();
-            HashMap<String, String> virtualUniqueIdMap = new HashMap<String, String>();
-
-            while (!displayDump.isEmpty()) {
-                final String line = displayDump.pop().trim();
-
-                Matcher matcher = sVirtualTouchViewportPattern.matcher(line);
-                if (matcher.matches()) {
-                    virtualUniqueIdMap.put(matcher.group(1), matcher.group(2));
-                }
-            }
-
-            while (!activityProcessDump.isEmpty()) {
-                final String line = activityProcessDump.pop().trim();
-
-
-                Matcher actMatcher = sDisplayOverrideConfigurationsPattern.matcher(line);
-                if (actMatcher.matches()) {
-                    log(line);
-                    while (ReportedDisplays.shouldContinueExtracting(activityProcessDump,
-                              sDisplayConfigPattern)) {
-                        final String displayOverrideConfigLine = activityProcessDump.pop().trim();
-                        log(displayOverrideConfigLine);
-                        actMatcher = sDisplayConfigPattern.matcher(displayOverrideConfigLine);
-                        actMatcher.matches();
-                        final String tempUniqueId = "local:" + actMatcher.group(1);
-                        final Integer displayId = Integer.valueOf(actMatcher.group(1));
-                        // Default unique ids for non virtual display.
-                        final String uniqueId =
-                                (virtualUniqueIdMap.containsKey(actMatcher.group(1)))
-                                ? virtualUniqueIdMap.get(actMatcher.group(1)) : tempUniqueId;
-                        result.mDisplayStates.put(displayId,
-                                new DisplayState(displayId, actMatcher.group(2), uniqueId));
-                    }
-                    continue;
-                }
-
-                actMatcher = sGlobalConfigurationPattern.matcher(line);
-                if (actMatcher.matches()) {
-                    log(line);
-                    result.mGlobalConfig = actMatcher.group(1);
-                }
-            }
-
-            return result;
-        }
-
-        /** Check if next line in dump matches the pattern and we should continue extracting. */
-        static boolean shouldContinueExtracting(LinkedList<String> dump, Pattern matchingPattern) {
-            if (dump.isEmpty()) {
-                return false;
-            }
-
-            final String line = dump.peek().trim();
-            return matchingPattern.matcher(line).matches();
-        }
-
-        DisplayState getDisplayState(int displayId) {
-            return mDisplayStates.get(displayId);
-        }
-
-        int getNumberOfDisplays() {
-            return mDisplayStates.size();
-        }
-
-        /** Return the display state with width, height, dpi */
-        DisplayState getDisplayState(int width, int height, int dpi) {
-            for (Map.Entry<Integer, DisplayState> entry : mDisplayStates.entrySet()) {
-                final DisplayState ds = entry.getValue();
-                if (ds.mDisplayId != DEFAULT_DISPLAY_ID && ds.getDpi() == dpi
-                        && ds.getWidth() == width && ds.getHeight() == height) {
-                    return ds;
-                }
-            }
-            return null;
-        }
-
-        /** Return the display state with the given unique id */
-        DisplayState getDisplayState(String viewportId) {
-            for (Map.Entry<Integer, DisplayState> entry : mDisplayStates.entrySet()) {
-                final DisplayState ds = entry.getValue();
-                if (ds.mDisplayViewportId.equals(viewportId)) {
-                    return ds;
-                }
-            }
-            return null;
-        }
-
-        /** Check if reported state is valid. */
-        boolean isValidState(int expectedDisplayCount) {
-            if (mDisplayStates.size() != expectedDisplayCount) {
-                return false;
-            }
-
-            for (Map.Entry<Integer, DisplayState> entry : mDisplayStates.entrySet()) {
-                final DisplayState ds = entry.getValue();
-                if (ds.mDisplayId != DEFAULT_DISPLAY_ID && ds.getDpi() == -1) {
-                    return false;
-                }
-            }
-            return true;
-        }
-    }
-
-    ReportedDisplays getDisplaysStates() throws DeviceNotAvailableException {
-        // Parse dumpsys activity processes.
-        final CollectingOutputReceiver outputReceiver = new CollectingOutputReceiver();
-        mDevice.executeShellCommand(DUMPSYS_ACTIVITY_PROCESSES, outputReceiver);
-        String dump = outputReceiver.getOutput();
-        mDumpLines.clear();
-
-        Collections.addAll(mDumpLines, dump.split("\\n"));
-
-        // Parse dumpsys display
-        final CollectingOutputReceiver outputReceiverNew = new CollectingOutputReceiver();
-        mDevice.executeShellCommand(DUMPSYS_DISPLAY, outputReceiverNew);
-        String dumpNew = outputReceiverNew.getOutput();
-        mAltDumpLines.clear();
-
-        Collections.addAll(mAltDumpLines, dumpNew.split("\\n"));
-
-        return ReportedDisplays.create(mDumpLines, mAltDumpLines);
-    }
-
-    /** Find the display that was not originally reported in oldDisplays and added in newDisplays */
-    protected List<ActivityManagerDisplayTests.DisplayState> findNewDisplayStates(
-            ReportedDisplays oldDisplays, ReportedDisplays newDisplays) {
-        final ArrayList<DisplayState> displays = new ArrayList();
-
-        for (Integer displayId : newDisplays.mDisplayStates.keySet()) {
-            if (!oldDisplays.mDisplayStates.containsKey(displayId)) {
-                displays.add(newDisplays.getDisplayState(displayId));
-            }
-        }
-
-        return displays;
-    }
-
-    /**
-     * Create new virtual display.
-     * @param densityDpi provide custom density for the display.
-     * @param launchInSplitScreen start {@link VirtualDisplayActivity} to side from
-     *                            {@link LaunchingActivity} on primary display.
-     * @param canShowWithInsecureKeyguard allow showing content when device is showing an insecure
-     *                                    keyguard.
-     * @param mustBeCreated should assert if the display was or wasn't created.
-     * @param publicDisplay make display public.
-     * @param resizeDisplay should resize display when surface size changes.
-     * @param launchActivity should launch test activity immediately after display creation.
-     * @return {@link ActivityManagerDisplayTests.DisplayState} of newly created display.
-     * @throws Exception
-     */
-    private List<ActivityManagerDisplayTests.DisplayState> createVirtualDisplays(int densityDpi,
-            boolean launchInSplitScreen, boolean canShowWithInsecureKeyguard, boolean mustBeCreated,
-            boolean publicDisplay, boolean resizeDisplay, String launchActivity, int displayCount)
-            throws Exception {
-        // Start an activity that is able to create virtual displays.
-        if (launchInSplitScreen) {
-            getLaunchActivityBuilder().setToSide(true)
-                    .setTargetActivityName(VIRTUAL_DISPLAY_ACTIVITY).execute();
-        } else {
-            launchActivity(VIRTUAL_DISPLAY_ACTIVITY);
-        }
-        mAmWmState.computeState(mDevice, new String[] {VIRTUAL_DISPLAY_ACTIVITY},
-                false /* compareTaskAndStackBounds */);
-        final ActivityManagerDisplayTests.ReportedDisplays originalDS = getDisplaysStates();
-
-        // Create virtual display with custom density dpi.
-        executeShellCommand(getCreateVirtualDisplayCommand(densityDpi, canShowWithInsecureKeyguard,
-                publicDisplay, resizeDisplay, launchActivity, displayCount));
-        mVirtualDisplayCreated = true;
-
-        return assertAndGetNewDisplays(mustBeCreated ? displayCount : -1, originalDS);
-    }
-
-    /**
-     * Simulate new display.
-     * @param densityDpi provide custom density for the display.
-     * @return {@link ActivityManagerDisplayTests.DisplayState} of newly created display.
-     */
-    private List<ActivityManagerDisplayTests.DisplayState> simulateDisplay(int densityDpi)
-            throws Exception {
-        final ActivityManagerDisplayTests.ReportedDisplays originalDs = getDisplaysStates();
-
-        // Create virtual display with custom density dpi.
-        executeShellCommand(getSimulateDisplayCommand(densityDpi));
-        mDisplaySimulated = true;
-
-        return assertAndGetNewDisplays(1, originalDs);
-    }
-
-    /**
-     * Wait for desired number of displays to be created and get their properties.
-     * @param newDisplayCount expected display count, -1 if display should not be created.
-     * @param originalDS display states before creation of new display(s).
-     */
-    private List<ActivityManagerDisplayTests.DisplayState> assertAndGetNewDisplays(
-            int newDisplayCount, ActivityManagerDisplayTests.ReportedDisplays originalDS)
-            throws Exception {
-        final int originalDisplayCount = originalDS.mDisplayStates.size();
-
-        // Wait for the display(s) to be created and get configurations.
-        final ActivityManagerDisplayTests.ReportedDisplays ds =
-                getDisplayStateAfterChange(originalDisplayCount + newDisplayCount);
-        if (newDisplayCount != -1) {
-            assertEquals("New virtual display(s) must be created",
-                    originalDisplayCount + newDisplayCount, ds.mDisplayStates.size());
-        } else {
-            assertEquals("New virtual display must not be created",
-                    originalDisplayCount, ds.mDisplayStates.size());
-            return null;
-        }
-
-        // Find the newly added display(s).
-        final List<ActivityManagerDisplayTests.DisplayState> newDisplays
-                = findNewDisplayStates(originalDS, ds);
-        assertTrue("New virtual display must be created", newDisplayCount == newDisplays.size());
-
-        return newDisplays;
-    }
-
-    /**
-     * Destroy existing virtual display.
-     */
-    void destroyVirtualDisplays() throws Exception {
-        if (mVirtualDisplayCreated) {
-            executeShellCommand(getDestroyVirtualDisplayCommand());
-            mVirtualDisplayCreated = false;
-        }
-    }
-
-    /**
-     * Destroy existing simulated display.
-     */
-    private void destroySimulatedDisplays() throws Exception {
-        if (mDisplaySimulated) {
-            executeShellCommand(getDestroySimulatedDisplayCommand());
-            mDisplaySimulated = false;
-        }
-    }
-
-    static class VirtualDisplayBuilder {
-        private final ActivityManagerDisplayTestBase mTests;
-
-        private int mDensityDpi = CUSTOM_DENSITY_DPI;
-        private boolean mLaunchInSplitScreen = false;
-        private boolean mCanShowWithInsecureKeyguard = false;
-        private boolean mPublicDisplay = false;
-        private boolean mResizeDisplay = true;
-        private String mLaunchActivity = null;
-        private boolean mSimulateDisplay = false;
-        private boolean mMustBeCreated = true;
-
-        public VirtualDisplayBuilder(ActivityManagerDisplayTestBase tests) {
-            mTests = tests;
-        }
-
-        public VirtualDisplayBuilder setDensityDpi(int densityDpi) {
-            mDensityDpi = densityDpi;
-            return this;
-        }
-
-        public VirtualDisplayBuilder setLaunchInSplitScreen(boolean launchInSplitScreen) {
-            mLaunchInSplitScreen = launchInSplitScreen;
-            return this;
-        }
-
-        public VirtualDisplayBuilder setCanShowWithInsecureKeyguard(
-                boolean canShowWithInsecureKeyguard) {
-            mCanShowWithInsecureKeyguard = canShowWithInsecureKeyguard;
-            return this;
-        }
-
-        public VirtualDisplayBuilder setPublicDisplay(boolean publicDisplay) {
-            mPublicDisplay = publicDisplay;
-            return this;
-        }
-
-        public VirtualDisplayBuilder setResizeDisplay(boolean resizeDisplay) {
-            mResizeDisplay = resizeDisplay;
-            return this;
-        }
-
-        public VirtualDisplayBuilder setLaunchActivity(String launchActivity) {
-            mLaunchActivity = launchActivity;
-            return this;
-        }
-
-        public VirtualDisplayBuilder setSimulateDisplay(boolean simulateDisplay) {
-            mSimulateDisplay = simulateDisplay;
-            return this;
-        }
-
-        public VirtualDisplayBuilder setMustBeCreated(boolean mustBeCreated) {
-            mMustBeCreated = mustBeCreated;
-            return this;
-        }
-
-        public DisplayState build() throws Exception {
-            final List<DisplayState> displays = build(1);
-            return displays != null && !displays.isEmpty() ? displays.get(0) : null;
-        }
-
-        public List<DisplayState> build(int count) throws Exception {
-            if (mSimulateDisplay) {
-                return mTests.simulateDisplay(mDensityDpi);
-            }
-
-            return mTests.createVirtualDisplays(mDensityDpi, mLaunchInSplitScreen,
-                    mCanShowWithInsecureKeyguard, mMustBeCreated, mPublicDisplay, mResizeDisplay,
-                    mLaunchActivity, count);
-        }
-    }
-
-    private static String getCreateVirtualDisplayCommand(int densityDpi,
-            boolean canShowWithInsecureKeyguard, boolean publicDisplay, boolean resizeDisplay,
-            String launchActivity, int displayCount) {
-        final StringBuilder commandBuilder
-                = new StringBuilder(getAmStartCmd(VIRTUAL_DISPLAY_ACTIVITY));
-        commandBuilder.append(" -f 0x20000000");
-        commandBuilder.append(" --es command create_display");
-        if (densityDpi != INVALID_DENSITY_DPI) {
-            commandBuilder.append(" --ei density_dpi ").append(densityDpi);
-        }
-        commandBuilder.append(" --ei count ").append(displayCount);
-        commandBuilder.append(" --ez can_show_with_insecure_keyguard ")
-                .append(canShowWithInsecureKeyguard);
-        commandBuilder.append(" --ez public_display ").append(publicDisplay);
-        commandBuilder.append(" --ez resize_display ").append(resizeDisplay);
-        if (launchActivity != null) {
-            commandBuilder.append(" --es launch_target_activity ").append(launchActivity);
-        }
-        return commandBuilder.toString();
-    }
-
-    private static String getDestroyVirtualDisplayCommand() {
-        return getAmStartCmd(VIRTUAL_DISPLAY_ACTIVITY) + " -f 0x20000000" +
-                " --es command destroy_display";
-    }
-
-    private static String getSimulateDisplayCommand(int densityDpi) {
-        return "settings put global overlay_display_devices 1024x768/" + densityDpi;
-    }
-
-    private static String getDestroySimulatedDisplayCommand() {
-        return "settings delete global overlay_display_devices";
-    }
-
-    /** Wait for provided number of displays and report their configurations. */
-    ReportedDisplays getDisplayStateAfterChange(int expectedDisplayCount)
-            throws DeviceNotAvailableException {
-        ReportedDisplays ds = getDisplaysStates();
-
-        int retriesLeft = 5;
-        while (!ds.isValidState(expectedDisplayCount) && retriesLeft-- > 0) {
-            log("***Waiting for the correct number of displays...");
-            try {
-                Thread.sleep(1000);
-            } catch (InterruptedException e) {
-                log(e.toString());
-            }
-            ds = getDisplaysStates();
-        }
-
-        return ds;
-    }
-
-    /** Checks if the device supports multi-display. */
-    boolean supportsMultiDisplay() throws Exception {
-        return hasDeviceFeature("android.software.activities_on_secondary_displays");
-    }
-}
diff --git a/hostsidetests/services/activityandwindowmanager/activitymanager/src/android/server/cts/ActivityManagerDisplayTests.java b/hostsidetests/services/activityandwindowmanager/activitymanager/src/android/server/cts/ActivityManagerDisplayTests.java
deleted file mode 100644
index 72a477f..0000000
--- a/hostsidetests/services/activityandwindowmanager/activitymanager/src/android/server/cts/ActivityManagerDisplayTests.java
+++ /dev/null
@@ -1,2041 +0,0 @@
-/*
- * Copyright (C) 2016 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License
- */
-package android.server.cts;
-
-import android.platform.test.annotations.Presubmit;
-import android.server.displayservice.DisplayHelper;
-
-import com.android.tradefed.device.CollectingOutputReceiver;
-import com.android.tradefed.device.DeviceNotAvailableException;
-
-import java.util.Collections;
-import java.util.LinkedList;
-import java.util.List;
-import java.util.regex.Matcher;
-import java.util.regex.Pattern;
-
-import static android.server.cts.ActivityAndWindowManagersState.DEFAULT_DISPLAY_ID;
-import static android.server.cts.ActivityManagerState.STATE_PAUSED;
-import static android.server.cts.ActivityManagerState.STATE_RESUMED;
-import static android.server.cts.ActivityManagerState.STATE_STOPPED;
-import static android.server.cts.StateLogger.log;
-import static android.server.cts.StateLogger.logE;
-
-/**
- * Build: mmma -j32 cts/hostsidetests/services
- * Run: cts/hostsidetests/services/activityandwindowmanager/util/run-test CtsServicesHostTestCases android.server.cts.ActivityManagerDisplayTests
- */
-public class ActivityManagerDisplayTests extends ActivityManagerDisplayTestBase {
-    private static final String WM_SIZE = "wm size";
-    private static final String WM_DENSITY = "wm density";
-
-    private static final String TEST_ACTIVITY_NAME = "TestActivity";
-    private static final String VIRTUAL_DISPLAY_ACTIVITY = "VirtualDisplayActivity";
-    private static final String RESIZEABLE_ACTIVITY_NAME = "ResizeableActivity";
-    private static final String NON_RESIZEABLE_ACTIVITY_NAME = "NonResizeableActivity";
-    private static final String SECOND_ACTIVITY_NAME = "SecondActivity";
-    private static final String SECOND_ACTIVITY_NO_EMBEDDING_NAME = "SecondActivityNoEmbedding";
-    private static final String THIRD_ACTIVITY_NAME = "ThirdActivity";
-    private static final String VR_TEST_ACTIVITY_NAME = "VrTestActivity";
-    private static final String SHOW_WHEN_LOCKED_ATTR_ACTIVITY_NAME = "ShowWhenLockedAttrActivity";
-    private static final String SECOND_PACKAGE_NAME = "android.server.cts.second";
-    private static final String THIRD_PACKAGE_NAME = "android.server.cts.third";
-    private static final String VR_UNIQUE_DISPLAY_ID =
-            "virtual:android:277f1a09-b88d-4d1e-8716-796f114d080b";
-    private static final String VR_STANDALONE_DEVICE_PROPERTY = "ro.boot.vr";
-
-    private DisplayHelper mExternalDisplayHelper;
-
-    /** Physical display metrics and overrides in the beginning of the test. */
-    private ReportedDisplayMetrics mInitialDisplayMetrics;
-
-    // Set on standalone VR devices to indicate that the VR virtual display is the display where 2d
-    // activities are launched.
-    private boolean mVrHeadset;
-    private int mVrVirtualDisplayId;
-
-    @Override
-    protected void setUp() throws Exception {
-        super.setUp();
-        mInitialDisplayMetrics = getDisplayMetrics();
-        mVrHeadset = isVrHeadset();
-        final DisplayState vrDisplay = getDisplaysStates().getDisplayState(VR_UNIQUE_DISPLAY_ID);
-        mVrVirtualDisplayId = mVrHeadset ? vrDisplay.mDisplayId : -1;
-    }
-
-    @Override
-    protected void tearDown() throws Exception {
-        try {
-            enablePersistentVrMode(false);
-            restoreDisplayMetricsOverrides();
-            if (mExternalDisplayHelper != null) {
-                mExternalDisplayHelper.releaseDisplay();
-                mExternalDisplayHelper = null;
-            }
-            setPrimaryDisplayState(true);
-        } catch (DeviceNotAvailableException e) {
-            logE(e.getMessage());
-        }
-        super.tearDown();
-    }
-
-    private void enablePersistentVrMode(boolean enabled) throws Exception {
-        if (enabled) {
-            executeShellCommand("setprop vr_virtualdisplay true");
-            executeShellCommand("vr set-persistent-vr-mode-enabled true");
-        } else {
-            executeShellCommand("vr set-persistent-vr-mode-enabled false");
-            executeShellCommand("setprop vr_virtualdisplay false");
-        }
-    }
-
-    private boolean isVrHeadset() {
-        try {
-            if (mDevice.getProperty(VR_STANDALONE_DEVICE_PROPERTY).equals("1")) {
-              return true;
-            }
-
-            return false;
-        } catch (DeviceNotAvailableException e) {
-            return false;
-        }
-    }
-
-    private void restoreDisplayMetricsOverrides() throws Exception {
-        if (mInitialDisplayMetrics.sizeOverrideSet) {
-            executeShellCommand(WM_SIZE + " " + mInitialDisplayMetrics.overrideWidth + "x"
-                    + mInitialDisplayMetrics.overrideHeight);
-        } else {
-            executeShellCommand("wm size reset");
-        }
-        if (mInitialDisplayMetrics.densityOverrideSet) {
-            executeShellCommand(WM_DENSITY + " " + mInitialDisplayMetrics.overrideDensity);
-        } else {
-            executeShellCommand("wm density reset");
-        }
-    }
-
-    /**
-     * Tests that the global configuration is equal to the default display's override configuration.
-     */
-    public void testDefaultDisplayOverrideConfiguration() throws Exception {
-        final ReportedDisplays reportedDisplays = getDisplaysStates();
-        assertNotNull("Global configuration must not be empty.", reportedDisplays.mGlobalConfig);
-        final DisplayState primaryDisplay = reportedDisplays.getDisplayState(DEFAULT_DISPLAY_ID);
-        assertEquals("Primary display's configuration should not be equal to global configuration.",
-                reportedDisplays.mGlobalConfig, primaryDisplay.mOverrideConfig);
-    }
-
-    /**
-     * Tests that secondary display has override configuration set.
-     */
-    public void testCreateVirtualDisplayWithCustomConfig() throws Exception {
-        // Create new virtual display.
-        final DisplayState newDisplay = new VirtualDisplayBuilder(this).build();
-
-        // Find the density of created display.
-        final int newDensityDpi = newDisplay.getDpi();
-        assertEquals(CUSTOM_DENSITY_DPI, newDensityDpi);
-    }
-
-    /**
-     * Tests that launch on secondary display is not permitted if device has the feature disabled.
-     * Activities requested to be launched on a secondary display in this case should land on the
-     * default display.
-     */
-    public void testMultiDisplayDisabled() throws Exception {
-        if (supportsMultiDisplay()) {
-            // Only check devices with the feature disabled.
-            return;
-        }
-
-        // Create new virtual display.
-        final DisplayState newDisplay = new VirtualDisplayBuilder(this).build();
-
-        // Launch activity on new secondary display.
-        launchActivityOnDisplay(TEST_ACTIVITY_NAME, newDisplay.mDisplayId);
-        mAmWmState.computeState(mDevice, new String[] {TEST_ACTIVITY_NAME});
-
-        mAmWmState.assertFocusedActivity("Launched activity must be focused", TEST_ACTIVITY_NAME);
-
-        // Check that activity is on the right display.
-        final int frontStackId = mAmWmState.getAmState().getFrontStackId(DEFAULT_DISPLAY_ID);
-        final ActivityManagerState.ActivityStack frontStack
-                = mAmWmState.getAmState().getStackById(frontStackId);
-        assertEquals("Launched activity must be resumed",
-                getActivityComponentName(TEST_ACTIVITY_NAME), frontStack.mResumedActivity);
-        assertEquals("Front stack must be on the default display", DEFAULT_DISPLAY_ID,
-                frontStack.mDisplayId);
-        mAmWmState.assertFocusedStack("Focus must be on the default display", frontStackId);
-    }
-
-    /**
-     * Tests that any new activity launch in Vr mode is in Vr display.
-     */
-    public void testVrActivityLaunch() throws Exception {
-        if (!supportsVrMode() || !supportsMultiDisplay()) {
-            // VR Mode is not supported on this device, bail from this test.
-            return;
-        }
-
-        // Put the device in persistent vr mode.
-        enablePersistentVrMode(true);
-
-        // Launch the VR activity.
-        launchActivity(VR_TEST_ACTIVITY_NAME);
-        mAmWmState.computeState(mDevice, new String[] {VR_TEST_ACTIVITY_NAME});
-        mAmWmState.assertVisibility(VR_TEST_ACTIVITY_NAME, true /* visible */);
-
-        // Launch the non-VR 2D activity and check where it ends up.
-        launchActivity(LAUNCHING_ACTIVITY);
-        mAmWmState.computeState(mDevice, new String[] {LAUNCHING_ACTIVITY});
-
-        // Ensure that the subsequent activity is visible
-        mAmWmState.assertVisibility(LAUNCHING_ACTIVITY, true /* visible */);
-
-        // Check that activity is launched in focused stack on primary display.
-        mAmWmState.assertFocusedActivity("Launched activity must be focused", LAUNCHING_ACTIVITY);
-        final int focusedStackId = mAmWmState.getAmState().getFocusedStackId();
-        final ActivityManagerState.ActivityStack focusedStack
-                = mAmWmState.getAmState().getStackById(focusedStackId);
-        assertEquals("Launched activity must be resumed in focused stack",
-            getActivityComponentName(LAUNCHING_ACTIVITY), focusedStack.mResumedActivity);
-
-        // Check if the launch activity is in Vr virtual display id.
-        final ReportedDisplays reportedDisplays = getDisplaysStates();
-        assertNotNull("Global configuration must not be empty.", reportedDisplays.mGlobalConfig);
-        final DisplayState vrDisplay = reportedDisplays.getDisplayState(VR_UNIQUE_DISPLAY_ID);
-        assertNotNull("Vr mode should have a virtual display", vrDisplay);
-
-        // Check if the focused activity is on this virtual stack.
-        assertEquals("Launch in Vr mode should be in virtual stack", vrDisplay.mDisplayId,
-            focusedStack.mDisplayId);
-
-        // Put the device out of persistent vr mode.
-        enablePersistentVrMode(false);
-    }
-
-    /**
-     * Tests that any activity already present is re-launched in Vr display in vr mode.
-     */
-    public void testVrActivityReLaunch() throws Exception {
-        if (!supportsVrMode() || !supportsMultiDisplay()) {
-            // VR Mode is not supported on this device, bail from this test.
-            return;
-        }
-
-        // Launch a 2D activity.
-        launchActivity(LAUNCHING_ACTIVITY);
-
-        // Put the device in persistent vr mode.
-        enablePersistentVrMode(true);
-
-        // Launch the VR activity.
-        launchActivity(VR_TEST_ACTIVITY_NAME);
-        mAmWmState.computeState(mDevice, new String[] {VR_TEST_ACTIVITY_NAME});
-        mAmWmState.assertVisibility(VR_TEST_ACTIVITY_NAME, true /* visible */);
-
-        // Re-launch the non-VR 2D activity and check where it ends up.
-        launchActivity(LAUNCHING_ACTIVITY);
-        mAmWmState.computeState(mDevice, new String[] {LAUNCHING_ACTIVITY});
-
-        // Ensure that the subsequent activity is visible
-        mAmWmState.assertVisibility(LAUNCHING_ACTIVITY, true /* visible */);
-
-        // Check that activity is launched in focused stack on primary display.
-        mAmWmState.assertFocusedActivity("Launched activity must be focused", LAUNCHING_ACTIVITY);
-        final int focusedStackId = mAmWmState.getAmState().getFocusedStackId();
-        final ActivityManagerState.ActivityStack focusedStack
-                = mAmWmState.getAmState().getStackById(focusedStackId);
-        assertEquals("Launched activity must be resumed in focused stack",
-            getActivityComponentName(LAUNCHING_ACTIVITY), focusedStack.mResumedActivity);
-
-        // Check if the launch activity is in Vr virtual display id.
-        final ReportedDisplays reportedDisplays = getDisplaysStates();
-        assertNotNull("Global configuration must not be empty.", reportedDisplays.mGlobalConfig);
-        final DisplayState vrDisplay = reportedDisplays.getDisplayState(VR_UNIQUE_DISPLAY_ID);
-        assertNotNull("Vr mode should have a virtual display", vrDisplay);
-
-        // Check if the focused activity is on this virtual stack.
-        assertEquals("Launch in Vr mode should be in virtual stack", vrDisplay.mDisplayId,
-            focusedStack.mDisplayId);
-
-        // Put the device out of persistent vr mode.
-        enablePersistentVrMode(false);
-    }
-
-    private int getCurrentDefaultDisplayId() {
-        return mVrHeadset ? mVrVirtualDisplayId : DEFAULT_DISPLAY_ID;
-    }
-
-    /**
-     * Tests that any new activity launch post Vr mode is in the main display.
-     */
-    public void testActivityLaunchPostVr() throws Exception {
-        if (!supportsVrMode() || !supportsMultiDisplay()) {
-            // VR Mode is not supported on this device, bail from this test.
-            return;
-        }
-
-        // Put the device in persistent vr mode.
-        enablePersistentVrMode(true);
-
-        // Launch the VR activity.
-        launchActivity(VR_TEST_ACTIVITY_NAME);
-        mAmWmState.computeState(mDevice, new String[] {VR_TEST_ACTIVITY_NAME});
-        mAmWmState.assertVisibility(VR_TEST_ACTIVITY_NAME, true /* visible */);
-
-        // Launch the non-VR 2D activity and check where it ends up.
-        launchActivity(ALT_LAUNCHING_ACTIVITY);
-        mAmWmState.computeState(mDevice, new String[] {ALT_LAUNCHING_ACTIVITY});
-
-        // Ensure that the subsequent activity is visible
-        mAmWmState.assertVisibility(ALT_LAUNCHING_ACTIVITY, true /* visible */);
-
-        // Check that activity is launched in focused stack on primary display.
-        mAmWmState.assertFocusedActivity("Launched activity must be focused", ALT_LAUNCHING_ACTIVITY);
-        final int focusedStackId = mAmWmState.getAmState().getFocusedStackId();
-        final ActivityManagerState.ActivityStack focusedStack
-                = mAmWmState.getAmState().getStackById(focusedStackId);
-        assertEquals("Launched activity must be resumed in focused stack",
-            getActivityComponentName(ALT_LAUNCHING_ACTIVITY), focusedStack.mResumedActivity);
-
-        // Check if the launch activity is in Vr virtual display id.
-        final ReportedDisplays reportedDisplays = getDisplaysStates();
-        assertNotNull("Global configuration must not be empty.", reportedDisplays.mGlobalConfig);
-        final DisplayState vrDisplay = reportedDisplays.getDisplayState(VR_UNIQUE_DISPLAY_ID);
-        assertNotNull("Vr mode should have a virtual display", vrDisplay);
-
-        // Check if the focused activity is on this virtual stack.
-        assertEquals("Launch in Vr mode should be in virtual stack", vrDisplay.mDisplayId,
-            focusedStack.mDisplayId);
-
-        // Put the device out of persistent vr mode.
-        enablePersistentVrMode(false);
-
-        // There isn't a direct launch of activity which can take an user out of persistent VR mode.
-        // This sleep is to account for that delay and let device settle once it comes out of VR
-        // mode.
-        try {
-            Thread.sleep(2000);
-        } catch (Exception e) {
-            e.printStackTrace();
-        }
-
-        // Launch the non-VR 2D activity and check where it ends up.
-        launchActivity(RESIZEABLE_ACTIVITY_NAME);
-        mAmWmState.computeState(mDevice, new String[] {RESIZEABLE_ACTIVITY_NAME});
-
-        // Ensure that the subsequent activity is visible
-        mAmWmState.assertVisibility(RESIZEABLE_ACTIVITY_NAME, true /* visible */);
-
-        // Check that activity is launched in focused stack on the correct display.
-        mAmWmState.assertFocusedActivity("Launched activity must be focused",
-                RESIZEABLE_ACTIVITY_NAME);
-        // The default display id for a 2d activity launch is vr virtual display for a vr headset.
-        int displayId = getCurrentDefaultDisplayId();
-        final int frontStackId = mAmWmState.getAmState().getFrontStackId(displayId);
-        final ActivityManagerState.ActivityStack frontStack
-                = mAmWmState.getAmState().getStackById(frontStackId);
-        assertEquals("Launched activity must be resumed in front stack",
-                getActivityComponentName(RESIZEABLE_ACTIVITY_NAME), frontStack.mResumedActivity);
-        assertEquals("Front stack must be on the correct display",
-                displayId, frontStack.mDisplayId);
-    }
-
-    public void testCreateMultipleVirtualDisplays() throws Exception {
-        // Create new virtual display.
-        final List<DisplayState> newDisplays = new VirtualDisplayBuilder(this).build(3);
-        destroyVirtualDisplays();
-        getDisplayStateAfterChange(1);
-    }
-
-    /**
-     * Tests launching an activity on virtual display.
-     */
-    @Presubmit
-    public void testLaunchActivityOnSecondaryDisplay() throws Exception {
-        if (!supportsMultiDisplay()) { return; }
-
-        // Create new virtual display.
-        final DisplayState newDisplay = new VirtualDisplayBuilder(this).build();
-
-        // Launch activity on new secondary display.
-        final String logSeparator = clearLogcat();
-        launchActivityOnDisplay(TEST_ACTIVITY_NAME, newDisplay.mDisplayId);
-        mAmWmState.computeState(mDevice, new String[] {TEST_ACTIVITY_NAME});
-
-        mAmWmState.assertFocusedActivity("Activity launched on secondary display must be focused",
-                TEST_ACTIVITY_NAME);
-
-        // Check that activity is on the right display.
-        final int frontStackId = mAmWmState.getAmState().getFrontStackId(newDisplay.mDisplayId);
-        final ActivityManagerState.ActivityStack frontStack
-                = mAmWmState.getAmState().getStackById(frontStackId);
-        assertEquals("Launched activity must be on the secondary display and resumed",
-                getActivityComponentName(TEST_ACTIVITY_NAME), frontStack.mResumedActivity);
-        mAmWmState.assertFocusedStack("Focus must be on secondary display", frontStackId);
-
-        // Check that activity config corresponds to display config.
-        final ReportedSizes reportedSizes = getLastReportedSizesForActivity(TEST_ACTIVITY_NAME,
-                logSeparator);
-        assertEquals("Activity launched on secondary display must have proper configuration",
-                CUSTOM_DENSITY_DPI, reportedSizes.densityDpi);
-    }
-
-    /**
-     * Tests launching a non-resizeable activity on virtual display. It should land on the
-     * default display.
-     */
-    public void testLaunchNonResizeableActivityOnSecondaryDisplay() throws Exception {
-        if (!supportsMultiDisplay()) { return; }
-
-        // Create new virtual display.
-        final DisplayState newDisplay = new VirtualDisplayBuilder(this).build();
-
-        // Launch activity on new secondary display.
-        launchActivityOnDisplay(NON_RESIZEABLE_ACTIVITY_NAME, newDisplay.mDisplayId);
-        mAmWmState.computeState(mDevice, new String[] {NON_RESIZEABLE_ACTIVITY_NAME});
-
-        mAmWmState.assertFocusedActivity("Activity launched on secondary display must be focused",
-                NON_RESIZEABLE_ACTIVITY_NAME);
-
-        // Check that activity is on the right display.
-        final int frontStackId = mAmWmState.getAmState().getFrontStackId(DEFAULT_DISPLAY_ID);
-        final ActivityManagerState.ActivityStack frontStack =
-                mAmWmState.getAmState().getStackById(frontStackId);
-        assertEquals("Launched activity must be on the primary display and resumed",
-                getActivityComponentName(NON_RESIZEABLE_ACTIVITY_NAME),
-                frontStack.mResumedActivity);
-        mAmWmState.assertFocusedStack("Focus must be on the primary display", frontStackId);
-    }
-
-    /**
-     * Tests launching a non-resizeable activity on virtual display while split-screen is active
-     * on the primary display. It should land on the primary display and dismiss docked stack.
-     */
-    public void testLaunchNonResizeableActivityWithSplitScreen() throws Exception {
-        if (!supportsMultiDisplay() || !supportsSplitScreenMultiWindow()) { return; }
-
-        // Start launching activity.
-        launchActivityInDockStack(LAUNCHING_ACTIVITY);
-        // Create new virtual display.
-        final DisplayState newDisplay =
-                new VirtualDisplayBuilder(this).setLaunchInSplitScreen(true).build();
-
-        // Launch activity on new secondary display.
-        launchActivityOnDisplay(NON_RESIZEABLE_ACTIVITY_NAME, newDisplay.mDisplayId);
-        mAmWmState.computeState(mDevice, new String[] {NON_RESIZEABLE_ACTIVITY_NAME});
-
-        mAmWmState.assertFocusedActivity("Activity launched on secondary display must be focused",
-                NON_RESIZEABLE_ACTIVITY_NAME);
-
-        // Check that activity is on the right display.
-        final int frontStackId = mAmWmState.getAmState().getFrontStackId(DEFAULT_DISPLAY_ID);
-        final ActivityManagerState.ActivityStack frontStack =
-                mAmWmState.getAmState().getStackById(frontStackId);
-        assertEquals("Launched activity must be on the primary display and resumed",
-                getActivityComponentName(NON_RESIZEABLE_ACTIVITY_NAME),
-                frontStack.mResumedActivity);
-        mAmWmState.assertFocusedStack("Focus must be on the primary display", frontStackId);
-        mAmWmState.assertDoesNotContainStack("Must not contain docked stack.", DOCKED_STACK_ID);
-    }
-
-    /**
-     * Tests moving a non-resizeable activity to a virtual display. It should land on the default
-     * display.
-     */
-    public void testMoveNonResizeableActivityToSecondaryDisplay() throws Exception {
-        if (!supportsMultiDisplay()) { return; }
-
-        // Create new virtual display.
-        final DisplayState newDisplay = new VirtualDisplayBuilder(this).build();
-        // Launch a non-resizeable activity on a primary display.
-        launchActivityInNewTask(NON_RESIZEABLE_ACTIVITY_NAME);
-        // Launch a resizeable activity on new secondary display to create a new stack there.
-        launchActivityOnDisplay(RESIZEABLE_ACTIVITY_NAME, newDisplay.mDisplayId);
-        int frontStackId = mAmWmState.getAmState().getFrontStackId(newDisplay.mDisplayId);
-
-        // Try to move the non-resizeable activity to new secondary display.
-        moveActivityToStack(NON_RESIZEABLE_ACTIVITY_NAME, frontStackId);
-        mAmWmState.computeState(mDevice, new String[] {NON_RESIZEABLE_ACTIVITY_NAME});
-
-        mAmWmState.assertFocusedActivity("Activity launched on secondary display must be focused",
-                NON_RESIZEABLE_ACTIVITY_NAME);
-
-        // Check that activity is on the right display.
-        frontStackId = mAmWmState.getAmState().getFrontStackId(DEFAULT_DISPLAY_ID);
-        final ActivityManagerState.ActivityStack frontStack =
-                mAmWmState.getAmState().getStackById(frontStackId);
-        assertEquals("Launched activity must be on the primary display and resumed",
-                getActivityComponentName(NON_RESIZEABLE_ACTIVITY_NAME),
-                frontStack.mResumedActivity);
-        mAmWmState.assertFocusedStack("Focus must be on the primary display", frontStackId);
-    }
-
-    /**
-     * Tests launching a non-resizeable activity on virtual display from activity there. It should
-     * land on the secondary display based on the resizeability of the root activity of the task.
-     */
-    public void testLaunchNonResizeableActivityFromSecondaryDisplaySameTask() throws Exception {
-        if (!supportsMultiDisplay()) { return; }
-
-        // Create new virtual display.
-        final DisplayState newDisplay = new VirtualDisplayBuilder(this).setSimulateDisplay(true)
-                .build();
-
-        // Launch activity on new secondary display.
-        launchActivityOnDisplay(BROADCAST_RECEIVER_ACTIVITY, newDisplay.mDisplayId);
-        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.mDisplayId);
-        ActivityManagerState.ActivityStack frontStack =
-                mAmWmState.getAmState().getStackById(frontStackId);
-        assertEquals("Launched activity must be on the secondary display and resumed",
-                getActivityComponentName(BROADCAST_RECEIVER_ACTIVITY),
-                frontStack.mResumedActivity);
-        mAmWmState.assertFocusedStack("Focus must be on the secondary display", frontStackId);
-
-        // Launch non-resizeable activity from secondary display.
-        executeShellCommand("am broadcast -a trigger_broadcast --ez launch_activity true "
-                + "--ez new_task true --es target_activity " + NON_RESIZEABLE_ACTIVITY_NAME);
-        mAmWmState.computeState(mDevice, new String[] {NON_RESIZEABLE_ACTIVITY_NAME});
-
-        // Check that non-resizeable activity is on the secondary display, because of the resizeable
-        // root of the task.
-        frontStackId = mAmWmState.getAmState().getFrontStackId(newDisplay.mDisplayId);
-        frontStack = mAmWmState.getAmState().getStackById(frontStackId);
-        assertEquals("Launched activity must be on the primary display and resumed",
-                getActivityComponentName(NON_RESIZEABLE_ACTIVITY_NAME),
-                frontStack.mResumedActivity);
-        mAmWmState.assertFocusedStack("Focus must be on the primary display", frontStackId);
-    }
-
-    /**
-     * 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).
-     */
-    public void testLaunchNonResizeableActivityFromSecondaryDisplayNewTask() throws Exception {
-        if (!supportsMultiDisplay()) { return; }
-
-        // Create new virtual display.
-        final DisplayState newDisplay = new VirtualDisplayBuilder(this).build();
-
-        // Launch activity on new secondary display.
-        launchActivityOnDisplay(LAUNCHING_ACTIVITY, newDisplay.mDisplayId);
-        mAmWmState.assertFocusedActivity("Activity launched on secondary display must be focused",
-                LAUNCHING_ACTIVITY);
-
-        // Check that launching activity is on the secondary display.
-        int frontStackId = mAmWmState.getAmState().getFrontStackId(newDisplay.mDisplayId);
-        ActivityManagerState.ActivityStack frontStack =
-                mAmWmState.getAmState().getStackById(frontStackId);
-        assertEquals("Launched activity must be on the secondary display and resumed",
-                getActivityComponentName(LAUNCHING_ACTIVITY),
-                frontStack.mResumedActivity);
-        mAmWmState.assertFocusedStack("Focus must be on the secondary display", frontStackId);
-
-        // Launch non-resizeable activity from secondary display.
-        getLaunchActivityBuilder().setTargetActivityName(NON_RESIZEABLE_ACTIVITY_NAME)
-                .setNewTask(true).setMultipleTask(true).execute();
-
-        // Check that non-resizeable activity is on the primary display.
-        frontStackId = mAmWmState.getAmState().getFocusedStackId();
-        frontStack = mAmWmState.getAmState().getStackById(frontStackId);
-        assertFalse("Launched activity must be on a different display",
-                newDisplay.mDisplayId == frontStack.mDisplayId);
-        assertEquals("Launched activity must be resumed",
-                getActivityComponentName(NON_RESIZEABLE_ACTIVITY_NAME),
-                frontStack.mResumedActivity);
-        mAmWmState.assertFocusedStack("Focus must be on a just launched activity", frontStackId);
-    }
-
-    /**
-     * Tests launching an activity on a virtual display without special permission must not be
-     * allowed.
-     */
-    public void testLaunchWithoutPermissionOnVirtualDisplay() throws Exception {
-        if (!supportsMultiDisplay()) { return; }
-
-        // Create new virtual display.
-        final DisplayState newDisplay = new VirtualDisplayBuilder(this).build();
-
-        final String logSeparator = clearLogcat();
-
-        // Try to launch an activity and check it security exception was triggered.
-        final String broadcastTarget = "-a " + SECOND_PACKAGE_NAME + ".LAUNCH_BROADCAST_ACTION"
-                + " -p " + SECOND_PACKAGE_NAME;
-        final String includeStoppedPackagesFlag = " -f 0x00000020";
-        executeShellCommand("am broadcast " + broadcastTarget
-                + " --ez launch_activity true --es target_activity " + TEST_ACTIVITY_NAME
-                + " --es package_name " + componentName
-                + " --ei display_id " + newDisplay.mDisplayId
-                + includeStoppedPackagesFlag);
-
-        assertSecurityException("LaunchBroadcastReceiver", logSeparator);
-
-        mAmWmState.computeState(mDevice, new String[] {TEST_ACTIVITY_NAME});
-        assertFalse("Restricted activity must not be launched",
-                mAmWmState.getAmState().containsActivity(TEST_ACTIVITY_NAME));
-    }
-
-    /**
-     * Tests launching an activity on a virtual display without special permission must be allowed
-     * for activities with same UID.
-     */
-    public void testLaunchWithoutPermissionOnVirtualDisplayByOwner() throws Exception {
-        if (!supportsMultiDisplay()) { return; }
-
-        // Create new virtual display.
-        final DisplayState newDisplay = new VirtualDisplayBuilder(this).build();
-
-        // Try to launch an activity and check it security exception was triggered.
-        final String broadcastTarget = "-a " + componentName + ".LAUNCH_BROADCAST_ACTION"
-                + " -p " + componentName;
-        executeShellCommand("am broadcast " + broadcastTarget
-                + " --ez launch_activity true --es target_activity " + TEST_ACTIVITY_NAME
-                + " --es package_name " + componentName
-                + " --ei display_id " + newDisplay.mDisplayId);
-
-        mAmWmState.waitForValidState(mDevice, TEST_ACTIVITY_NAME);
-
-        final int externalFocusedStackId = mAmWmState.getAmState().getFocusedStackId();
-        final ActivityManagerState.ActivityStack focusedStack = mAmWmState.getAmState()
-                .getStackById(externalFocusedStackId);
-        assertEquals("Focused stack must be on secondary display", newDisplay.mDisplayId,
-                focusedStack.mDisplayId);
-
-        mAmWmState.assertFocusedActivity("Focus must be on newly launched app", TEST_ACTIVITY_NAME);
-        assertEquals("Activity launched by owner must be on external display",
-                externalFocusedStackId, mAmWmState.getAmState().getFocusedStackId());
-    }
-
-    /**
-     * Tests launching an activity on virtual display and then launching another activity via shell
-     * command and without specifying the display id - the second activity must appear on the
-     * primary display.
-     */
-    @Presubmit
-    public void testConsequentLaunchActivity() throws Exception {
-        if (!supportsMultiDisplay()) { return; }
-
-        // Create new virtual display.
-        final DisplayState newDisplay = new VirtualDisplayBuilder(this).build();
-
-        // Launch activity on new secondary display.
-        launchActivityOnDisplay(TEST_ACTIVITY_NAME, newDisplay.mDisplayId);
-        mAmWmState.computeState(mDevice, new String[] {TEST_ACTIVITY_NAME});
-
-        mAmWmState.assertFocusedActivity("Activity launched on secondary display must be focused",
-                TEST_ACTIVITY_NAME);
-
-        // Launch second activity without specifying display.
-        launchActivity(LAUNCHING_ACTIVITY);
-        mAmWmState.computeState(mDevice, new String[] {LAUNCHING_ACTIVITY});
-
-        // Check that activity is launched in focused stack on the correct display.
-        mAmWmState.assertFocusedActivity("Launched activity must be focused", LAUNCHING_ACTIVITY);
-
-        // The default display id for a 2d activity launch is vr virtual display for a vr headset.
-        int displayId = getCurrentDefaultDisplayId();
-        final int frontStackId = mAmWmState.getAmState().getFrontStackId(displayId);
-        final ActivityManagerState.ActivityStack frontStack
-                = mAmWmState.getAmState().getStackById(frontStackId);
-        assertEquals("Launched activity must be resumed in front stack",
-                getActivityComponentName(LAUNCHING_ACTIVITY), frontStack.mResumedActivity);
-        assertEquals("Front stack must be on the correct display",
-                displayId, frontStack.mDisplayId);
-    }
-
-    /**
-     * Tests launching an activity on simulated display and then launching another activity from the
-     * first one - it must appear on the secondary display, because it was launched from there.
-     */
-    @Presubmit
-    public void testConsequentLaunchActivityFromSecondaryDisplay() throws Exception {
-        if (!supportsMultiDisplay()) { return; }
-
-        // Create new virtual display.
-        final DisplayState newDisplay = new VirtualDisplayBuilder(this).setSimulateDisplay(true)
-                .build();
-
-        // Launch activity on new secondary display.
-        launchActivityOnDisplay(LAUNCHING_ACTIVITY, newDisplay.mDisplayId);
-        mAmWmState.computeState(mDevice, new String[] {LAUNCHING_ACTIVITY});
-
-        mAmWmState.assertFocusedActivity("Activity launched on secondary display must be resumed",
-                LAUNCHING_ACTIVITY);
-
-        // Launch second activity from app on secondary display without specifying display id.
-        getLaunchActivityBuilder().setTargetActivityName(TEST_ACTIVITY_NAME).execute();
-        mAmWmState.computeState(mDevice, new String[] {TEST_ACTIVITY_NAME});
-
-        // Check that activity is launched in focused stack on external display.
-        mAmWmState.assertFocusedActivity("Launched activity must be focused", TEST_ACTIVITY_NAME);
-        final int frontStackId = mAmWmState.getAmState().getFrontStackId(newDisplay.mDisplayId);
-        final ActivityManagerState.ActivityStack frontStack
-                = mAmWmState.getAmState().getStackById(frontStackId);
-        assertEquals("Launched activity must be resumed in front stack",
-                getActivityComponentName(TEST_ACTIVITY_NAME), frontStack.mResumedActivity);
-    }
-
-    /**
-     * Tests launching an activity on virtual display and then launching another activity from the
-     * first one - it must appear on the secondary display, because it was launched from there.
-     */
-    public void testConsequentLaunchActivityFromVirtualDisplay() throws Exception {
-        if (!supportsMultiDisplay()) { return; }
-
-        // Create new virtual display.
-        final DisplayState newDisplay = new VirtualDisplayBuilder(this).build();
-
-        // Launch activity on new secondary display.
-        launchActivityOnDisplay(LAUNCHING_ACTIVITY, newDisplay.mDisplayId);
-        mAmWmState.computeState(mDevice, new String[] {LAUNCHING_ACTIVITY});
-
-        mAmWmState.assertFocusedActivity("Activity launched on secondary display must be resumed",
-                LAUNCHING_ACTIVITY);
-
-        // Launch second activity from app on secondary display without specifying display id.
-        getLaunchActivityBuilder().setTargetActivityName(TEST_ACTIVITY_NAME).execute();
-        mAmWmState.computeState(mDevice, new String[] {TEST_ACTIVITY_NAME});
-
-        // Check that activity is launched in focused stack on external display.
-        mAmWmState.assertFocusedActivity("Launched activity must be focused", TEST_ACTIVITY_NAME);
-        final int frontStackId = mAmWmState.getAmState().getFrontStackId(newDisplay.mDisplayId);
-        final ActivityManagerState.ActivityStack frontStack
-                = mAmWmState.getAmState().getStackById(frontStackId);
-        assertEquals("Launched activity must be resumed in front stack",
-                getActivityComponentName(TEST_ACTIVITY_NAME), frontStack.mResumedActivity);
-    }
-
-    /**
-     * Tests launching an activity on virtual display and then launching another activity from the
-     * first one with specifying the target display - it must appear on the secondary display.
-     */
-    public void testConsequentLaunchActivityFromVirtualDisplayToTargetDisplay() throws Exception {
-        if (!supportsMultiDisplay()) { return; }
-
-        // Create new virtual display.
-        final DisplayState newDisplay = new VirtualDisplayBuilder(this).build();
-
-        // Launch activity on new secondary display.
-        launchActivityOnDisplay(LAUNCHING_ACTIVITY, newDisplay.mDisplayId);
-        mAmWmState.computeState(mDevice, new String[] {LAUNCHING_ACTIVITY});
-
-        mAmWmState.assertFocusedActivity("Activity launched on secondary display must be resumed",
-                LAUNCHING_ACTIVITY);
-
-        // Launch second activity from app on secondary display specifying same display id.
-        getLaunchActivityBuilder().setTargetActivityName(SECOND_ACTIVITY_NAME)
-                .setTargetPackage(SECOND_PACKAGE_NAME)
-                .setDisplayId(newDisplay.mDisplayId).execute();
-        mAmWmState.computeState(mDevice, new String[] {TEST_ACTIVITY_NAME});
-
-        // Check that activity is launched in focused stack on external display.
-        mAmWmState.assertFocusedActivity("Launched activity must be focused", SECOND_PACKAGE_NAME,
-                SECOND_ACTIVITY_NAME);
-        int frontStackId = mAmWmState.getAmState().getFrontStackId(newDisplay.mDisplayId);
-        ActivityManagerState.ActivityStack frontStack
-                = mAmWmState.getAmState().getStackById(frontStackId);
-        assertEquals("Launched activity must be resumed in front stack",
-                getActivityComponentName(SECOND_PACKAGE_NAME, SECOND_ACTIVITY_NAME),
-                frontStack.mResumedActivity);
-
-        // Launch other activity with different uid and check if it has launched successfully.
-        final String broadcastAction = SECOND_PACKAGE_NAME + ".LAUNCH_BROADCAST_ACTION";
-        executeShellCommand("am broadcast -a " + broadcastAction + " -p " + SECOND_PACKAGE_NAME
-                + " --ei display_id " + newDisplay.mDisplayId
-                + " --es target_activity " + THIRD_ACTIVITY_NAME
-                + " --es package_name " + THIRD_PACKAGE_NAME);
-        mAmWmState.waitForValidState(mDevice, new String[] {THIRD_ACTIVITY_NAME},
-                null /* stackIds */, false /* compareTaskAndStackBounds */, THIRD_PACKAGE_NAME);
-
-        // Check that activity is launched in focused stack on external display.
-        mAmWmState.assertFocusedActivity("Launched activity must be focused", THIRD_PACKAGE_NAME,
-                THIRD_ACTIVITY_NAME);
-        frontStackId = mAmWmState.getAmState().getFrontStackId(newDisplay.mDisplayId);
-        frontStack = mAmWmState.getAmState().getStackById(frontStackId);
-        assertEquals("Launched activity must be resumed in front stack",
-                getActivityComponentName(THIRD_PACKAGE_NAME, THIRD_ACTIVITY_NAME),
-                frontStack.mResumedActivity);
-    }
-
-    /**
-     * Tests launching an activity on virtual display and then launching another activity that
-     * doesn't allow embedding - it should fail with security exception.
-     */
-    public void testConsequentLaunchActivityFromVirtualDisplayNoEmbedding() throws Exception {
-        if (!supportsMultiDisplay()) { return; }
-
-        // Create new virtual display.
-        final DisplayState newDisplay = new VirtualDisplayBuilder(this).build();
-
-        // Launch activity on new secondary display.
-        launchActivityOnDisplay(LAUNCHING_ACTIVITY, newDisplay.mDisplayId);
-        mAmWmState.computeState(mDevice, new String[] {LAUNCHING_ACTIVITY});
-
-        mAmWmState.assertFocusedActivity("Activity launched on secondary display must be resumed",
-                LAUNCHING_ACTIVITY);
-
-        final String logSeparator = clearLogcat();
-
-        // Launch second activity from app on secondary display specifying same display id.
-        getLaunchActivityBuilder().setTargetActivityName(SECOND_ACTIVITY_NO_EMBEDDING_NAME)
-                .setTargetPackage(SECOND_PACKAGE_NAME)
-                .setDisplayId(newDisplay.mDisplayId).execute();
-
-        assertSecurityException("ActivityLauncher", logSeparator);
-    }
-
-    /**
-     * Tests launching an activity to secondary display from activity on primary display.
-     */
-    public void testLaunchActivityFromAppToSecondaryDisplay() throws Exception {
-        if (!supportsMultiDisplay()) { return; }
-
-        // Start launching activity.
-        launchActivity(LAUNCHING_ACTIVITY);
-        // Create new virtual display.
-        final DisplayState newDisplay = new VirtualDisplayBuilder(this).setSimulateDisplay(true)
-                .build();
-
-        // Launch activity on secondary display from the app on primary display.
-        getLaunchActivityBuilder().setTargetActivityName(TEST_ACTIVITY_NAME)
-                .setDisplayId(newDisplay.mDisplayId).execute();
-
-        // Check that activity is launched on external display.
-        mAmWmState.computeState(mDevice, new String[] {TEST_ACTIVITY_NAME});
-        mAmWmState.assertFocusedActivity("Activity launched on secondary display must be focused",
-                TEST_ACTIVITY_NAME);
-        final int frontStackId = mAmWmState.getAmState().getFrontStackId(newDisplay.mDisplayId);
-        final ActivityManagerState.ActivityStack frontStack
-                = mAmWmState.getAmState().getStackById(frontStackId);
-        assertEquals("Launched activity must be resumed in front stack",
-                getActivityComponentName(TEST_ACTIVITY_NAME), frontStack.mResumedActivity);
-    }
-
-    /**
-     * Tests launching activities on secondary and then on primary display to see if the stack
-     * visibility is not affected.
-     */
-    @Presubmit
-    public void testLaunchActivitiesAffectsVisibility() throws Exception {
-        if (!supportsMultiDisplay()) { return; }
-
-        // Start launching activity.
-        launchActivity(LAUNCHING_ACTIVITY);
-        // Create new virtual display.
-        final DisplayState newDisplay = new VirtualDisplayBuilder(this).build();
-        mAmWmState.assertVisibility(VIRTUAL_DISPLAY_ACTIVITY, true /* visible */);
-
-        // Launch activity on new secondary display.
-        launchActivityOnDisplay(TEST_ACTIVITY_NAME, newDisplay.mDisplayId);
-        mAmWmState.assertVisibility(TEST_ACTIVITY_NAME, true /* visible */);
-        mAmWmState.assertVisibility(VIRTUAL_DISPLAY_ACTIVITY, true /* visible */);
-
-        // Launch activity on primary display and check if it doesn't affect activity on secondary
-        // display.
-        getLaunchActivityBuilder().setTargetActivityName(RESIZEABLE_ACTIVITY_NAME).execute();
-        mAmWmState.waitForValidState(mDevice, RESIZEABLE_ACTIVITY_NAME);
-        mAmWmState.assertVisibility(TEST_ACTIVITY_NAME, true /* visible */);
-        mAmWmState.assertVisibility(RESIZEABLE_ACTIVITY_NAME, true /* visible */);
-    }
-
-    /**
-     * Test that move-task works when moving between displays.
-     */
-    @Presubmit
-    public void testMoveTaskBetweenDisplays() throws Exception {
-        if (!supportsMultiDisplay()) { return; }
-
-        // Create new virtual display.
-        final DisplayState newDisplay = new VirtualDisplayBuilder(this).build();
-        mAmWmState.assertVisibility(VIRTUAL_DISPLAY_ACTIVITY, true /* visible */);
-        mAmWmState.assertFocusedActivity("Virtual display activity must be focused",
-                VIRTUAL_DISPLAY_ACTIVITY);
-        // The default display id for a 2d activity launch is vr virtual display for a vr headset.
-        int displayId = getCurrentDefaultDisplayId();
-        final int defaultDisplayStackId = mAmWmState.getAmState().getFocusedStackId();
-        ActivityManagerState.ActivityStack focusedStack
-                = mAmWmState.getAmState().getStackById(defaultDisplayStackId);
-        assertEquals("Focus must remain on the correct display", displayId,
-                focusedStack.mDisplayId);
-
-        // Launch activity on new secondary display.
-        launchActivityOnDisplay(TEST_ACTIVITY_NAME, newDisplay.mDisplayId);
-        mAmWmState.assertFocusedActivity("Focus must be on secondary display", TEST_ACTIVITY_NAME);
-        int focusedStackId = mAmWmState.getAmState().getFocusedStackId();
-        focusedStack = mAmWmState.getAmState().getStackById(focusedStackId);
-        assertEquals("Focused stack must be on secondary display",
-                newDisplay.mDisplayId, focusedStack.mDisplayId);
-
-        // Move activity from secondary display to primary.
-        moveActivityToStack(TEST_ACTIVITY_NAME, defaultDisplayStackId);
-        mAmWmState.waitForFocusedStack(mDevice, defaultDisplayStackId);
-        mAmWmState.assertFocusedActivity("Focus must be on moved activity", TEST_ACTIVITY_NAME);
-        focusedStackId = mAmWmState.getAmState().getFocusedStackId();
-        focusedStack = mAmWmState.getAmState().getStackById(focusedStackId);
-        assertEquals("Focus must return to primary display", displayId,
-                focusedStack.mDisplayId);
-    }
-
-    /**
-     * Tests launching activities on secondary display and then removing it to see if stack focus
-     * is moved correctly.
-     * This version launches virtual display creator to fullscreen stack in split-screen.
-     */
-    @Presubmit
-    public void testStackFocusSwitchOnDisplayRemoved() throws Exception {
-        if (!supportsMultiDisplay() || !supportsSplitScreenMultiWindow()) { return; }
-
-        // Start launching activity into docked stack.
-        launchActivityInDockStack(LAUNCHING_ACTIVITY);
-        mAmWmState.assertVisibility(LAUNCHING_ACTIVITY, true /* visible */);
-
-        tryCreatingAndRemovingDisplayWithActivity(true /* splitScreen */,
-                FULLSCREEN_WORKSPACE_STACK_ID);
-    }
-
-    /**
-     * Tests launching activities on secondary display and then removing it to see if stack focus
-     * is moved correctly.
-     * This version launches virtual display creator to docked stack in split-screen.
-     */
-    public void testStackFocusSwitchOnDisplayRemoved2() throws Exception {
-        if (!supportsMultiDisplay() || !supportsSplitScreenMultiWindow()) { return; }
-
-        // Setup split-screen.
-        launchActivityInDockStack(RESIZEABLE_ACTIVITY_NAME);
-
-        // Start launching activity into fullscreen stack.
-        launchActivityInStack(LAUNCHING_ACTIVITY, FULLSCREEN_WORKSPACE_STACK_ID);
-        mAmWmState.assertVisibility(LAUNCHING_ACTIVITY, true /* visible */);
-
-        tryCreatingAndRemovingDisplayWithActivity(true /* splitScreen */,
-                FULLSCREEN_WORKSPACE_STACK_ID);
-    }
-
-    /**
-     * Tests launching activities on secondary display and then removing it to see if stack focus
-     * is moved correctly.
-     * This version works without split-screen.
-     */
-    public void testStackFocusSwitchOnDisplayRemoved3() throws Exception {
-        if (!supportsMultiDisplay()) { return; }
-
-        // Start an activity on default display to determine default stack.
-        launchActivity(BROADCAST_RECEIVER_ACTIVITY);
-        final int focusedStackId = mAmWmState.getAmState().getFrontStackId(DEFAULT_DISPLAY_ID);
-        // Finish probing activity.
-        executeShellCommand(FINISH_ACTIVITY_BROADCAST);
-
-        tryCreatingAndRemovingDisplayWithActivity(false /* splitScreen */, focusedStackId);
-    }
-
-    /**
-     * Create a virtual display, launch a test activity there, destroy the display and check if test
-     * activity is moved to a stack on the default display.
-     */
-    private void tryCreatingAndRemovingDisplayWithActivity(boolean splitScreen, int defaultStackId)
-            throws Exception {
-        // Create new virtual display.
-        final VirtualDisplayBuilder builder = new VirtualDisplayBuilder(this)
-                .setPublicDisplay(true);
-        if (splitScreen) {
-            builder.setLaunchInSplitScreen(true);
-        }
-        final DisplayState newDisplay = builder.build();
-        mAmWmState.assertVisibility(VIRTUAL_DISPLAY_ACTIVITY, true /* visible */);
-        if (splitScreen) {
-            mAmWmState.assertVisibility(LAUNCHING_ACTIVITY, true /* visible */);
-        }
-
-        // Launch activity on new secondary display.
-        launchActivityOnDisplay(TEST_ACTIVITY_NAME, newDisplay.mDisplayId);
-        mAmWmState.assertFocusedActivity("Focus must be on secondary display",
-                TEST_ACTIVITY_NAME);
-        final int frontStackId = mAmWmState.getAmState().getFrontStackId(newDisplay.mDisplayId);
-        mAmWmState.assertFocusedStack("Focus must be on secondary display", frontStackId);
-
-        // Destroy virtual display.
-        destroyVirtualDisplays();
-        mAmWmState.waitForValidState(mDevice, TEST_ACTIVITY_NAME, defaultStackId);
-        mAmWmState.assertSanity();
-        mAmWmState.assertValidBounds(true /* compareTaskAndStackBounds */);
-
-        // Check if the focus is switched back to primary display.
-        mAmWmState.assertVisibility(TEST_ACTIVITY_NAME, true /* visible */);
-        mAmWmState.assertFocusedStack(
-                "Default stack on primary display must be focused after display removed",
-                defaultStackId);
-        mAmWmState.assertFocusedActivity(
-                "Focus must be switched back to activity on primary display",
-                TEST_ACTIVITY_NAME);
-    }
-
-    /**
-     * Tests launching activities on secondary display and then removing it to see if stack focus
-     * is moved correctly.
-     */
-    public void testStackFocusSwitchOnStackEmptied() throws Exception {
-        if (!supportsMultiDisplay()) { return; }
-
-        // Create new virtual display.
-        final DisplayState newDisplay = new VirtualDisplayBuilder(this).build();
-        mAmWmState.assertVisibility(VIRTUAL_DISPLAY_ACTIVITY, true /* visible */);
-        final int focusedStackId = mAmWmState.getAmState().getFrontStackId(DEFAULT_DISPLAY_ID);
-
-        // Launch activity on new secondary display.
-        launchActivityOnDisplay(BROADCAST_RECEIVER_ACTIVITY, newDisplay.mDisplayId);
-        mAmWmState.assertFocusedActivity("Focus must be on secondary display",
-                BROADCAST_RECEIVER_ACTIVITY);
-
-        // Lock the device, so that activity containers will be detached.
-        sleepDevice();
-
-        // Finish activity on secondary display.
-        executeShellCommand(FINISH_ACTIVITY_BROADCAST);
-
-        // Unlock and check if the focus is switched back to primary display.
-        wakeUpAndUnlockDevice();
-        mAmWmState.waitForFocusedStack(mDevice, focusedStackId);
-        mAmWmState.waitForValidState(mDevice, VIRTUAL_DISPLAY_ACTIVITY);
-        mAmWmState.assertVisibility(VIRTUAL_DISPLAY_ACTIVITY, true /* visible */);
-        mAmWmState.assertFocusedActivity("Focus must be switched back to primary display",
-                VIRTUAL_DISPLAY_ACTIVITY);
-    }
-
-    /**
-     * Tests that input events on the primary display take focus from the virtual display.
-     */
-    public void testStackFocusSwitchOnTouchEvent() throws Exception {
-        if (!supportsMultiDisplay()) { return; }
-
-        // Create new virtual display.
-        final DisplayState newDisplay = new VirtualDisplayBuilder(this).build();
-
-        mAmWmState.computeState(mDevice, new String[] {VIRTUAL_DISPLAY_ACTIVITY});
-        mAmWmState.assertFocusedActivity("Focus must be switched back to primary display",
-                VIRTUAL_DISPLAY_ACTIVITY);
-
-        launchActivityOnDisplay(TEST_ACTIVITY_NAME, newDisplay.mDisplayId);
-
-        mAmWmState.computeState(mDevice, new String[] {TEST_ACTIVITY_NAME});
-        mAmWmState.assertFocusedActivity("Activity launched on secondary display must be focused",
-                TEST_ACTIVITY_NAME);
-
-        final ReportedDisplayMetrics displayMetrics = getDisplayMetrics();
-        final int width = displayMetrics.getWidth();
-        final int height = displayMetrics.getHeight();
-        executeShellCommand("input tap " + (width / 2) + " " + (height / 2));
-
-        mAmWmState.computeState(mDevice, new String[] {VIRTUAL_DISPLAY_ACTIVITY});
-        mAmWmState.assertFocusedActivity("Focus must be switched back to primary display",
-                VIRTUAL_DISPLAY_ACTIVITY);
-    }
-
-    /** Test that shell is allowed to launch on secondary displays. */
-    public void testPermissionLaunchFromShell() throws Exception {
-        if (!supportsMultiDisplay()) { return; }
-
-        // Create new virtual display.
-        final DisplayState newDisplay = new VirtualDisplayBuilder(this).build();
-        mAmWmState.assertVisibility(VIRTUAL_DISPLAY_ACTIVITY, true /* visible */);
-        mAmWmState.assertFocusedActivity("Virtual display activity must be focused",
-                VIRTUAL_DISPLAY_ACTIVITY);
-        final int defaultDisplayFocusedStackId = mAmWmState.getAmState().getFocusedStackId();
-        // The default display id for a 2d activity launch is vr virtual display for a vr headset.
-        int displayId = getCurrentDefaultDisplayId();
-        ActivityManagerState.ActivityStack focusedStack
-                = mAmWmState.getAmState().getStackById(defaultDisplayFocusedStackId);
-        assertEquals("Focus must remain on the correct display", displayId,
-                focusedStack.mDisplayId);
-
-        // Launch activity on new secondary display.
-        launchActivityOnDisplay(TEST_ACTIVITY_NAME, newDisplay.mDisplayId);
-        mAmWmState.assertFocusedActivity("Focus must be on secondary display",
-                TEST_ACTIVITY_NAME);
-        final int externalFocusedStackId = mAmWmState.getAmState().getFocusedStackId();
-        focusedStack = mAmWmState.getAmState().getStackById(externalFocusedStackId);
-        assertEquals("Focused stack must be on secondary display", newDisplay.mDisplayId,
-                focusedStack.mDisplayId);
-
-        // Launch other activity with different uid and check it is launched on dynamic stack on
-        // secondary display.
-        final String startCmd =  "am start -n " + SECOND_PACKAGE_NAME + "/." + SECOND_ACTIVITY_NAME
-                + " --display " + newDisplay.mDisplayId;
-        executeShellCommand(startCmd);
-
-        mAmWmState.waitForValidState(mDevice, new String[] {SECOND_ACTIVITY_NAME},
-                null /* stackIds */, false /* compareTaskAndStackBounds */, SECOND_PACKAGE_NAME);
-        mAmWmState.assertFocusedActivity("Focus must be on newly launched app", SECOND_PACKAGE_NAME,
-                SECOND_ACTIVITY_NAME);
-        assertEquals("Activity launched by system must be on external display",
-                externalFocusedStackId, mAmWmState.getAmState().getFocusedStackId());
-    }
-
-    /** Test that launching from app that is on external display is allowed. */
-    public void testPermissionLaunchFromAppOnSecondary() throws Exception {
-        if (!supportsMultiDisplay()) { return; }
-
-        // Create new virtual display.
-        final DisplayState newDisplay = new VirtualDisplayBuilder(this).setSimulateDisplay(true)
-                .build();
-
-        // Launch activity with different uid on secondary display.
-        final String startCmd =  "am start -n " + SECOND_PACKAGE_NAME + "/." + SECOND_ACTIVITY_NAME;
-        final String displayTarget = " --display " + newDisplay.mDisplayId;
-        executeShellCommand(startCmd + displayTarget);
-
-        mAmWmState.waitForValidState(mDevice, new String[] {SECOND_ACTIVITY_NAME},
-                null /* stackIds */, false /* compareTaskAndStackBounds */, SECOND_PACKAGE_NAME);
-        mAmWmState.assertFocusedActivity("Focus must be on newly launched app",
-                SECOND_PACKAGE_NAME, SECOND_ACTIVITY_NAME);
-        final int externalFocusedStackId = mAmWmState.getAmState().getFocusedStackId();
-        ActivityManagerState.ActivityStack focusedStack
-                = mAmWmState.getAmState().getStackById(externalFocusedStackId);
-        assertEquals("Focused stack must be on secondary display", newDisplay.mDisplayId,
-                focusedStack.mDisplayId);
-
-        // Launch another activity with third different uid from app on secondary display and check
-        // it is launched on secondary display.
-        final String broadcastAction = SECOND_PACKAGE_NAME + ".LAUNCH_BROADCAST_ACTION";
-        final String targetActivity = " --es target_activity " + THIRD_ACTIVITY_NAME
-                + " --es package_name " + THIRD_PACKAGE_NAME
-                + " --ei display_id " + newDisplay.mDisplayId;
-        final String includeStoppedPackagesFlag = " -f 0x00000020";
-        executeShellCommand("am broadcast -a " + broadcastAction + " -p " + SECOND_PACKAGE_NAME
-                + targetActivity + includeStoppedPackagesFlag);
-
-        mAmWmState.waitForValidState(mDevice, new String[] {THIRD_ACTIVITY_NAME},
-                null /* stackIds */, false /* compareTaskAndStackBounds */, THIRD_PACKAGE_NAME);
-        mAmWmState.assertFocusedActivity("Focus must be on newly launched app",
-                THIRD_PACKAGE_NAME, THIRD_ACTIVITY_NAME);
-        assertEquals("Activity launched by app on secondary display must be on that display",
-                externalFocusedStackId, mAmWmState.getAmState().getFocusedStackId());
-    }
-
-    /** Tests that an activity can launch an activity from a different UID into its own task. */
-    public void testPermissionLaunchMultiUidTask() throws Exception {
-        if (!supportsMultiDisplay()) { return; }
-
-        final DisplayState newDisplay = new VirtualDisplayBuilder(this).setSimulateDisplay(true)
-                .build();
-
-        launchActivityOnDisplay(LAUNCHING_ACTIVITY, newDisplay.mDisplayId);
-        mAmWmState.computeState(mDevice, new String[] {LAUNCHING_ACTIVITY});
-
-        // Check that the first activity is launched onto the secondary display
-        final int frontStackId = mAmWmState.getAmState().getFrontStackId(newDisplay.mDisplayId);
-        ActivityManagerState.ActivityStack frontStack =
-                mAmWmState.getAmState().getStackById(frontStackId);
-        assertEquals("Activity launched on secondary display must be resumed",
-                getActivityComponentName(LAUNCHING_ACTIVITY),
-                frontStack.mResumedActivity);
-        mAmWmState.assertFocusedStack("Focus must be on secondary display", frontStackId);
-
-        // Launch an activity from a different UID into the first activity's task
-        getLaunchActivityBuilder()
-                .setTargetPackage(SECOND_PACKAGE_NAME)
-                .setTargetActivityName(SECOND_ACTIVITY_NAME).execute();
-
-        mAmWmState.assertFocusedStack("Focus must be on secondary display", frontStackId);
-        frontStack = mAmWmState.getAmState().getStackById(frontStackId);
-        mAmWmState.assertFocusedActivity("Focus must be on newly launched app",
-                SECOND_PACKAGE_NAME, SECOND_ACTIVITY_NAME);
-        assertEquals("Secondary display must contain 1 task", 1, frontStack.getTasks().size());
-    }
-
-    /**
-     * Test that launching from display owner is allowed even when the the display owner
-     * doesn't have anything on the display.
-     */
-    public void testPermissionLaunchFromOwner() throws Exception {
-        if (!supportsMultiDisplay()) { return; }
-
-        // Create new virtual display.
-        final DisplayState newDisplay = new VirtualDisplayBuilder(this).build();
-        mAmWmState.assertVisibility(VIRTUAL_DISPLAY_ACTIVITY, true /* visible */);
-        mAmWmState.assertFocusedActivity("Virtual display activity must be focused",
-                VIRTUAL_DISPLAY_ACTIVITY);
-        final int defaultDisplayFocusedStackId = mAmWmState.getAmState().getFocusedStackId();
-        ActivityManagerState.ActivityStack focusedStack
-                = mAmWmState.getAmState().getStackById(defaultDisplayFocusedStackId);
-        assertEquals("Focus must remain on primary display", DEFAULT_DISPLAY_ID,
-                focusedStack.mDisplayId);
-
-        // Launch other activity with different uid on secondary display.
-        final String startCmd =  "am start -n " + SECOND_PACKAGE_NAME + "/." + SECOND_ACTIVITY_NAME;
-        final String displayTarget = " --display " + newDisplay.mDisplayId;
-        executeShellCommand(startCmd + displayTarget);
-
-        mAmWmState.waitForValidState(mDevice, new String[] {SECOND_ACTIVITY_NAME},
-                null /* stackIds */, false /* compareTaskAndStackBounds */, SECOND_PACKAGE_NAME);
-        mAmWmState.assertFocusedActivity("Focus must be on newly launched app",
-                SECOND_PACKAGE_NAME, SECOND_ACTIVITY_NAME);
-        final int externalFocusedStackId = mAmWmState.getAmState().getFocusedStackId();
-        focusedStack = mAmWmState.getAmState().getStackById(externalFocusedStackId);
-        assertEquals("Focused stack must be on secondary display", newDisplay.mDisplayId,
-                focusedStack.mDisplayId);
-
-        // Check that owner uid can launch its own activity on secondary display.
-        final String broadcastAction = componentName + ".LAUNCH_BROADCAST_ACTION";
-        executeShellCommand("am broadcast -a " + broadcastAction + " -p " + componentName
-                + " --ez launch_activity true --ez new_task true --ez multiple_task true"
-                + " --ei display_id " + newDisplay.mDisplayId);
-
-        mAmWmState.waitForValidState(mDevice, TEST_ACTIVITY_NAME);
-        mAmWmState.assertFocusedActivity("Focus must be on newly launched app", TEST_ACTIVITY_NAME);
-        assertEquals("Activity launched by owner must be on external display",
-                externalFocusedStackId, mAmWmState.getAmState().getFocusedStackId());
-    }
-
-    /**
-     * Test that launching from app that is not present on external display and doesn't own it to
-     * that external display is not allowed.
-     */
-    public void testPermissionLaunchFromDifferentApp() throws Exception {
-        if (!supportsMultiDisplay()) { return; }
-
-        // Create new virtual display.
-        final DisplayState newDisplay = new VirtualDisplayBuilder(this).build();
-        mAmWmState.assertVisibility(VIRTUAL_DISPLAY_ACTIVITY, true /* visible */);
-        mAmWmState.assertFocusedActivity("Virtual display activity must be focused",
-                VIRTUAL_DISPLAY_ACTIVITY);
-        final int defaultDisplayFocusedStackId = mAmWmState.getAmState().getFocusedStackId();
-        // The default display id for a 2d activity launch is vr virtual display for a vr headset.
-        int displayId = getCurrentDefaultDisplayId();
-        ActivityManagerState.ActivityStack focusedStack
-                = mAmWmState.getAmState().getStackById(defaultDisplayFocusedStackId);
-        assertEquals("Focus must remain on the correct display", displayId,
-                focusedStack.mDisplayId);
-
-        // Launch activity on new secondary display.
-        launchActivityOnDisplay(TEST_ACTIVITY_NAME, newDisplay.mDisplayId);
-        mAmWmState.assertFocusedActivity("Focus must be on secondary display",
-                TEST_ACTIVITY_NAME);
-        final int externalFocusedStackId = mAmWmState.getAmState().getFocusedStackId();
-        focusedStack = mAmWmState.getAmState().getStackById(externalFocusedStackId);
-        assertEquals("Focused stack must be on secondary display", newDisplay.mDisplayId,
-                focusedStack.mDisplayId);
-
-        final String logSeparator = clearLogcat();
-
-        // Launch other activity with different uid and check security exception is triggered.
-        final String broadcastAction = SECOND_PACKAGE_NAME + ".LAUNCH_BROADCAST_ACTION";
-        final String includeStoppedPackagesFlag = " -f 0x00000020";
-        executeShellCommand("am broadcast -a " + broadcastAction + " -p " + SECOND_PACKAGE_NAME
-                + " --ei display_id " + newDisplay.mDisplayId + includeStoppedPackagesFlag);
-
-        assertSecurityException("LaunchBroadcastReceiver", logSeparator);
-
-        mAmWmState.waitForValidState(mDevice, new String[] {TEST_ACTIVITY_NAME},
-                null /* stackIds */, false /* compareTaskAndStackBounds */, componentName);
-        mAmWmState.assertFocusedActivity(
-                "Focus must be on first activity", componentName, TEST_ACTIVITY_NAME);
-        assertEquals("Focused stack must be on secondary display's stack",
-                externalFocusedStackId, mAmWmState.getAmState().getFocusedStackId());
-    }
-
-    private void assertSecurityException(String component, String logSeparator) throws Exception {
-        int tries = 0;
-        boolean match = false;
-        final Pattern pattern = Pattern.compile(".*SecurityException launching activity.*");
-        while (tries < 5 && !match) {
-            String[] logs = getDeviceLogsForComponent(component, logSeparator);
-            for (String line : logs) {
-                Matcher m = pattern.matcher(line);
-                if (m.matches()) {
-                    match = true;
-                    break;
-                }
-            }
-            tries++;
-            try {
-                Thread.sleep(500);
-            } catch (InterruptedException e) {
-            }
-        }
-
-        assertTrue("Expected exception not found", match);
-    }
-
-    /**
-     * Test that only private virtual display can show content with insecure keyguard.
-     */
-    public void testFlagShowWithInsecureKeyguardOnPublicVirtualDisplay() throws Exception {
-        if (!supportsMultiDisplay()) {
-            return;
-        }
-
-        // Try to create new show-with-insecure-keyguard public virtual display.
-        final DisplayState newDisplay = new VirtualDisplayBuilder(this)
-                .setPublicDisplay(true)
-                .setCanShowWithInsecureKeyguard(true)
-                .setMustBeCreated(false)
-                .build();
-
-        // Check that the display is not created.
-        assertNull(newDisplay);
-    }
-
-    /**
-     * Test that all activities that were on the private display are destroyed on display removal.
-     */
-    // TODO: Flaky, add to presubmit when b/63404575 is fixed.
-    public void testContentDestroyOnDisplayRemoved() throws Exception {
-        if (!supportsMultiDisplay()) { return; }
-
-        // Create new private virtual display.
-        final DisplayState newDisplay = new VirtualDisplayBuilder(this).build();
-        mAmWmState.assertVisibility(VIRTUAL_DISPLAY_ACTIVITY, true /* visible */);
-
-        // Launch activities on new secondary display.
-        launchActivityOnDisplay(TEST_ACTIVITY_NAME, newDisplay.mDisplayId);
-        mAmWmState.assertVisibility(TEST_ACTIVITY_NAME, true /* visible */);
-        mAmWmState.assertFocusedActivity("Launched activity must be focused", TEST_ACTIVITY_NAME);
-        launchActivityOnDisplay(RESIZEABLE_ACTIVITY_NAME, newDisplay.mDisplayId);
-        mAmWmState.assertVisibility(RESIZEABLE_ACTIVITY_NAME, true /* visible */);
-        mAmWmState.assertFocusedActivity("Launched activity must be focused",
-                RESIZEABLE_ACTIVITY_NAME);
-
-        // Destroy the display and check if activities are removed from system.
-        final String logSeparator = clearLogcat();
-        destroyVirtualDisplays();
-        final String activityName1
-                = ActivityManagerTestBase.getActivityComponentName(TEST_ACTIVITY_NAME);
-        final String activityName2
-                = ActivityManagerTestBase.getActivityComponentName(RESIZEABLE_ACTIVITY_NAME);
-        final String windowName1
-                = ActivityManagerTestBase.getWindowName(TEST_ACTIVITY_NAME);
-        final String windowName2
-                = ActivityManagerTestBase.getWindowName(RESIZEABLE_ACTIVITY_NAME);
-        mAmWmState.waitForWithAmState(mDevice,
-                (state) -> !state.containsActivity(activityName1)
-                        && !state.containsActivity(activityName2),
-                "Waiting for activity to be removed");
-        mAmWmState.waitForWithWmState(mDevice,
-                (state) -> !state.containsWindow(windowName1)
-                        && !state.containsWindow(windowName2),
-                "Waiting for activity window to be gone");
-
-        // Check AM state.
-        assertFalse("Activity from removed display must be destroyed",
-                mAmWmState.getAmState().containsActivity(activityName1));
-        assertFalse("Activity from removed display must be destroyed",
-                mAmWmState.getAmState().containsActivity(activityName2));
-        // Check WM state.
-        assertFalse("Activity windows from removed display must be destroyed",
-                mAmWmState.getWmState().containsWindow(windowName1));
-        assertFalse("Activity windows from removed display must be destroyed",
-                mAmWmState.getWmState().containsWindow(windowName2));
-        // Check activity logs.
-        assertActivityDestroyed(TEST_ACTIVITY_NAME, logSeparator);
-        assertActivityDestroyed(RESIZEABLE_ACTIVITY_NAME, logSeparator);
-    }
-
-    /**
-     * Test that the update of display metrics updates all its content.
-     */
-    @Presubmit
-    public void testDisplayResize() throws Exception {
-        if (!supportsMultiDisplay()) { return; }
-
-        // Create new virtual display.
-        final DisplayState newDisplay = new VirtualDisplayBuilder(this).build();
-        mAmWmState.assertVisibility(VIRTUAL_DISPLAY_ACTIVITY, true /* visible */);
-
-        // Launch a resizeable activity on new secondary display.
-        final String initialLogSeparator = clearLogcat();
-        launchActivityOnDisplay(RESIZEABLE_ACTIVITY_NAME, newDisplay.mDisplayId);
-        mAmWmState.assertVisibility(RESIZEABLE_ACTIVITY_NAME, true /* visible */);
-        mAmWmState.assertFocusedActivity("Launched activity must be focused",
-                RESIZEABLE_ACTIVITY_NAME);
-
-        // Grab reported sizes and compute new with slight size change.
-        final ReportedSizes initialSize = getLastReportedSizesForActivity(RESIZEABLE_ACTIVITY_NAME,
-                initialLogSeparator);
-
-        // Resize the docked stack, so that activity with virtual display will also be resized.
-        final String logSeparator = clearLogcat();
-        executeShellCommand(getResizeVirtualDisplayCommand());
-
-        mAmWmState.waitForWithAmState(mDevice, amState -> {
-            try {
-                return readConfigChangeNumber(RESIZEABLE_ACTIVITY_NAME, logSeparator) == 1
-                        && amState.hasActivityState(RESIZEABLE_ACTIVITY_NAME, STATE_RESUMED);
-            } catch (Exception e) {
-                logE("Error waiting for valid state: " + e.getMessage());
-                return false;
-            }
-        }, "Wait for the configuration change to happen and for activity to be resumed.");
-
-        mAmWmState.computeState(mDevice, new String[] {RESIZEABLE_ACTIVITY_NAME,
-                VIRTUAL_DISPLAY_ACTIVITY}, false /* compareTaskAndStackBounds */);
-        mAmWmState.assertVisibility(VIRTUAL_DISPLAY_ACTIVITY, true);
-        mAmWmState.assertVisibility(RESIZEABLE_ACTIVITY_NAME, true);
-
-        // Check if activity in virtual display was resized properly.
-        assertRelaunchOrConfigChanged(RESIZEABLE_ACTIVITY_NAME, 0 /* numRelaunch */,
-                1 /* numConfigChange */, logSeparator);
-
-        final ReportedSizes updatedSize = getLastReportedSizesForActivity(RESIZEABLE_ACTIVITY_NAME,
-                logSeparator);
-        assertTrue(updatedSize.widthDp <= initialSize.widthDp);
-        assertTrue(updatedSize.heightDp <= initialSize.heightDp);
-        assertTrue(updatedSize.displayWidth == initialSize.displayWidth / 2);
-        assertTrue(updatedSize.displayHeight == initialSize.displayHeight / 2);
-    }
-
-    /** Read the number of configuration changes sent to activity from logs. */
-    private int readConfigChangeNumber(String activityName, String logSeparator) throws Exception {
-        return (new ActivityLifecycleCounts(activityName, logSeparator)).mConfigurationChangedCount;
-    }
-
-    /**
-     * Tests that when an activity is launched with displayId specified and there is an existing
-     * matching task on some other display - that task will moved to the target display.
-     */
-    public void testMoveToDisplayOnLaunch() throws Exception {
-        if (!supportsMultiDisplay()) { return; }
-
-        // Launch activity with unique affinity, so it will the only one in its task.
-        launchActivity(LAUNCHING_ACTIVITY);
-
-        // Create new virtual display.
-        final DisplayState newDisplay = new VirtualDisplayBuilder(this).build();
-        mAmWmState.assertVisibility(VIRTUAL_DISPLAY_ACTIVITY, true /* visible */);
-        final int defaultDisplayStackId = mAmWmState.getAmState().getFocusedStackId();
-        // Launch something to that display so that a new stack is created. We need this to be able
-        // to compare task numbers in stacks later.
-        launchActivityOnDisplay(RESIZEABLE_ACTIVITY_NAME, newDisplay.mDisplayId);
-        mAmWmState.assertVisibility(RESIZEABLE_ACTIVITY_NAME, true /* visible */);
-
-        final int taskNum = mAmWmState.getAmState().getStackById(defaultDisplayStackId)
-                .getTasks().size();
-        final int frontStackId = mAmWmState.getAmState().getFrontStackId(newDisplay.mDisplayId);
-        final int taskNumOnSecondary = mAmWmState.getAmState().getStackById(frontStackId)
-                .getTasks().size();
-
-        // Launch activity on new secondary display.
-        // Using custom command here, because normally we add flags Intent#FLAG_ACTIVITY_NEW_TASK
-        // and Intent#FLAG_ACTIVITY_MULTIPLE_TASK when launching on some specific display. We don't
-        // do it here as we want an existing task to be used.
-        final String launchCommand = "am start -n " + getActivityComponentName(LAUNCHING_ACTIVITY)
-                + " --display " + newDisplay.mDisplayId;
-        executeShellCommand(launchCommand);
-        mAmWmState.waitForActivityState(mDevice, 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",
-                getActivityComponentName(LAUNCHING_ACTIVITY), firstFrontStack.mResumedActivity);
-        mAmWmState.assertFocusedStack("Focus must be on secondary display", frontStackId);
-
-        // Check that task has moved from primary display to secondary.
-        final int taskNumFinal = mAmWmState.getAmState().getStackById(defaultDisplayStackId)
-                .getTasks().size();
-        mAmWmState.assertEquals("Task number in default stack must be decremented.", taskNum - 1,
-                taskNumFinal);
-        final int taskNumFinalOnSecondary = mAmWmState.getAmState().getStackById(frontStackId)
-                .getTasks().size();
-        mAmWmState.assertEquals("Task number in stack on external display must be incremented.",
-                taskNumOnSecondary + 1, taskNumFinalOnSecondary);
-    }
-
-    /**
-     * Tests that when primary display is rotated secondary displays are not affected.
-     */
-    public void testRotationNotAffectingSecondaryScreen() throws Exception {
-        if (!supportsMultiDisplay()) { return; }
-
-        // Create new virtual display.
-        final DisplayState newDisplay = new VirtualDisplayBuilder(this)
-                .setResizeDisplay(false)
-                .build();
-        mAmWmState.assertVisibility(VIRTUAL_DISPLAY_ACTIVITY, true /* visible */);
-
-        // Launch activity on new secondary display.
-        String logSeparator = clearLogcat();
-        launchActivityOnDisplay(RESIZEABLE_ACTIVITY_NAME, newDisplay.mDisplayId);
-        mAmWmState.assertFocusedActivity("Focus must be on secondary display",
-                RESIZEABLE_ACTIVITY_NAME);
-        final ReportedSizes initialSizes = getLastReportedSizesForActivity(
-                RESIZEABLE_ACTIVITY_NAME, logSeparator);
-        assertNotNull("Test activity must have reported initial sizes on launch", initialSizes);
-
-        // Rotate primary display and check that activity on secondary display is not affected.
-        rotateAndCheckSameSizes(RESIZEABLE_ACTIVITY_NAME);
-
-        // Launch activity to secondary display when primary one is rotated.
-        final int initialRotation = mAmWmState.getWmState().getRotation();
-        setDeviceRotation((initialRotation + 1) % 4);
-
-        logSeparator = clearLogcat();
-        launchActivityOnDisplay(TEST_ACTIVITY_NAME, newDisplay.mDisplayId);
-        mAmWmState.waitForActivityState(mDevice, TEST_ACTIVITY_NAME, STATE_RESUMED);
-        mAmWmState.assertFocusedActivity("Focus must be on secondary display",
-                TEST_ACTIVITY_NAME);
-        final ReportedSizes testActivitySizes = getLastReportedSizesForActivity(
-                TEST_ACTIVITY_NAME, logSeparator);
-        assertEquals("Sizes of secondary display must not change after rotation of primary display",
-                initialSizes, testActivitySizes);
-    }
-
-    private void rotateAndCheckSameSizes(String activityName) throws Exception {
-        for (int rotation = 3; rotation >= 0; --rotation) {
-            final String logSeparator = clearLogcat();
-            setDeviceRotation(rotation);
-            final ReportedSizes rotatedSizes = getLastReportedSizesForActivity(activityName,
-                    logSeparator);
-            assertNull("Sizes must not change after rotation", rotatedSizes);
-        }
-    }
-
-    /**
-     * Tests that task affinity does affect what display an activity is launched on but that
-     * matching the task component root does.
-     */
-    public void testTaskMatchAcrossDisplays() throws Exception {
-        if (!supportsMultiDisplay()) { return; }
-
-        final DisplayState newDisplay = new VirtualDisplayBuilder(this).build();
-
-        launchActivityOnDisplay(LAUNCHING_ACTIVITY, newDisplay.mDisplayId);
-        mAmWmState.computeState(mDevice, new String[] {LAUNCHING_ACTIVITY});
-
-        // Check that activity is on the right display.
-        final int frontStackId = mAmWmState.getAmState().getFrontStackId(newDisplay.mDisplayId);
-        final ActivityManagerState.ActivityStack firstFrontStack =
-                mAmWmState.getAmState().getStackById(frontStackId);
-        assertEquals("Activity launched on secondary display must be resumed",
-                getActivityComponentName(LAUNCHING_ACTIVITY), firstFrontStack.mResumedActivity);
-        mAmWmState.assertFocusedStack("Focus must be on secondary display", frontStackId);
-
-        executeShellCommand("am start -n " + getActivityComponentName(ALT_LAUNCHING_ACTIVITY));
-        mAmWmState.waitForValidState(mDevice, new String[] {ALT_LAUNCHING_ACTIVITY},
-                null /* stackIds */, false /* compareTaskAndStackBounds */, componentName);
-
-        // The default display id for a 2d activity launch is vr virtual display for a vr headset.
-        final int displayId = getCurrentDefaultDisplayId();
-        // Check that second activity gets launched on the correct display
-        final int defaultDisplayFrontStackId = mAmWmState.getAmState().getFrontStackId(
-                displayId);
-        final ActivityManagerState.ActivityStack defaultDisplayFrontStack =
-                mAmWmState.getAmState().getStackById(defaultDisplayFrontStackId);
-        assertEquals("Activity launched on default display must be resumed",
-                getActivityComponentName(ALT_LAUNCHING_ACTIVITY),
-                defaultDisplayFrontStack.mResumedActivity);
-        mAmWmState.assertFocusedStack("Focus must be on primary display",
-                defaultDisplayFrontStackId);
-
-        executeShellCommand("am start -n " + getActivityComponentName(LAUNCHING_ACTIVITY));
-        final int stackId = mVrHeadset ? defaultDisplayFrontStackId : frontStackId;
-        final int focusedStackTaskCount = mVrHeadset ? 3 : 1;
-        mAmWmState.waitForFocusedStack(mDevice, stackId);
-
-        // Check that the third intent is redirected to the first task
-        final ActivityManagerState.ActivityStack secondFrontStack
-                = mAmWmState.getAmState().getStackById(stackId);
-        assertEquals("Activity launched on default display must be resumed",
-                getActivityComponentName(LAUNCHING_ACTIVITY), secondFrontStack.mResumedActivity);
-        mAmWmState.assertFocusedStack("Focus must be on primary display", stackId);
-        assertEquals("Focused stack must contain correct tasks",
-                focusedStackTaskCount, secondFrontStack.getTasks().size());
-        assertEquals("Focused task must only contain 1 activity",
-                1, secondFrontStack.getTasks().get(0).mActivities.size());
-    }
-
-    /**
-     * Tests than 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.
-     */
-    public void testNewTaskSameDisplay() throws Exception {
-        if (!supportsMultiDisplay()) { return; }
-
-        final DisplayState newDisplay = new VirtualDisplayBuilder(this).setSimulateDisplay(true)
-                .build();
-
-        launchActivityOnDisplay(BROADCAST_RECEIVER_ACTIVITY, newDisplay.mDisplayId);
-        mAmWmState.computeState(mDevice, new String[] {BROADCAST_RECEIVER_ACTIVITY});
-
-        // Check that the first activity is launched onto the secondary display
-        final int frontStackId = mAmWmState.getAmState().getFrontStackId(newDisplay.mDisplayId);
-        final ActivityManagerState.ActivityStack firstFrontStack =
-                mAmWmState.getAmState().getStackById(frontStackId);
-        assertEquals("Activity launched on secondary display must be resumed",
-                getActivityComponentName(BROADCAST_RECEIVER_ACTIVITY),
-                firstFrontStack.mResumedActivity);
-        mAmWmState.assertFocusedStack("Focus must be on secondary display", frontStackId);
-
-        executeShellCommand("am start -n " + getActivityComponentName(TEST_ACTIVITY_NAME));
-        mAmWmState.waitForValidState(mDevice, new String[] {TEST_ACTIVITY_NAME},
-                null /* stackIds */, false /* compareTaskAndStackBounds */, componentName);
-
-        // Check that the second activity is launched on the default display
-        final int focusedStackId = mAmWmState.getAmState().getFocusedStackId();
-        final ActivityManagerState.ActivityStack focusedStack
-                = mAmWmState.getAmState().getStackById(focusedStackId);
-        assertEquals("Activity launched on default display must be resumed",
-                getActivityComponentName(TEST_ACTIVITY_NAME), focusedStack.mResumedActivity);
-        // The default display id for a 2d activity launch is vr virtual display for a vr headset.
-        int displayId = getCurrentDefaultDisplayId();
-        assertEquals("Focus must be on the correct display", displayId,
-                focusedStack.mDisplayId);
-
-        executeShellCommand("am broadcast -a trigger_broadcast --ez launch_activity true "
-                + "--ez new_task true --es target_activity " + LAUNCHING_ACTIVITY);
-
-        // Check that the third activity ends up in a new task in the same stack as the
-        // first activity
-        mAmWmState.waitForValidState(mDevice, new String[] {LAUNCHING_ACTIVITY},
-                null /* stackIds */, false /* compareTaskAndStackBounds */, componentName);
-
-        int stackId = mAmWmState.getAmState().getFrontStackId(displayId);
-        mAmWmState.assertFocusedStack("Focus must be on secondary display", stackId);
-        final ActivityManagerState.ActivityStack secondFrontStack =
-                mAmWmState.getAmState().getStackById(stackId);
-        assertEquals("Activity must be launched on secondary display",
-                getActivityComponentName(LAUNCHING_ACTIVITY),
-                secondFrontStack.mResumedActivity);
-        final int taskCount = mVrHeadset ? 3 : 2;
-        assertEquals("Secondary display must contain correct tasks",
-                taskCount, secondFrontStack.getTasks().size());
-    }
-
-    /**
-     * Test that display overrides apply correctly and won't be affected by display changes.
-     * This sets overrides to display size and density, initiates a display changed event by locking
-     * and unlocking the phone and verifies that overrides are kept.
-     */
-    @Presubmit
-    public void testForceDisplayMetrics() throws Exception {
-        launchHomeActivity();
-
-        // Read initial sizes.
-        final ReportedDisplayMetrics originalDisplayMetrics = getDisplayMetrics();
-
-        // Apply new override values that don't match the physical metrics.
-        final int overrideWidth = (int) (originalDisplayMetrics.physicalWidth * 1.5);
-        final int overrideHeight = (int) (originalDisplayMetrics.physicalHeight * 1.5);
-        executeShellCommand(WM_SIZE + " " + overrideWidth + "x" + overrideHeight);
-        final int overrideDensity = (int) (originalDisplayMetrics.physicalDensity * 1.1);
-        executeShellCommand(WM_DENSITY + " " + overrideDensity);
-
-        // Check if overrides applied correctly.
-        ReportedDisplayMetrics displayMetrics = getDisplayMetrics();
-        assertEquals(overrideWidth, displayMetrics.overrideWidth);
-        assertEquals(overrideHeight, displayMetrics.overrideHeight);
-        assertEquals(overrideDensity, displayMetrics.overrideDensity);
-
-        // Lock and unlock device. This will cause a DISPLAY_CHANGED event to be triggered and
-        // might update the metrics.
-        sleepDevice();
-        wakeUpAndUnlockDevice();
-        mAmWmState.waitForHomeActivityVisible(mDevice);
-
-        // Check if overrides are still applied.
-        displayMetrics = getDisplayMetrics();
-        assertEquals(overrideWidth, displayMetrics.overrideWidth);
-        assertEquals(overrideHeight, displayMetrics.overrideHeight);
-        assertEquals(overrideDensity, displayMetrics.overrideDensity);
-
-        // All overrides will be cleared in tearDown.
-    }
-
-    /**
-     * Tests than an immediate launch after new display creation is handled correctly.
-     */
-    public void testImmediateLaunchOnNewDisplay() throws Exception {
-        if (!supportsMultiDisplay()) { return; }
-
-        // Create new virtual display and immediately launch an activity on it.
-        final DisplayState newDisplay = new VirtualDisplayBuilder(this)
-                .setLaunchActivity(TEST_ACTIVITY_NAME).build();
-
-        // Check that activity is launched and placed correctly.
-        mAmWmState.waitForActivityState(mDevice, TEST_ACTIVITY_NAME, STATE_RESUMED);
-        mAmWmState.assertResumedActivity("Test activity must be launched on a new display",
-                TEST_ACTIVITY_NAME);
-        final int frontStackId = mAmWmState.getAmState().getFrontStackId(newDisplay.mDisplayId);
-        final ActivityManagerState.ActivityStack firstFrontStack =
-                mAmWmState.getAmState().getStackById(frontStackId);
-        assertEquals("Activity launched on secondary display must be resumed",
-                getActivityComponentName(TEST_ACTIVITY_NAME), firstFrontStack.mResumedActivity);
-        mAmWmState.assertFocusedStack("Focus must be on secondary display", frontStackId);
-    }
-
-    /**
-     * Tests that turning the primary display off does not affect the activity running
-     * on an external secondary display.
-     */
-    public void testExternalDisplayActivityTurnPrimaryOff() throws Exception {
-        // Launch something on the primary display so we know there is a resumed activity there
-        launchActivity(RESIZEABLE_ACTIVITY_NAME);
-        waitAndAssertActivityResumed(RESIZEABLE_ACTIVITY_NAME, DEFAULT_DISPLAY_ID,
-                "Activity launched on primary display must be resumed");
-
-        final DisplayState newDisplay = createExternalVirtualDisplay(
-                true /* showContentWhenLocked */);
-
-        launchActivityOnDisplay(TEST_ACTIVITY_NAME, newDisplay.mDisplayId);
-
-        // Check that the activity is launched onto the external display
-        waitAndAssertActivityResumed(TEST_ACTIVITY_NAME, newDisplay.mDisplayId,
-                "Activity launched on external display must be resumed");
-
-        setPrimaryDisplayState(false);
-
-        // Wait for the fullscreen stack to start sleeping, and then make sure the
-        // test activity is still resumed. Note that on some devices, the top activity may go to
-        // the stopped state by itself on sleep, causing the server side to believe it is still
-        // paused.
-        waitAndAssertActivityPausedOrStopped(RESIZEABLE_ACTIVITY_NAME,
-                "Activity launched on primary display must be stopped or paused after turning off");
-        waitAndAssertActivityResumed(TEST_ACTIVITY_NAME, newDisplay.mDisplayId,
-                "Activity launched on external display must be resumed");
-    }
-
-    /**
-     * Tests that an activity can be launched on a secondary display while the primary
-     * display is off.
-     */
-    public void testLaunchExternalDisplayActivityWhilePrimaryOff() throws Exception {
-        // Launch something on the primary display so we know there is a resumed activity there
-        launchActivity(RESIZEABLE_ACTIVITY_NAME);
-        waitAndAssertActivityResumed(RESIZEABLE_ACTIVITY_NAME, DEFAULT_DISPLAY_ID,
-                "Activity launched on primary display must be resumed");
-
-        setPrimaryDisplayState(false);
-
-        // Make sure there is no resumed activity when the primary display is off
-        waitAndAssertActivityStopped(RESIZEABLE_ACTIVITY_NAME,
-                "Activity launched on primary display must be stopped after turning off");
-        assertEquals("Unexpected resumed activity",
-                0, mAmWmState.getAmState().getResumedActivitiesCount());
-
-        final DisplayState newDisplay = createExternalVirtualDisplay(
-                true /* showContentWhenLocked */);
-
-        launchActivityOnDisplay(TEST_ACTIVITY_NAME, newDisplay.mDisplayId);
-
-        // Check that the test activity is resumed on the external display
-        waitAndAssertActivityResumed(TEST_ACTIVITY_NAME, newDisplay.mDisplayId,
-                "Activity launched on external display must be resumed");
-    }
-
-    /**
-     * Tests that turning the secondary display off stops activities running on that display.
-     */
-    public void testExternalDisplayToggleState() throws Exception {
-        final DisplayState newDisplay = createExternalVirtualDisplay(
-                false /* showContentWhenLocked */);
-
-        launchActivityOnDisplay(TEST_ACTIVITY_NAME, newDisplay.mDisplayId);
-
-        // Check that the test activity is resumed on the external display
-        waitAndAssertActivityResumed(TEST_ACTIVITY_NAME, newDisplay.mDisplayId,
-                "Activity launched on external display must be resumed");
-
-        mExternalDisplayHelper.turnDisplayOff();
-
-        // Check that turning off the external display stops the activity
-        waitAndAssertActivityStopped(TEST_ACTIVITY_NAME,
-                "Activity launched on external display must be stopped after turning off");
-
-        mExternalDisplayHelper.turnDisplayOn();
-
-        // Check that turning on the external display resumes the activity
-        waitAndAssertActivityResumed(TEST_ACTIVITY_NAME, newDisplay.mDisplayId,
-                "Activity launched on external display must be resumed");
-    }
-
-    /**
-     * Tests that tapping on the primary display after showing the keyguard resumes the
-     * activity on the primary display.
-     */
-    public void testStackFocusSwitchOnTouchEventAfterKeyguard() throws Exception {
-        // Launch something on the primary display so we know there is a resumed activity there
-        launchActivity(RESIZEABLE_ACTIVITY_NAME);
-        waitAndAssertActivityResumed(RESIZEABLE_ACTIVITY_NAME, DEFAULT_DISPLAY_ID,
-                "Activity launched on primary display must be resumed");
-
-        sleepDevice();
-
-        // Make sure there is no resumed activity when the primary display is off
-        waitAndAssertActivityStopped(RESIZEABLE_ACTIVITY_NAME,
-                "Activity launched on primary display must be stopped after turning off");
-        assertEquals("Unexpected resumed activity",
-                0, mAmWmState.getAmState().getResumedActivitiesCount());
-
-        final DisplayState newDisplay = createExternalVirtualDisplay(
-                true /* showContentWhenLocked */);
-
-        launchActivityOnDisplay(TEST_ACTIVITY_NAME, newDisplay.mDisplayId);
-
-        // Check that the test activity is resumed on the external display
-        waitAndAssertActivityResumed(TEST_ACTIVITY_NAME, newDisplay.mDisplayId,
-                "Activity launched on external display must be resumed");
-
-        // Unlock the device and tap on the middle of the primary display
-        wakeUpDevice();
-        executeShellCommand("wm dismiss-keyguard");
-        final ReportedDisplayMetrics displayMetrics = getDisplayMetrics();
-        final int width = displayMetrics.getWidth();
-        final int height = displayMetrics.getHeight();
-        executeShellCommand("input tap " + (width / 2) + " " + (height / 2));
-
-        // Check that the activity on the primary display is resumed
-        waitAndAssertActivityResumed(RESIZEABLE_ACTIVITY_NAME, DEFAULT_DISPLAY_ID,
-                "Activity launched on primary display must be resumed");
-        assertEquals("Unexpected resumed activity",
-                1, mAmWmState.getAmState().getResumedActivitiesCount());
-    }
-
-    private void waitAndAssertActivityResumed(String activityName, int displayId, String message)
-            throws Exception {
-        mAmWmState.waitForActivityState(mDevice, activityName, STATE_RESUMED);
-
-        final String fullActivityName = getActivityComponentName(activityName);
-        assertEquals(message, fullActivityName, mAmWmState.getAmState().getResumedActivity());
-        final int frontStackId = mAmWmState.getAmState().getFrontStackId(displayId);
-        ActivityManagerState.ActivityStack firstFrontStack =
-                mAmWmState.getAmState().getStackById(frontStackId);
-        assertEquals(message, fullActivityName, firstFrontStack.mResumedActivity);
-        assertTrue(message,
-                mAmWmState.getAmState().hasActivityState(activityName, STATE_RESUMED));
-        mAmWmState.assertFocusedStack("Focus must be on external display", frontStackId);
-        mAmWmState.assertVisibility(activityName, true /* visible */);
-    }
-
-    private void waitAndAssertActivityStopped(String activityName, String message)
-            throws Exception {
-        waitAndAssertActivityState(activityName, message, STATE_STOPPED);
-    }
-
-    private void waitAndAssertActivityPausedOrStopped(String activityName, String message)
-            throws Exception {
-        waitAndAssertActivityState(activityName, message, STATE_PAUSED, STATE_STOPPED);
-    }
-
-    private void waitAndAssertActivityState(String activityName, String message, String... states)
-            throws Exception {
-        mAmWmState.waitForActivityState(mDevice, activityName, states);
-
-        boolean stateFound = false;
-
-        for (String state : states) {
-            if (mAmWmState.getAmState().hasActivityState(activityName, state)) {
-                stateFound = true;
-                break;
-            }
-        }
-
-        assertTrue(message, stateFound);
-    }
-
-    /**
-     * Tests that showWhenLocked works on a secondary display.
-     */
-    public void testSecondaryDisplayShowWhenLocked() throws Exception {
-        try {
-            setLockCredential();
-
-            launchActivity(TEST_ACTIVITY_NAME);
-
-            final DisplayState newDisplay = createExternalVirtualDisplay(
-                    false /* showContentWhenLocked */);
-            launchActivityOnDisplay(SHOW_WHEN_LOCKED_ATTR_ACTIVITY_NAME, newDisplay.mDisplayId);
-
-            gotoKeyguard();
-            mAmWmState.waitForKeyguardShowingAndNotOccluded(mDevice);
-
-            mAmWmState.waitForActivityState(mDevice, TEST_ACTIVITY_NAME, STATE_STOPPED);
-            mAmWmState.waitForActivityState(
-                    mDevice, SHOW_WHEN_LOCKED_ATTR_ACTIVITY_NAME, STATE_RESUMED);
-
-            mAmWmState.computeState(mDevice, new String[] { SHOW_WHEN_LOCKED_ATTR_ACTIVITY_NAME });
-            assertTrue("Expected resumed activity on secondary display", mAmWmState.getAmState()
-                    .hasActivityState(SHOW_WHEN_LOCKED_ATTR_ACTIVITY_NAME, STATE_RESUMED));
-        } finally {
-            tearDownLockCredentials();
-        }
-    }
-
-    /** Get physical and override display metrics from WM. */
-    private ReportedDisplayMetrics getDisplayMetrics() throws Exception {
-        mDumpLines.clear();
-        final CollectingOutputReceiver outputReceiver = new CollectingOutputReceiver();
-        mDevice.executeShellCommand(WM_SIZE, outputReceiver);
-        mDevice.executeShellCommand(WM_DENSITY, outputReceiver);
-        final String dump = outputReceiver.getOutput();
-        mDumpLines.clear();
-        Collections.addAll(mDumpLines, dump.split("\\n"));
-        return ReportedDisplayMetrics.create(mDumpLines);
-    }
-
-    private static class ReportedDisplayMetrics {
-        private static final Pattern sPhysicalSizePattern =
-                Pattern.compile("Physical size: (\\d+)x(\\d+)");
-        private static final Pattern sOverrideSizePattern =
-                Pattern.compile("Override size: (\\d+)x(\\d+)");
-        private static final Pattern sPhysicalDensityPattern =
-                Pattern.compile("Physical density: (\\d+)");
-        private static final Pattern sOverrideDensityPattern =
-                Pattern.compile("Override density: (\\d+)");
-
-        int physicalWidth;
-        int physicalHeight;
-        int physicalDensity;
-
-        boolean sizeOverrideSet;
-        int overrideWidth;
-        int overrideHeight;
-        boolean densityOverrideSet;
-        int overrideDensity;
-
-        /** Get width that WM operates with. */
-        int getWidth() {
-            return sizeOverrideSet ? overrideWidth : physicalWidth;
-        }
-
-        /** Get height that WM operates with. */
-        int getHeight() {
-            return sizeOverrideSet ? overrideHeight : physicalHeight;
-        }
-
-        /** Get density that WM operates with. */
-        int getDensity() {
-            return densityOverrideSet ? overrideDensity : physicalDensity;
-        }
-
-        static ReportedDisplayMetrics create(LinkedList<String> dump) {
-            final ReportedDisplayMetrics result = new ReportedDisplayMetrics();
-
-            boolean physicalSizeFound = false;
-            boolean physicalDensityFound = false;
-
-            while (!dump.isEmpty()) {
-                final String line = dump.pop().trim();
-
-                Matcher matcher = sPhysicalSizePattern.matcher(line);
-                if (matcher.matches()) {
-                    physicalSizeFound = true;
-                    log(line);
-                    result.physicalWidth = Integer.parseInt(matcher.group(1));
-                    result.physicalHeight = Integer.parseInt(matcher.group(2));
-                    continue;
-                }
-
-                matcher = sOverrideSizePattern.matcher(line);
-                if (matcher.matches()) {
-                    log(line);
-                    result.overrideWidth = Integer.parseInt(matcher.group(1));
-                    result.overrideHeight = Integer.parseInt(matcher.group(2));
-                    result.sizeOverrideSet = true;
-                    continue;
-                }
-
-                matcher = sPhysicalDensityPattern.matcher(line);
-                if (matcher.matches()) {
-                    physicalDensityFound = true;
-                    log(line);
-                    result.physicalDensity = Integer.parseInt(matcher.group(1));
-                    continue;
-                }
-
-                matcher = sOverrideDensityPattern.matcher(line);
-                if (matcher.matches()) {
-                    log(line);
-                    result.overrideDensity = Integer.parseInt(matcher.group(1));
-                    result.densityOverrideSet = true;
-                    continue;
-                }
-            }
-
-            assertTrue("Physical display size must be reported", physicalSizeFound);
-            assertTrue("Physical display density must be reported", physicalDensityFound);
-
-            return result;
-        }
-    }
-
-    /** Assert that component received onMovedToDisplay and onConfigurationChanged callbacks. */
-    private void assertMovedToDisplay(String componentName, String 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 static String getResizeVirtualDisplayCommand() {
-        return getAmStartCmd(VIRTUAL_DISPLAY_ACTIVITY) + " -f 0x20000000" +
-                " --es command resize_display";
-    }
-
-    /**
-     * Creates a private virtual display with the external and show with insecure
-     * keyguard flags set.
-     */
-    private DisplayState createExternalVirtualDisplay(boolean showContentWhenLocked)
-            throws Exception {
-        final ReportedDisplays originalDS = getDisplaysStates();
-        final int originalDisplayCount = originalDS.getNumberOfDisplays();
-
-        mExternalDisplayHelper = new DisplayHelper(getDevice());
-        mExternalDisplayHelper.createAndWaitForDisplay(true /* external */, showContentWhenLocked);
-
-        // Wait for the virtual display to be created and get configurations.
-        final ReportedDisplays ds =
-                getDisplayStateAfterChange(originalDisplayCount + 1);
-        assertEquals("New virtual display must be created",
-                originalDisplayCount + 1, ds.getNumberOfDisplays());
-
-        // Find the newly added display.
-        final List<DisplayState> newDisplays = findNewDisplayStates(originalDS, ds);
-        return newDisplays.get(0);
-    }
-
-    /** Turns the primary display on/off by pressing the power key */
-    private void setPrimaryDisplayState(boolean wantOn) throws DeviceNotAvailableException {
-        // Either KeyEvent.KEYCODE_WAKEUP or KeyEvent.KEYCODE_SLEEP
-        int keycode = wantOn ? 224 : 223;
-        getDevice().executeShellCommand("input keyevent " + keycode);
-        DisplayHelper.waitForDefaultDisplayState(getDevice(), wantOn);
-    }
-}
diff --git a/hostsidetests/services/activityandwindowmanager/activitymanager/src/android/server/cts/ActivityManagerDockedStackTests.java b/hostsidetests/services/activityandwindowmanager/activitymanager/src/android/server/cts/ActivityManagerDockedStackTests.java
deleted file mode 100644
index cf3540c..0000000
--- a/hostsidetests/services/activityandwindowmanager/activitymanager/src/android/server/cts/ActivityManagerDockedStackTests.java
+++ /dev/null
@@ -1,608 +0,0 @@
-/*
- * Copyright (C) 2015 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package android.server.cts;
-
-import com.android.ddmlib.Log.LogLevel;
-import com.android.tradefed.log.LogUtil.CLog;
-
-import java.awt.Rectangle;
-
-import static android.server.cts.WindowManagerState.TRANSIT_WALLPAPER_OPEN;
-
-/**
- * Build: mmma -j32 cts/hostsidetests/services
- * Run: cts/hostsidetests/services/activityandwindowmanager/util/run-test CtsServicesHostTestCases android.server.cts.ActivityManagerDockedStackTests
- */
-public class ActivityManagerDockedStackTests extends ActivityManagerTestBase {
-
-    private static final String TEST_ACTIVITY_NAME = "TestActivity";
-    private static final String FINISHABLE_ACTIVITY_NAME = "FinishableActivity";
-    private static final String NON_RESIZEABLE_ACTIVITY_NAME = "NonResizeableActivity";
-    private static final String DOCKED_ACTIVITY_NAME = "DockedActivity";
-    private static final String NO_RELAUNCH_ACTIVITY_NAME = "NoRelaunchActivity";
-    private static final String SINGLE_INSTANCE_ACTIVITY_NAME = "SingleInstanceActivity";
-    private static final String SINGLE_TASK_ACTIVITY_NAME = "SingleTaskActivity";
-
-    private static final int TASK_SIZE = 600;
-    private static final int STACK_SIZE = 300;
-
-    public void testMinimumDeviceSize() throws Exception {
-        if (!supportsSplitScreenMultiWindow()) {
-            CLog.logAndDisplay(LogLevel.INFO, "Skipping test: no split multi-window support");
-            return;
-        }
-
-        mAmWmState.assertDeviceDefaultDisplaySize(mDevice,
-                "Devices supporting multi-window must be larger than the default minimum"
-                        + " task size");
-    }
-
-    public void testStackList() throws Exception {
-        if (!supportsSplitScreenMultiWindow()) {
-            CLog.logAndDisplay(LogLevel.INFO, "Skipping test: no split multi-window support");
-            return;
-        }
-
-        launchActivity(TEST_ACTIVITY_NAME);
-        mAmWmState.computeState(mDevice, new String[] {TEST_ACTIVITY_NAME});
-        mAmWmState.assertContainsStack("Must contain home stack.", HOME_STACK_ID);
-        mAmWmState.assertContainsStack(
-                "Must contain fullscreen stack.", FULLSCREEN_WORKSPACE_STACK_ID);
-        mAmWmState.assertDoesNotContainStack("Must not contain docked stack.", DOCKED_STACK_ID);
-    }
-
-    public void testDockActivity() throws Exception {
-        if (!supportsSplitScreenMultiWindow()) {
-            CLog.logAndDisplay(LogLevel.INFO, "Skipping test: no split multi-window support");
-            return;
-        }
-
-        launchActivityInDockStack(TEST_ACTIVITY_NAME);
-        mAmWmState.computeState(mDevice, new String[] {TEST_ACTIVITY_NAME});
-        mAmWmState.assertContainsStack("Must contain home stack.", HOME_STACK_ID);
-        mAmWmState.assertContainsStack("Must contain docked stack.", DOCKED_STACK_ID);
-    }
-
-    public void testNonResizeableNotDocked() throws Exception {
-        if (!supportsSplitScreenMultiWindow()) {
-            CLog.logAndDisplay(LogLevel.INFO, "Skipping test: no split multi-window support");
-            return;
-        }
-
-        launchActivityInDockStack(NON_RESIZEABLE_ACTIVITY_NAME);
-        mAmWmState.computeState(mDevice, new String[] {NON_RESIZEABLE_ACTIVITY_NAME});
-
-        mAmWmState.assertContainsStack("Must contain home stack.", HOME_STACK_ID);
-        mAmWmState.assertDoesNotContainStack("Must not contain docked stack.", DOCKED_STACK_ID);
-        mAmWmState.assertFrontStack(
-                "Fullscreen stack must be front stack.", FULLSCREEN_WORKSPACE_STACK_ID);
-    }
-
-    public void testLaunchToSide() throws Exception {
-        if (!supportsSplitScreenMultiWindow()) {
-            CLog.logAndDisplay(LogLevel.INFO, "Skipping test: no split multi-window support");
-            return;
-        }
-
-        launchActivityInDockStack(LAUNCHING_ACTIVITY);
-        mAmWmState.computeState(mDevice, new String[] {LAUNCHING_ACTIVITY});
-        getLaunchActivityBuilder().setToSide(true).execute();
-        mAmWmState.computeState(mDevice, new String[] {TEST_ACTIVITY_NAME});
-        mAmWmState.assertContainsStack(
-                "Must contain fullscreen stack.", FULLSCREEN_WORKSPACE_STACK_ID);
-        mAmWmState.assertContainsStack("Must contain docked stack.", DOCKED_STACK_ID);
-    }
-
-    public void testLaunchToSideMultiWindowCallbacks() throws Exception {
-        if (!supportsSplitScreenMultiWindow()) {
-            CLog.logAndDisplay(LogLevel.INFO, "Skipping test: no split multi-window support");
-            return;
-        }
-
-        // Launch two activities, one docked, one adjacent
-        launchActivityInDockStack(LAUNCHING_ACTIVITY);
-        mAmWmState.computeState(mDevice, new String[] {LAUNCHING_ACTIVITY});
-        getLaunchActivityBuilder().setToSide(true).execute();
-        mAmWmState.computeState(mDevice, new String[] {TEST_ACTIVITY_NAME});
-        mAmWmState.assertContainsStack(
-                "Must contain fullscreen stack.", FULLSCREEN_WORKSPACE_STACK_ID);
-        mAmWmState.assertContainsStack("Must contain docked stack.", DOCKED_STACK_ID);
-
-        // Remove the docked stack, and ensure that
-        final String logSeparator = clearLogcat();
-        removeStacks(DOCKED_STACK_ID);
-        final ActivityLifecycleCounts lifecycleCounts = new ActivityLifecycleCounts(
-                TEST_ACTIVITY_NAME, logSeparator);
-        if (lifecycleCounts.mMultiWindowModeChangedCount != 1) {
-            fail(TEST_ACTIVITY_NAME + " has received "
-                    + lifecycleCounts.mMultiWindowModeChangedCount
-                    + " onMultiWindowModeChanged() calls, expecting 1");
-        }
-    }
-
-    public void testLaunchToSideAndBringToFront() throws Exception {
-        if (!supportsSplitScreenMultiWindow()) {
-            CLog.logAndDisplay(LogLevel.INFO, "Skipping test: no split multi-window support");
-            return;
-        }
-
-        launchActivityInDockStack(LAUNCHING_ACTIVITY);
-        final String[] waitForFirstVisible = new String[] {TEST_ACTIVITY_NAME};
-        final String[] waitForSecondVisible = new String[] {NO_RELAUNCH_ACTIVITY_NAME};
-        mAmWmState.computeState(mDevice, new String[] {LAUNCHING_ACTIVITY});
-
-        // Launch activity to side.
-        getLaunchActivityBuilder().setToSide(true).execute();
-        mAmWmState.computeState(mDevice, waitForFirstVisible);
-        int taskNumberInitial = mAmWmState.getAmState().getStackById(FULLSCREEN_WORKSPACE_STACK_ID)
-                .getTasks().size();
-        mAmWmState.assertFocusedActivity("Launched to side activity must be in front.",
-                TEST_ACTIVITY_NAME);
-
-        // Launch another activity to side to cover first one.
-        launchActivityInStack(NO_RELAUNCH_ACTIVITY_NAME, FULLSCREEN_WORKSPACE_STACK_ID);
-        mAmWmState.computeState(mDevice, waitForSecondVisible);
-        int taskNumberCovered = mAmWmState.getAmState().getStackById(FULLSCREEN_WORKSPACE_STACK_ID)
-                .getTasks().size();
-        mAmWmState.assertEquals("Fullscreen stack must have one task added.",
-                taskNumberInitial + 1, taskNumberCovered);
-        mAmWmState.assertFocusedActivity("Launched to side covering activity must be in front.",
-                NO_RELAUNCH_ACTIVITY_NAME);
-
-        // Launch activity that was first launched to side. It should be brought to front.
-        getLaunchActivityBuilder().setToSide(true).execute();
-        mAmWmState.computeState(mDevice, waitForFirstVisible);
-        int taskNumberFinal = mAmWmState.getAmState().getStackById(FULLSCREEN_WORKSPACE_STACK_ID)
-                .getTasks().size();
-        mAmWmState.assertEquals("Task number in fullscreen stack must remain the same.",
-                taskNumberCovered, taskNumberFinal);
-        mAmWmState.assertFocusedActivity("Launched to side covering activity must be in front.",
-                TEST_ACTIVITY_NAME);
-    }
-
-    public void testLaunchToSideMultiple() throws Exception {
-        if (!supportsSplitScreenMultiWindow()) {
-            CLog.logAndDisplay(LogLevel.INFO, "Skipping test: no split multi-window support");
-            return;
-        }
-
-        launchActivityInDockStack(LAUNCHING_ACTIVITY);
-        mAmWmState.computeState(mDevice, new String[] {LAUNCHING_ACTIVITY});
-        final String[] waitForActivitiesVisible =
-            new String[] {TEST_ACTIVITY_NAME, LAUNCHING_ACTIVITY};
-
-        // Launch activity to side.
-        getLaunchActivityBuilder().setToSide(true).execute();
-        mAmWmState.computeState(mDevice, waitForActivitiesVisible);
-        int taskNumberInitial = mAmWmState.getAmState().getStackById(FULLSCREEN_WORKSPACE_STACK_ID)
-                .getTasks().size();
-        mAmWmState.assertNotNull("Launched to side activity must be in fullscreen stack.",
-                mAmWmState.getAmState()
-                        .getTaskByActivityName(TEST_ACTIVITY_NAME, FULLSCREEN_WORKSPACE_STACK_ID));
-
-        // Try to launch to side same activity again.
-        getLaunchActivityBuilder().setToSide(true).execute();
-        mAmWmState.computeState(mDevice, waitForActivitiesVisible);
-        int taskNumberFinal = mAmWmState.getAmState().getStackById(FULLSCREEN_WORKSPACE_STACK_ID)
-                .getTasks().size();
-        mAmWmState.assertEquals("Task number mustn't change.", taskNumberInitial, taskNumberFinal);
-        mAmWmState.assertFocusedActivity("Launched to side activity must remain in front.",
-                TEST_ACTIVITY_NAME);
-        mAmWmState.assertNotNull("Launched to side activity must remain in fullscreen stack.",
-                mAmWmState.getAmState()
-                        .getTaskByActivityName(TEST_ACTIVITY_NAME, FULLSCREEN_WORKSPACE_STACK_ID));
-    }
-
-    public void testLaunchToSideSingleInstance() throws Exception {
-        launchTargetToSide(SINGLE_INSTANCE_ACTIVITY_NAME, false);
-    }
-
-    public void testLaunchToSideSingleTask() throws Exception {
-        launchTargetToSide(SINGLE_TASK_ACTIVITY_NAME, false);
-    }
-
-    public void testLaunchToSideMultipleWithDifferentIntent() throws Exception {
-        launchTargetToSide(TEST_ACTIVITY_NAME, true);
-    }
-
-    private void launchTargetToSide(String targetActivityName,
-                                    boolean taskCountMustIncrement) throws Exception {
-        if (!supportsSplitScreenMultiWindow()) {
-            CLog.logAndDisplay(LogLevel.INFO, "Skipping test: no split multi-window support");
-            return;
-        }
-
-        launchActivityInDockStack(LAUNCHING_ACTIVITY);
-        mAmWmState.computeState(mDevice, new String[] {LAUNCHING_ACTIVITY});
-
-        final String[] waitForActivitiesVisible =
-            new String[] {targetActivityName, LAUNCHING_ACTIVITY};
-
-        // Launch activity to side with data.
-        launchActivityToSide(true, false, targetActivityName);
-        mAmWmState.computeState(mDevice, waitForActivitiesVisible);
-        mAmWmState.assertContainsStack(
-                "Must contain fullscreen stack.", FULLSCREEN_WORKSPACE_STACK_ID);
-        int taskNumberInitial = mAmWmState.getAmState().getStackById(FULLSCREEN_WORKSPACE_STACK_ID)
-                .getTasks().size();
-        mAmWmState.assertNotNull("Launched to side activity must be in fullscreen stack.",
-                mAmWmState.getAmState()
-                        .getTaskByActivityName(targetActivityName, FULLSCREEN_WORKSPACE_STACK_ID));
-
-        // Try to launch to side same activity again with different data.
-        launchActivityToSide(true, false, targetActivityName);
-        mAmWmState.computeState(mDevice, waitForActivitiesVisible);
-        int taskNumberSecondLaunch = mAmWmState.getAmState()
-                .getStackById(FULLSCREEN_WORKSPACE_STACK_ID).getTasks().size();
-        if (taskCountMustIncrement) {
-            mAmWmState.assertEquals("Task number must be incremented.", taskNumberInitial + 1,
-                    taskNumberSecondLaunch);
-        } else {
-            mAmWmState.assertEquals("Task number must not change.", taskNumberInitial,
-                    taskNumberSecondLaunch);
-        }
-        mAmWmState.assertFocusedActivity("Launched to side activity must be in front.",
-                targetActivityName);
-        mAmWmState.assertNotNull("Launched to side activity must be launched in fullscreen stack.",
-                mAmWmState.getAmState()
-                        .getTaskByActivityName(targetActivityName, FULLSCREEN_WORKSPACE_STACK_ID));
-
-        // Try to launch to side same activity again with no data.
-        launchActivityToSide(false, false, targetActivityName);
-        mAmWmState.computeState(mDevice, waitForActivitiesVisible);
-        int taskNumberFinal = mAmWmState.getAmState().getStackById(FULLSCREEN_WORKSPACE_STACK_ID)
-                .getTasks().size();
-        if (taskCountMustIncrement) {
-            mAmWmState.assertEquals("Task number must be incremented.", taskNumberSecondLaunch + 1,
-                    taskNumberFinal);
-        } else {
-            mAmWmState.assertEquals("Task number must not change.", taskNumberSecondLaunch,
-                    taskNumberFinal);
-        }
-        mAmWmState.assertFocusedActivity("Launched to side activity must be in front.",
-                targetActivityName);
-        mAmWmState.assertNotNull("Launched to side activity must be launched in fullscreen stack.",
-                mAmWmState.getAmState()
-                        .getTaskByActivityName(targetActivityName, FULLSCREEN_WORKSPACE_STACK_ID));
-    }
-
-    public void testLaunchToSideMultipleWithFlag() throws Exception {
-        if (!supportsSplitScreenMultiWindow()) {
-            CLog.logAndDisplay(LogLevel.INFO, "Skipping test: no split multi-window support");
-            return;
-        }
-
-        launchActivityInDockStack(LAUNCHING_ACTIVITY);
-        mAmWmState.computeState(mDevice, new String[] {LAUNCHING_ACTIVITY});
-        final String[] waitForActivitiesVisible =
-            new String[] {LAUNCHING_ACTIVITY, TEST_ACTIVITY_NAME};
-
-        // Launch activity to side.
-        getLaunchActivityBuilder().setToSide(true).execute();
-        mAmWmState.computeState(mDevice, waitForActivitiesVisible);
-        int taskNumberInitial = mAmWmState.getAmState().getStackById(FULLSCREEN_WORKSPACE_STACK_ID)
-                .getTasks().size();
-        mAmWmState.assertNotNull("Launched to side activity must be in fullscreen stack.",
-                mAmWmState.getAmState()
-                        .getTaskByActivityName(TEST_ACTIVITY_NAME, FULLSCREEN_WORKSPACE_STACK_ID));
-
-        // Try to launch to side same activity again, but with Intent#FLAG_ACTIVITY_MULTIPLE_TASK.
-        getLaunchActivityBuilder().setToSide(true).setMultipleTask(true).execute();
-        mAmWmState.computeState(mDevice, waitForActivitiesVisible);
-        int taskNumberFinal = mAmWmState.getAmState().getStackById(FULLSCREEN_WORKSPACE_STACK_ID)
-                .getTasks().size();
-        mAmWmState.assertEquals("Task number must be incremented.", taskNumberInitial + 1,
-                taskNumberFinal);
-        mAmWmState.assertFocusedActivity("Launched to side activity must be in front.",
-                TEST_ACTIVITY_NAME);
-        mAmWmState.assertNotNull("Launched to side activity must remain in fullscreen stack.",
-                mAmWmState.getAmState()
-                        .getTaskByActivityName(TEST_ACTIVITY_NAME, FULLSCREEN_WORKSPACE_STACK_ID));
-    }
-
-    public void testRotationWhenDocked() throws Exception {
-        if (!supportsSplitScreenMultiWindow()) {
-            CLog.logAndDisplay(LogLevel.INFO, "Skipping test: no split multi-window support");
-            return;
-        }
-
-        launchActivityInDockStack(LAUNCHING_ACTIVITY);
-        mAmWmState.computeState(mDevice, new String[] {LAUNCHING_ACTIVITY});
-        getLaunchActivityBuilder().setToSide(true).execute();
-        mAmWmState.computeState(mDevice, new String[] {TEST_ACTIVITY_NAME});
-        mAmWmState.assertContainsStack(
-                "Must contain fullscreen stack.", FULLSCREEN_WORKSPACE_STACK_ID);
-        mAmWmState.assertContainsStack("Must contain docked stack.", DOCKED_STACK_ID);
-
-        // Rotate device single steps (90°) 0-1-2-3.
-        // Each time we compute the state we implicitly assert valid bounds.
-        String[] waitForActivitiesVisible =
-            new String[] {LAUNCHING_ACTIVITY, TEST_ACTIVITY_NAME};
-        for (int i = 0; i < 4; i++) {
-            setDeviceRotation(i);
-            mAmWmState.computeState(mDevice, waitForActivitiesVisible);
-        }
-        // Double steps (180°) We ended the single step at 3. So, we jump directly to 1 for double
-        // step. So, we are testing 3-1-3 for one side and 0-2-0 for the other side.
-        setDeviceRotation(1);
-        mAmWmState.computeState(mDevice, waitForActivitiesVisible);
-        setDeviceRotation(3);
-        mAmWmState.computeState(mDevice, waitForActivitiesVisible);
-        setDeviceRotation(0);
-        mAmWmState.computeState(mDevice, waitForActivitiesVisible);
-        setDeviceRotation(2);
-        mAmWmState.computeState(mDevice, waitForActivitiesVisible);
-        setDeviceRotation(0);
-        mAmWmState.computeState(mDevice, waitForActivitiesVisible);
-    }
-
-    public void testRotationWhenDockedWhileLocked() throws Exception {
-        if (!supportsSplitScreenMultiWindow()) {
-            CLog.logAndDisplay(LogLevel.INFO, "Skipping test: no split multi-window support");
-            return;
-        }
-
-        launchActivityInDockStack(LAUNCHING_ACTIVITY);
-        mAmWmState.computeState(mDevice, new String[] {LAUNCHING_ACTIVITY});
-        getLaunchActivityBuilder().setToSide(true).execute();
-        mAmWmState.computeState(mDevice, new String[] {TEST_ACTIVITY_NAME});
-        mAmWmState.assertSanity();
-        mAmWmState.assertContainsStack(
-                "Must contain fullscreen stack.", FULLSCREEN_WORKSPACE_STACK_ID);
-        mAmWmState.assertContainsStack("Must contain docked stack.", DOCKED_STACK_ID);
-
-        String[] waitForActivitiesVisible =
-            new String[] {LAUNCHING_ACTIVITY, TEST_ACTIVITY_NAME};
-        for (int i = 0; i < 4; i++) {
-            sleepDevice();
-            setDeviceRotation(i);
-            wakeUpAndUnlockDevice();
-            mAmWmState.computeState(mDevice, waitForActivitiesVisible);
-        }
-    }
-
-    public void testRotationWhileDockMinimized() throws Exception {
-        if (!supportsSplitScreenMultiWindow()) {
-            CLog.logAndDisplay(LogLevel.INFO, "Skipping test: no split multi-window support");
-            return;
-        }
-
-        launchActivityInDockStackAndMinimize(TEST_ACTIVITY_NAME);
-        assertDockMinimized();
-        mAmWmState.computeState(mDevice, new String[] {TEST_ACTIVITY_NAME});
-        mAmWmState.assertContainsStack("Must contain docked stack.", DOCKED_STACK_ID);
-        mAmWmState.assertFocusedStack("Home activity should be focused in minimized mode",
-                HOME_STACK_ID);
-
-        // Rotate device single steps (90°) 0-1-2-3.
-        // Each time we compute the state we implicitly assert valid bounds in minimized mode.
-        String[] waitForActivitiesVisible = new String[] {TEST_ACTIVITY_NAME};
-        for (int i = 0; i < 4; i++) {
-            setDeviceRotation(i);
-            mAmWmState.computeState(mDevice, waitForActivitiesVisible);
-        }
-
-        // Double steps (180°) We ended the single step at 3. So, we jump directly to 1 for double
-        // step. So, we are testing 3-1-3 for one side and 0-2-0 for the other side in minimized
-        // mode.
-        setDeviceRotation(1);
-        mAmWmState.computeState(mDevice, waitForActivitiesVisible);
-        setDeviceRotation(3);
-        mAmWmState.computeState(mDevice, waitForActivitiesVisible);
-        setDeviceRotation(0);
-        mAmWmState.computeState(mDevice, waitForActivitiesVisible);
-        setDeviceRotation(2);
-        mAmWmState.computeState(mDevice, waitForActivitiesVisible);
-        setDeviceRotation(0);
-        mAmWmState.computeState(mDevice, waitForActivitiesVisible);
-    }
-
-    public void testMinimizeAndUnminimizeThenGoingHome() throws Exception {
-        if (!supportsSplitScreenMultiWindow()) {
-            CLog.logAndDisplay(LogLevel.INFO, "Skipping test: no split multi-window support");
-            return;
-        }
-
-        // Rotate the screen to check that minimize, unminimize, dismiss the docked stack and then
-        // going home has the correct app transition
-        for (int i = 0; i < 4; i++) {
-            setDeviceRotation(i);
-            launchActivityInDockStackAndMinimize(DOCKED_ACTIVITY_NAME);
-            assertDockMinimized();
-
-            // Unminimize the docked stack
-            pressAppSwitchButton();
-            waitForDockNotMinimized();
-            assertDockNotMinimized();
-
-            // Dismiss the dock stack
-            launchActivityInStack(TEST_ACTIVITY_NAME, FULLSCREEN_WORKSPACE_STACK_ID);
-            moveActivityToStack(DOCKED_ACTIVITY_NAME, FULLSCREEN_WORKSPACE_STACK_ID);
-            mAmWmState.computeState(mDevice, new String[]{DOCKED_ACTIVITY_NAME});
-
-            // Go home and check the app transition
-            assertNotSame(TRANSIT_WALLPAPER_OPEN, mAmWmState.getWmState().getLastTransition());
-            pressHomeButton();
-            mAmWmState.computeState(mDevice, null);
-            assertEquals(TRANSIT_WALLPAPER_OPEN, mAmWmState.getWmState().getLastTransition());
-        }
-    }
-
-    public void testFinishDockActivityWhileMinimized() throws Exception {
-        if (!supportsSplitScreenMultiWindow()) {
-            CLog.logAndDisplay(LogLevel.INFO, "Skipping test: no split multi-window support");
-            return;
-        }
-
-        launchActivityInDockStackAndMinimize(FINISHABLE_ACTIVITY_NAME);
-        assertDockMinimized();
-
-        runCommandAndPrintOutput("am broadcast -a 'android.server.cts.FinishableActivity.finish'");
-        waitForDockNotMinimized();
-        mAmWmState.assertVisibility(FINISHABLE_ACTIVITY_NAME, false);
-        assertDockNotMinimized();
-    }
-
-    public void testDockedStackToMinimizeWhenUnlocked() throws Exception {
-        if (!supportsSplitScreenMultiWindow()) {
-            CLog.logAndDisplay(LogLevel.INFO, "Skipping test: no split multi-window support");
-            return;
-        }
-
-        launchActivityInDockStack(TEST_ACTIVITY_NAME);
-        mAmWmState.computeState(mDevice, new String[] {TEST_ACTIVITY_NAME});
-        sleepDevice();
-        wakeUpAndUnlockDevice();
-        mAmWmState.computeState(mDevice, new String[] {TEST_ACTIVITY_NAME});
-        assertDockMinimized();
-    }
-
-    public void testMinimizedStateWhenUnlockedAndUnMinimized() throws Exception {
-        if (!supportsSplitScreenMultiWindow()) {
-            CLog.logAndDisplay(LogLevel.INFO, "Skipping test: no split multi-window support");
-            return;
-        }
-
-        launchActivityInDockStackAndMinimize(FINISHABLE_ACTIVITY_NAME);
-        assertDockMinimized();
-
-        sleepDevice();
-        wakeUpAndUnlockDevice();
-        mAmWmState.computeState(mDevice, new String[] {TEST_ACTIVITY_NAME});
-
-        // Unminimized back to splitscreen
-        pressAppSwitchButton();
-        mAmWmState.computeState(mDevice, new String[] {TEST_ACTIVITY_NAME});
-    }
-
-    public void testResizeDockedStack() throws Exception {
-        if (!supportsSplitScreenMultiWindow()) {
-            CLog.logAndDisplay(LogLevel.INFO, "Skipping test: no split multi-window support");
-            return;
-        }
-
-        launchActivityInDockStack(DOCKED_ACTIVITY_NAME);
-        mAmWmState.computeState(mDevice, new String[] {DOCKED_ACTIVITY_NAME});
-        launchActivityInStack(TEST_ACTIVITY_NAME, FULLSCREEN_WORKSPACE_STACK_ID);
-        mAmWmState.computeState(mDevice, new String[] {TEST_ACTIVITY_NAME});
-        resizeDockedStack(STACK_SIZE, STACK_SIZE, TASK_SIZE, TASK_SIZE);
-        mAmWmState.computeState(mDevice, new String[] {TEST_ACTIVITY_NAME, DOCKED_ACTIVITY_NAME},
-                false /* compareTaskAndStackBounds */);
-        mAmWmState.assertContainsStack("Must contain docked stack", DOCKED_STACK_ID);
-        mAmWmState.assertContainsStack("Must contain fullscreen stack",
-                FULLSCREEN_WORKSPACE_STACK_ID);
-        assertEquals(new Rectangle(0, 0, STACK_SIZE, STACK_SIZE),
-                mAmWmState.getAmState().getStackById(DOCKED_STACK_ID).getBounds());
-        mAmWmState.assertDockedTaskBounds(TASK_SIZE, TASK_SIZE, DOCKED_ACTIVITY_NAME);
-        mAmWmState.assertVisibility(DOCKED_ACTIVITY_NAME, true);
-        mAmWmState.assertVisibility(TEST_ACTIVITY_NAME, true);
-    }
-
-    public void testActivityLifeCycleOnResizeDockedStack() throws Exception {
-        if (!supportsSplitScreenMultiWindow()) {
-            CLog.logAndDisplay(LogLevel.INFO, "Skipping test: no split multi-window support");
-            return;
-        }
-
-        final String[] waitTestActivityName = new String[] {TEST_ACTIVITY_NAME};
-        launchActivity(TEST_ACTIVITY_NAME);
-        mAmWmState.computeState(mDevice, waitTestActivityName);
-        final Rectangle fullScreenBounds =
-                mAmWmState.getWmState().getStack(FULLSCREEN_WORKSPACE_STACK_ID).getBounds();
-
-        moveActivityToDockStack(TEST_ACTIVITY_NAME);
-        mAmWmState.computeState(mDevice, waitTestActivityName);
-        launchActivityInStack(NO_RELAUNCH_ACTIVITY_NAME, FULLSCREEN_WORKSPACE_STACK_ID);
-
-        mAmWmState.computeState(mDevice,
-                new String[]{TEST_ACTIVITY_NAME, NO_RELAUNCH_ACTIVITY_NAME});
-        final Rectangle initialDockBounds =
-                mAmWmState.getWmState().getStack(DOCKED_STACK_ID).getBounds();
-
-        final String logSeparator = clearLogcat();
-
-        Rectangle newBounds = computeNewDockBounds(fullScreenBounds, initialDockBounds, true);
-        resizeDockedStack(newBounds.width, newBounds.height, newBounds.width, newBounds.height);
-        mAmWmState.computeState(mDevice,
-                new String[]{TEST_ACTIVITY_NAME, NO_RELAUNCH_ACTIVITY_NAME});
-
-        // We resize twice to make sure we cross an orientation change threshold for both
-        // activities.
-        newBounds = computeNewDockBounds(fullScreenBounds, initialDockBounds, false);
-        resizeDockedStack(newBounds.width, newBounds.height, newBounds.width, newBounds.height);
-        mAmWmState.computeState(mDevice,
-                new String[]{TEST_ACTIVITY_NAME, NO_RELAUNCH_ACTIVITY_NAME});
-        assertActivityLifecycle(TEST_ACTIVITY_NAME, true /* relaunched */, logSeparator);
-        assertActivityLifecycle(NO_RELAUNCH_ACTIVITY_NAME, false /* relaunched */, logSeparator);
-    }
-
-    private Rectangle computeNewDockBounds(
-            Rectangle fullscreenBounds, Rectangle dockBounds, boolean reduceSize) {
-        final boolean inLandscape = fullscreenBounds.width > dockBounds.width;
-        // We are either increasing size or reducing it.
-        final float sizeChangeFactor = reduceSize ? 0.5f : 1.5f;
-        final Rectangle newBounds = new Rectangle(dockBounds);
-        if (inLandscape) {
-            // In landscape we change the width.
-            newBounds.width *= sizeChangeFactor;
-        } else {
-            // In portrait we change the height
-            newBounds.height *= sizeChangeFactor;
-        }
-
-        return newBounds;
-    }
-
-    public void testStackListOrderLaunchDockedActivity() throws Exception {
-        if (!supportsSplitScreenMultiWindow()) {
-            CLog.logAndDisplay(LogLevel.INFO, "Skipping test: no split multi-window support");
-            return;
-        }
-
-        launchActivityInDockStack(TEST_ACTIVITY_NAME);
-        mAmWmState.computeState(mDevice, new String[]{TEST_ACTIVITY_NAME});
-
-        final int homeStackIndex = mAmWmState.getStackPosition(HOME_STACK_ID);
-        final int recentsStackIndex = mAmWmState.getStackPosition(RECENTS_STACK_ID);
-        assertTrue("Recents stack should be on top of home stack",
-                recentsStackIndex < homeStackIndex);
-    }
-
-    private void launchActivityInDockStackAndMinimize(String activityName) throws Exception {
-        launchActivityInDockStack(activityName);
-        pressHomeButton();
-        waitForDockMinimized();
-    }
-
-    private void assertDockMinimized() {
-        assertTrue(mAmWmState.getWmState().isDockedStackMinimized());
-    }
-
-    private void assertDockNotMinimized() {
-        assertFalse(mAmWmState.getWmState().isDockedStackMinimized());
-    }
-
-    private void waitForDockMinimized() throws Exception {
-        mAmWmState.waitForWithWmState(mDevice, state -> state.isDockedStackMinimized(),
-                "***Waiting for Dock stack to be minimized");
-    }
-
-    private void waitForDockNotMinimized() throws Exception {
-        mAmWmState.waitForWithWmState(mDevice, state -> !state.isDockedStackMinimized(),
-                "***Waiting for Dock stack to not be minimized");
-    }
-}
diff --git a/hostsidetests/services/activityandwindowmanager/activitymanager/src/android/server/cts/ActivityManagerFreeformStackTests.java b/hostsidetests/services/activityandwindowmanager/activitymanager/src/android/server/cts/ActivityManagerFreeformStackTests.java
deleted file mode 100644
index a1d76c8..0000000
--- a/hostsidetests/services/activityandwindowmanager/activitymanager/src/android/server/cts/ActivityManagerFreeformStackTests.java
+++ /dev/null
@@ -1,126 +0,0 @@
-/*
- * Copyright (C) 2016 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License
- */
-
-package android.server.cts;
-
-import android.server.cts.ActivityManagerState.ActivityStack;
-import android.server.cts.ActivityManagerState.ActivityTask;
-
-import java.awt.Rectangle;
-import java.util.ArrayList;
-
-/**
- * Build: mmma -j32 cts/hostsidetests/services
- * Run: cts/hostsidetests/services/activityandwindowmanager/util/run-test CtsServicesHostTestCases android.server.cts.ActivityManagerFreeformStackTests
- */
-public class ActivityManagerFreeformStackTests extends ActivityManagerTestBase {
-
-    private static final String TEST_ACTIVITY = "TestActivity";
-    private static final int TEST_TASK_OFFSET = 20;
-    private static final int TEST_TASK_OFFSET_2 = 100;
-    private static final int TEST_TASK_SIZE_1 = 900;
-    private static final int TEST_TASK_SIZE_2 = TEST_TASK_SIZE_1 * 2;
-    private static final int TEST_TASK_SIZE_DP_1 = 220;
-    private static final int TEST_TASK_SIZE_DP_2 = TEST_TASK_SIZE_DP_1 * 2;
-
-    // NOTE: Launching the FreeformActivity will automatically launch the TestActivity
-    // with bounds (0, 0, 900, 900)
-    private static final String FREEFORM_ACTIVITY = "FreeformActivity";
-    private static final String NON_RESIZEABLE_ACTIVITY = "NonResizeableActivity";
-    private static final String NO_RELAUNCH_ACTIVITY = "NoRelaunchActivity";
-
-    public void testFreeformWindowManagementSupport() throws Exception {
-
-        launchActivityInStack(FREEFORM_ACTIVITY, FREEFORM_WORKSPACE_STACK_ID);
-
-        mAmWmState.computeState(mDevice, new String[] {FREEFORM_ACTIVITY, TEST_ACTIVITY});
-
-        if (!supportsFreeform()) {
-            mAmWmState.assertDoesNotContainStack(
-                    "Must not contain freeform stack.", FREEFORM_WORKSPACE_STACK_ID);
-            return;
-        }
-
-        mAmWmState.assertFrontStack(
-                "Freeform stack must be the front stack.", FREEFORM_WORKSPACE_STACK_ID);
-        mAmWmState.assertVisibility(FREEFORM_ACTIVITY, true);
-        mAmWmState.assertVisibility(TEST_ACTIVITY, true);
-        mAmWmState.assertFocusedActivity(
-                TEST_ACTIVITY + " must be focused Activity", TEST_ACTIVITY);
-        assertEquals(new Rectangle(0, 0, TEST_TASK_SIZE_1, TEST_TASK_SIZE_1),
-                mAmWmState.getAmState().getTaskByActivityName(TEST_ACTIVITY).getBounds());
-    }
-
-    public void testNonResizeableActivityHasFullDisplayBounds() throws Exception {
-        launchActivityInStack(NON_RESIZEABLE_ACTIVITY, FREEFORM_WORKSPACE_STACK_ID);
-
-        mAmWmState.computeState(mDevice, new String[] {NON_RESIZEABLE_ACTIVITY});
-
-        final ActivityTask task =
-                mAmWmState.getAmState().getTaskByActivityName(NON_RESIZEABLE_ACTIVITY);
-        final ActivityStack stack = mAmWmState.getAmState().getStackById(task.mStackId);
-
-        if (task.isFullscreen()) {
-            // If the task is on the fullscreen stack, then we know that it will have bounds that
-            // fill the entire display.
-            return;
-        }
-
-        // If the task is not on the fullscreen stack, then compare the task bounds to the display
-        // bounds.
-        assertEquals(mAmWmState.getWmState().getDisplay(stack.mDisplayId).getDisplayRect(),
-                task.getBounds());
-    }
-
-    public void testActivityLifeCycleOnResizeFreeformTask() throws Exception {
-        launchActivityInStack(TEST_ACTIVITY, FREEFORM_WORKSPACE_STACK_ID);
-        launchActivityInStack(NO_RELAUNCH_ACTIVITY, FREEFORM_WORKSPACE_STACK_ID);
-
-        mAmWmState.computeState(mDevice, new String[]{TEST_ACTIVITY, NO_RELAUNCH_ACTIVITY});
-
-        if (!supportsFreeform()) {
-            mAmWmState.assertDoesNotContainStack(
-                    "Must not contain freeform stack.", FREEFORM_WORKSPACE_STACK_ID);
-            return;
-        }
-
-        final int displayId = mAmWmState.getAmState().getStackById(
-                ActivityManagerTestBase.FREEFORM_WORKSPACE_STACK_ID).mDisplayId;
-        final int densityDpi =
-                mAmWmState.getWmState().getDisplay(displayId).getDpi();
-        final int testTaskSize1 =
-                ActivityAndWindowManagersState.dpToPx(TEST_TASK_SIZE_DP_1, densityDpi);
-        final int testTaskSize2 =
-                ActivityAndWindowManagersState.dpToPx(TEST_TASK_SIZE_DP_2, densityDpi);
-
-        resizeActivityTask(TEST_ACTIVITY,
-                TEST_TASK_OFFSET, TEST_TASK_OFFSET, testTaskSize1, testTaskSize2);
-        resizeActivityTask(NO_RELAUNCH_ACTIVITY,
-                TEST_TASK_OFFSET_2, TEST_TASK_OFFSET_2, testTaskSize1, testTaskSize2);
-
-        mAmWmState.computeState(mDevice, new String[]{TEST_ACTIVITY, NO_RELAUNCH_ACTIVITY});
-
-        final String logSeparator = clearLogcat();
-        resizeActivityTask(TEST_ACTIVITY,
-                TEST_TASK_OFFSET, TEST_TASK_OFFSET, testTaskSize2, testTaskSize1);
-        resizeActivityTask(NO_RELAUNCH_ACTIVITY,
-                TEST_TASK_OFFSET_2, TEST_TASK_OFFSET_2, testTaskSize2, testTaskSize1);
-        mAmWmState.computeState(mDevice, new String[]{TEST_ACTIVITY, NO_RELAUNCH_ACTIVITY});
-
-        assertActivityLifecycle(TEST_ACTIVITY, true /* relaunched */, logSeparator);
-        assertActivityLifecycle(NO_RELAUNCH_ACTIVITY, false /* relaunched */, logSeparator);
-    }
-}
diff --git a/hostsidetests/services/activityandwindowmanager/activitymanager/src/android/server/cts/ActivityManagerManifestLayoutTests.java b/hostsidetests/services/activityandwindowmanager/activitymanager/src/android/server/cts/ActivityManagerManifestLayoutTests.java
deleted file mode 100644
index e6df4b2..0000000
--- a/hostsidetests/services/activityandwindowmanager/activitymanager/src/android/server/cts/ActivityManagerManifestLayoutTests.java
+++ /dev/null
@@ -1,192 +0,0 @@
-/*
- * Copyright (C) 2016 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package android.server.cts;
-
-import java.lang.Exception;
-import java.lang.String;
-import java.util.ArrayList;
-import java.util.List;
-
-import junit.framework.Assert;
-
-import java.awt.Rectangle;
-import android.server.cts.WindowManagerState.WindowState;
-import android.server.cts.WindowManagerState.Display;
-
-import static android.server.cts.ActivityAndWindowManagersState.dpToPx;
-import static com.android.ddmlib.Log.LogLevel.INFO;
-
-import com.android.tradefed.log.LogUtil.CLog;
-
-/**
- * Build: mmma -j32 cts/hostsidetests/services
- * Run: cts/hostsidetests/services/activityandwindowmanager/util/run-test CtsServicesHostTestCases android.server.cts.ActivityManagerManifestLayoutTests
- */
-public class ActivityManagerManifestLayoutTests extends ActivityManagerTestBase {
-
-    // Test parameters
-    private static final int DEFAULT_WIDTH_DP = 240;
-    private static final int DEFAULT_HEIGHT_DP = 160;
-    private static final float DEFAULT_WIDTH_FRACTION = 0.25f;
-    private static final float DEFAULT_HEIGHT_FRACTION = 0.35f;
-    private static final int MIN_WIDTH_DP = 100;
-    private static final int MIN_HEIGHT_DP = 80;
-
-    private static final int GRAVITY_VER_CENTER = 0x01;
-    private static final int GRAVITY_VER_TOP    = 0x02;
-    private static final int GRAVITY_VER_BOTTOM = 0x04;
-    private static final int GRAVITY_HOR_CENTER = 0x10;
-    private static final int GRAVITY_HOR_LEFT   = 0x20;
-    private static final int GRAVITY_HOR_RIGHT  = 0x40;
-
-    private List<WindowState> mTempWindowList = new ArrayList();
-    private Display mDisplay;
-    private WindowState mWindowState;
-
-    public void testGravityAndDefaultSizeTopLeft() throws Exception {
-        testLayout(GRAVITY_VER_TOP, GRAVITY_HOR_LEFT, false /*fraction*/);
-    }
-
-    public void testGravityAndDefaultSizeTopRight() throws Exception {
-        testLayout(GRAVITY_VER_TOP, GRAVITY_HOR_RIGHT, true /*fraction*/);
-    }
-
-    public void testGravityAndDefaultSizeBottomLeft() throws Exception {
-        testLayout(GRAVITY_VER_BOTTOM, GRAVITY_HOR_LEFT, true /*fraction*/);
-    }
-
-    public void testGravityAndDefaultSizeBottomRight() throws Exception {
-        testLayout(GRAVITY_VER_BOTTOM, GRAVITY_HOR_RIGHT, false /*fraction*/);
-    }
-
-    public void testMinimalSizeFreeform() throws Exception {
-        if (!supportsFreeform()) {
-            CLog.logAndDisplay(INFO, "Skipping test: no freeform support");
-            return;
-        }
-        testMinimalSize(FREEFORM_WORKSPACE_STACK_ID);
-    }
-
-    public void testMinimalSizeDocked() throws Exception {
-        if (!supportsSplitScreenMultiWindow()) {
-            CLog.logAndDisplay(INFO, "Skipping test: no multi-window support");
-            return;
-        }
-        testMinimalSize(DOCKED_STACK_ID);
-    }
-
-    private void testMinimalSize(int stackId) throws Exception {
-        final String activityName = "BottomRightLayoutActivity";
-
-        // Issue command to resize to <0,0,1,1>. We expect the size to be floored at
-        // MIN_WIDTH_DPxMIN_HEIGHT_DP.
-        if (stackId == FREEFORM_WORKSPACE_STACK_ID) {
-            launchActivityInStack(activityName, stackId);
-            resizeActivityTask(activityName, 0, 0, 1, 1);
-        } else { // stackId == DOCKED_STACK_ID
-            launchActivityInDockStack(activityName);
-            resizeDockedStack(1, 1, 1, 1);
-        }
-        getDisplayAndWindowState(activityName, false);
-
-        final int minWidth = dpToPx(MIN_WIDTH_DP, mDisplay.getDpi());
-        final int minHeight = dpToPx(MIN_HEIGHT_DP, mDisplay.getDpi());
-        final Rectangle containingRect = mWindowState.getContainingFrame();
-
-        Assert.assertEquals("Min width is incorrect", minWidth, containingRect.width);
-        Assert.assertEquals("Min height is incorrect", minHeight, containingRect.height);
-    }
-
-    private void testLayout(
-            int vGravity, int hGravity, boolean fraction) throws Exception {
-        if (!supportsFreeform()) {
-            CLog.logAndDisplay(INFO, "Skipping test: no freeform support");
-            return;
-        }
-
-        final String activityName = (vGravity == GRAVITY_VER_TOP ? "Top" : "Bottom")
-                + (hGravity == GRAVITY_HOR_LEFT ? "Left" : "Right") + "LayoutActivity";
-
-        // Launch in freeform stack
-        launchActivityInStack(activityName, FREEFORM_WORKSPACE_STACK_ID);
-
-        getDisplayAndWindowState(activityName, true);
-
-        final Rectangle containingRect = mWindowState.getContainingFrame();
-        final Rectangle appRect = mDisplay.getAppRect();
-        final int expectedWidthPx, expectedHeightPx;
-        // Evaluate the expected window size in px. If we're using fraction dimensions,
-        // calculate the size based on the app rect size. Otherwise, convert the expected
-        // size in dp to px.
-        if (fraction) {
-            expectedWidthPx = (int) (appRect.width * DEFAULT_WIDTH_FRACTION);
-            expectedHeightPx = (int) (appRect.height * DEFAULT_HEIGHT_FRACTION);
-        } else {
-            final int densityDpi = mDisplay.getDpi();
-            expectedWidthPx = dpToPx(DEFAULT_WIDTH_DP, densityDpi);
-            expectedHeightPx = dpToPx(DEFAULT_HEIGHT_DP, densityDpi);
-        }
-
-        verifyFrameSizeAndPosition(
-                vGravity, hGravity, expectedWidthPx, expectedHeightPx, containingRect, appRect);
-    }
-
-    private void getDisplayAndWindowState(String activityName, boolean checkFocus)
-            throws Exception {
-        final String windowName = getWindowName(activityName);
-
-        mAmWmState.computeState(mDevice, new String[] {activityName});
-
-        if (checkFocus) {
-            mAmWmState.assertFocusedWindow("Test window must be the front window.", windowName);
-        } else {
-            mAmWmState.assertVisibility(activityName, true);
-        }
-
-        mAmWmState.getWmState().getMatchingVisibleWindowState(windowName, mTempWindowList);
-
-        Assert.assertEquals("Should have exactly one window state for the activity.",
-                1, mTempWindowList.size());
-
-        mWindowState = mTempWindowList.get(0);
-        Assert.assertNotNull("Should have a valid window", mWindowState);
-
-        mDisplay = mAmWmState.getWmState().getDisplay(mWindowState.getDisplayId());
-        Assert.assertNotNull("Should be on a display", mDisplay);
-    }
-
-    private void verifyFrameSizeAndPosition(
-            int vGravity, int hGravity, int expectedWidthPx, int expectedHeightPx,
-            Rectangle containingFrame, Rectangle parentFrame) {
-        Assert.assertEquals("Width is incorrect", expectedWidthPx, containingFrame.width);
-        Assert.assertEquals("Height is incorrect", expectedHeightPx, containingFrame.height);
-
-        if (vGravity == GRAVITY_VER_TOP) {
-            Assert.assertEquals("Should be on the top", parentFrame.y, containingFrame.y);
-        } else if (vGravity == GRAVITY_VER_BOTTOM) {
-            Assert.assertEquals("Should be on the bottom",
-                    parentFrame.y + parentFrame.height, containingFrame.y + containingFrame.height);
-        }
-
-        if (hGravity == GRAVITY_HOR_LEFT) {
-            Assert.assertEquals("Should be on the left", parentFrame.x, containingFrame.x);
-        } else if (hGravity == GRAVITY_HOR_RIGHT){
-            Assert.assertEquals("Should be on the right",
-                    parentFrame.x + parentFrame.width, containingFrame.x + containingFrame.width);
-        }
-    }
-}
diff --git a/hostsidetests/services/activityandwindowmanager/activitymanager/src/android/server/cts/ActivityManagerPinnedStackTests.java b/hostsidetests/services/activityandwindowmanager/activitymanager/src/android/server/cts/ActivityManagerPinnedStackTests.java
deleted file mode 100644
index 5f63580..0000000
--- a/hostsidetests/services/activityandwindowmanager/activitymanager/src/android/server/cts/ActivityManagerPinnedStackTests.java
+++ /dev/null
@@ -1,1338 +0,0 @@
-/*
- * Copyright (C) 2016 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License
- */
-
-package android.server.cts;
-
-import static android.server.cts.ActivityAndWindowManagersState.DEFAULT_DISPLAY_ID;
-import static android.server.cts.ActivityManagerState.STATE_STOPPED;
-
-import android.server.cts.ActivityManagerState.ActivityStack;
-import android.server.cts.ActivityManagerState.ActivityTask;
-
-import java.awt.Rectangle;
-import java.lang.Exception;
-import java.lang.String;
-import java.util.ArrayList;
-import java.util.List;
-import java.util.regex.Matcher;
-import java.util.regex.Pattern;
-
-/**
- * Build: mmma -j32 cts/hostsidetests/services
- * Run: cts/hostsidetests/services/activityandwindowmanager/util/run-test CtsServicesHostTestCases android.server.cts.ActivityManagerPinnedStackTests
- */
-public class ActivityManagerPinnedStackTests extends ActivityManagerTestBase {
-    private static final String TEST_ACTIVITY = "TestActivity";
-    private static final String TEST_ACTIVITY_WITH_SAME_AFFINITY = "TestActivityWithSameAffinity";
-    private static final String TRANSLUCENT_TEST_ACTIVITY = "TranslucentTestActivity";
-    private static final String NON_RESIZEABLE_ACTIVITY = "NonResizeableActivity";
-    private static final String RESUME_WHILE_PAUSING_ACTIVITY = "ResumeWhilePausingActivity";
-    private static final String PIP_ACTIVITY = "PipActivity";
-    private static final String PIP_ACTIVITY2 = "PipActivity2";
-    private static final String PIP_ACTIVITY_WITH_SAME_AFFINITY = "PipActivityWithSameAffinity";
-    private static final String ALWAYS_FOCUSABLE_PIP_ACTIVITY = "AlwaysFocusablePipActivity";
-    private static final String LAUNCH_INTO_PINNED_STACK_PIP_ACTIVITY =
-            "LaunchIntoPinnedStackPipActivity";
-    private static final String LAUNCH_ENTER_PIP_ACTIVITY = "LaunchEnterPipActivity";
-    private static final String PIP_ON_STOP_ACTIVITY = "PipOnStopActivity";
-
-    private static final String EXTRA_FIXED_ORIENTATION = "fixed_orientation";
-    private static final String EXTRA_ENTER_PIP = "enter_pip";
-    private static final String EXTRA_ENTER_PIP_ASPECT_RATIO_NUMERATOR =
-            "enter_pip_aspect_ratio_numerator";
-    private static final String EXTRA_ENTER_PIP_ASPECT_RATIO_DENOMINATOR =
-            "enter_pip_aspect_ratio_denominator";
-    private static final String EXTRA_SET_ASPECT_RATIO_NUMERATOR = "set_aspect_ratio_numerator";
-    private static final String EXTRA_SET_ASPECT_RATIO_DENOMINATOR = "set_aspect_ratio_denominator";
-    private static final String EXTRA_SET_ASPECT_RATIO_WITH_DELAY_NUMERATOR =
-            "set_aspect_ratio_with_delay_numerator";
-    private static final String EXTRA_SET_ASPECT_RATIO_WITH_DELAY_DENOMINATOR =
-            "set_aspect_ratio_with_delay_denominator";
-    private static final String EXTRA_ENTER_PIP_ON_PAUSE = "enter_pip_on_pause";
-    private static final String EXTRA_TAP_TO_FINISH = "tap_to_finish";
-    private static final String EXTRA_START_ACTIVITY = "start_activity";
-    private static final String EXTRA_FINISH_SELF_ON_RESUME = "finish_self_on_resume";
-    private static final String EXTRA_REENTER_PIP_ON_EXIT = "reenter_pip_on_exit";
-    private static final String EXTRA_ASSERT_NO_ON_STOP_BEFORE_PIP = "assert_no_on_stop_before_pip";
-    private static final String EXTRA_ON_PAUSE_DELAY = "on_pause_delay";
-
-    private static final String PIP_ACTIVITY_ACTION_ENTER_PIP =
-            "android.server.cts.PipActivity.enter_pip";
-    private static final String PIP_ACTIVITY_ACTION_MOVE_TO_BACK =
-            "android.server.cts.PipActivity.move_to_back";
-    private static final String PIP_ACTIVITY_ACTION_EXPAND_PIP =
-            "android.server.cts.PipActivity.expand_pip";
-    private static final String PIP_ACTIVITY_ACTION_SET_REQUESTED_ORIENTATION =
-            "android.server.cts.PipActivity.set_requested_orientation";
-    private static final String PIP_ACTIVITY_ACTION_FINISH =
-            "android.server.cts.PipActivity.finish";
-    private static final String TEST_ACTIVITY_ACTION_FINISH =
-            "android.server.cts.TestActivity.finish_self";
-
-    private static final String APP_OPS_OP_ENTER_PICTURE_IN_PICTURE = "PICTURE_IN_PICTURE";
-    private static final int APP_OPS_MODE_ALLOWED = 0;
-    private static final int APP_OPS_MODE_IGNORED = 1;
-    private static final int APP_OPS_MODE_ERRORED = 2;
-
-    private static final int ROTATION_0 = 0;
-    private static final int ROTATION_90 = 1;
-    private static final int ROTATION_180 = 2;
-    private static final int ROTATION_270 = 3;
-
-    // Corresponds to ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE
-    private static final int ORIENTATION_LANDSCAPE = 0;
-    // Corresponds to ActivityInfo.SCREEN_ORIENTATION_PORTRAIT
-    private static final int ORIENTATION_PORTRAIT = 1;
-
-    private static final float FLOAT_COMPARE_EPSILON = 0.005f;
-
-    // Corresponds to com.android.internal.R.dimen.config_pictureInPictureMinAspectRatio
-    private static final int MIN_ASPECT_RATIO_NUMERATOR = 100;
-    private static final int MIN_ASPECT_RATIO_DENOMINATOR = 239;
-    private static final int BELOW_MIN_ASPECT_RATIO_DENOMINATOR = MIN_ASPECT_RATIO_DENOMINATOR + 1;
-    // Corresponds to com.android.internal.R.dimen.config_pictureInPictureMaxAspectRatio
-    private static final int MAX_ASPECT_RATIO_NUMERATOR = 239;
-    private static final int MAX_ASPECT_RATIO_DENOMINATOR = 100;
-    private static final int ABOVE_MAX_ASPECT_RATIO_NUMERATOR = MAX_ASPECT_RATIO_NUMERATOR + 1;
-
-    public void testMinimumDeviceSize() throws Exception {
-        if (!supportsPip()) return;
-
-        mAmWmState.assertDeviceDefaultDisplaySize(mDevice,
-                "Devices supporting picture-in-picture must be larger than the default minimum"
-                        + " task size");
-    }
-
-    public void testEnterPictureInPictureMode() throws Exception {
-        pinnedStackTester(getAmStartCmd(PIP_ACTIVITY, EXTRA_ENTER_PIP, "true"), PIP_ACTIVITY,
-                false /* moveTopToPinnedStack */, false /* isFocusable */);
-    }
-
-    public void testMoveTopActivityToPinnedStack() throws Exception {
-        pinnedStackTester(getAmStartCmd(PIP_ACTIVITY), PIP_ACTIVITY,
-                true /* moveTopToPinnedStack */, false /* isFocusable */);
-    }
-
-    public void testAlwaysFocusablePipActivity() throws Exception {
-        pinnedStackTester(getAmStartCmd(ALWAYS_FOCUSABLE_PIP_ACTIVITY),
-                ALWAYS_FOCUSABLE_PIP_ACTIVITY, false /* moveTopToPinnedStack */,
-                true /* isFocusable */);
-    }
-
-    public void testLaunchIntoPinnedStack() throws Exception {
-        pinnedStackTester(getAmStartCmd(LAUNCH_INTO_PINNED_STACK_PIP_ACTIVITY),
-                ALWAYS_FOCUSABLE_PIP_ACTIVITY, false /* moveTopToPinnedStack */,
-                true /* isFocusable */);
-    }
-
-    public void testNonTappablePipActivity() throws Exception {
-        if (!supportsPip()) return;
-
-        // Launch the tap-to-finish activity at a specific place
-        launchActivity(PIP_ACTIVITY,
-                EXTRA_ENTER_PIP, "true",
-                EXTRA_TAP_TO_FINISH, "true");
-        mAmWmState.waitForValidState(mDevice, PIP_ACTIVITY, PINNED_STACK_ID);
-        assertPinnedStackExists();
-
-        // Tap the screen at a known location in the pinned stack bounds, and ensure that it is
-        // not passed down to the top task
-        tapToFinishPip();
-        mAmWmState.computeState(mDevice, new String[] {PIP_ACTIVITY},
-                false /* compareTaskAndStackBounds */);
-        mAmWmState.assertVisibility(PIP_ACTIVITY, true);
-    }
-
-    public void testPinnedStackDefaultBounds() throws Exception {
-        if (!supportsPip()) return;
-
-        // Launch a PIP activity
-        launchActivity(PIP_ACTIVITY, EXTRA_ENTER_PIP, "true");
-
-        setDeviceRotation(ROTATION_0);
-        WindowManagerState wmState = mAmWmState.getWmState();
-        wmState.computeState(mDevice);
-        Rectangle defaultPipBounds = wmState.getDefaultPinnedStackBounds();
-        Rectangle stableBounds = wmState.getStableBounds();
-        assertTrue(defaultPipBounds.width > 0 && defaultPipBounds.height > 0);
-        assertTrue(stableBounds.contains(defaultPipBounds));
-
-        setDeviceRotation(ROTATION_90);
-        wmState = mAmWmState.getWmState();
-        wmState.computeState(mDevice);
-        defaultPipBounds = wmState.getDefaultPinnedStackBounds();
-        stableBounds = wmState.getStableBounds();
-        assertTrue(defaultPipBounds.width > 0 && defaultPipBounds.height > 0);
-        assertTrue(stableBounds.contains(defaultPipBounds));
-        setDeviceRotation(ROTATION_0);
-    }
-
-    public void testPinnedStackMovementBounds() throws Exception {
-        if (!supportsPip()) return;
-
-        // Launch a PIP activity
-        launchActivity(PIP_ACTIVITY, EXTRA_ENTER_PIP, "true");
-
-        setDeviceRotation(ROTATION_0);
-        WindowManagerState wmState = mAmWmState.getWmState();
-        wmState.computeState(mDevice);
-        Rectangle pipMovementBounds = wmState.getPinnedStackMomentBounds();
-        Rectangle stableBounds = wmState.getStableBounds();
-        assertTrue(pipMovementBounds.width > 0 && pipMovementBounds.height > 0);
-        assertTrue(stableBounds.contains(pipMovementBounds));
-
-        setDeviceRotation(ROTATION_90);
-        wmState = mAmWmState.getWmState();
-        wmState.computeState(mDevice);
-        pipMovementBounds = wmState.getPinnedStackMomentBounds();
-        stableBounds = wmState.getStableBounds();
-        assertTrue(pipMovementBounds.width > 0 && pipMovementBounds.height > 0);
-        assertTrue(stableBounds.contains(pipMovementBounds));
-        setDeviceRotation(ROTATION_0);
-    }
-
-    public void testPinnedStackOutOfBoundsInsetsNonNegative() throws Exception {
-        if (!supportsPip()) return;
-
-        final WindowManagerState wmState = mAmWmState.getWmState();
-
-        // Launch an activity into the pinned stack
-        launchActivity(PIP_ACTIVITY,
-                EXTRA_ENTER_PIP, "true",
-                EXTRA_TAP_TO_FINISH, "true");
-        mAmWmState.waitForValidState(mDevice, PIP_ACTIVITY, PINNED_STACK_ID);
-
-        // Get the display dimensions
-        WindowManagerState.WindowState windowState = getWindowState(PIP_ACTIVITY);
-        WindowManagerState.Display display = wmState.getDisplay(windowState.getDisplayId());
-        Rectangle displayRect = display.getDisplayRect();
-
-        // Move the pinned stack offscreen
-        String moveStackOffscreenCommand = String.format("am stack resize 4 %d %d %d %d",
-                displayRect.width - 200, 0, displayRect.width + 200, 500);
-        executeShellCommand(moveStackOffscreenCommand);
-
-        // Ensure that the surface insets are not negative
-        windowState = getWindowState(PIP_ACTIVITY);
-        Rectangle contentInsets = windowState.getContentInsets();
-        assertTrue(contentInsets.x >= 0 && contentInsets.y >= 0 && contentInsets.width >= 0 &&
-                contentInsets.height >= 0);
-    }
-
-    public void testPinnedStackInBoundsAfterRotation() throws Exception {
-        if (!supportsPip()) return;
-
-        // Launch an activity into the pinned stack
-        launchActivity(PIP_ACTIVITY,
-                EXTRA_ENTER_PIP, "true",
-                EXTRA_TAP_TO_FINISH, "true");
-        mAmWmState.waitForValidState(mDevice, PIP_ACTIVITY, PINNED_STACK_ID);
-
-        // Ensure that the PIP stack is fully visible in each orientation
-        setDeviceRotation(ROTATION_0);
-        assertPinnedStackActivityIsInDisplayBounds(PIP_ACTIVITY);
-        setDeviceRotation(ROTATION_90);
-        assertPinnedStackActivityIsInDisplayBounds(PIP_ACTIVITY);
-        setDeviceRotation(ROTATION_180);
-        assertPinnedStackActivityIsInDisplayBounds(PIP_ACTIVITY);
-        setDeviceRotation(ROTATION_270);
-        assertPinnedStackActivityIsInDisplayBounds(PIP_ACTIVITY);
-        setDeviceRotation(ROTATION_0);
-    }
-
-    public void testEnterPipToOtherOrientation() throws Exception {
-        if (!supportsPip()) return;
-
-        // Launch a portrait only app on the fullscreen stack
-        launchActivity(TEST_ACTIVITY,
-                EXTRA_FIXED_ORIENTATION, String.valueOf(ORIENTATION_PORTRAIT));
-        // Launch the PiP activity fixed as landscape
-        launchActivity(PIP_ACTIVITY,
-                EXTRA_FIXED_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 " + PIP_ACTIVITY_ACTION_ENTER_PIP);
-        mAmWmState.waitForValidState(mDevice, PIP_ACTIVITY, PINNED_STACK_ID);
-        assertPinnedStackExists();
-        assertPinnedStackActivityIsInDisplayBounds(PIP_ACTIVITY);
-    }
-
-    public void testEnterPipAspectRatioMin() throws Exception {
-        testEnterPipAspectRatio(MIN_ASPECT_RATIO_NUMERATOR, MIN_ASPECT_RATIO_DENOMINATOR);
-    }
-
-    public void testEnterPipAspectRatioMax() throws Exception {
-        testEnterPipAspectRatio(MAX_ASPECT_RATIO_NUMERATOR, MAX_ASPECT_RATIO_DENOMINATOR);
-    }
-
-    private void testEnterPipAspectRatio(int num, int denom) throws Exception {
-        if (!supportsPip()) return;
-
-        launchActivity(PIP_ACTIVITY,
-                EXTRA_ENTER_PIP, "true",
-                EXTRA_ENTER_PIP_ASPECT_RATIO_NUMERATOR, Integer.toString(num),
-                EXTRA_ENTER_PIP_ASPECT_RATIO_DENOMINATOR, Integer.toString(denom));
-        assertPinnedStackExists();
-
-        // Assert that we have entered PIP and that the aspect ratio is correct
-        Rectangle pinnedStackBounds =
-                mAmWmState.getAmState().getStackById(PINNED_STACK_ID).getBounds();
-        assertTrue(floatEquals((float) pinnedStackBounds.width / pinnedStackBounds.height,
-                (float) num / denom));
-    }
-
-    public void testResizePipAspectRatioMin() throws Exception {
-        testResizePipAspectRatio(MIN_ASPECT_RATIO_NUMERATOR, MIN_ASPECT_RATIO_DENOMINATOR);
-    }
-
-    public void testResizePipAspectRatioMax() throws Exception {
-        testResizePipAspectRatio(MAX_ASPECT_RATIO_NUMERATOR, MAX_ASPECT_RATIO_DENOMINATOR);
-    }
-
-    private void testResizePipAspectRatio(int num, int denom) throws Exception {
-        if (!supportsPip()) return;
-
-        launchActivity(PIP_ACTIVITY,
-                EXTRA_ENTER_PIP, "true",
-                EXTRA_SET_ASPECT_RATIO_NUMERATOR, Integer.toString(num),
-                EXTRA_SET_ASPECT_RATIO_DENOMINATOR, Integer.toString(denom));
-        assertPinnedStackExists();
-
-        // Hacky, but we need to wait for the enterPictureInPicture animation to complete and
-        // the resize to be called before we can check the pinned stack bounds
-        final boolean[] result = new boolean[1];
-        mAmWmState.waitForWithAmState(mDevice, (state) -> {
-            Rectangle pinnedStackBounds = state.getStackById(PINNED_STACK_ID).getBounds();
-            boolean isValidAspectRatio = floatEquals(
-                    (float) pinnedStackBounds.width / pinnedStackBounds.height,
-                    (float) num / denom);
-            result[0] = isValidAspectRatio;
-            return isValidAspectRatio;
-        }, "Waiting for pinned stack to be resized");
-        assertTrue(result[0]);
-    }
-
-    public void testEnterPipExtremeAspectRatioMin() throws Exception {
-        testEnterPipExtremeAspectRatio(MIN_ASPECT_RATIO_NUMERATOR,
-                BELOW_MIN_ASPECT_RATIO_DENOMINATOR);
-    }
-
-    public void testEnterPipExtremeAspectRatioMax() throws Exception {
-        testEnterPipExtremeAspectRatio(ABOVE_MAX_ASPECT_RATIO_NUMERATOR,
-                MAX_ASPECT_RATIO_DENOMINATOR);
-    }
-
-    private void testEnterPipExtremeAspectRatio(int num, int denom) throws Exception {
-        if (!supportsPip()) return;
-
-        // Assert that we could not create a pinned stack with an extreme aspect ratio
-        launchActivity(PIP_ACTIVITY,
-                EXTRA_ENTER_PIP, "true",
-                EXTRA_ENTER_PIP_ASPECT_RATIO_NUMERATOR, Integer.toString(num),
-                EXTRA_ENTER_PIP_ASPECT_RATIO_DENOMINATOR, Integer.toString(denom));
-        assertPinnedStackDoesNotExist();
-    }
-
-    public void testSetPipExtremeAspectRatioMin() throws Exception {
-        testSetPipExtremeAspectRatio(MIN_ASPECT_RATIO_NUMERATOR,
-                BELOW_MIN_ASPECT_RATIO_DENOMINATOR);
-    }
-
-    public void testSetPipExtremeAspectRatioMax() throws Exception {
-        testSetPipExtremeAspectRatio(ABOVE_MAX_ASPECT_RATIO_NUMERATOR,
-                MAX_ASPECT_RATIO_DENOMINATOR);
-    }
-
-    private void testSetPipExtremeAspectRatio(int num, int denom) throws Exception {
-        if (!supportsPip()) return;
-
-        // Try to resize the a normal pinned stack to an extreme aspect ratio and ensure that
-        // fails (the aspect ratio remains the same)
-        launchActivity(PIP_ACTIVITY,
-                EXTRA_ENTER_PIP, "true",
-                EXTRA_ENTER_PIP_ASPECT_RATIO_NUMERATOR,
-                        Integer.toString(MAX_ASPECT_RATIO_NUMERATOR),
-                EXTRA_ENTER_PIP_ASPECT_RATIO_DENOMINATOR,
-                        Integer.toString(MAX_ASPECT_RATIO_DENOMINATOR),
-                EXTRA_SET_ASPECT_RATIO_NUMERATOR, Integer.toString(num),
-                EXTRA_SET_ASPECT_RATIO_DENOMINATOR, Integer.toString(denom));
-        assertPinnedStackExists();
-        Rectangle pinnedStackBounds =
-                mAmWmState.getAmState().getStackById(PINNED_STACK_ID).getBounds();
-        assertTrue(floatEquals((float) pinnedStackBounds.width / pinnedStackBounds.height,
-                (float) MAX_ASPECT_RATIO_NUMERATOR / MAX_ASPECT_RATIO_DENOMINATOR));
-    }
-
-    public void testDisallowPipLaunchFromStoppedActivity() throws Exception {
-        if (!supportsPip()) return;
-
-        // Launch the bottom pip activity
-        launchActivity(PIP_ON_STOP_ACTIVITY);
-        mAmWmState.waitForValidState(mDevice, PIP_ACTIVITY, PINNED_STACK_ID);
-
-        // Wait for the bottom pip activity to be stopped
-        mAmWmState.waitForActivityState(mDevice, PIP_ON_STOP_ACTIVITY, STATE_STOPPED);
-
-        // Assert that there is no pinned stack (that enterPictureInPicture() failed)
-        assertPinnedStackDoesNotExist();
-    }
-
-    public void testAutoEnterPictureInPicture() throws Exception {
-        if (!supportsPip()) return;
-
-        // Launch a test activity so that we're not over home
-        launchActivity(TEST_ACTIVITY);
-
-        // Launch the PIP activity on pause
-        launchActivity(PIP_ACTIVITY, EXTRA_ENTER_PIP_ON_PAUSE, "true");
-        assertPinnedStackDoesNotExist();
-
-        // Go home and ensure that there is a pinned stack
-        launchHomeActivity();
-        assertPinnedStackExists();
-    }
-
-    public void testAutoEnterPictureInPictureLaunchActivity() throws Exception {
-        if (!supportsPip()) return;
-
-        // Launch a test activity so that we're not over home
-        launchActivity(TEST_ACTIVITY);
-
-        // Launch the PIP activity on pause, and have it start another activity on
-        // top of itself.  Wait for the new activity to be visible and ensure that the pinned stack
-        // was not created in the process
-        launchActivity(PIP_ACTIVITY,
-                EXTRA_ENTER_PIP_ON_PAUSE, "true",
-                EXTRA_START_ACTIVITY, getActivityComponentName(NON_RESIZEABLE_ACTIVITY));
-        mAmWmState.computeState(mDevice, new String[] {NON_RESIZEABLE_ACTIVITY},
-                false /* compareTaskAndStackBounds */);
-        assertPinnedStackDoesNotExist();
-
-        // Go home while the pip activity is open and ensure the previous activity is not PIPed
-        launchHomeActivity();
-        assertPinnedStackDoesNotExist();
-    }
-
-    public void testAutoEnterPictureInPictureFinish() throws Exception {
-        if (!supportsPip()) return;
-
-        // Launch a test activity so that we're not over home
-        launchActivity(TEST_ACTIVITY);
-
-        // Launch the PIP activity on pause, and set it to finish itself after
-        // some period.  Wait for the previous activity to be visible, and ensure that the pinned
-        // stack was not created in the process
-        launchActivity(PIP_ACTIVITY,
-                EXTRA_ENTER_PIP_ON_PAUSE, "true",
-                EXTRA_FINISH_SELF_ON_RESUME, "true");
-        assertPinnedStackDoesNotExist();
-    }
-
-    public void testAutoEnterPictureInPictureAspectRatio() throws Exception {
-        if (!supportsPip()) return;
-
-        // Launch the PIP activity on pause, and set the aspect ratio
-        launchActivity(PIP_ACTIVITY,
-                EXTRA_ENTER_PIP_ON_PAUSE, "true",
-                EXTRA_SET_ASPECT_RATIO_NUMERATOR, Integer.toString(MAX_ASPECT_RATIO_NUMERATOR),
-                EXTRA_SET_ASPECT_RATIO_DENOMINATOR, Integer.toString(MAX_ASPECT_RATIO_DENOMINATOR));
-
-        // Go home while the pip activity is open to trigger auto-PIP
-        launchHomeActivity();
-        assertPinnedStackExists();
-
-        // Hacky, but we need to wait for the auto-enter picture-in-picture animation to complete
-        // and before we can check the pinned stack bounds
-        final boolean[] result = new boolean[1];
-        mAmWmState.waitForWithAmState(mDevice, (state) -> {
-            Rectangle pinnedStackBounds = state.getStackById(PINNED_STACK_ID).getBounds();
-            boolean isValidAspectRatio = floatEquals(
-                    (float) pinnedStackBounds.width / pinnedStackBounds.height,
-                    (float) MAX_ASPECT_RATIO_NUMERATOR / MAX_ASPECT_RATIO_DENOMINATOR);
-            result[0] = isValidAspectRatio;
-            return isValidAspectRatio;
-        }, "Waiting for pinned stack to be resized");
-        assertTrue(result[0]);
-    }
-
-    public void testAutoEnterPictureInPictureOverPip() throws Exception {
-        if (!supportsPip()) return;
-
-        // Launch another PIP activity
-        launchActivity(LAUNCH_INTO_PINNED_STACK_PIP_ACTIVITY);
-        mAmWmState.waitForValidState(mDevice, PIP_ACTIVITY, PINNED_STACK_ID);
-        assertPinnedStackExists();
-
-        // Launch the PIP activity on pause
-        launchActivity(PIP_ACTIVITY, EXTRA_ENTER_PIP_ON_PAUSE, "true");
-
-        // Go home while the PIP activity is open to trigger auto-enter PIP
-        launchHomeActivity();
-        assertPinnedStackExists();
-
-        // Ensure that auto-enter pip failed and that the resumed activity in the pinned stack is
-        // still the first activity
-        final ActivityStack pinnedStack = mAmWmState.getAmState().getStackById(PINNED_STACK_ID);
-        assertTrue(pinnedStack.getTasks().size() == 1);
-        assertTrue(pinnedStack.getTasks().get(0).mRealActivity.equals(getActivityComponentName(
-                ALWAYS_FOCUSABLE_PIP_ACTIVITY)));
-    }
-
-    public void testDisallowMultipleTasksInPinnedStack() throws Exception {
-        if (!supportsPip()) return;
-
-        // Launch a test activity so that we have multiple fullscreen tasks
-        launchActivity(TEST_ACTIVITY);
-
-        // Launch first PIP activity
-        launchActivity(PIP_ACTIVITY, EXTRA_ENTER_PIP, "true");
-
-        // Launch second PIP activity
-        launchActivity(PIP_ACTIVITY2, EXTRA_ENTER_PIP, "true");
-
-        final ActivityStack pinnedStack = mAmWmState.getAmState().getStackById(PINNED_STACK_ID);
-        assertEquals(1, pinnedStack.getTasks().size());
-
-        assertTrue(pinnedStack.getTasks().get(0).mRealActivity.equals(getActivityComponentName(
-                PIP_ACTIVITY2)));
-
-        final ActivityStack fullScreenStack = mAmWmState.getAmState().getStackById(
-                FULLSCREEN_WORKSPACE_STACK_ID);
-        assertTrue(fullScreenStack.getBottomTask().mRealActivity.equals(getActivityComponentName(
-                PIP_ACTIVITY)));
-    }
-
-    public void testPipUnPipOverHome() throws Exception {
-        if (!supportsPip()) return;
-
-        // Go home
-        launchHomeActivity();
-        // Launch an auto pip activity
-        launchActivity(PIP_ACTIVITY,
-                EXTRA_ENTER_PIP, "true",
-                EXTRA_REENTER_PIP_ON_EXIT, "true");
-        assertPinnedStackExists();
-
-        // Relaunch the activity to fullscreen to trigger the activity to exit and re-enter pip
-        launchActivity(PIP_ACTIVITY);
-        mAmWmState.waitForWithAmState(mDevice, (amState) -> {
-            return amState.getFrontStackId(DEFAULT_DISPLAY_ID) == FULLSCREEN_WORKSPACE_STACK_ID;
-        }, "Waiting for PIP to exit to fullscreen");
-        mAmWmState.waitForWithAmState(mDevice, (amState) -> {
-            return amState.getFrontStackId(DEFAULT_DISPLAY_ID) == PINNED_STACK_ID;
-        }, "Waiting to re-enter PIP");
-        mAmWmState.assertFocusedStack("Expected home stack focused", HOME_STACK_ID);
-    }
-
-    public void testPipUnPipOverApp() throws Exception {
-        if (!supportsPip()) return;
-
-        // Launch a test activity so that we're not over home
-        launchActivity(TEST_ACTIVITY);
-
-        // Launch an auto pip activity
-        launchActivity(PIP_ACTIVITY,
-                EXTRA_ENTER_PIP, "true",
-                EXTRA_REENTER_PIP_ON_EXIT, "true");
-        assertPinnedStackExists();
-
-        // Relaunch the activity to fullscreen to trigger the activity to exit and re-enter pip
-        launchActivity(PIP_ACTIVITY);
-        mAmWmState.waitForWithAmState(mDevice, (amState) -> {
-            return amState.getFrontStackId(DEFAULT_DISPLAY_ID) == FULLSCREEN_WORKSPACE_STACK_ID;
-        }, "Waiting for PIP to exit to fullscreen");
-        mAmWmState.waitForWithAmState(mDevice, (amState) -> {
-            return amState.getFrontStackId(DEFAULT_DISPLAY_ID) == PINNED_STACK_ID;
-        }, "Waiting to re-enter PIP");
-        mAmWmState.assertFocusedStack("Expected fullscreen stack focused",
-                FULLSCREEN_WORKSPACE_STACK_ID);
-    }
-
-    public void testRemovePipWithNoFullscreenStack() throws Exception {
-        if (!supportsPip()) return;
-
-        // Start with a clean slate, remove all the stacks but home
-        removeStacks(ALL_STACK_IDS_BUT_HOME);
-
-        // Launch a pip activity
-        launchActivity(PIP_ACTIVITY, EXTRA_ENTER_PIP, "true");
-        assertPinnedStackExists();
-
-        // Remove the stack and ensure that the task is now in the fullscreen stack (when no
-        // fullscreen stack existed before)
-        removeStacks(PINNED_STACK_ID);
-        assertPinnedStackStateOnMoveToFullscreen(PIP_ACTIVITY, HOME_STACK_ID,
-                true /* expectTopTaskHasActivity */, true /* expectBottomTaskHasActivity */);
-    }
-
-    public void testRemovePipWithVisibleFullscreenStack() throws Exception {
-        if (!supportsPip()) return;
-
-        // Launch a fullscreen activity, and a pip activity over that
-        launchActivity(TEST_ACTIVITY);
-        launchActivity(PIP_ACTIVITY, EXTRA_ENTER_PIP, "true");
-        assertPinnedStackExists();
-
-        // Remove the stack and ensure that the task is placed in the fullscreen stack, behind the
-        // top fullscreen activity
-        removeStacks(PINNED_STACK_ID);
-        assertPinnedStackStateOnMoveToFullscreen(PIP_ACTIVITY, FULLSCREEN_WORKSPACE_STACK_ID,
-                false /* expectTopTaskHasActivity */, true /* expectBottomTaskHasActivity */);
-    }
-
-    public void testRemovePipWithHiddenFullscreenStack() throws Exception {
-        if (!supportsPip()) return;
-
-        // Launch a fullscreen activity, return home and while the fullscreen stack is hidden,
-        // launch a pip activity over home
-        launchActivity(TEST_ACTIVITY);
-        launchHomeActivity();
-        launchActivity(PIP_ACTIVITY, EXTRA_ENTER_PIP, "true");
-        assertPinnedStackExists();
-
-        // 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
-        removeStacks(PINNED_STACK_ID);
-        assertPinnedStackStateOnMoveToFullscreen(PIP_ACTIVITY, HOME_STACK_ID,
-                false /* expectTopTaskHasActivity */, true /* expectBottomTaskHasActivity */);
-    }
-
-    public void testMovePipToBackWithNoFullscreenStack() throws Exception {
-        if (!supportsPip()) return;
-
-        // Start with a clean slate, remove all the stacks but home
-        removeStacks(ALL_STACK_IDS_BUT_HOME);
-
-        // Launch a pip activity
-        launchActivity(PIP_ACTIVITY, EXTRA_ENTER_PIP, "true");
-        assertPinnedStackExists();
-
-        // Remove the stack and ensure that the task is now in the fullscreen stack (when no
-        // fullscreen stack existed before)
-        executeShellCommand("am broadcast -a " + PIP_ACTIVITY_ACTION_MOVE_TO_BACK);
-        assertPinnedStackStateOnMoveToFullscreen(PIP_ACTIVITY, HOME_STACK_ID,
-                false /* expectTopTaskHasActivity */, true /* expectBottomTaskHasActivity */);
-    }
-
-    public void testMovePipToBackWithVisibleFullscreenStack() throws Exception {
-        if (!supportsPip()) return;
-
-        // Launch a fullscreen activity, and a pip activity over that
-        launchActivity(TEST_ACTIVITY);
-        launchActivity(PIP_ACTIVITY, EXTRA_ENTER_PIP, "true");
-        assertPinnedStackExists();
-
-        // Remove the stack and ensure that the task is placed in the fullscreen stack, behind the
-        // top fullscreen activity
-        executeShellCommand("am broadcast -a " + PIP_ACTIVITY_ACTION_MOVE_TO_BACK);
-        assertPinnedStackStateOnMoveToFullscreen(PIP_ACTIVITY, FULLSCREEN_WORKSPACE_STACK_ID,
-                false /* expectTopTaskHasActivity */, true /* expectBottomTaskHasActivity */);
-    }
-
-    public void testMovePipToBackWithHiddenFullscreenStack() throws Exception {
-        if (!supportsPip()) return;
-
-        // Launch a fullscreen activity, return home and while the fullscreen stack is hidden,
-        // launch a pip activity over home
-        launchActivity(TEST_ACTIVITY);
-        launchHomeActivity();
-        launchActivity(PIP_ACTIVITY, EXTRA_ENTER_PIP, "true");
-        assertPinnedStackExists();
-
-        // 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 " + PIP_ACTIVITY_ACTION_MOVE_TO_BACK);
-        assertPinnedStackStateOnMoveToFullscreen(PIP_ACTIVITY, HOME_STACK_ID,
-                false /* expectTopTaskHasActivity */, true /* expectBottomTaskHasActivity */);
-    }
-
-    public void testPinnedStackAlwaysOnTop() throws Exception {
-        if (!supportsPip()) return;
-
-        // Launch activity into pinned stack and assert it's on top.
-        launchActivity(PIP_ACTIVITY, EXTRA_ENTER_PIP, "true");
-        assertPinnedStackExists();
-        assertPinnedStackIsOnTop();
-
-        // Launch another activity in fullscreen stack and check that pinned stack is still on top.
-        launchActivity(TEST_ACTIVITY);
-        assertPinnedStackExists();
-        assertPinnedStackIsOnTop();
-
-        // Launch home and check that pinned stack is still on top.
-        launchHomeActivity();
-        assertPinnedStackExists();
-        assertPinnedStackIsOnTop();
-    }
-
-    public void testAppOpsDenyPipOnPause() throws Exception {
-        if (!supportsPip()) return;
-
-        // Disable enter-pip and try to enter pip
-        setAppOpsOpToMode(ActivityManagerTestBase.componentName,
-                APP_OPS_OP_ENTER_PICTURE_IN_PICTURE, APP_OPS_MODE_IGNORED);
-
-        // Launch the PIP activity on pause
-        launchActivity(PIP_ACTIVITY, EXTRA_ENTER_PIP, "true");
-        assertPinnedStackDoesNotExist();
-
-        // Go home and ensure that there is no pinned stack
-        launchHomeActivity();
-        assertPinnedStackDoesNotExist();
-
-        // Re-enable enter-pip-on-hide
-        setAppOpsOpToMode(ActivityManagerTestBase.componentName,
-                APP_OPS_OP_ENTER_PICTURE_IN_PICTURE, APP_OPS_MODE_ALLOWED);
-    }
-
-    public void testEnterPipFromTaskWithMultipleActivities() throws Exception {
-        if (!supportsPip()) return;
-
-        // Try to enter picture-in-picture from an activity that has more than one activity in the
-        // task and ensure that it works
-        launchActivity(LAUNCH_ENTER_PIP_ACTIVITY);
-        mAmWmState.waitForValidState(mDevice, PIP_ACTIVITY, PINNED_STACK_ID);
-        assertPinnedStackExists();
-    }
-
-    public void testEnterPipWithResumeWhilePausingActivityNoStop() throws Exception {
-        if (!supportsPip()) return;
-
-        /*
-         * Launch the resumeWhilePausing activity and ensure that the PiP activity did not get
-         * stopped and actually went into the pinned stack.
-         *
-         * Note that this is a workaround because to trigger the path that we want to happen in
-         * activity manager, we need to add the leaving activity to the stopping state, which only
-         * happens when a hidden stack is brought forward. Normally, this happens when you go home,
-         * 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
-         *    fullscreen stack
-         * 4) Bring the activity in the dynamic stack forward to trigger PiP
-         */
-        int stackId = launchActivityInNewDynamicStack(RESUME_WHILE_PAUSING_ACTIVITY);
-        resizeStack(stackId, 0, 0, 500, 500);
-        // 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)
-        launchActivityInStack(PIP_ACTIVITY, FULLSCREEN_WORKSPACE_STACK_ID,
-                EXTRA_ENTER_PIP_ON_PAUSE, "true",
-                EXTRA_ON_PAUSE_DELAY, "350",
-                EXTRA_ASSERT_NO_ON_STOP_BEFORE_PIP, "true");
-        launchActivity(RESUME_WHILE_PAUSING_ACTIVITY);
-        assertPinnedStackExists();
-    }
-
-    public void testDisallowEnterPipActivityLocked() throws Exception {
-        if (!supportsPip()) return;
-
-        launchActivity(PIP_ACTIVITY, EXTRA_ENTER_PIP_ON_PAUSE, "true");
-        ActivityTask task =
-                mAmWmState.getAmState().getStackById(FULLSCREEN_WORKSPACE_STACK_ID).getTopTask();
-
-        // 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 " + PIP_ACTIVITY_ACTION_ENTER_PIP);
-        mAmWmState.waitForValidState(mDevice, PIP_ACTIVITY, PINNED_STACK_ID);
-        assertPinnedStackDoesNotExist();
-        launchHomeActivity();
-        assertPinnedStackDoesNotExist();
-        executeShellCommand("am task lock stop");
-    }
-
-    public void testConfigurationChangeOrderDuringTransition() throws Exception {
-        if (!supportsPip()) return;
-
-        // Launch a PiP activity and ensure configuration change only happened once, and that the
-        // configuration change happened after the picture-in-picture and multi-window callbacks
-        launchActivity(PIP_ACTIVITY);
-        String logSeparator = clearLogcat();
-        executeShellCommand("am broadcast -a " + PIP_ACTIVITY_ACTION_ENTER_PIP);
-        mAmWmState.waitForValidState(mDevice, PIP_ACTIVITY, PINNED_STACK_ID);
-        assertPinnedStackExists();
-        waitForValidPictureInPictureCallbacks(PIP_ACTIVITY, logSeparator);
-        assertValidPictureInPictureCallbackOrder(PIP_ACTIVITY, logSeparator);
-
-        // Trigger it to go back to fullscreen and ensure that only triggered one configuration
-        // change as well
-        logSeparator = clearLogcat();
-        launchActivity(PIP_ACTIVITY);
-        waitForValidPictureInPictureCallbacks(PIP_ACTIVITY, logSeparator);
-        assertValidPictureInPictureCallbackOrder(PIP_ACTIVITY, logSeparator);
-    }
-
-    public void testEnterPipInterruptedCallbacks() throws Exception {
-        if (!supportsPip()) return;
-
-        // Slow down the transition animations for this test
-        setWindowTransitionAnimationDurationScale(20);
-
-        // Launch a PiP activity
-        launchActivity(PIP_ACTIVITY, EXTRA_ENTER_PIP, "true");
-        // Wait until the PiP activity has moved into the pinned stack (happens before the
-        // transition has started)
-        mAmWmState.waitForValidState(mDevice, PIP_ACTIVITY, PINNED_STACK_ID);
-        assertPinnedStackExists();
-
-        // Relaunch the PiP activity back into fullscreen
-        String logSeparator = clearLogcat();
-        launchActivity(PIP_ACTIVITY);
-        // Wait until the PiP activity is reparented into the fullscreen stack (happens after the
-        // transition has finished)
-        mAmWmState.waitForValidState(mDevice, PIP_ACTIVITY, FULLSCREEN_WORKSPACE_STACK_ID);
-
-        // Ensure that we get the callbacks indicating that PiP/MW mode was cancelled, but no
-        // configuration change (since none was sent)
-        final ActivityLifecycleCounts lifecycleCounts = new ActivityLifecycleCounts(
-                PIP_ACTIVITY, logSeparator);
-        assertTrue(lifecycleCounts.mConfigurationChangedCount == 0);
-        assertTrue(lifecycleCounts.mPictureInPictureModeChangedCount == 1);
-        assertTrue(lifecycleCounts.mMultiWindowModeChangedCount == 1);
-
-        // Reset the animation scale
-        setWindowTransitionAnimationDurationScale(1);
-    }
-
-    public void testStopBeforeMultiWindowCallbacksOnDismiss() throws Exception {
-        if (!supportsPip()) return;
-
-        // Launch a PiP activity
-        launchActivity(PIP_ACTIVITY, EXTRA_ENTER_PIP, "true");
-        assertPinnedStackExists();
-
-        // Dismiss it
-        String logSeparator = clearLogcat();
-        removeStacks(PINNED_STACK_ID);
-        mAmWmState.waitForValidState(mDevice, PIP_ACTIVITY, FULLSCREEN_WORKSPACE_STACK_ID);
-
-        // Confirm that we get stop before the multi-window and picture-in-picture mode change
-        // callbacks
-        final ActivityLifecycleCounts lifecycleCounts = new ActivityLifecycleCounts(PIP_ACTIVITY,
-                logSeparator);
-        if (lifecycleCounts.mStopCount != 1) {
-            fail(PIP_ACTIVITY + " has received " + lifecycleCounts.mStopCount
-                    + " onStop() calls, expecting 1");
-        } else if (lifecycleCounts.mPictureInPictureModeChangedCount != 1) {
-            fail(PIP_ACTIVITY + " has received " + lifecycleCounts.mPictureInPictureModeChangedCount
-                    + " onPictureInPictureModeChanged() calls, expecting 1");
-        } else if (lifecycleCounts.mMultiWindowModeChangedCount != 1) {
-            fail(PIP_ACTIVITY + " has received " + lifecycleCounts.mMultiWindowModeChangedCount
-                    + " onMultiWindowModeChanged() calls, expecting 1");
-        } else {
-            int lastStopLine = lifecycleCounts.mLastStopLineIndex;
-            int lastPipLine = lifecycleCounts.mLastPictureInPictureModeChangedLineIndex;
-            int lastMwLine = lifecycleCounts.mLastMultiWindowModeChangedLineIndex;
-            if (!(lastStopLine < lastPipLine && lastPipLine < lastMwLine)) {
-                fail(PIP_ACTIVITY + " has received callbacks in unexpected order.  Expected:"
-                        + " stop < pip < mw, but got line indices: " + lastStopLine + ", "
-                        + lastPipLine + ", " + lastMwLine + " respectively");
-            }
-        }
-    }
-
-    public void testPreventSetAspectRatioWhileExpanding() throws Exception {
-        if (!supportsPip()) return;
-
-        // Launch the PiP activity
-        launchActivity(PIP_ACTIVITY, EXTRA_ENTER_PIP, "true");
-
-        // 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 " + PIP_ACTIVITY_ACTION_EXPAND_PIP
-                + " -e " + EXTRA_SET_ASPECT_RATIO_WITH_DELAY_NUMERATOR + " 123456789"
-                + " -e " + EXTRA_SET_ASPECT_RATIO_WITH_DELAY_DENOMINATOR + " 100000000");
-        mAmWmState.waitForValidState(mDevice, PIP_ACTIVITY, FULLSCREEN_WORKSPACE_STACK_ID);
-        assertPinnedStackDoesNotExist();
-    }
-
-    public void testSetRequestedOrientationWhilePinned() throws Exception {
-        if (!supportsPip()) return;
-
-        // Launch the PiP activity fixed as portrait, and enter picture-in-picture
-        launchActivity(PIP_ACTIVITY,
-                EXTRA_FIXED_ORIENTATION, String.valueOf(ORIENTATION_PORTRAIT),
-                EXTRA_ENTER_PIP, "true");
-        assertPinnedStackExists();
-
-        // Request that the orientation is set to landscape
-        executeShellCommand("am broadcast -a "
-                + PIP_ACTIVITY_ACTION_SET_REQUESTED_ORIENTATION + " -e "
-                + EXTRA_FIXED_ORIENTATION + " " + String.valueOf(ORIENTATION_LANDSCAPE));
-
-        // Launch the activity back into fullscreen and ensure that it is now in landscape
-        launchActivity(PIP_ACTIVITY);
-        mAmWmState.waitForValidState(mDevice, PIP_ACTIVITY, FULLSCREEN_WORKSPACE_STACK_ID);
-        assertPinnedStackDoesNotExist();
-        assertTrue(mAmWmState.getWmState().getLastOrientation() == ORIENTATION_LANDSCAPE);
-    }
-
-    public void testWindowButtonEntersPip() throws Exception {
-        if (!supportsPip()) return;
-
-        // Launch the PiP activity trigger the window button, ensure that we have entered PiP
-        launchActivity(PIP_ACTIVITY);
-        pressWindowButton();
-        mAmWmState.waitForValidState(mDevice, PIP_ACTIVITY, PINNED_STACK_ID);
-        assertPinnedStackExists();
-    }
-
-    public void testFinishPipActivityWithTaskOverlay() throws Exception {
-        if (!supportsPip()) return;
-
-        // Launch PiP activity
-        launchActivity(PIP_ACTIVITY, EXTRA_ENTER_PIP, "true");
-        assertPinnedStackExists();
-        int taskId = mAmWmState.getAmState().getStackById(PINNED_STACK_ID).getTopTask().mTaskId;
-
-        // Ensure that we don't any any other overlays as a result of launching into PIP
-        launchHomeActivity();
-
-        // Launch task overlay activity into PiP activity task
-        launchActivityAsTaskOverlay(TRANSLUCENT_TEST_ACTIVITY, taskId, PINNED_STACK_ID);
-
-        // Finish the PiP activity and ensure that there is no pinned stack
-        executeShellCommand("am broadcast -a " + PIP_ACTIVITY_ACTION_FINISH);
-        mAmWmState.waitForWithAmState(mDevice, (amState) -> {
-            ActivityStack stack = amState.getStackById(PINNED_STACK_ID);
-            return stack == null;
-        }, "Waiting for pinned stack to be removed...");
-        assertPinnedStackDoesNotExist();
-    }
-
-    public void testNoResumeAfterTaskOverlayFinishes() throws Exception {
-        if (!supportsPip()) return;
-
-        // Launch PiP activity
-        launchActivity(PIP_ACTIVITY, EXTRA_ENTER_PIP, "true");
-        assertPinnedStackExists();
-        int taskId = mAmWmState.getAmState().getStackById(PINNED_STACK_ID).getTopTask().mTaskId;
-
-        // Launch task overlay activity into PiP activity task
-        launchActivityAsTaskOverlay(TRANSLUCENT_TEST_ACTIVITY, taskId, PINNED_STACK_ID);
-
-        // Finish the task overlay activity while animating and ensure that the PiP activity never
-        // got resumed
-        String logSeparator = clearLogcat();
-        executeShellCommand("am stack resize-animated 4 20 20 500 500");
-        executeShellCommand("am broadcast -a " + TEST_ACTIVITY_ACTION_FINISH);
-        mAmWmState.waitFor(mDevice, (amState, wmState) -> !amState.containsActivity(
-                TRANSLUCENT_TEST_ACTIVITY), "Waiting for test activity to finish...");
-        final ActivityLifecycleCounts lifecycleCounts = new ActivityLifecycleCounts(PIP_ACTIVITY,
-                logSeparator);
-        assertTrue(lifecycleCounts.mResumeCount == 0);
-        assertTrue(lifecycleCounts.mPauseCount == 0);
-    }
-
-    public void testPinnedStackWithDockedStack() throws Exception {
-        if (!supportsPip() || !supportsSplitScreenMultiWindow()) return;
-
-        launchActivity(PIP_ACTIVITY, EXTRA_ENTER_PIP, "true");
-        launchActivityInDockStack(LAUNCHING_ACTIVITY);
-        launchActivityToSide(true, false, TEST_ACTIVITY);
-        mAmWmState.assertVisibility(PIP_ACTIVITY, true);
-        mAmWmState.assertVisibility(LAUNCHING_ACTIVITY, true);
-        mAmWmState.assertVisibility(TEST_ACTIVITY, true);
-
-        // Launch the activities again to take focus and make sure nothing is hidden
-        launchActivityInDockStack(LAUNCHING_ACTIVITY);
-        mAmWmState.assertVisibility(LAUNCHING_ACTIVITY, true);
-        mAmWmState.assertVisibility(TEST_ACTIVITY, true);
-
-        launchActivityToSide(true, false, TEST_ACTIVITY);
-        mAmWmState.assertVisibility(LAUNCHING_ACTIVITY, true);
-        mAmWmState.assertVisibility(TEST_ACTIVITY, true);
-
-        // Go to recents to make sure that fullscreen stack is invisible
-        // Some devices do not support recents or implement it differently (instead of using a
-        // separate stack id or as an activity), for those cases the visibility asserts will be
-        // ignored
-        pressAppSwitchButton();
-        if (mAmWmState.waitForRecentsActivityVisible(mDevice)) {
-            mAmWmState.assertVisibility(LAUNCHING_ACTIVITY, true);
-            mAmWmState.assertVisibility(TEST_ACTIVITY, false);
-        }
-    }
-
-    public void testLaunchTaskByComponentMatchMultipleTasks() throws Exception {
-        if (!supportsPip()) return;
-
-        // Launch a fullscreen activity which will launch a PiP activity in a new task with the same
-        // affinity
-        launchActivity(TEST_ACTIVITY_WITH_SAME_AFFINITY);
-        launchActivity(PIP_ACTIVITY_WITH_SAME_AFFINITY);
-        assertPinnedStackExists();
-
-        // Launch the root activity again...
-        int rootActivityTaskId = mAmWmState.getAmState().getTaskByActivityName(
-                TEST_ACTIVITY_WITH_SAME_AFFINITY).mTaskId;
-        launchHomeActivity();
-        launchActivity(TEST_ACTIVITY_WITH_SAME_AFFINITY);
-
-        // ...and ensure that the root activity task is found and reused, and that the pinned stack
-        // is unaffected
-        assertPinnedStackExists();
-        mAmWmState.assertFocusedActivity("Expected root activity focused",
-                TEST_ACTIVITY_WITH_SAME_AFFINITY);
-        assertTrue(rootActivityTaskId == mAmWmState.getAmState().getTaskByActivityName(
-                TEST_ACTIVITY_WITH_SAME_AFFINITY).mTaskId);
-    }
-
-    public void testLaunchTaskByAffinityMatchMultipleTasks() throws Exception {
-        if (!supportsPip()) return;
-
-        // Launch a fullscreen activity which will launch a PiP activity in a new task with the same
-        // affinity, and also launch another activity in the same task, while finishing itself. As
-        // a result, the task will not have a component matching the same activity as what it was
-        // started with
-        launchActivity(TEST_ACTIVITY_WITH_SAME_AFFINITY,
-                EXTRA_START_ACTIVITY, getActivityComponentName(TEST_ACTIVITY),
-                EXTRA_FINISH_SELF_ON_RESUME, "true");
-        mAmWmState.waitForValidState(mDevice, TEST_ACTIVITY, FULLSCREEN_WORKSPACE_STACK_ID);
-        launchActivity(PIP_ACTIVITY_WITH_SAME_AFFINITY);
-        mAmWmState.waitForValidState(mDevice, PIP_ACTIVITY_WITH_SAME_AFFINITY, PINNED_STACK_ID);
-        assertPinnedStackExists();
-
-        // Launch the root activity again...
-        int rootActivityTaskId = mAmWmState.getAmState().getTaskByActivityName(
-                TEST_ACTIVITY).mTaskId;
-        launchHomeActivity();
-        launchActivity(TEST_ACTIVITY_WITH_SAME_AFFINITY);
-
-        // ...and ensure that even while matching purely by task affinity, the root activity task is
-        // found and reused, and that the pinned stack is unaffected
-        assertPinnedStackExists();
-        mAmWmState.assertFocusedActivity("Expected root activity focused", TEST_ACTIVITY);
-        assertTrue(rootActivityTaskId == mAmWmState.getAmState().getTaskByActivityName(
-                TEST_ACTIVITY).mTaskId);
-    }
-
-    public void testLaunchTaskByAffinityMatchSingleTask() throws Exception {
-        if (!supportsPip()) return;
-
-        // Launch an activity into the pinned stack with a fixed affinity
-        launchActivity(TEST_ACTIVITY_WITH_SAME_AFFINITY,
-                EXTRA_ENTER_PIP, "true",
-                EXTRA_START_ACTIVITY, getActivityComponentName(PIP_ACTIVITY),
-                EXTRA_FINISH_SELF_ON_RESUME, "true");
-        mAmWmState.waitForValidState(mDevice, PIP_ACTIVITY, PINNED_STACK_ID);
-        assertPinnedStackExists();
-
-        // Launch the root activity again, of the matching task and ensure that we expand to
-        // fullscreen
-        int activityTaskId = mAmWmState.getAmState().getTaskByActivityName(
-                PIP_ACTIVITY).mTaskId;
-        launchHomeActivity();
-        launchActivity(TEST_ACTIVITY_WITH_SAME_AFFINITY);
-        mAmWmState.waitForValidState(mDevice, PIP_ACTIVITY, FULLSCREEN_WORKSPACE_STACK_ID);
-        assertPinnedStackDoesNotExist();
-        assertTrue(activityTaskId == mAmWmState.getAmState().getTaskByActivityName(
-                PIP_ACTIVITY).mTaskId);
-    }
-
-    /** Test that reported display size corresponds to fullscreen after exiting PiP. */
-    public void testDisplayMetricsPinUnpin() throws Exception {
-        if (!supportsPip()) return;
-
-        String logSeparator = clearLogcat();
-        launchActivity(TEST_ACTIVITY);
-        final int defaultDisplayStackId = mAmWmState.getAmState().getFocusedStackId();
-        final ReportedSizes initialSizes = getLastReportedSizesForActivity(TEST_ACTIVITY,
-                logSeparator);
-        final Rectangle initialAppBounds = readAppBounds(TEST_ACTIVITY, logSeparator);
-        assertNotNull("Must report display dimensions", initialSizes);
-        assertNotNull("Must report app bounds", initialAppBounds);
-
-        logSeparator = clearLogcat();
-        launchActivity(PIP_ACTIVITY, EXTRA_ENTER_PIP, "true");
-        mAmWmState.waitForValidState(mDevice, PIP_ACTIVITY, PINNED_STACK_ID);
-        final ReportedSizes pinnedSizes = getLastReportedSizesForActivity(PIP_ACTIVITY,
-                logSeparator);
-        final Rectangle pinnedAppBounds = readAppBounds(PIP_ACTIVITY, logSeparator);
-        assertFalse("Reported display size when pinned must be different from default",
-                initialSizes.equals(pinnedSizes));
-        assertFalse("Reported app bounds when pinned must be different from default",
-                initialAppBounds.width == pinnedAppBounds.width
-                        && initialAppBounds.height == pinnedAppBounds.height);
-
-        logSeparator = clearLogcat();
-        launchActivityInStack(PIP_ACTIVITY, defaultDisplayStackId);
-        final ReportedSizes finalSizes = getLastReportedSizesForActivity(PIP_ACTIVITY,
-                logSeparator);
-        final Rectangle finalAppBounds = readAppBounds(PIP_ACTIVITY, logSeparator);
-        assertEquals("Must report default size after exiting PiP", initialSizes, finalSizes);
-        assertEquals("Must report default app width after exiting PiP", initialAppBounds.width,
-                finalAppBounds.width);
-        assertEquals("Must report default app height after exiting PiP", initialAppBounds.height,
-                finalAppBounds.height);
-    }
-
-    private static final Pattern sAppBoundsPattern = Pattern.compile(
-            "(.+)appBounds=Rect\\((\\d+), (\\d+) - (\\d+), (\\d+)\\)(.*)");
-
-    /** Read app bounds in last applied configuration from logs. */
-    private Rectangle readAppBounds(String activityName, String logSeparator) throws Exception {
-        final String[] lines = getDeviceLogsForComponent(activityName, logSeparator);
-        for (int i = lines.length - 1; i >= 0; i--) {
-            final String line = lines[i].trim();
-            final Matcher matcher = sAppBoundsPattern.matcher(line);
-            if (matcher.matches()) {
-                final int left = Integer.parseInt(matcher.group(2));
-                final int top = Integer.parseInt(matcher.group(3));
-                final int right = Integer.parseInt(matcher.group(4));
-                final int bottom = Integer.parseInt(matcher.group(5));
-                return new Rectangle(left, top, right - left, bottom - top);
-            }
-        }
-        return null;
-    }
-
-    /**
-     * Called after the given {@param activityName} has been moved to the fullscreen stack. Ensures
-     * that the {@param focusedStackId} is focused, and checks the top and/or bottom tasks in the
-     * fullscreen stack if {@param expectTopTaskHasActivity} or {@param expectBottomTaskHasActivity}
-     * are set respectively.
-     */
-    private void assertPinnedStackStateOnMoveToFullscreen(String activityName, int focusedStackId,
-            boolean expectTopTaskHasActivity, boolean expectBottomTaskHasActivity)
-                    throws Exception {
-        mAmWmState.waitForFocusedStack(mDevice, focusedStackId);
-        mAmWmState.assertFocusedStack("Wrong focused stack", focusedStackId);
-        mAmWmState.waitForActivityState(mDevice, activityName, STATE_STOPPED);
-        assertTrue(mAmWmState.getAmState().hasActivityState(activityName, STATE_STOPPED));
-        assertPinnedStackDoesNotExist();
-
-        if (expectTopTaskHasActivity) {
-            ActivityTask topTask = mAmWmState.getAmState().getStackById(
-                    FULLSCREEN_WORKSPACE_STACK_ID).getTopTask();
-            assertTrue(topTask.containsActivity(ActivityManagerTestBase.getActivityComponentName(
-                    activityName)));
-        }
-        if (expectBottomTaskHasActivity) {
-            ActivityTask bottomTask = mAmWmState.getAmState().getStackById(
-                    FULLSCREEN_WORKSPACE_STACK_ID).getBottomTask();
-            assertTrue(bottomTask.containsActivity(ActivityManagerTestBase.getActivityComponentName(
-                    activityName)));
-        }
-    }
-
-    /**
-     * Asserts that the pinned stack bounds does not intersect with the IME bounds.
-     */
-    private void assertPinnedStackDoesNotIntersectIME() throws Exception {
-        // Ensure that the IME is visible
-        WindowManagerState wmState = mAmWmState.getWmState();
-        wmState.computeState(mDevice);
-        WindowManagerState.WindowState imeWinState = wmState.getInputMethodWindowState();
-        assertTrue(imeWinState != null);
-
-        // Ensure that the PIP movement is constrained by the display bounds intersecting the
-        // non-IME bounds
-        Rectangle imeContentFrame = imeWinState.getContentFrame();
-        Rectangle imeContentInsets = imeWinState.getGivenContentInsets();
-        Rectangle imeBounds = new Rectangle(imeContentFrame.x + imeContentInsets.x,
-                imeContentFrame.y + imeContentInsets.y,
-                imeContentFrame.width - imeContentInsets.width,
-                imeContentFrame.height - imeContentInsets.height);
-        wmState.computeState(mDevice);
-        Rectangle pipMovementBounds = wmState.getPinnedStackMomentBounds();
-        assertTrue(!pipMovementBounds.intersects(imeBounds));
-    }
-
-    /**
-     * Asserts that the pinned stack bounds is contained in the display bounds.
-     */
-    private void assertPinnedStackActivityIsInDisplayBounds(String activity) throws Exception {
-        final WindowManagerState.WindowState windowState = getWindowState(activity);
-        final WindowManagerState.Display display = mAmWmState.getWmState().getDisplay(
-                windowState.getDisplayId());
-        final Rectangle displayRect = display.getDisplayRect();
-        final Rectangle pinnedStackBounds =
-                mAmWmState.getAmState().getStackById(PINNED_STACK_ID).getBounds();
-        assertTrue(displayRect.contains(pinnedStackBounds));
-    }
-
-    /**
-     * Asserts that the pinned stack exists.
-     */
-    private void assertPinnedStackExists() throws Exception {
-        mAmWmState.assertContainsStack("Must contain pinned stack.", PINNED_STACK_ID);
-    }
-
-    /**
-     * Asserts that the pinned stack does not exist.
-     */
-    private void assertPinnedStackDoesNotExist() throws Exception {
-        mAmWmState.assertDoesNotContainStack("Must not contain pinned stack.", PINNED_STACK_ID);
-    }
-
-    /**
-     * Asserts that the pinned stack is the front stack.
-     */
-    private void assertPinnedStackIsOnTop() throws Exception {
-        mAmWmState.assertFrontStack("Pinned stack must always be on top.", PINNED_STACK_ID);
-    }
-
-    /**
-     * Asserts that the activity received exactly one of each of the callbacks when entering and
-     * exiting picture-in-picture.
-     */
-    private void assertValidPictureInPictureCallbackOrder(String activityName, String logSeparator)
-            throws Exception {
-        final ActivityLifecycleCounts lifecycleCounts = new ActivityLifecycleCounts(activityName,
-                logSeparator);
-
-        if (lifecycleCounts.mConfigurationChangedCount != 1) {
-            fail(activityName + " has received " + lifecycleCounts.mConfigurationChangedCount
-                    + " onConfigurationChanged() calls, expecting 1");
-        } else if (lifecycleCounts.mPictureInPictureModeChangedCount != 1) {
-            fail(activityName + " has received " + lifecycleCounts.mPictureInPictureModeChangedCount
-                    + " onPictureInPictureModeChanged() calls, expecting 1");
-        } else if (lifecycleCounts.mMultiWindowModeChangedCount != 1) {
-            fail(activityName + " has received " + lifecycleCounts.mMultiWindowModeChangedCount
-                    + " onMultiWindowModeChanged() calls, expecting 1");
-        } else {
-            int lastPipLine = lifecycleCounts.mLastPictureInPictureModeChangedLineIndex;
-            int lastMwLine = lifecycleCounts.mLastMultiWindowModeChangedLineIndex;
-            int lastConfigLine = lifecycleCounts.mLastConfigurationChangedLineIndex;
-            if (!(lastPipLine < lastMwLine && lastMwLine < lastConfigLine)) {
-                fail(activityName + " has received callbacks in unexpected order.  Expected:"
-                        + " pip < mw < config change, but got line indices: " + lastPipLine + ", "
-                        + lastMwLine + ", " + lastConfigLine + " respectively");
-            }
-        }
-    }
-
-    /**
-     * Waits until the expected picture-in-picture callbacks have been made.
-     */
-    private void waitForValidPictureInPictureCallbacks(String activityName, String logSeparator)
-            throws Exception {
-        mAmWmState.waitFor(mDevice, (amState, wmState) -> {
-            try {
-                final ActivityLifecycleCounts lifecycleCounts = new ActivityLifecycleCounts(
-                        activityName, logSeparator);
-                return lifecycleCounts.mConfigurationChangedCount == 1 &&
-                        lifecycleCounts.mPictureInPictureModeChangedCount == 1 &&
-                        lifecycleCounts.mMultiWindowModeChangedCount == 1;
-            } catch (Exception e) {
-                return false;
-            }
-        }, "Waiting for picture-in-picture activity callbacks...");
-    }
-
-    /**
-     * @return the window state for the given {@param activity}'s window.
-     */
-    private WindowManagerState.WindowState getWindowState(String activity) throws Exception {
-        String windowName = getWindowName(activity);
-        mAmWmState.computeState(mDevice, new String[] {activity});
-        final List<WindowManagerState.WindowState> tempWindowList = new ArrayList<>();
-        mAmWmState.getWmState().getMatchingVisibleWindowState(windowName, tempWindowList);
-        return tempWindowList.get(0);
-    }
-
-    /**
-     * Compares two floats with a common epsilon.
-     */
-    private boolean floatEquals(float f1, float f2) {
-        return Math.abs(f1 - f2) < FLOAT_COMPARE_EPSILON;
-    }
-
-    /**
-     * Triggers a tap over the pinned stack bounds to trigger the PIP to close.
-     */
-    private void tapToFinishPip() throws Exception {
-        Rectangle pinnedStackBounds =
-                mAmWmState.getAmState().getStackById(PINNED_STACK_ID).getBounds();
-        int tapX = pinnedStackBounds.x + pinnedStackBounds.width - 100;
-        int tapY = pinnedStackBounds.y + pinnedStackBounds.height - 100;
-        executeShellCommand(String.format("input tap %d %d", tapX, tapY));
-    }
-
-    /**
-     * Launches the given {@param activityName} into the {@param taskId} as a task overlay.
-     */
-    private void launchActivityAsTaskOverlay(String activityName, int taskId, int stackId)
-            throws Exception {
-        executeShellCommand(getAmStartCmd(activityName) + " --task " + taskId + " --task-overlay");
-
-        mAmWmState.waitForValidState(mDevice, activityName, stackId);
-    }
-
-    /**
-     * Sets an app-ops op for a given package to a given mode.
-     */
-    private void setAppOpsOpToMode(String packageName, String op, int mode) throws Exception {
-        executeShellCommand(String.format("appops set %s %s %d", packageName, op, mode));
-    }
-
-    /**
-     * Triggers the window keycode.
-     */
-    private void pressWindowButton() throws Exception {
-        executeShellCommand(INPUT_KEYEVENT_WINDOW);
-    }
-
-    /**
-     * TODO: Improve tests check to actually check that apps are not interactive instead of checking
-     *       if the stack is focused.
-     */
-    private void pinnedStackTester(String startActivityCmd, String topActivityName,
-            boolean moveTopToPinnedStack, boolean isFocusable) throws Exception {
-
-        executeShellCommand(startActivityCmd);
-        if (moveTopToPinnedStack) {
-            executeShellCommand(AM_MOVE_TOP_ACTIVITY_TO_PINNED_STACK_COMMAND);
-        }
-
-        mAmWmState.waitForValidState(mDevice, topActivityName, PINNED_STACK_ID);
-        mAmWmState.computeState(mDevice, null);
-
-        if (supportsPip()) {
-            final String windowName = getWindowName(topActivityName);
-            assertPinnedStackExists();
-            mAmWmState.assertFrontStack("Pinned stack must be the front stack.", PINNED_STACK_ID);
-            mAmWmState.assertVisibility(topActivityName, true);
-
-            if (isFocusable) {
-                mAmWmState.assertFocusedStack(
-                        "Pinned stack must be the focused stack.", PINNED_STACK_ID);
-                mAmWmState.assertFocusedActivity(
-                        "Pinned activity must be focused activity.", topActivityName);
-                mAmWmState.assertFocusedWindow(
-                        "Pinned window must be focused window.", windowName);
-                // Not checking for resumed state here because PiP overlay can be launched on top
-                // in different task by SystemUI.
-            } else {
-                // Don't assert that the stack is not focused as a focusable PiP overlay can be
-                // launched on top as a task overlay by SystemUI.
-                mAmWmState.assertNotFocusedActivity(
-                        "Pinned activity can't be the focused activity.", topActivityName);
-                mAmWmState.assertNotResumedActivity(
-                        "Pinned activity can't be the resumed activity.", topActivityName);
-                mAmWmState.assertNotFocusedWindow(
-                        "Pinned window can't be focused window.", windowName);
-            }
-        } else {
-            mAmWmState.assertDoesNotContainStack(
-                    "Must not contain pinned stack.", PINNED_STACK_ID);
-        }
-    }
-}
diff --git a/hostsidetests/services/activityandwindowmanager/activitymanager/src/android/server/cts/ActivityManagerReplaceWindowTests.java b/hostsidetests/services/activityandwindowmanager/activitymanager/src/android/server/cts/ActivityManagerReplaceWindowTests.java
deleted file mode 100644
index 3aa3ce0..0000000
--- a/hostsidetests/services/activityandwindowmanager/activitymanager/src/android/server/cts/ActivityManagerReplaceWindowTests.java
+++ /dev/null
@@ -1,104 +0,0 @@
-/*
- * Copyright (C) 2016 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package android.server.cts;
-
-import java.lang.Exception;
-import java.lang.String;
-import java.util.ArrayList;
-import java.util.List;
-import java.util.regex.Matcher;
-import java.util.regex.Pattern;
-import junit.framework.Assert;
-
-import static com.android.ddmlib.Log.LogLevel.INFO;
-
-import com.android.tradefed.device.DeviceNotAvailableException;
-import com.android.tradefed.log.LogUtil.CLog;
-
-/**
- * Build: mmma -j32 cts/hostsidetests/services
- * Run: cts/hostsidetests/services/activityandwindowmanager/util/run-test CtsServicesHostTestCases android.server.cts.ActivityManagerReplaceWindowTests
- */
-public class ActivityManagerReplaceWindowTests extends ActivityManagerTestBase {
-
-    private static final String SLOW_CREATE_ACTIVITY_NAME = "SlowCreateActivity";
-    private static final String NO_RELAUNCH_ACTIVITY_NAME = "NoRelaunchActivity";
-
-    private List<String> mTempWindowTokens = new ArrayList();
-
-    public void testReplaceWindow_Dock_Relaunch() throws Exception {
-        testReplaceWindow_Dock(true);
-    }
-
-    public void testReplaceWindow_Dock_NoRelaunch() throws Exception {
-        testReplaceWindow_Dock(false);
-    }
-
-    private void testReplaceWindow_Dock(boolean relaunch) throws Exception {
-        if (!supportsSplitScreenMultiWindow()) {
-            CLog.logAndDisplay(INFO, "Skipping test: no multi-window support");
-            return;
-        }
-
-        final String activityName =
-                relaunch ? SLOW_CREATE_ACTIVITY_NAME : NO_RELAUNCH_ACTIVITY_NAME;
-        final String windowName = getWindowName(activityName);
-        final String amStartCmd = getAmStartCmd(activityName);
-
-        executeShellCommand(amStartCmd);
-
-        // Sleep 2 seconds, then check if the window is started properly.
-        // SlowCreateActivity will do a sleep inside its onCreate() to simulate a
-        // slow-starting app. So instead of relying on WindowManagerState's
-        // retrying mechanism, we do an explicit sleep to avoid excess spews
-        // from WindowManagerState.
-        if (SLOW_CREATE_ACTIVITY_NAME.equals(activityName)) {
-            Thread.sleep(2000);
-        }
-
-        CLog.logAndDisplay(INFO, "==========Before Docking========");
-        final String oldToken = getWindowToken(windowName, activityName);
-
-        // Move to docked stack
-        final int taskId = getActivityTaskId(activityName);
-        final String cmd = AM_MOVE_TASK + taskId + " " + DOCKED_STACK_ID + " true";
-        executeShellCommand(cmd);
-
-        // Sleep 5 seconds, then check if the window is replaced properly.
-        Thread.sleep(5000);
-
-        CLog.logAndDisplay(INFO, "==========After Docking========");
-        final String newToken = getWindowToken(windowName, activityName);
-
-        // For both relaunch and not relaunch case, we'd like the window to be kept.
-        Assert.assertEquals("Window replaced while docking.", oldToken, newToken);
-    }
-
-    private String getWindowToken(String windowName, String activityName)
-            throws Exception {
-        mAmWmState.computeState(mDevice, new String[] {activityName});
-
-        mAmWmState.assertVisibility(activityName, true);
-
-        mAmWmState.getWmState().getMatchingWindowTokens(windowName, mTempWindowTokens);
-
-        Assert.assertEquals("Should have exactly one window for the activity.",
-                1, mTempWindowTokens.size());
-
-        return mTempWindowTokens.get(0);
-    }
-}
diff --git a/hostsidetests/services/activityandwindowmanager/activitymanager/src/android/server/cts/ActivityManagerTransitionSelectionTests.java b/hostsidetests/services/activityandwindowmanager/activitymanager/src/android/server/cts/ActivityManagerTransitionSelectionTests.java
deleted file mode 100644
index 63aa1ab..0000000
--- a/hostsidetests/services/activityandwindowmanager/activitymanager/src/android/server/cts/ActivityManagerTransitionSelectionTests.java
+++ /dev/null
@@ -1,266 +0,0 @@
-/*
- * Copyright (C) 2016 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package android.server.cts;
-
-import android.platform.test.annotations.Presubmit;
-
-import static android.server.cts.WindowManagerState.TRANSIT_ACTIVITY_CLOSE;
-import static android.server.cts.WindowManagerState.TRANSIT_ACTIVITY_OPEN;
-import static android.server.cts.WindowManagerState.TRANSIT_TASK_CLOSE;
-import static android.server.cts.WindowManagerState.TRANSIT_TASK_OPEN;
-import static android.server.cts.WindowManagerState.TRANSIT_WALLPAPER_CLOSE;
-import static android.server.cts.WindowManagerState.TRANSIT_WALLPAPER_INTRA_CLOSE;
-import static android.server.cts.WindowManagerState.TRANSIT_WALLPAPER_INTRA_OPEN;
-import static android.server.cts.WindowManagerState.TRANSIT_WALLPAPER_OPEN;
-
-/**
- * This test tests the transition type selection logic in ActivityManager/
- * WindowManager. BottomActivity is started first, then TopActivity, and we
- * check the transition type that the system selects when TopActivity enters
- * or exits under various setups.
- *
- * Note that we only require the correct transition type to be reported (eg.
- * TRANSIT_ACTIVITY_OPEN, TRANSIT_TASK_CLOSE, TRANSIT_WALLPAPER_OPEN, etc.).
- * The exact animation is unspecified and can be overridden.
- *
- * Build: mmma -j32 cts/hostsidetests/services
- * Run: cts/hostsidetests/services/activityandwindowmanager/util/run-test CtsServicesHostTestCases android.server.cts.ActivityManagerTransitionSelectionTests
- */
-@Presubmit
-public class ActivityManagerTransitionSelectionTests extends ActivityManagerTestBase {
-
-    private static final String BOTTOM_ACTIVITY_NAME = "BottomActivity";
-    private static final String TOP_ACTIVITY_NAME = "TopActivity";
-    private static final String TRANSLUCENT_TOP_ACTIVITY_NAME = "TranslucentTopActivity";
-
-    //------------------------------------------------------------------------//
-
-    // Test activity open/close under normal timing
-    public void testOpenActivity_NeitherWallpaper() throws Exception {
-        testOpenActivity(false /*bottomWallpaper*/, false /*topWallpaper*/,
-                false /*slowStop*/, TRANSIT_ACTIVITY_OPEN);
-    }
-
-    public void testCloseActivity_NeitherWallpaper() throws Exception {
-        testCloseActivity(false /*bottomWallpaper*/, false /*topWallpaper*/,
-                false /*slowStop*/, TRANSIT_ACTIVITY_CLOSE);
-    }
-
-    public void testOpenActivity_BottomWallpaper() throws Exception {
-        testOpenActivity(true /*bottomWallpaper*/, false /*topWallpaper*/,
-                false /*slowStop*/, TRANSIT_WALLPAPER_CLOSE);
-    }
-
-    public void testCloseActivity_BottomWallpaper() throws Exception {
-        testCloseActivity(true /*bottomWallpaper*/, false /*topWallpaper*/,
-                false /*slowStop*/, TRANSIT_WALLPAPER_OPEN);
-    }
-
-    public void testOpenActivity_BothWallpaper() throws Exception {
-        testOpenActivity(true /*bottomWallpaper*/, true /*topWallpaper*/,
-                false /*slowStop*/, TRANSIT_WALLPAPER_INTRA_OPEN);
-    }
-
-    public void testCloseActivity_BothWallpaper() throws Exception {
-        testCloseActivity(true /*bottomWallpaper*/, true /*topWallpaper*/,
-                false /*slowStop*/, TRANSIT_WALLPAPER_INTRA_CLOSE);
-    }
-
-    //------------------------------------------------------------------------//
-
-    // Test task open/close under normal timing
-    public void testOpenTask_NeitherWallpaper() throws Exception {
-        testOpenTask(false /*bottomWallpaper*/, false /*topWallpaper*/,
-                false /*slowStop*/, TRANSIT_TASK_OPEN);
-    }
-
-    public void testCloseTask_NeitherWallpaper() throws Exception {
-        testCloseTask(false /*bottomWallpaper*/, false /*topWallpaper*/,
-                false /*slowStop*/, TRANSIT_TASK_CLOSE);
-    }
-
-    public void testOpenTask_BottomWallpaper() throws Exception {
-        testOpenTask(true /*bottomWallpaper*/, false /*topWallpaper*/,
-                false /*slowStop*/, TRANSIT_WALLPAPER_CLOSE);
-    }
-
-    public void testCloseTask_BottomWallpaper() throws Exception {
-        testCloseTask(true /*bottomWallpaper*/, false /*topWallpaper*/,
-                false /*slowStop*/, TRANSIT_WALLPAPER_OPEN);
-    }
-
-    public void testOpenTask_BothWallpaper() throws Exception {
-        testOpenTask(true /*bottomWallpaper*/, true /*topWallpaper*/,
-                false /*slowStop*/, TRANSIT_WALLPAPER_INTRA_OPEN);
-    }
-
-    public void testCloseTask_BothWallpaper() throws Exception {
-        testCloseTask(true /*bottomWallpaper*/, true /*topWallpaper*/,
-                false /*slowStop*/, TRANSIT_WALLPAPER_INTRA_CLOSE);
-    }
-
-    //------------------------------------------------------------------------//
-
-    // Test activity close -- bottom activity slow in stopping
-    // These simulate the case where the bottom activity is resumed
-    // before AM receives its activitiyStopped
-    public void testCloseActivity_NeitherWallpaper_SlowStop() throws Exception {
-        testCloseActivity(false /*bottomWallpaper*/, false /*topWallpaper*/,
-                true /*slowStop*/, TRANSIT_ACTIVITY_CLOSE);
-    }
-
-    public void testCloseActivity_BottomWallpaper_SlowStop() throws Exception {
-        testCloseActivity(true /*bottomWallpaper*/, false /*topWallpaper*/,
-                true /*slowStop*/, TRANSIT_WALLPAPER_OPEN);
-    }
-
-    public void testCloseActivity_BothWallpaper_SlowStop() throws Exception {
-        testCloseActivity(true /*bottomWallpaper*/, true /*topWallpaper*/,
-                true /*slowStop*/, TRANSIT_WALLPAPER_INTRA_CLOSE);
-    }
-
-    //------------------------------------------------------------------------//
-
-    // Test task close -- bottom task top activity slow in stopping
-    // These simulate the case where the bottom activity is resumed
-    // before AM receives its activitiyStopped
-    public void testCloseTask_NeitherWallpaper_SlowStop() throws Exception {
-        testCloseTask(false /*bottomWallpaper*/, false /*topWallpaper*/,
-                true /*slowStop*/, TRANSIT_TASK_CLOSE);
-    }
-
-    public void testCloseTask_BottomWallpaper_SlowStop() throws Exception {
-        testCloseTask(true /*bottomWallpaper*/, false /*topWallpaper*/,
-                true /*slowStop*/, TRANSIT_WALLPAPER_OPEN);
-    }
-
-    public void testCloseTask_BothWallpaper_SlowStop() throws Exception {
-        testCloseTask(true /*bottomWallpaper*/, true /*topWallpaper*/,
-                true /*slowStop*/, TRANSIT_WALLPAPER_INTRA_CLOSE);
-    }
-
-    //------------------------------------------------------------------------//
-
-    /// Test closing of translucent activity/task
-    public void testCloseActivity_NeitherWallpaper_Translucent() throws Exception {
-        testCloseActivityTranslucent(false /*bottomWallpaper*/, false /*topWallpaper*/,
-                TRANSIT_ACTIVITY_CLOSE);
-    }
-
-    public void testCloseActivity_BottomWallpaper_Translucent() throws Exception {
-        testCloseActivityTranslucent(true /*bottomWallpaper*/, false /*topWallpaper*/,
-                TRANSIT_WALLPAPER_OPEN);
-    }
-
-    public void testCloseActivity_BothWallpaper_Translucent() throws Exception {
-        testCloseActivityTranslucent(true /*bottomWallpaper*/, true /*topWallpaper*/,
-                TRANSIT_WALLPAPER_INTRA_CLOSE);
-    }
-
-    public void testCloseTask_NeitherWallpaper_Translucent() throws Exception {
-        testCloseTaskTranslucent(false /*bottomWallpaper*/, false /*topWallpaper*/,
-                TRANSIT_TASK_CLOSE);
-    }
-
-    public void testCloseTask_BottomWallpaper_Translucent() throws Exception {
-        testCloseTaskTranslucent(true /*bottomWallpaper*/, false /*topWallpaper*/,
-                TRANSIT_WALLPAPER_OPEN);
-    }
-
-    public void testCloseTask_BothWallpaper_Translucent() throws Exception {
-        testCloseTaskTranslucent(true /*bottomWallpaper*/, true /*topWallpaper*/,
-                TRANSIT_WALLPAPER_INTRA_CLOSE);
-    }
-
-    //------------------------------------------------------------------------//
-
-    private void testOpenActivity(boolean bottomWallpaper,
-            boolean topWallpaper, boolean slowStop, String expectedTransit) throws Exception {
-        testTransitionSelection(true /*testOpen*/, false /*testNewTask*/,
-                bottomWallpaper, topWallpaper, false /*topTranslucent*/, slowStop, expectedTransit);
-    }
-    private void testCloseActivity(boolean bottomWallpaper,
-            boolean topWallpaper, boolean slowStop, String expectedTransit) throws Exception {
-        testTransitionSelection(false /*testOpen*/, false /*testNewTask*/,
-                bottomWallpaper, topWallpaper, false /*topTranslucent*/, slowStop, expectedTransit);
-    }
-    private void testOpenTask(boolean bottomWallpaper,
-            boolean topWallpaper, boolean slowStop, String expectedTransit) throws Exception {
-        testTransitionSelection(true /*testOpen*/, true /*testNewTask*/,
-                bottomWallpaper, topWallpaper, false /*topTranslucent*/, slowStop, expectedTransit);
-    }
-    private void testCloseTask(boolean bottomWallpaper,
-            boolean topWallpaper, boolean slowStop, String expectedTransit) throws Exception {
-        testTransitionSelection(false /*testOpen*/, true /*testNewTask*/,
-                bottomWallpaper, topWallpaper, false /*topTranslucent*/, slowStop, expectedTransit);
-    }
-    private void testCloseActivityTranslucent(boolean bottomWallpaper,
-            boolean topWallpaper, String expectedTransit) throws Exception {
-        testTransitionSelection(false /*testOpen*/, false /*testNewTask*/,
-                bottomWallpaper, topWallpaper, true /*topTranslucent*/,
-                false /*slowStop*/, expectedTransit);
-    }
-    private void testCloseTaskTranslucent(boolean bottomWallpaper,
-            boolean topWallpaper, String expectedTransit) throws Exception {
-        testTransitionSelection(false /*testOpen*/, true /*testNewTask*/,
-                bottomWallpaper, topWallpaper, true /*topTranslucent*/,
-                false /*slowStop*/, expectedTransit);
-    }
-    //------------------------------------------------------------------------//
-
-    private void testTransitionSelection(
-            boolean testOpen, boolean testNewTask,
-            boolean bottomWallpaper, boolean topWallpaper, boolean topTranslucent,
-            boolean testSlowStop, String expectedTransit) throws Exception {
-        String bottomStartCmd = getAmStartCmd(BOTTOM_ACTIVITY_NAME);
-        if (bottomWallpaper) {
-            bottomStartCmd += " --ez USE_WALLPAPER true";
-        }
-        if (testSlowStop) {
-            bottomStartCmd += " --ei STOP_DELAY 3000";
-        }
-        executeShellCommand(bottomStartCmd);
-
-        final String topActivityName = topTranslucent ?
-                TRANSLUCENT_TOP_ACTIVITY_NAME : TOP_ACTIVITY_NAME;
-        final String[] bottomActivityArray = new String[] {BOTTOM_ACTIVITY_NAME};
-        final String[] topActivityArray = new String[] {topActivityName};
-
-        mAmWmState.computeState(mDevice, bottomActivityArray);
-
-        String topStartCmd = getAmStartCmd(topActivityName);
-        if (testNewTask) {
-            topStartCmd += " -f 0x18000000";
-        }
-        if (topWallpaper) {
-            topStartCmd += " --ez USE_WALLPAPER true";
-        }
-        if (!testOpen) {
-            topStartCmd += " --ei FINISH_DELAY 1000";
-        }
-        executeShellCommand(topStartCmd);
-        Thread.sleep(5000);
-        if (testOpen) {
-            mAmWmState.computeState(mDevice, topActivityArray);
-        } else {
-            mAmWmState.computeState(mDevice, bottomActivityArray);
-        }
-
-        assertEquals("Picked wrong transition", expectedTransit,
-                mAmWmState.getWmState().getLastTransition());
-    }
-}
diff --git a/hostsidetests/services/activityandwindowmanager/activitymanager/src/android/server/cts/AnimationBackgroundTests.java b/hostsidetests/services/activityandwindowmanager/activitymanager/src/android/server/cts/AnimationBackgroundTests.java
deleted file mode 100644
index 8467af2..0000000
--- a/hostsidetests/services/activityandwindowmanager/activitymanager/src/android/server/cts/AnimationBackgroundTests.java
+++ /dev/null
@@ -1,50 +0,0 @@
-/*
- * Copyright (C) 2016 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package android.server.cts;
-
-/**
- * Build: mmma -j32 cts/hostsidetests/services
- * Run: cts/hostsidetests/services/activityandwindowmanager/util/run-test CtsServicesHostTestCases android.server.cts.AnimationBackgroundTests
- */
-public class AnimationBackgroundTests extends ActivityManagerTestBase {
-
-    public void testAnimationBackground_duringAnimation() throws Exception {
-        launchActivity(LAUNCHING_ACTIVITY);
-        getLaunchActivityBuilder()
-                .setTargetActivityName("AnimationTestActivity")
-                .setWaitForLaunched(false)
-                .execute();
-
-        // Make sure we are in the middle of the animation.
-        mAmWmState.waitForWithWmState(mDevice, state -> state
-                .getStack(FULLSCREEN_WORKSPACE_STACK_ID)
-                        .isWindowAnimationBackgroundSurfaceShowing(),
-                "***Waiting for animation background showing");
-        assertTrue("window animation background needs to be showing", mAmWmState.getWmState()
-                .getStack(FULLSCREEN_WORKSPACE_STACK_ID)
-                .isWindowAnimationBackgroundSurfaceShowing());
-    }
-
-    public void testAnimationBackground_gone() throws Exception {
-        launchActivity(LAUNCHING_ACTIVITY);
-        getLaunchActivityBuilder().setTargetActivityName("AnimationTestActivity").execute();
-        mAmWmState.computeState(mDevice, new String[] { "AnimationTestActivity "});
-        assertFalse("window animation background needs to be gone", mAmWmState.getWmState()
-                .getStack(FULLSCREEN_WORKSPACE_STACK_ID)
-                .isWindowAnimationBackgroundSurfaceShowing());
-    }
-}
diff --git a/hostsidetests/services/activityandwindowmanager/activitymanager/src/android/server/cts/DisplaySizeTest.java b/hostsidetests/services/activityandwindowmanager/activitymanager/src/android/server/cts/DisplaySizeTest.java
deleted file mode 100644
index 0ebbfe2..0000000
--- a/hostsidetests/services/activityandwindowmanager/activitymanager/src/android/server/cts/DisplaySizeTest.java
+++ /dev/null
@@ -1,174 +0,0 @@
-/*
- * Copyright (C) 2016 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package android.server.cts;
-
-import com.android.tradefed.device.DeviceNotAvailableException;
-import com.android.tradefed.device.ITestDevice;
-import com.android.tradefed.testtype.DeviceTestCase;
-
-/**
- * Ensure that compatibility dialog is shown when launching an application with
- * an unsupported smallest width.
- *
- * Build: mmma -j32 cts/hostsidetests/services
- * Run: cts/hostsidetests/services/activityandwindowmanager/util/run-test CtsServicesHostTestCases android.server.cts.DisplaySizeTest
- */
-public class DisplaySizeTest extends DeviceTestCase {
-    private static final String DENSITY_PROP_DEVICE = "ro.sf.lcd_density";
-    private static final String DENSITY_PROP_EMULATOR = "qemu.sf.lcd_density";
-
-    private static final String AM_START_COMMAND = "am start -n %s/%s.%s";
-    private static final String AM_FORCE_STOP = "am force-stop %s";
-
-    private static final int ACTIVITY_TIMEOUT_MILLIS = 1000;
-    private static final int WINDOW_TIMEOUT_MILLIS = 1000;
-
-    private ITestDevice mDevice;
-
-    @Override
-    protected void setUp() throws Exception {
-        super.setUp();
-
-        mDevice = getDevice();
-    }
-
-    @Override
-    protected void tearDown() throws Exception {
-        super.tearDown();
-
-        try {
-            resetDensity();
-
-            // Ensure app process is stopped.
-            forceStopPackage("android.displaysize.app");
-            forceStopPackage("android.server.cts");
-        } catch (DeviceNotAvailableException e) {
-            // Do nothing.
-        }
-    }
-
-    public void testCompatibilityDialog() throws Exception {
-        // Launch some other app (not to perform density change on launcher).
-        startActivity("android.server.cts", "TestActivity");
-        verifyWindowDisplayed("TestActivity", ACTIVITY_TIMEOUT_MILLIS);
-
-        setUnsupportedDensity();
-
-        // Launch target app.
-        startActivity("android.displaysize.app", "SmallestWidthActivity");
-        verifyWindowDisplayed("SmallestWidthActivity", ACTIVITY_TIMEOUT_MILLIS);
-        verifyWindowDisplayed("UnsupportedDisplaySizeDialog", WINDOW_TIMEOUT_MILLIS);
-    }
-
-    public void testCompatibilityDialogWhenFocused() throws Exception {
-        startActivity("android.displaysize.app", "SmallestWidthActivity");
-        verifyWindowDisplayed("SmallestWidthActivity", ACTIVITY_TIMEOUT_MILLIS);
-
-        setUnsupportedDensity();
-
-        verifyWindowDisplayed("UnsupportedDisplaySizeDialog", WINDOW_TIMEOUT_MILLIS);
-    }
-
-    public void testCompatibilityDialogAfterReturn() throws Exception {
-        // Launch target app.
-        startActivity("android.displaysize.app", "SmallestWidthActivity");
-        verifyWindowDisplayed("SmallestWidthActivity", ACTIVITY_TIMEOUT_MILLIS);
-        // Launch another activity.
-        startOtherActivityOnTop("android.displaysize.app", "SmallestWidthActivity");
-        verifyWindowDisplayed("TestActivity", ACTIVITY_TIMEOUT_MILLIS);
-
-        setUnsupportedDensity();
-
-        // Go back.
-        mDevice.executeShellCommand("input keyevent 4");
-
-        verifyWindowDisplayed("SmallestWidthActivity", ACTIVITY_TIMEOUT_MILLIS);
-        verifyWindowDisplayed("UnsupportedDisplaySizeDialog", WINDOW_TIMEOUT_MILLIS);
-    }
-
-    private void setUnsupportedDensity() throws DeviceNotAvailableException {
-        // Set device to 0.85 zoom. It doesn't matter that we're zooming out
-        // since the feature verifies that we're in a non-default density.
-        final int stableDensity = getStableDensity();
-        final int targetDensity = (int) (stableDensity * 0.85);
-        setDensity(targetDensity);
-    }
-
-    private int getStableDensity() {
-        try {
-            final String densityProp;
-            if (mDevice.getSerialNumber().startsWith("emulator-")) {
-                densityProp = DENSITY_PROP_EMULATOR;
-            } else {
-                densityProp = DENSITY_PROP_DEVICE;
-            }
-
-            return Integer.parseInt(mDevice.getProperty(densityProp));
-        } catch (DeviceNotAvailableException e) {
-            return 0;
-        }
-    }
-
-    private void setDensity(int targetDensity) throws DeviceNotAvailableException {
-        mDevice.executeShellCommand("wm density " + targetDensity);
-
-        // Verify that the density is changed.
-        final String output = mDevice.executeShellCommand("wm density");
-        final boolean success = output.contains("Override density: " + targetDensity);
-
-        assertTrue("Failed to set density to " + targetDensity, success);
-    }
-
-    private void resetDensity() throws DeviceNotAvailableException {
-        mDevice.executeShellCommand("wm density reset");
-    }
-
-    private void forceStopPackage(String packageName) throws DeviceNotAvailableException {
-        final String forceStopCmd = String.format(AM_FORCE_STOP, packageName);
-        mDevice.executeShellCommand(forceStopCmd);
-    }
-
-    private void startActivity(String packageName, String activityName)
-            throws DeviceNotAvailableException {
-        mDevice.executeShellCommand(getStartCommand(packageName, activityName));
-    }
-
-    private void startOtherActivityOnTop(String packageName, String activityName)
-            throws DeviceNotAvailableException {
-        final String startCmd = getStartCommand(packageName, activityName)
-                + " -f 0x20000000 --ez launch_another_activity true";
-        mDevice.executeShellCommand(startCmd);
-    }
-
-    private String getStartCommand(String packageName, String activityName) {
-        return String.format(AM_START_COMMAND, packageName, packageName, activityName);
-    }
-
-    private void verifyWindowDisplayed(String windowName, long timeoutMillis)
-            throws DeviceNotAvailableException {
-        boolean success = false;
-
-        // Verify that compatibility dialog is shown within 1000ms.
-        final long timeoutTimeMillis = System.currentTimeMillis() + timeoutMillis;
-        while (!success && System.currentTimeMillis() < timeoutTimeMillis) {
-            final String output = mDevice.executeShellCommand("dumpsys window");
-            success = output.contains(windowName);
-        }
-
-        assertTrue(windowName + " was not displayed", success);
-    }
-}
diff --git a/hostsidetests/services/activityandwindowmanager/activitymanager/src/android/server/cts/KeyguardLockedTests.java b/hostsidetests/services/activityandwindowmanager/activitymanager/src/android/server/cts/KeyguardLockedTests.java
deleted file mode 100644
index a441e56..0000000
--- a/hostsidetests/services/activityandwindowmanager/activitymanager/src/android/server/cts/KeyguardLockedTests.java
+++ /dev/null
@@ -1,202 +0,0 @@
-/*
- * Copyright (C) 2016 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License
- */
-
-package android.server.cts;
-
-/**
- * Build: mmma -j32 cts/hostsidetests/services
- * Run: cts/hostsidetests/services/activityandwindowmanager/util/run-test CtsServicesHostTestCases android.server.cts.KeyguardLockedTests
- */
-public class KeyguardLockedTests extends KeyguardTestBase {
-
-    private static final String SHOW_WHEN_LOCKED_ACTIVITY = "ShowWhenLockedActivity";
-    private static final String PIP_ACTIVITY = "PipActivity";
-    private static final String PIP_ACTIVITY_ACTION_ENTER_PIP =
-            "android.server.cts.PipActivity.enter_pip";
-    private static final String EXTRA_SHOW_OVER_KEYGUARD = "show_over_keyguard";
-
-    @Override
-    protected void setUp() throws Exception {
-        super.setUp();
-        setLockCredential();
-    }
-
-    @Override
-    protected void tearDown() throws Exception {
-        super.tearDown();
-        tearDownLockCredentials();
-    }
-
-    public void testLockAndUnlock() throws Exception {
-        if (!isHandheld()) {
-            return;
-        }
-        gotoKeyguard();
-        mAmWmState.waitForKeyguardShowingAndNotOccluded(mDevice);
-        assertShowingAndNotOccluded();
-        unlockDeviceWithCredential();
-        mAmWmState.waitForKeyguardGone(mDevice);
-        assertKeyguardGone();
-    }
-
-    public void testDismissKeyguard() throws Exception {
-        if (!isHandheld()) {
-            return;
-        }
-        gotoKeyguard();
-        mAmWmState.waitForKeyguardShowingAndNotOccluded(mDevice);
-        assertShowingAndNotOccluded();
-        launchActivity("DismissKeyguardActivity");
-        enterAndConfirmLockCredential();
-        mAmWmState.waitForKeyguardGone(mDevice);
-        assertKeyguardGone();
-        mAmWmState.assertVisibility("DismissKeyguardActivity", true);
-    }
-
-    public void testDismissKeyguard_whileOccluded() throws Exception {
-        if (!isHandheld()) {
-            return;
-        }
-        gotoKeyguard();
-        mAmWmState.waitForKeyguardShowingAndNotOccluded(mDevice);
-        assertShowingAndNotOccluded();
-        launchActivity(SHOW_WHEN_LOCKED_ACTIVITY);
-        mAmWmState.computeState(mDevice, new String[] { SHOW_WHEN_LOCKED_ACTIVITY });
-        mAmWmState.assertVisibility(SHOW_WHEN_LOCKED_ACTIVITY, true);
-        launchActivity("DismissKeyguardActivity");
-        enterAndConfirmLockCredential();
-        mAmWmState.waitForKeyguardGone(mDevice);
-        assertKeyguardGone();
-        mAmWmState.assertVisibility("DismissKeyguardActivity", true);
-        mAmWmState.assertVisibility(SHOW_WHEN_LOCKED_ACTIVITY, false);
-    }
-
-    public void testDismissKeyguard_fromShowWhenLocked_notAllowed() throws Exception {
-        if (!isHandheld()) {
-            return;
-        }
-        gotoKeyguard();
-        mAmWmState.waitForKeyguardShowingAndNotOccluded(mDevice);
-        assertShowingAndNotOccluded();
-        launchActivity(SHOW_WHEN_LOCKED_ACTIVITY);
-        mAmWmState.computeState(mDevice, new String[] { SHOW_WHEN_LOCKED_ACTIVITY });
-        mAmWmState.assertVisibility(SHOW_WHEN_LOCKED_ACTIVITY, true);
-        executeShellCommand("am broadcast -a trigger_broadcast --ez dismissKeyguard true");
-        enterAndConfirmLockCredential();
-
-        // Make sure we stay on Keyguard.
-        assertShowingAndOccluded();
-        mAmWmState.assertVisibility(SHOW_WHEN_LOCKED_ACTIVITY, true);
-    }
-
-    public void testDismissKeyguardActivity_method() throws Exception {
-        if (!isHandheld()) {
-            return;
-        }
-        final String logSeparator = clearLogcat();
-        gotoKeyguard();
-        mAmWmState.computeState(mDevice, null);
-        assertTrue(mAmWmState.getAmState().getKeyguardControllerState().keyguardShowing);
-        launchActivity("DismissKeyguardMethodActivity");
-        enterAndConfirmLockCredential();
-        mAmWmState.waitForKeyguardGone(mDevice);
-        mAmWmState.computeState(mDevice, new String[] { "DismissKeyguardMethodActivity"});
-        mAmWmState.assertVisibility("DismissKeyguardMethodActivity", true);
-        assertFalse(mAmWmState.getAmState().getKeyguardControllerState().keyguardShowing);
-        assertOnDismissSucceededInLogcat(logSeparator);
-    }
-
-    public void testDismissKeyguardActivity_method_cancelled() throws Exception {
-        if (!isHandheld()) {
-            return;
-        }
-        final String logSeparator = clearLogcat();
-        gotoKeyguard();
-        mAmWmState.computeState(mDevice, null);
-        assertTrue(mAmWmState.getAmState().getKeyguardControllerState().keyguardShowing);
-        launchActivity("DismissKeyguardMethodActivity");
-        pressBackButton();
-        assertOnDismissCancelledInLogcat(logSeparator);
-        mAmWmState.computeState(mDevice, new String[] {});
-        mAmWmState.assertVisibility("DismissKeyguardMethodActivity", false);
-        assertTrue(mAmWmState.getAmState().getKeyguardControllerState().keyguardShowing);
-        unlockDeviceWithCredential();
-    }
-
-    public void testEnterPipOverKeyguard() throws Exception {
-        if (!isHandheld() || !supportsPip()) {
-            return;
-        }
-
-        // Go to the keyguard
-        gotoKeyguard();
-        mAmWmState.waitForKeyguardShowingAndNotOccluded(mDevice);
-        assertTrue(mAmWmState.getAmState().getKeyguardControllerState().keyguardShowing);
-
-        // Enter PiP on an activity on top of the keyguard, and ensure that it prompts the user for
-        // their credentials and does not enter picture-in-picture yet
-        launchActivity(PIP_ACTIVITY, EXTRA_SHOW_OVER_KEYGUARD, "true");
-        executeShellCommand("am broadcast -a " + PIP_ACTIVITY_ACTION_ENTER_PIP);
-        mAmWmState.waitForKeyguardShowingAndOccluded(mDevice);
-        assertShowingAndOccluded();
-        mAmWmState.assertDoesNotContainStack("Must not contain pinned stack.", PINNED_STACK_ID);
-
-        // Enter the credentials and ensure that the activity actually entered picture-in-picture
-        enterAndConfirmLockCredential();
-        mAmWmState.waitForKeyguardGone(mDevice);
-        assertKeyguardGone();
-        mAmWmState.assertContainsStack("Must contain pinned stack.", PINNED_STACK_ID);
-    }
-
-    public void testShowWhenLockedActivityAndPipActivity() throws Exception {
-        if (!isHandheld() || !supportsPip()) {
-            return;
-        }
-
-        launchActivity(PIP_ACTIVITY);
-        executeShellCommand("am broadcast -a " + PIP_ACTIVITY_ACTION_ENTER_PIP);
-        mAmWmState.computeState(mDevice, new String[] { PIP_ACTIVITY });
-        mAmWmState.assertContainsStack("Must contain pinned stack.", PINNED_STACK_ID);
-        mAmWmState.assertVisibility(PIP_ACTIVITY, true);
-
-        launchActivity(SHOW_WHEN_LOCKED_ACTIVITY);
-        mAmWmState.computeState(mDevice, new String[] { SHOW_WHEN_LOCKED_ACTIVITY });
-        mAmWmState.assertVisibility(SHOW_WHEN_LOCKED_ACTIVITY, true);
-
-        gotoKeyguard();
-        mAmWmState.waitForKeyguardShowingAndOccluded(mDevice);
-        assertShowingAndOccluded();
-        mAmWmState.assertVisibility(SHOW_WHEN_LOCKED_ACTIVITY, true);
-        mAmWmState.assertVisibility(PIP_ACTIVITY, false);
-    }
-
-    public void testShowWhenLockedPipActivity() throws Exception {
-        if (!isHandheld() || !supportsPip()) {
-            return;
-        }
-
-        launchActivity(PIP_ACTIVITY, EXTRA_SHOW_OVER_KEYGUARD, "true");
-        executeShellCommand("am broadcast -a " + PIP_ACTIVITY_ACTION_ENTER_PIP);
-        mAmWmState.computeState(mDevice, new String[] { PIP_ACTIVITY });
-        mAmWmState.assertContainsStack("Must contain pinned stack.", PINNED_STACK_ID);
-        mAmWmState.assertVisibility(PIP_ACTIVITY, true);
-
-        gotoKeyguard();
-        mAmWmState.waitForKeyguardShowingAndNotOccluded(mDevice);
-        assertShowingAndNotOccluded();
-        mAmWmState.assertVisibility(PIP_ACTIVITY, false);
-    }
-}
diff --git a/hostsidetests/services/activityandwindowmanager/activitymanager/src/android/server/cts/KeyguardTestBase.java b/hostsidetests/services/activityandwindowmanager/activitymanager/src/android/server/cts/KeyguardTestBase.java
deleted file mode 100644
index d578644..0000000
--- a/hostsidetests/services/activityandwindowmanager/activitymanager/src/android/server/cts/KeyguardTestBase.java
+++ /dev/null
@@ -1,72 +0,0 @@
-/*
- * Copyright (C) 2016 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License
- */
-
-package android.server.cts;
-
-import static android.server.cts.StateLogger.log;
-
-import java.util.regex.Matcher;
-import java.util.regex.Pattern;
-
-public class KeyguardTestBase extends ActivityManagerTestBase {
-
-    protected void assertShowingAndOccluded() {
-        assertTrue(mAmWmState.getAmState().getKeyguardControllerState().keyguardShowing);
-        assertTrue(mAmWmState.getAmState().getKeyguardControllerState().keyguardOccluded);
-    }
-
-    protected void assertShowingAndNotOccluded() {
-        assertTrue(mAmWmState.getAmState().getKeyguardControllerState().keyguardShowing);
-        assertFalse(mAmWmState.getAmState().getKeyguardControllerState().keyguardOccluded);
-    }
-
-    protected void assertKeyguardGone() {
-        assertFalse(mAmWmState.getAmState().getKeyguardControllerState().keyguardShowing);
-    }
-
-    protected void assertOnDismissSucceededInLogcat(String logSeparator) throws Exception {
-        assertInLogcat("KeyguardDismissLoggerCallback", "onDismissSucceeded", logSeparator);
-    }
-
-    protected void assertOnDismissCancelledInLogcat(String logSeparator) throws Exception {
-        assertInLogcat("KeyguardDismissLoggerCallback", "onDismissCancelled", logSeparator);
-    }
-
-    protected void assertOnDismissErrorInLogcat(String logSeparator) throws Exception {
-        assertInLogcat("KeyguardDismissLoggerCallback", "onDismissError", logSeparator);
-    }
-
-    private void assertInLogcat(String activityName, String entry, String logSeparator)
-            throws Exception {
-        final Pattern pattern = Pattern.compile("(.+)" + entry);
-        int tries = 0;
-        while (tries < 5) {
-            final String[] lines = getDeviceLogsForComponent(activityName, logSeparator);
-            log("Looking at logcat");
-            for (int i = lines.length - 1; i >= 0; i--) {
-                final String line = lines[i].trim();
-                log(line);
-                Matcher matcher = pattern.matcher(line);
-                if (matcher.matches()) {
-                    return;
-                }
-            }
-            tries++;
-            Thread.sleep(500);
-        }
-        fail("Not in logcat: " + entry);
-    }
-}
diff --git a/hostsidetests/services/activityandwindowmanager/activitymanager/src/android/server/cts/KeyguardTests.java b/hostsidetests/services/activityandwindowmanager/activitymanager/src/android/server/cts/KeyguardTests.java
deleted file mode 100644
index 8c6f037..0000000
--- a/hostsidetests/services/activityandwindowmanager/activitymanager/src/android/server/cts/KeyguardTests.java
+++ /dev/null
@@ -1,378 +0,0 @@
-/*
- * Copyright (C) 2016 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License
- */
-
-package android.server.cts;
-
-import android.server.cts.WindowManagerState.WindowState;
-
-/**
- * Build: mmma -j32 cts/hostsidetests/services
- * Run: cts/hostsidetests/services/activityandwindowmanager/util/run-test CtsServicesHostTestCases android.server.cts.KeyguardTests
- */
-public class KeyguardTests extends KeyguardTestBase {
-
-    @Override
-    public void setUp() throws Exception {
-        super.setUp();
-
-        // Set screen lock (swipe)
-        setLockDisabled(false);
-    }
-
-    @Override
-    public void tearDown() throws Exception {
-        super.tearDown();
-
-        tearDownLockCredentials();
-    }
-
-    public void testKeyguardHidesActivity() throws Exception {
-        if (!isHandheld() || isUiModeLockedToVrHeadset()) {
-            return;
-        }
-        launchActivity("TestActivity");
-        mAmWmState.computeState(mDevice, new String[] { "TestActivity"});
-        mAmWmState.assertVisibility("TestActivity", true);
-        gotoKeyguard();
-        mAmWmState.computeState(mDevice, null);
-        assertShowingAndNotOccluded();
-        mAmWmState.assertVisibility("TestActivity", false);
-        unlockDevice();
-    }
-
-    public void testShowWhenLockedActivity() throws Exception {
-        if (!isHandheld() || isUiModeLockedToVrHeadset()) {
-            return;
-        }
-        launchActivity("ShowWhenLockedActivity");
-        mAmWmState.computeState(mDevice, new String[] { "ShowWhenLockedActivity"});
-        mAmWmState.assertVisibility("ShowWhenLockedActivity", true);
-        gotoKeyguard();
-        mAmWmState.computeState(mDevice, null);
-        mAmWmState.assertVisibility("ShowWhenLockedActivity", true);
-        assertShowingAndOccluded();
-        pressHomeButton();
-        unlockDevice();
-    }
-
-    /**
-     * Tests whether dialogs from SHOW_WHEN_LOCKED activities are also visible if Keyguard is
-     * showing.
-     */
-    public void testShowWhenLockedActivity_withDialog() throws Exception {
-        if (!isHandheld() || isUiModeLockedToVrHeadset()) {
-            return;
-        }
-        launchActivity("ShowWhenLockedWithDialogActivity");
-        mAmWmState.computeState(mDevice, new String[] { "ShowWhenLockedWithDialogActivity"});
-        mAmWmState.assertVisibility("ShowWhenLockedWithDialogActivity", true);
-        gotoKeyguard();
-        mAmWmState.computeState(mDevice, null);
-        mAmWmState.assertVisibility("ShowWhenLockedWithDialogActivity", true);
-        assertTrue(mAmWmState.getWmState().allWindowsVisible(
-                getWindowName("ShowWhenLockedWithDialogActivity")));
-        assertShowingAndOccluded();
-        pressHomeButton();
-        unlockDevice();
-    }
-
-    /**
-     * Tests whether multiple SHOW_WHEN_LOCKED activities are shown if the topmost is translucent.
-     */
-    public void testMultipleShowWhenLockedActivities() throws Exception {
-        if (!isHandheld() || isUiModeLockedToVrHeadset()) {
-            return;
-        }
-        launchActivity("ShowWhenLockedActivity");
-        launchActivity("ShowWhenLockedTranslucentActivity");
-        mAmWmState.computeState(mDevice, new String[] { "ShowWhenLockedActivity",
-                "ShowWhenLockedTranslucentActivity"});
-        mAmWmState.assertVisibility("ShowWhenLockedActivity", true);
-        mAmWmState.assertVisibility("ShowWhenLockedTranslucentActivity", true);
-        gotoKeyguard();
-        mAmWmState.computeState(mDevice, null);
-        mAmWmState.assertVisibility("ShowWhenLockedActivity", true);
-        mAmWmState.assertVisibility("ShowWhenLockedTranslucentActivity", true);
-        assertShowingAndOccluded();
-        pressHomeButton();
-        unlockDevice();
-    }
-
-    /**
-     * If we have a translucent SHOW_WHEN_LOCKED_ACTIVITY, the wallpaper should also be showing.
-     */
-    public void testTranslucentShowWhenLockedActivity() throws Exception {
-        if (!isHandheld() || isUiModeLockedToVrHeadset()) {
-            return;
-        }
-        launchActivity("ShowWhenLockedTranslucentActivity");
-        mAmWmState.computeState(mDevice, new String[] { "ShowWhenLockedTranslucentActivity"});
-        mAmWmState.assertVisibility("ShowWhenLockedTranslucentActivity", true);
-        gotoKeyguard();
-        mAmWmState.computeState(mDevice, null);
-        mAmWmState.assertVisibility("ShowWhenLockedTranslucentActivity", true);
-        assertWallpaperShowing();
-        assertShowingAndOccluded();
-        pressHomeButton();
-        unlockDevice();
-    }
-
-    /**
-     * If we have a translucent SHOW_WHEN_LOCKED activity, the activity behind should not be shown.
-     */
-    public void testTranslucentDoesntRevealBehind() throws Exception {
-        if (!isHandheld() || isUiModeLockedToVrHeadset()) {
-            return;
-        }
-        launchActivity("TestActivity");
-        launchActivity("ShowWhenLockedTranslucentActivity");
-        mAmWmState.computeState(mDevice, new String[] { "TestActivity",
-                "ShowWhenLockedTranslucentActivity"});
-        mAmWmState.assertVisibility("TestActivity", true);
-        mAmWmState.assertVisibility("ShowWhenLockedTranslucentActivity", true);
-        gotoKeyguard();
-        mAmWmState.computeState(mDevice, null);
-        mAmWmState.assertVisibility("ShowWhenLockedTranslucentActivity", true);
-        mAmWmState.assertVisibility("TestActivity", false);
-        assertShowingAndOccluded();
-        pressHomeButton();
-        unlockDevice();
-    }
-
-    public void testDialogShowWhenLockedActivity() throws Exception {
-        if (!isHandheld() || isUiModeLockedToVrHeadset()) {
-            return;
-        }
-        launchActivity("ShowWhenLockedDialogActivity");
-        mAmWmState.computeState(mDevice, new String[] { "ShowWhenLockedDialogActivity"});
-        mAmWmState.assertVisibility("ShowWhenLockedDialogActivity", true);
-        gotoKeyguard();
-        mAmWmState.computeState(mDevice, null);
-        mAmWmState.assertVisibility("ShowWhenLockedDialogActivity", true);
-        assertWallpaperShowing();
-        assertShowingAndOccluded();
-        pressHomeButton();
-        unlockDevice();
-    }
-
-    /**
-     * Test that showWhenLocked activity is fullscreen when shown over keyguard
-     */
-    public void testShowWhenLockedActivityWhileSplit() throws Exception {
-        if (!isHandheld() || isUiModeLockedToVrHeadset() || !supportsSplitScreenMultiWindow()) {
-            return;
-        }
-        launchActivityInDockStack(LAUNCHING_ACTIVITY);
-        launchActivityToSide(true, false, "ShowWhenLockedActivity");
-        mAmWmState.assertVisibility("ShowWhenLockedActivity", true);
-        gotoKeyguard();
-        mAmWmState.computeState(mDevice, new String[] { "ShowWhenLockedActivity" });
-        mAmWmState.assertVisibility("ShowWhenLockedActivity", true);
-        assertShowingAndOccluded();
-        mAmWmState.assertDoesNotContainStack("Activity must be full screen.",
-                ActivityManagerTestBase.DOCKED_STACK_ID);
-        pressHomeButton();
-        unlockDevice();
-    }
-
-    /**
-     * Tests whether a FLAG_DISMISS_KEYGUARD activity occludes Keyguard.
-     */
-    public void testDismissKeyguardActivity() throws Exception {
-        if (!isHandheld() || isUiModeLockedToVrHeadset()) {
-            return;
-        }
-        gotoKeyguard();
-        mAmWmState.computeState(mDevice, null);
-        assertTrue(mAmWmState.getAmState().getKeyguardControllerState().keyguardShowing);
-        launchActivity("DismissKeyguardActivity");
-        mAmWmState.waitForKeyguardShowingAndOccluded(mDevice);
-        mAmWmState.computeState(mDevice, new String[] { "DismissKeyguardActivity"});
-        mAmWmState.assertVisibility("DismissKeyguardActivity", true);
-        assertShowingAndOccluded();
-    }
-
-    public void testDismissKeyguardActivity_method() throws Exception {
-        if (!isHandheld() || isUiModeLockedToVrHeadset()) {
-            return;
-        }
-        final String logSeparator = clearLogcat();
-        gotoKeyguard();
-        mAmWmState.computeState(mDevice, null);
-        assertTrue(mAmWmState.getAmState().getKeyguardControllerState().keyguardShowing);
-        launchActivity("DismissKeyguardMethodActivity");
-        mAmWmState.waitForKeyguardGone(mDevice);
-        mAmWmState.computeState(mDevice, new String[] { "DismissKeyguardMethodActivity"});
-        mAmWmState.assertVisibility("DismissKeyguardMethodActivity", true);
-        assertFalse(mAmWmState.getAmState().getKeyguardControllerState().keyguardShowing);
-        assertOnDismissSucceededInLogcat(logSeparator);
-    }
-
-    public void testDismissKeyguardActivity_method_notTop() throws Exception {
-        if (!isHandheld() || isUiModeLockedToVrHeadset()) {
-            return;
-        }
-        final String logSeparator = clearLogcat();
-        gotoKeyguard();
-        mAmWmState.computeState(mDevice, null);
-        assertTrue(mAmWmState.getAmState().getKeyguardControllerState().keyguardShowing);
-        launchActivity("BroadcastReceiverActivity");
-        launchActivity("TestActivity");
-        executeShellCommand("am broadcast -a trigger_broadcast --ez dismissKeyguardMethod true");
-        assertOnDismissErrorInLogcat(logSeparator);
-    }
-
-    public void testDismissKeyguardActivity_method_turnScreenOn() throws Exception {
-        if (!isHandheld() || isUiModeLockedToVrHeadset()) {
-            return;
-        }
-        final String logSeparator = clearLogcat();
-        sleepDevice();
-        mAmWmState.computeState(mDevice, null);
-        assertTrue(mAmWmState.getAmState().getKeyguardControllerState().keyguardShowing);
-        launchActivity("TurnScreenOnDismissKeyguardActivity");
-        mAmWmState.waitForKeyguardGone(mDevice);
-        mAmWmState.computeState(mDevice, new String[] { "TurnScreenOnDismissKeyguardActivity"});
-        mAmWmState.assertVisibility("TurnScreenOnDismissKeyguardActivity", true);
-        assertFalse(mAmWmState.getAmState().getKeyguardControllerState().keyguardShowing);
-        assertOnDismissSucceededInLogcat(logSeparator);
-    }
-
-    public void testDismissKeyguard_fromShowWhenLocked_notAllowed() throws Exception {
-        if (!isHandheld() || isUiModeLockedToVrHeadset()) {
-            return;
-        }
-        gotoKeyguard();
-        mAmWmState.waitForKeyguardShowingAndNotOccluded(mDevice);
-        assertShowingAndNotOccluded();
-        launchActivity("ShowWhenLockedActivity");
-        mAmWmState.computeState(mDevice, new String[] { "ShowWhenLockedActivity" });
-        mAmWmState.assertVisibility("ShowWhenLockedActivity", true);
-        assertShowingAndOccluded();
-        executeShellCommand("am broadcast -a trigger_broadcast --ez dismissKeyguard true");
-        assertShowingAndOccluded();
-        mAmWmState.assertVisibility("ShowWhenLockedActivity", true);
-    }
-
-    public void testKeyguardLock() throws Exception {
-        if (!isHandheld() || isUiModeLockedToVrHeadset()) {
-            return;
-        }
-        gotoKeyguard();
-        mAmWmState.waitForKeyguardShowingAndNotOccluded(mDevice);
-        assertShowingAndNotOccluded();
-        launchActivity("KeyguardLockActivity");
-        mAmWmState.computeState(mDevice, new String[] { "KeyguardLockActivity" });
-        mAmWmState.assertVisibility("KeyguardLockActivity", true);
-        executeShellCommand(FINISH_ACTIVITY_BROADCAST);
-        mAmWmState.waitForKeyguardShowingAndNotOccluded(mDevice);
-        assertShowingAndNotOccluded();
-    }
-
-    public void testUnoccludeRotationChange() throws Exception {
-        if (!isHandheld() || isUiModeLockedToVrHeadset()) {
-            return;
-        }
-        gotoKeyguard();
-        mAmWmState.waitForKeyguardShowingAndNotOccluded(mDevice);
-        assertShowingAndNotOccluded();
-        executeShellCommand(getAmStartCmd("ShowWhenLockedActivity"));
-        mAmWmState.computeState(mDevice, new String[] { "ShowWhenLockedActivity" });
-        mAmWmState.assertVisibility("ShowWhenLockedActivity", true);
-        setDeviceRotation(1);
-        pressHomeButton();
-        mAmWmState.waitForKeyguardShowingAndNotOccluded(mDevice);
-        mAmWmState.waitForDisplayUnfrozen(mDevice);
-        mAmWmState.assertSanity();
-        mAmWmState.assertHomeActivityVisible(false);
-        assertShowingAndNotOccluded();
-        mAmWmState.assertVisibility("ShowWhenLockedActivity", false);
-    }
-
-    private void assertWallpaperShowing() {
-        WindowState wallpaper =
-                mAmWmState.getWmState().findFirstWindowWithType(WindowState.TYPE_WALLPAPER);
-        assertNotNull(wallpaper);
-        assertTrue(wallpaper.isShown());
-    }
-
-    public void testDismissKeyguardAttrActivity_method_turnScreenOn() throws Exception {
-        if (!isHandheld()) {
-            return;
-        }
-
-        final String activityName = "TurnScreenOnAttrDismissKeyguardActivity";
-        sleepDevice();
-
-        final String logSeparator = clearLogcat();
-        mAmWmState.computeState(mDevice, null);
-        assertTrue(mAmWmState.getAmState().getKeyguardControllerState().keyguardShowing);
-        launchActivity(activityName);
-        mAmWmState.waitForKeyguardGone(mDevice);
-        mAmWmState.assertVisibility(activityName, true);
-        assertFalse(mAmWmState.getAmState().getKeyguardControllerState().keyguardShowing);
-        assertOnDismissSucceededInLogcat(logSeparator);
-        assertTrue(isDisplayOn());
-    }
-
-    public void testDismissKeyguardAttrActivity_method_turnScreenOn_withSecureKeyguard() throws Exception {
-        if (!isHandheld()) {
-            return;
-        }
-
-        final String activityName = "TurnScreenOnAttrDismissKeyguardActivity";
-
-        setLockCredential();
-        sleepDevice();
-
-        mAmWmState.computeState(mDevice, null);
-        assertTrue(mAmWmState.getAmState().getKeyguardControllerState().keyguardShowing);
-        launchActivity(activityName);
-        mAmWmState.waitForKeyguardShowingAndNotOccluded(mDevice);
-        mAmWmState.assertVisibility(activityName, false);
-        assertTrue(mAmWmState.getAmState().getKeyguardControllerState().keyguardShowing);
-        assertTrue(isDisplayOn());
-    }
-
-    public void testScreenOffWhileOccludedStopsActivity() throws Exception {
-        if (!isHandheld()) {
-            return;
-        }
-
-        final String logSeparator = clearLogcat();
-        gotoKeyguard();
-        mAmWmState.waitForKeyguardShowingAndNotOccluded(mDevice);
-        assertShowingAndNotOccluded();
-        launchActivity("ShowWhenLockedAttrActivity");
-        mAmWmState.computeState(mDevice, new String[] { "ShowWhenLockedAttrActivity" });
-        mAmWmState.assertVisibility("ShowWhenLockedAttrActivity", true);
-        assertShowingAndOccluded();
-        sleepDevice();
-        assertSingleLaunchAndStop("ShowWhenLockedAttrActivity", logSeparator);
-    }
-
-    public void testScreenOffCausesSingleStop() throws Exception {
-        if (!isHandheld()) {
-            return;
-        }
-
-        final String logSeparator = clearLogcat();
-        launchActivity("TestActivity");
-        mAmWmState.assertVisibility("TestActivity", true);
-        sleepDevice();
-        assertSingleLaunchAndStop("TestActivity", logSeparator);
-    }
-}
diff --git a/hostsidetests/services/activityandwindowmanager/activitymanager/src/android/server/cts/KeyguardTransitionTests.java b/hostsidetests/services/activityandwindowmanager/activitymanager/src/android/server/cts/KeyguardTransitionTests.java
deleted file mode 100644
index 5971ccb..0000000
--- a/hostsidetests/services/activityandwindowmanager/activitymanager/src/android/server/cts/KeyguardTransitionTests.java
+++ /dev/null
@@ -1,153 +0,0 @@
-/*
- * Copyright (C) 2016 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License
- */
-
-package android.server.cts;
-
-import static android.server.cts.WindowManagerState.TRANSIT_ACTIVITY_OPEN;
-import static android.server.cts.WindowManagerState.TRANSIT_KEYGUARD_GOING_AWAY;
-import static android.server.cts.WindowManagerState.TRANSIT_KEYGUARD_GOING_AWAY_ON_WALLPAPER;
-import static android.server.cts.WindowManagerState.TRANSIT_KEYGUARD_OCCLUDE;
-import static android.server.cts.WindowManagerState.TRANSIT_KEYGUARD_UNOCCLUDE;
-
-/**
- * Build: mmma -j32 cts/hostsidetests/services
- * Run: cts/hostsidetests/services/activityandwindowmanager/util/run-test CtsServicesHostTestCases android.server.cts.KeyguardTransitionTests
- */
-public class KeyguardTransitionTests extends ActivityManagerTestBase {
-
-    @Override
-    public void setUp() throws Exception {
-        super.setUp();
-
-        // Set screen lock (swipe)
-        setLockDisabled(false);
-    }
-
-    public void testUnlock() throws Exception {
-        if (!isHandheld() || isUiModeLockedToVrHeadset()) {
-            return;
-        }
-        launchActivity("TestActivity");
-        gotoKeyguard();
-        unlockDevice();
-        mAmWmState.computeState(mDevice, new String[] { "TestActivity"} );
-        assertEquals("Picked wrong transition", TRANSIT_KEYGUARD_GOING_AWAY,
-                mAmWmState.getWmState().getLastTransition());
-    }
-
-    public void testUnlockWallpaper() throws Exception {
-        if (!isHandheld() || isUiModeLockedToVrHeadset()) {
-            return;
-        }
-        launchActivity("WallpaperActivity");
-        gotoKeyguard();
-        unlockDevice();
-        mAmWmState.computeState(mDevice, new String[] { "WallpaperActivity"} );
-        assertEquals("Picked wrong transition", TRANSIT_KEYGUARD_GOING_AWAY_ON_WALLPAPER,
-                mAmWmState.getWmState().getLastTransition());
-    }
-
-    public void testOcclude() throws Exception {
-        if (!isHandheld() || isUiModeLockedToVrHeadset()) {
-            return;
-        }
-        gotoKeyguard();
-        launchActivity("ShowWhenLockedActivity");
-        mAmWmState.computeState(mDevice, new String[] { "ShowWhenLockedActivity"} );
-        assertEquals("Picked wrong transition", TRANSIT_KEYGUARD_OCCLUDE,
-                mAmWmState.getWmState().getLastTransition());
-    }
-
-    public void testUnocclude() throws Exception {
-        if (!isHandheld() || isUiModeLockedToVrHeadset()) {
-            return;
-        }
-        gotoKeyguard();
-        launchActivity("ShowWhenLockedActivity");
-        launchActivity("TestActivity");
-        mAmWmState.waitForKeyguardShowingAndNotOccluded(mDevice);
-        mAmWmState.computeState(mDevice, null);
-        assertEquals("Picked wrong transition", TRANSIT_KEYGUARD_UNOCCLUDE,
-                mAmWmState.getWmState().getLastTransition());
-    }
-
-    public void testNewActivityDuringOccluded() throws Exception {
-        if (!isHandheld() || isUiModeLockedToVrHeadset()) {
-            return;
-        }
-        launchActivity("ShowWhenLockedActivity");
-        gotoKeyguard();
-        launchActivity("ShowWhenLockedWithDialogActivity");
-        mAmWmState.computeState(mDevice, new String[] { "ShowWhenLockedWithDialogActivity" });
-        assertEquals("Picked wrong transition", TRANSIT_ACTIVITY_OPEN,
-                mAmWmState.getWmState().getLastTransition());
-    }
-
-    public void testOccludeManifestAttr() throws Exception {
-         if (!isHandheld()) {
-             return;
-         }
-
-         String activityName = "ShowWhenLockedAttrActivity";
-
-         gotoKeyguard();
-         final String logSeparator = clearLogcat();
-         launchActivity(activityName);
-         mAmWmState.computeState(mDevice, new String[] {activityName});
-         assertEquals("Picked wrong transition", TRANSIT_KEYGUARD_OCCLUDE,
-                 mAmWmState.getWmState().getLastTransition());
-         assertSingleLaunch(activityName, logSeparator);
-    }
-
-    public void testOccludeAttrRemove() throws Exception {
-        if (!isHandheld()) {
-            return;
-        }
-
-        String activityName = "ShowWhenLockedAttrRemoveAttrActivity";
-
-        gotoKeyguard();
-        String logSeparator = clearLogcat();
-        launchActivity(activityName);
-        mAmWmState.computeState(mDevice, new String[] {activityName});
-        assertEquals("Picked wrong transition", TRANSIT_KEYGUARD_OCCLUDE,
-                mAmWmState.getWmState().getLastTransition());
-        assertSingleLaunch(activityName, logSeparator);
-
-        gotoKeyguard();
-        logSeparator = clearLogcat();
-        launchActivity(activityName);
-        mAmWmState.computeState(mDevice, new String[] {activityName});
-        assertSingleStartAndStop(activityName, logSeparator);
-    }
-
-    public void testNewActivityDuringOccludedWithAttr() throws Exception {
-        if (!isHandheld()) {
-            return;
-        }
-
-        String activityName1 = "ShowWhenLockedAttrActivity";
-        String activityName2 = "ShowWhenLockedAttrWithDialogActivity";
-
-        launchActivity(activityName1);
-        gotoKeyguard();
-        launchActivity(activityName2);
-        mAmWmState.computeState(mDevice, new String[] { activityName2 });
-        assertEquals("Picked wrong transition", TRANSIT_ACTIVITY_OPEN,
-                mAmWmState.getWmState().getLastTransition());
-    }
-
-}
diff --git a/hostsidetests/services/activityandwindowmanager/activitymanager/src/android/server/cts/SplashscreenTests.java b/hostsidetests/services/activityandwindowmanager/activitymanager/src/android/server/cts/SplashscreenTests.java
deleted file mode 100644
index ed7f383..0000000
--- a/hostsidetests/services/activityandwindowmanager/activitymanager/src/android/server/cts/SplashscreenTests.java
+++ /dev/null
@@ -1,79 +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.server.cts;
-
-import java.awt.Color;
-import java.awt.Rectangle;
-import java.awt.image.BufferedImage;
-
-/**
- * Build: mmma -j32 cts/hostsidetests/services
- * Run: cts/hostsidetests/services/activityandwindowmanager/util/run-test CtsServicesHostTestCases android.server.cts.SplashscreenTests
- */
-public class SplashscreenTests extends ActivityManagerTestBase {
-
-    public void testSplashscreenContent() throws Exception {
-        launchActivityNoWait("SplashscreenActivity");
-        mAmWmState.waitForAppTransitionIdle(mDevice);
-        mAmWmState.getWmState().getStableBounds();
-        final BufferedImage image = takeScreenshot();
-
-        // Use ratios to flexibly accomodate circular or not quite rectangular displays
-        // Note: Color.BLACK is the pixel color outside of the display region
-        assertColors(image, mAmWmState.getWmState().getStableBounds(),
-            Color.RED.getRGB(), 0.50f, Color.BLACK.getRGB(), 0.01f);
-    }
-
-    private void assertColors(BufferedImage img, Rectangle bounds, int primaryColor,
-        float expectedPrimaryRatio, int secondaryColor, float acceptableWrongRatio) {
-
-        int primaryPixels = 0;
-        int secondaryPixels = 0;
-        int wrongPixels = 0;
-        for (int x = bounds.x; x < bounds.x + bounds.width; x++) {
-            for (int y = bounds.y; y < bounds.y + bounds.height; y++) {
-                assertTrue(x < img.getWidth());
-                assertTrue(y < img.getHeight());
-                final int color = img.getRGB(x, y);
-                if (primaryColor == color) {
-                    primaryPixels++;
-                } else if (secondaryColor == color) {
-                    secondaryPixels++;
-                } else {
-                    wrongPixels++;
-                }
-            }
-        }
-
-        final int totalPixels = bounds.width * bounds.height;
-        final float primaryRatio = (float) primaryPixels / totalPixels;
-        if (primaryRatio < expectedPrimaryRatio) {
-            fail("Less than " + (expectedPrimaryRatio * 100.0f)
-                    + "% of pixels have non-primary color primaryPixels=" + primaryPixels
-                    + " secondaryPixels=" + secondaryPixels + " wrongPixels=" + wrongPixels);
-        }
-
-        // Some pixels might be covered by screen shape decorations, like rounded corners.
-        // On circular displays, there is an antialiased edge.
-        final float wrongRatio = (float) wrongPixels / totalPixels;
-        if (wrongRatio > acceptableWrongRatio) {
-            fail("More than " + (acceptableWrongRatio * 100.0f)
-                    + "% of pixels have wrong color primaryPixels=" + primaryPixels
-                    + " secondaryPixels=" + secondaryPixels + " wrongPixels=" + wrongPixels);
-        }
-    }
-}
diff --git a/hostsidetests/services/activityandwindowmanager/activitymanager/translucentapp/AndroidManifest.xml b/hostsidetests/services/activityandwindowmanager/activitymanager/translucentapp/AndroidManifest.xml
deleted file mode 100755
index ee3bff8..0000000
--- a/hostsidetests/services/activityandwindowmanager/activitymanager/translucentapp/AndroidManifest.xml
+++ /dev/null
@@ -1,33 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
-  ~ Copyright (C) 2017 The Android Open Source Project
-  ~
-  ~ Licensed under the Apache License, Version 2.0 (the "License");
-  ~ you may not use this file except in compliance with the License.
-  ~ You may obtain a copy of the License at
-  ~
-  ~      http://www.apache.org/licenses/LICENSE-2.0
-  ~
-  ~ Unless required by applicable law or agreed to in writing, software
-  ~ distributed under the License is distributed on an "AS IS" BASIS,
-  ~ WITHOUT 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"
-          xmlns:androidprv="http://schemas.android.com/apk/prv/res/android"
-          package="android.server.translucentapp">
-    <application android:label="CtsTranslucentApp">
-        <activity android:name=".TranslucentLandscapeActivity"
-                android:theme="@android:style/Theme.Translucent.NoTitleBar"
-                android:exported="true"
-                android:screenOrientation="landscape">
-            <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/services/activityandwindowmanager/activitymanager/translucentapp/src/android/server/translucentapp/TranslucentLandscapeActivity.java b/hostsidetests/services/activityandwindowmanager/activitymanager/translucentapp/src/android/server/translucentapp/TranslucentLandscapeActivity.java
deleted file mode 100644
index 471e3b6..0000000
--- a/hostsidetests/services/activityandwindowmanager/activitymanager/translucentapp/src/android/server/translucentapp/TranslucentLandscapeActivity.java
+++ /dev/null
@@ -1,22 +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.server.translucentapp;
-
-import android.app.Activity;
-
-public class TranslucentLandscapeActivity extends Activity {
-}
diff --git a/hostsidetests/services/activityandwindowmanager/activitymanager/translucentappsdk26/AndroidManifest.xml b/hostsidetests/services/activityandwindowmanager/activitymanager/translucentappsdk26/AndroidManifest.xml
deleted file mode 100755
index 43c85f5..0000000
--- a/hostsidetests/services/activityandwindowmanager/activitymanager/translucentappsdk26/AndroidManifest.xml
+++ /dev/null
@@ -1,33 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
-  ~ Copyright (C) 2017 The Android Open Source Project
-  ~
-  ~ Licensed under the Apache License, Version 2.0 (the "License");
-  ~ you may not use this file except in compliance with the License.
-  ~ You may obtain a copy of the License at
-  ~
-  ~      http://www.apache.org/licenses/LICENSE-2.0
-  ~
-  ~ Unless required by applicable law or agreed to in writing, software
-  ~ distributed under the License is distributed on an "AS IS" BASIS,
-  ~ WITHOUT 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"
-          xmlns:androidprv="http://schemas.android.com/apk/prv/res/android"
-          package="android.server.translucentapp26">
-    <application android:label="CtsTranslucentApp26">
-        <activity android:name="android.server.translucentapp.TranslucentLandscapeActivity"
-                  android:theme="@android:style/Theme.Translucent.NoTitleBar"
-                android:exported="true"
-                android:screenOrientation="landscape">
-            <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/services/activityandwindowmanager/displayserviceapp/app/AndroidManifest.xml b/hostsidetests/services/activityandwindowmanager/displayserviceapp/app/AndroidManifest.xml
deleted file mode 100644
index d3ae55e..0000000
--- a/hostsidetests/services/activityandwindowmanager/displayserviceapp/app/AndroidManifest.xml
+++ /dev/null
@@ -1,25 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
- * Copyright (C) 2017 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT 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"
-          xmlns:androidprv="http://schemas.android.com/apk/prv/res/android"
-          package="android.server.displayservice">
-    <uses-permission android:name="android.permission.WAKE_LOCK" />
-    <application android:label="CtsDisplayService">
-        <service android:name=".VirtualDisplayService"
-                 android:exported="true" />
-    </application>
-</manifest>
diff --git a/hostsidetests/services/activityandwindowmanager/displayserviceapp/app/src/android/server/displayservice/VirtualDisplayService.java b/hostsidetests/services/activityandwindowmanager/displayserviceapp/app/src/android/server/displayservice/VirtualDisplayService.java
deleted file mode 100644
index eb58963..0000000
--- a/hostsidetests/services/activityandwindowmanager/displayserviceapp/app/src/android/server/displayservice/VirtualDisplayService.java
+++ /dev/null
@@ -1,105 +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.server.displayservice;
-
-import static android.hardware.display.DisplayManager.VIRTUAL_DISPLAY_FLAG_OWN_CONTENT_ONLY;
-import static android.hardware.display.DisplayManager.VIRTUAL_DISPLAY_FLAG_PRESENTATION;
-
-import android.app.Notification;
-import android.app.NotificationChannel;
-import android.app.NotificationManager;
-import android.app.Service;
-import android.content.Intent;
-import android.graphics.PixelFormat;
-import android.hardware.display.DisplayManager;
-import android.hardware.display.VirtualDisplay;
-import android.media.ImageReader;
-import android.os.Bundle;
-import android.os.IBinder;
-import android.util.Log;
-import android.view.Surface;
-
-public class VirtualDisplayService extends Service {
-    private static final String NOTIFICATION_CHANNEL_ID = "cts/VirtualDisplayService";
-    private static final String TAG = "VirtualDisplayService";
-
-    private static final int FOREGROUND_ID = 1;
-
-    private static final int DENSITY = 160;
-    private static final int HEIGHT = 480;
-    private static final int WIDTH = 800;
-
-    private ImageReader mReader;
-    private VirtualDisplay mVirtualDisplay;
-
-    @Override
-    public void onCreate() {
-        super.onCreate();
-
-        NotificationManager notificationManager = getSystemService(NotificationManager.class);
-        notificationManager.createNotificationChannel(new NotificationChannel(
-            NOTIFICATION_CHANNEL_ID, NOTIFICATION_CHANNEL_ID,
-            NotificationManager.IMPORTANCE_DEFAULT));
-        Notification notif = new Notification.Builder(this, NOTIFICATION_CHANNEL_ID)
-                .setSmallIcon(android.R.drawable.ic_dialog_alert)
-                .build();
-        startForeground(FOREGROUND_ID, notif);
-    }
-
-    @Override
-    public int onStartCommand(Intent intent, int flags, int startId) {
-        String command = intent.getStringExtra("command");
-        Log.d(TAG, "Got command: " + command);
-
-        if ("create".equals(command)) {
-            createVirtualDisplay(intent);
-        } if ("off".equals(command)) {
-            mVirtualDisplay.setSurface(null);
-        } else if ("on".equals(command)) {
-            mVirtualDisplay.setSurface(mReader.getSurface());
-        } else if ("destroy".equals(command)) {
-            destroyVirtualDisplay();
-            stopSelf();
-        }
-
-        return START_NOT_STICKY;
-    }
-
-    @Override
-    public IBinder onBind(Intent intent) {
-        return null;
-    }
-
-    private void createVirtualDisplay(Intent intent) {
-        mReader = ImageReader.newInstance(WIDTH, HEIGHT, PixelFormat.RGBA_8888, 2);
-
-        final DisplayManager displayManager = getSystemService(DisplayManager.class);
-        final String name = "CtsVirtualDisplay";
-
-        int flags = VIRTUAL_DISPLAY_FLAG_PRESENTATION | VIRTUAL_DISPLAY_FLAG_OWN_CONTENT_ONLY;
-        if (intent.getBooleanExtra("show_content_when_locked", false /* defaultValue */)) {
-            flags |= 1 << 5; // VIRTUAL_DISPLAY_FLAG_CAN_SHOW_WITH_INSECURE_KEYGUARD
-        }
-        mVirtualDisplay = displayManager.createVirtualDisplay(
-                name, WIDTH, HEIGHT, DENSITY, mReader.getSurface(), flags);
-    }
-
-    private void destroyVirtualDisplay() {
-        mVirtualDisplay.release();
-        mReader.close();
-    }
-}
diff --git a/hostsidetests/services/activityandwindowmanager/displayserviceapp/util/Android.mk b/hostsidetests/services/activityandwindowmanager/displayserviceapp/util/Android.mk
deleted file mode 100644
index 0df59e7..0000000
--- a/hostsidetests/services/activityandwindowmanager/displayserviceapp/util/Android.mk
+++ /dev/null
@@ -1,31 +0,0 @@
-# Copyright (C) 2017 The Android Open Source Project
-#
-# Licensed under the Apache License, Version 2.0 (the "License");
-# you may not use this file except in compliance with the License.
-# You may obtain a copy of the License at
-#
-#      http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing, software
-# distributed under the License is distributed on an "AS IS" BASIS,
-# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-# See the License for the specific language governing permissions and
-# limitations under the License.
-
-LOCAL_PATH := $(call my-dir)
-
-include $(CLEAR_VARS)
-
-LOCAL_MODULE_TAGS := tests
-
-LOCAL_SRC_FILES := $(call all-java-files-under, src)
-
-LOCAL_ADDITIONAL_DEPENDENCIES := $(LOCAL_PATH)/Android.mk
-
-LOCAL_JAVA_LIBRARIES := cts-tradefed tradefed
-
-LOCAL_MODULE := cts-display-service-app-util
-
-LOCAL_SDK_VERSION := current
-
-include $(BUILD_HOST_JAVA_LIBRARY)
diff --git a/hostsidetests/services/activityandwindowmanager/displayserviceapp/util/src/android/server/displayservice/DisplayHelper.java b/hostsidetests/services/activityandwindowmanager/displayserviceapp/util/src/android/server/displayservice/DisplayHelper.java
deleted file mode 100644
index 89683a0..0000000
--- a/hostsidetests/services/activityandwindowmanager/displayserviceapp/util/src/android/server/displayservice/DisplayHelper.java
+++ /dev/null
@@ -1,132 +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.server.displayservice;
-
-import static junit.framework.Assert.assertTrue;
-
-import com.android.tradefed.device.CollectingOutputReceiver;
-import com.android.tradefed.device.DeviceNotAvailableException;
-import com.android.tradefed.device.ITestDevice;
-import com.android.tradefed.testtype.DeviceTestCase;
-import java.util.regex.Pattern;
-import java.util.regex.Matcher;
-
-public class DisplayHelper {
-    private static final String VIRTUAL_DISPLAY_NAME = "CtsVirtualDisplay";
-    private static final String VIRTUAL_DISPLAY_SERVICE =
-            "android.server.displayservice/.VirtualDisplayService";
-    private static final Pattern mDisplayDevicePattern = Pattern.compile(
-            ".*DisplayDeviceInfo\\{\"([^\"]+)\":.*, state (\\S+),.*\\}.*");
-
-    private boolean mCreated;
-    private final ITestDevice mDevice;
-
-    public DisplayHelper(ITestDevice device) {
-        mDevice = device;
-    }
-
-    public void createAndWaitForDisplay(boolean external, boolean requestShowWhenLocked)
-            throws DeviceNotAvailableException {
-        StringBuilder command =
-                new StringBuilder("am startfgservice -n " + VIRTUAL_DISPLAY_SERVICE);
-        command.append(" --es command create");
-        if (external) {
-            command.append(" --ez external_display true");
-        }
-        if (requestShowWhenLocked) {
-            command.append(" --ez show_content_when_locked true");
-        }
-        mDevice.executeShellCommand(command.toString());
-
-        waitForDisplayState(mDevice, false /* default */, true /* exists */, true /* on */);
-        mCreated = true;
-    }
-
-    public void turnDisplayOff() throws DeviceNotAvailableException {
-        mDevice.executeShellCommand(
-                "am start-service -n " + VIRTUAL_DISPLAY_SERVICE + " --es command off");
-        waitForDisplayState(mDevice, false /* default */, true /* exists */, false /* on */);
-    }
-
-    public void turnDisplayOn() throws DeviceNotAvailableException {
-        mDevice.executeShellCommand(
-                "am start-service -n " + VIRTUAL_DISPLAY_SERVICE + " --es command on");
-        waitForDisplayState(mDevice, false /* default */, true /* exists */, true /* on */);
-    }
-
-    public void releaseDisplay() throws DeviceNotAvailableException {
-        if (mCreated) {
-            mDevice.executeShellCommand(
-                    "am start-service -n " + VIRTUAL_DISPLAY_SERVICE + " --es command destroy");
-            waitForDisplayState(mDevice, false /* default */, false /* exists */, true /* on */);
-        }
-        mCreated = false;
-    }
-
-    public static void waitForDefaultDisplayState(ITestDevice device, boolean wantOn)
-            throws DeviceNotAvailableException {
-        waitForDisplayState(device, true /* default */, true /* exists */, wantOn);
-    }
-
-    public static boolean getDefaultDisplayState(ITestDevice device)
-            throws DeviceNotAvailableException {
-        return getDisplayState(device, true);
-    }
-
-    private static void waitForDisplayState(
-            ITestDevice device, boolean defaultDisplay, boolean wantExists, boolean wantOn)
-            throws DeviceNotAvailableException {
-        int tries = 0;
-        boolean done = false;
-        do {
-            if (tries > 0) {
-                try {
-                    Thread.sleep(500);
-                } catch (InterruptedException e) {
-                    // Oh well
-                }
-            }
-
-            Boolean state = getDisplayState(device, defaultDisplay);
-            done = (!wantExists && state == null)
-                    || (wantExists && state != null && state == wantOn);
-
-            tries++;
-        } while (tries < 10 && !done);
-
-        assertTrue(done);
-    }
-
-    private static Boolean getDisplayState(ITestDevice device, boolean defaultDisplay)
-            throws DeviceNotAvailableException {
-        final CollectingOutputReceiver outputReceiver = new CollectingOutputReceiver();
-        device.executeShellCommand("dumpsys display", outputReceiver);
-        String dump = outputReceiver.getOutput();
-
-        boolean displayExists = false;
-        boolean displayOn = false;
-        for (String line : dump.split("\\n")) {
-            Matcher matcher = mDisplayDevicePattern.matcher(line);
-            if (matcher.matches()) {
-                if ((defaultDisplay && line.contains("FLAG_DEFAULT_DISPLAY"))
-                        || (!defaultDisplay && VIRTUAL_DISPLAY_NAME.equals(matcher.group(1)))) {
-                    return "ON".equals(matcher.group(2));
-                }
-            }
-        }
-        return null;
-    }
-}
diff --git a/hostsidetests/services/activityandwindowmanager/util/Android.mk b/hostsidetests/services/activityandwindowmanager/util/Android.mk
deleted file mode 100644
index 993ba94..0000000
--- a/hostsidetests/services/activityandwindowmanager/util/Android.mk
+++ /dev/null
@@ -1,34 +0,0 @@
-# Copyright (C) 2012 The Android Open Source Project
-#
-# Licensed under the Apache License, Version 2.0 (the "License");
-# you may not use this file except in compliance with the License.
-# You may obtain a copy of the License at
-#
-#      http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing, software
-# distributed under the License is distributed on an "AS IS" BASIS,
-# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-# See the License for the specific language governing permissions and
-# limitations under the License.
-
-LOCAL_PATH:= $(call my-dir)
-
-include $(CLEAR_VARS)
-
-LOCAL_MODULE_TAGS := tests
-
-LOCAL_SRC_FILES := \
-    $(call all-java-files-under, src)
-
-LOCAL_ADDITIONAL_DEPENDENCIES := $(LOCAL_PATH)/Android.mk
-
-LOCAL_JAVA_LIBRARIES := cts-tradefed tradefed
-
-LOCAL_MODULE := cts-amwm-util
-
-LOCAL_SDK_VERSION := current
-
-include $(BUILD_HOST_JAVA_LIBRARY)
-
-include $(call all-makefiles-under,$(LOCAL_PATH))
diff --git a/hostsidetests/services/activityandwindowmanager/util/src/android/server/cts/ActivityAndWindowManagersState.java b/hostsidetests/services/activityandwindowmanager/util/src/android/server/cts/ActivityAndWindowManagersState.java
deleted file mode 100644
index a54ed19..0000000
--- a/hostsidetests/services/activityandwindowmanager/util/src/android/server/cts/ActivityAndWindowManagersState.java
+++ /dev/null
@@ -1,844 +0,0 @@
-/*
- * Copyright (C) 2016 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License
- */
-
-package android.server.cts;
-
-import static android.server.cts.ActivityManagerState.RESIZE_MODE_RESIZEABLE;
-import static android.server.cts.ActivityManagerTestBase.DOCKED_STACK_ID;
-import static android.server.cts.ActivityManagerTestBase.FREEFORM_WORKSPACE_STACK_ID;
-import static android.server.cts.ActivityManagerTestBase.HOME_STACK_ID;
-import static android.server.cts.ActivityManagerTestBase.PINNED_STACK_ID;
-import static android.server.cts.ActivityManagerTestBase.componentName;
-import static android.server.cts.StateLogger.log;
-import static android.server.cts.StateLogger.logE;
-
-import android.server.cts.ActivityManagerState.ActivityStack;
-import android.server.cts.ActivityManagerState.ActivityTask;
-import android.server.cts.WindowManagerState.Display;
-import android.server.cts.WindowManagerState.WindowStack;
-import android.server.cts.WindowManagerState.WindowState;
-import android.server.cts.WindowManagerState.WindowTask;
-
-import com.android.tradefed.device.ITestDevice;
-import com.android.tradefed.device.DeviceNotAvailableException;
-
-import junit.framework.Assert;
-
-import java.awt.Rectangle;
-import java.util.ArrayList;
-import java.util.List;
-import java.util.Objects;
-import java.util.function.BiPredicate;
-import java.util.function.BooleanSupplier;
-import java.util.function.Predicate;
-
-/** Combined state of the activity manager and window manager. */
-public class ActivityAndWindowManagersState extends Assert {
-
-    // Clone of android DisplayMetrics.DENSITY_DEFAULT (DENSITY_MEDIUM)
-    // (Needed in host-side tests to convert dp to px.)
-    private static final int DISPLAY_DENSITY_DEFAULT = 160;
-    public static final int DEFAULT_DISPLAY_ID = 0;
-
-    // Default minimal size of resizable task, used if none is set explicitly.
-    // Must be kept in sync with 'default_minimal_size_resizable_task' dimen from frameworks/base.
-    private static final int DEFAULT_RESIZABLE_TASK_SIZE_DP = 220;
-
-    // Default minimal size of a resizable PiP task, used if none is set explicitly.
-    // Must be kept in sync with 'default_minimal_size_pip_resizable_task' dimen from
-    // frameworks/base.
-    private static final int DEFAULT_PIP_RESIZABLE_TASK_SIZE_DP = 108;
-
-    private ActivityManagerState mAmState = new ActivityManagerState();
-    private WindowManagerState mWmState = new WindowManagerState();
-
-    private final List<WindowManagerState.WindowState> mTempWindowList = new ArrayList<>();
-
-    private boolean mUseActivityNames = true;
-
-    /**
-     * Compute AM and WM state of device, check sanity and bounds.
-     * WM state will include only visible windows, stack and task bounds will be compared.
-     *
-     * @param device test device.
-     * @param waitForActivitiesVisible array of activity names to wait for.
-     */
-    public void computeState(ITestDevice device, String[] waitForActivitiesVisible)
-            throws Exception {
-        computeState(device, waitForActivitiesVisible, true);
-    }
-
-    /**
-     * Compute AM and WM state of device, check sanity and bounds.
-     *
-     * @param device test device.
-     * @param waitForActivitiesVisible array of activity names to wait for.
-     * @param compareTaskAndStackBounds pass 'true' if stack and task bounds should be compared,
-     *                                  'false' otherwise.
-     */
-    void computeState(ITestDevice device, String[] waitForActivitiesVisible,
-                      boolean compareTaskAndStackBounds) throws Exception {
-        waitForValidState(device, waitForActivitiesVisible, null /* stackIds */,
-                compareTaskAndStackBounds);
-
-        assertSanity();
-        assertValidBounds(compareTaskAndStackBounds);
-    }
-
-    /**
-     * By default computeState allows you to pass only the activity name it and
-     * it will generate the full window name for the main activity window. In the
-     * case of secondary application windows though this isn't helpful, as they
-     * may follow a different format, so this method lets you disable that behavior,
-     * prior to calling a computeState variant
-     */
-    void setUseActivityNamesForWindowNames(boolean useActivityNames) {
-        mUseActivityNames = useActivityNames;
-    }
-
-    /**
-     * Compute AM and WM state of device, wait for the activity records to be added, and
-     * wait for debugger window to show up.
-     *
-     * This should only be used when starting with -D (debugger) option, where we pop up the
-     * waiting-for-debugger window, but real activity window won't show up since we're waiting
-     * for debugger.
-     */
-    void waitForDebuggerWindowVisible(
-            ITestDevice device, String[] waitForActivityRecords) throws Exception {
-        int retriesLeft = 5;
-        do {
-            mAmState.computeState(device);
-            mWmState.computeState(device);
-            if (shouldWaitForDebuggerWindow() ||
-                    shouldWaitForActivityRecords(waitForActivityRecords)) {
-                try {
-                    Thread.sleep(1000);
-                } catch (InterruptedException e) {
-                    log(e.toString());
-                    // Well I guess we are not waiting...
-                }
-            } else {
-                break;
-            }
-        } while (retriesLeft-- > 0);
-    }
-
-    /**
-     * Wait for the activity to appear and for valid state in AM and WM.
-     *
-     * @param device test device.
-     * @param waitForActivityVisible name of activity to wait for.
-     */
-    void waitForValidState(ITestDevice device, String waitForActivityVisible)
-            throws Exception {
-        waitForValidState(device, new String[]{waitForActivityVisible}, null /* stackIds */,
-                false /* compareTaskAndStackBounds */);
-    }
-
-    /**
-     * Wait for the activity to appear in proper stack and for valid state in AM and WM.
-     *
-     * @param device test device.
-     * @param waitForActivityVisible name of activity to wait for.
-     * @param stackId id of the stack where provided activity should be found.
-     */
-    void waitForValidState(ITestDevice device, String waitForActivityVisible, int stackId)
-            throws Exception {
-        waitForValidState(device, new String[]{waitForActivityVisible}, new int[]{stackId},
-                false /* compareTaskAndStackBounds */);
-    }
-
-    /**
-     * Wait for the activities to appear in proper stacks and for valid state in AM and WM.
-     *
-     * @param device test device.
-     * @param waitForActivitiesVisible array of activity names to wait for.
-     * @param stackIds ids of stack where provided activities should be found.
-     *                 Pass null to skip this check.
-     * @param compareTaskAndStackBounds flag indicating if we should compare task and stack bounds
-     *                                  for equality.
-     */
-    void waitForValidState(ITestDevice device, String[] waitForActivitiesVisible, int[] stackIds,
-            boolean compareTaskAndStackBounds) throws Exception {
-        waitForValidState(device, waitForActivitiesVisible, stackIds, compareTaskAndStackBounds,
-                componentName);
-    }
-
-    /**
-     * Wait for the activities to appear in proper stacks and for valid state in AM and WM.
-     *
-     * @param device test device.
-     * @param waitForActivitiesVisible array of activity names to wait for.
-     * @param stackIds ids of stack where provided activities should be found.
-     *                 Pass null to skip this check.
-     * @param compareTaskAndStackBounds flag indicating if we should compare task and stack bounds
-     *                                  for equality.
-     * @param packageName name of the package of activities that we're waiting for.
-     */
-    void waitForValidState(ITestDevice device, String[] waitForActivitiesVisible, int[] stackIds,
-            boolean compareTaskAndStackBounds, String packageName) throws Exception {
-        int retriesLeft = 5;
-        do {
-            // TODO: Get state of AM and WM at the same time to avoid mismatches caused by
-            // requesting dump in some intermediate state.
-            mAmState.computeState(device);
-            mWmState.computeState(device);
-            if (shouldWaitForValidStacks(compareTaskAndStackBounds)
-                    || shouldWaitForActivities(waitForActivitiesVisible, stackIds, packageName)
-                    || shouldWaitForWindows()) {
-                log("***Waiting for valid stacks and activities states...");
-                try {
-                    Thread.sleep(1000);
-                } catch (InterruptedException e) {
-                    log(e.toString());
-                    // Well I guess we are not waiting...
-                }
-            } else {
-                break;
-            }
-        } while (retriesLeft-- > 0);
-    }
-
-    void waitForAllStoppedActivities(ITestDevice device) throws Exception {
-        int retriesLeft = 5;
-        do {
-            mAmState.computeState(device);
-            if (mAmState.containsStartedActivities()){
-                log("***Waiting for valid stacks and activities states...");
-                try {
-                    Thread.sleep(1500);
-                } catch (InterruptedException e) {
-                    log(e.toString());
-                    // Well I guess we are not waiting...
-                }
-            } else {
-                break;
-            }
-        } while (retriesLeft-- > 0);
-
-        assertFalse(mAmState.containsStartedActivities());
-    }
-
-    void waitForHomeActivityVisible(ITestDevice device) throws Exception {
-        waitForValidState(device, mAmState.getHomeActivityName());
-    }
-
-    /** @return true if recents activity is visible. Devices without recents will return false */
-    boolean waitForRecentsActivityVisible(ITestDevice device) throws Exception {
-        waitForWithAmState(device, ActivityManagerState::isRecentsActivityVisible,
-                "***Waiting for recents activity to be visible...");
-        return mAmState.isRecentsActivityVisible();
-    }
-
-    void waitForKeyguardShowingAndNotOccluded(ITestDevice device) throws Exception {
-        waitForWithAmState(device, state -> state.getKeyguardControllerState().keyguardShowing
-                        && !state.getKeyguardControllerState().keyguardOccluded,
-                "***Waiting for Keyguard showing...");
-    }
-
-    void waitForKeyguardShowingAndOccluded(ITestDevice device) throws Exception {
-        waitForWithAmState(device, state -> state.getKeyguardControllerState().keyguardShowing
-                        && state.getKeyguardControllerState().keyguardOccluded,
-                "***Waiting for Keyguard showing and occluded...");
-    }
-
-    void waitForKeyguardGone(ITestDevice device) throws Exception {
-        waitForWithAmState(device, state -> !state.getKeyguardControllerState().keyguardShowing,
-                "***Waiting for Keyguard gone...");
-    }
-
-    void waitForRotation(ITestDevice device, int rotation) throws Exception {
-        waitForWithWmState(device, state -> state.getRotation() == rotation,
-                "***Waiting for Rotation: " + rotation);
-    }
-
-    void waitForDisplayUnfrozen(ITestDevice device) throws Exception {
-        waitForWithWmState(device, state -> !state.isDisplayFrozen(),
-                "***Waiting for Display unfrozen");
-    }
-
-    void waitForActivityState(ITestDevice device, String activityName, String... activityStates)
-        throws Exception {
-        waitForWithAmState(device, state -> {
-                for (String activityState : activityStates) {
-                    if (state.hasActivityState(activityName, activityState)) {
-                        return true;
-                    }
-                }
-                return false;
-            },
-            "***Waiting for Activity State: " + activityStates);
-    }
-
-    void waitForFocusedStack(ITestDevice device, int stackId) throws Exception {
-        waitForWithAmState(device, state -> state.getFocusedStackId() == stackId,
-                "***Waiting for focused stack...");
-    }
-
-    void waitForAppTransitionIdle(ITestDevice device) throws Exception {
-        waitForWithWmState(device,
-                state -> WindowManagerState.APP_STATE_IDLE.equals(state.getAppTransitionState()),
-                "***Waiting for app transition idle...");
-    }
-
-    void waitForWithAmState(ITestDevice device, Predicate<ActivityManagerState> waitCondition,
-            String message) throws Exception{
-        waitFor(device, (amState, wmState) -> waitCondition.test(amState), message);
-    }
-
-    void waitForWithWmState(ITestDevice device, Predicate<WindowManagerState> waitCondition,
-            String message) throws Exception{
-        waitFor(device, (amState, wmState) -> waitCondition.test(wmState), message);
-    }
-
-    void waitFor(ITestDevice device,
-            BiPredicate<ActivityManagerState, WindowManagerState> waitCondition, String message)
-            throws Exception {
-        waitFor(message, () -> {
-            try {
-                mAmState.computeState(device);
-                mWmState.computeState(device);
-            } catch (Exception e) {
-                logE(e.toString());
-                return false;
-            }
-            return waitCondition.test(mAmState, mWmState);
-        });
-    }
-
-    void waitFor(String message, BooleanSupplier waitCondition) throws Exception {
-        int retriesLeft = 5;
-        do {
-            if (!waitCondition.getAsBoolean()) {
-                log(message);
-                try {
-                    Thread.sleep(1000);
-                } catch (InterruptedException e) {
-                    log(e.toString());
-                    // Well I guess we are not waiting...
-                }
-            } else {
-                break;
-            }
-        } while (retriesLeft-- > 0);
-    }
-
-    /** @return true if should wait for valid stacks state. */
-    private boolean shouldWaitForValidStacks(boolean compareTaskAndStackBounds) {
-        if (!taskListsInAmAndWmAreEqual()) {
-            // We want to wait for equal task lists in AM and WM in case we caught them in the
-            // middle of some state change operations.
-            log("***taskListsInAmAndWmAreEqual=false");
-            return true;
-        }
-        if (!stackBoundsInAMAndWMAreEqual()) {
-            // We want to wait a little for the stacks in AM and WM to have equal bounds as there
-            // might be a transition animation ongoing when we got the states from WM AM separately.
-            log("***stackBoundsInAMAndWMAreEqual=false");
-            return true;
-        }
-        try {
-            // Temporary fix to avoid catching intermediate state with different task bounds in AM
-            // and WM.
-            assertValidBounds(compareTaskAndStackBounds);
-        } catch (AssertionError e) {
-            log("***taskBoundsInAMAndWMAreEqual=false : " + e.getMessage());
-            return true;
-        }
-        final int stackCount = mAmState.getStackCount();
-        if (stackCount == 0) {
-            log("***stackCount=" + stackCount);
-            return true;
-        }
-        final int resumedActivitiesCount = mAmState.getResumedActivitiesCount();
-        if (!mAmState.getKeyguardControllerState().keyguardShowing && resumedActivitiesCount != 1) {
-            log("***resumedActivitiesCount=" + resumedActivitiesCount);
-            return true;
-        }
-        if (mAmState.getFocusedActivity() == null) {
-            log("***focusedActivity=null");
-            return true;
-        }
-        return false;
-    }
-
-    /** @return true if should wait for some activities to become visible. */
-    private boolean shouldWaitForActivities(String[] waitForActivitiesVisible, int[] stackIds,
-            String packageName) {
-        if (waitForActivitiesVisible == null || waitForActivitiesVisible.length == 0) {
-            return false;
-        }
-        // If the caller is interested in us waiting for some particular activity windows to be
-        // visible before compute the state. Check for the visibility of those activity windows
-        // and for placing them in correct stacks (if requested).
-        boolean allActivityWindowsVisible = true;
-        boolean tasksInCorrectStacks = true;
-        List<WindowManagerState.WindowState> matchingWindowStates = new ArrayList<>();
-        for (int i = 0; i < waitForActivitiesVisible.length; i++) {
-            // Check if window is visible - it should be represented as one of the window states.
-            final String windowName = mUseActivityNames ?
-                    ActivityManagerTestBase.getWindowName(packageName, waitForActivitiesVisible[i])
-                    : waitForActivitiesVisible[i];
-            final String activityComponentName =
-                    ActivityManagerTestBase.getActivityComponentName(packageName,
-                            waitForActivitiesVisible[i]);
-
-            mWmState.getMatchingVisibleWindowState(windowName, matchingWindowStates);
-            boolean activityWindowVisible = !matchingWindowStates.isEmpty();
-            if (!activityWindowVisible) {
-                log("Activity window not visible: " + windowName);
-                allActivityWindowsVisible = false;
-            } else if (!mAmState.isActivityVisible(activityComponentName)) {
-                log("Activity not visible: " + activityComponentName);
-                allActivityWindowsVisible = false;
-            } else if (stackIds != null) {
-                // Check if window is already in stack requested by test.
-                boolean windowInCorrectStack = false;
-                for (WindowManagerState.WindowState ws : matchingWindowStates) {
-                    if (ws.getStackId() == stackIds[i]) {
-                        windowInCorrectStack = true;
-                        break;
-                    }
-                }
-                if (!windowInCorrectStack) {
-                    log("Window in incorrect stack: " + waitForActivitiesVisible[i]);
-                    tasksInCorrectStacks = false;
-                }
-            }
-        }
-        return !allActivityWindowsVisible || !tasksInCorrectStacks;
-    }
-
-    /** @return true if should wait valid windows state. */
-    private boolean shouldWaitForWindows() {
-        if (mWmState.getFrontWindow() == null) {
-            log("***frontWindow=null");
-            return true;
-        }
-        if (mWmState.getFocusedWindow() == null) {
-            log("***focusedWindow=null");
-            return true;
-        }
-        if (mWmState.getFocusedApp() == null) {
-            log("***focusedApp=null");
-            return true;
-        }
-
-        return false;
-    }
-
-    private boolean shouldWaitForDebuggerWindow() {
-        List<WindowManagerState.WindowState> matchingWindowStates = new ArrayList<>();
-        mWmState.getMatchingVisibleWindowState("android.server.cts", matchingWindowStates);
-        for (WindowState ws : matchingWindowStates) {
-            if (ws.isDebuggerWindow()) {
-                return false;
-            }
-        }
-        log("Debugger window not available yet");
-        return true;
-    }
-
-    private boolean shouldWaitForActivityRecords(String[] waitForActivityRecords) {
-        if (waitForActivityRecords == null || waitForActivityRecords.length == 0) {
-            return false;
-        }
-        // Check if the activity records we're looking for is already added.
-        for (int i = 0; i < waitForActivityRecords.length; i++) {
-            if (!mAmState.isActivityVisible(waitForActivityRecords[i])) {
-                log("ActivityRecord " + waitForActivityRecords[i] + " not visible yet");
-                return true;
-            }
-        }
-        return false;
-    }
-
-    ActivityManagerState getAmState() {
-        return mAmState;
-    }
-
-    public WindowManagerState getWmState() {
-        return mWmState;
-    }
-
-    void assertSanity() throws Exception {
-        assertTrue("Must have stacks", mAmState.getStackCount() > 0);
-        if (!mAmState.getKeyguardControllerState().keyguardShowing) {
-            assertEquals("There should be one and only one resumed activity in the system.",
-                    1, mAmState.getResumedActivitiesCount());
-        }
-        assertNotNull("Must have focus activity.", mAmState.getFocusedActivity());
-
-        for (ActivityStack aStack : mAmState.getStacks()) {
-            final int stackId = aStack.mStackId;
-            for (ActivityTask aTask : aStack.getTasks()) {
-                assertEquals("Stack can only contain its own tasks", stackId, aTask.mStackId);
-            }
-        }
-
-        assertNotNull("Must have front window.", mWmState.getFrontWindow());
-        assertNotNull("Must have focused window.", mWmState.getFocusedWindow());
-        assertNotNull("Must have app.", mWmState.getFocusedApp());
-    }
-
-    void assertContainsStack(String msg, int stackId) throws Exception {
-        assertTrue(msg, mAmState.containsStack(stackId));
-        assertTrue(msg, mWmState.containsStack(stackId));
-    }
-
-    void assertDoesNotContainStack(String msg, int stackId) throws Exception {
-        assertFalse(msg, mAmState.containsStack(stackId));
-        assertFalse(msg, mWmState.containsStack(stackId));
-    }
-
-    void assertFrontStack(String msg, int stackId) throws Exception {
-        assertEquals(msg, stackId, mAmState.getFrontStackId(DEFAULT_DISPLAY_ID));
-        assertEquals(msg, stackId, mWmState.getFrontStackId(DEFAULT_DISPLAY_ID));
-    }
-
-    void assertFocusedStack(String msg, int stackId) throws Exception {
-        assertEquals(msg, stackId, mAmState.getFocusedStackId());
-    }
-
-    void assertNotFocusedStack(String msg, int stackId) throws Exception {
-        if (stackId == mAmState.getFocusedStackId()) {
-            failNotEquals(msg, stackId, mAmState.getFocusedStackId());
-        }
-    }
-
-    void assertFocusedActivity(String msg, String activityName) throws Exception {
-        assertFocusedActivity(msg, componentName, activityName);
-    }
-
-    void assertFocusedActivity(String msg, String packageName, String activityName)
-            throws Exception {
-        final String componentName = ActivityManagerTestBase.getActivityComponentName(packageName,
-                activityName);
-        assertEquals(msg, componentName, mAmState.getFocusedActivity());
-        assertEquals(msg, componentName, mWmState.getFocusedApp());
-    }
-
-    void assertNotFocusedActivity(String msg, String activityName) throws Exception {
-        final String componentName = ActivityManagerTestBase.getActivityComponentName(activityName);
-        if (mAmState.getFocusedActivity().equals(componentName)) {
-            failNotEquals(msg, mAmState.getFocusedActivity(), componentName);
-        }
-        if (mWmState.getFocusedApp().equals(componentName)) {
-            failNotEquals(msg, mWmState.getFocusedApp(), componentName);
-        }
-    }
-
-    void assertResumedActivity(String msg, String activityName) throws Exception {
-        final String componentName = ActivityManagerTestBase.getActivityComponentName(activityName);
-        assertEquals(msg, componentName, mAmState.getResumedActivity());
-    }
-
-    void assertNotResumedActivity(String msg, String activityName) throws Exception {
-        final String componentName = ActivityManagerTestBase.getActivityComponentName(activityName);
-        if (mAmState.getResumedActivity().equals(componentName)) {
-            failNotEquals(msg, mAmState.getResumedActivity(), componentName);
-        }
-    }
-
-    void assertFocusedWindow(String msg, String windowName) {
-        assertEquals(msg, windowName, mWmState.getFocusedWindow());
-    }
-
-    void assertNotFocusedWindow(String msg, String windowName) {
-        if (mWmState.getFocusedWindow().equals(windowName)) {
-            failNotEquals(msg, mWmState.getFocusedWindow(), windowName);
-        }
-    }
-
-    void assertFrontWindow(String msg, String windowName) {
-        assertEquals(msg, windowName, mWmState.getFrontWindow());
-    }
-
-    void assertVisibility(String activityName, boolean visible) {
-        final String activityComponentName =
-                ActivityManagerTestBase.getActivityComponentName(activityName);
-        final String windowName =
-                ActivityManagerTestBase.getWindowName(activityName);
-        assertVisibility(activityComponentName, windowName, visible);
-    }
-
-    private void assertVisibility(String activityComponentName, String windowName,
-            boolean visible) {
-        final boolean activityVisible = mAmState.isActivityVisible(activityComponentName);
-        final boolean windowVisible = mWmState.isWindowVisible(windowName);
-
-        if (visible) {
-            assertTrue("Activity=" + activityComponentName + " must be visible.", activityVisible);
-            assertTrue("Window=" + windowName + " must be visible.", windowVisible);
-        } else {
-            assertFalse("Activity=" + activityComponentName + " must NOT be visible.",
-                    activityVisible);
-            assertFalse("Window=" + windowName + " must NOT be visible.", windowVisible);
-        }
-    }
-
-    void assertHomeActivityVisible(boolean visible) {
-        String name = mAmState.getHomeActivityName();
-        assertNotNull(name);
-        assertVisibility(name, getWindowNameForActivityName(name), visible);
-    }
-
-    /**
-     * Asserts that the device default display minimim width is larger than the minimum task width.
-     */
-    void assertDeviceDefaultDisplaySize(ITestDevice device, String errorMessage) throws Exception {
-        computeState(device, null);
-        final int minTaskSizePx = defaultMinimalTaskSize(DEFAULT_DISPLAY_ID);
-        final Display display = getWmState().getDisplay(DEFAULT_DISPLAY_ID);
-        final Rectangle displayRect = display.getDisplayRect();
-        if (Math.min(displayRect.width, displayRect.height) < minTaskSizePx) {
-            fail(errorMessage);
-        }
-    }
-
-    private String getWindowNameForActivityName(String activityName) {
-        return activityName.replaceAll("(.*)\\/\\.", "$1/$1.");
-    }
-
-    boolean taskListsInAmAndWmAreEqual() {
-        for (ActivityStack aStack : mAmState.getStacks()) {
-            final int stackId = aStack.mStackId;
-            final WindowStack wStack = mWmState.getStack(stackId);
-            if (wStack == null) {
-                log("Waiting for stack setup in WM, stackId=" + stackId);
-                return false;
-            }
-
-            for (ActivityTask aTask : aStack.getTasks()) {
-                if (wStack.getTask(aTask.mTaskId) == null) {
-                    log("Task is in AM but not in WM, waiting for it to settle, taskId="
-                            + aTask.mTaskId);
-                    return false;
-                }
-            }
-
-            for (WindowTask wTask : wStack.mTasks) {
-                if (aStack.getTask(wTask.mTaskId) == null) {
-                    log("Task is in WM but not in AM, waiting for it to settle, taskId="
-                            + wTask.mTaskId);
-                    return false;
-                }
-            }
-        }
-        return true;
-    }
-
-    int getStackPosition(int stackId) {
-        int wmStackIndex = mWmState.getStackPosition(stackId);
-        int amStackIndex = mAmState.getStackPosition(stackId);
-        assertEquals("Window and activity manager must have the same stack position index",
-                amStackIndex, wmStackIndex);
-        return wmStackIndex;
-    }
-
-    boolean stackBoundsInAMAndWMAreEqual() {
-        for (ActivityStack aStack : mAmState.getStacks()) {
-            final int stackId = aStack.mStackId;
-            final WindowStack wStack = mWmState.getStack(stackId);
-            if (aStack.isFullscreen() != wStack.isFullscreen()) {
-                log("Waiting for correct fullscreen state, stackId=" + stackId);
-                return false;
-            }
-
-            final Rectangle aStackBounds = aStack.getBounds();
-            final Rectangle wStackBounds = wStack.getBounds();
-
-            if (aStack.isFullscreen()) {
-                if (aStackBounds != null) {
-                    log("Waiting for correct stack state in AM, stackId=" + stackId);
-                    return false;
-                }
-            } else if (!Objects.equals(aStackBounds, wStackBounds)) {
-                // If stack is not fullscreen - comparing bounds. Not doing it always because
-                // for fullscreen stack bounds in WM can be either null or equal to display size.
-                log("Waiting for stack bound equality in AM and WM, stackId=" + stackId);
-                return false;
-            }
-        }
-
-        return true;
-    }
-
-    /** Check task bounds when docked to top/left. */
-    void assertDockedTaskBounds(int taskWidth, int taskHeight, String activityName) {
-        // Task size can be affected by default minimal size.
-        int defaultMinimalTaskSize = defaultMinimalTaskSize(
-                mAmState.getStackById(ActivityManagerTestBase.DOCKED_STACK_ID).mDisplayId);
-        int targetWidth = Math.max(taskWidth, defaultMinimalTaskSize);
-        int targetHeight = Math.max(taskHeight, defaultMinimalTaskSize);
-
-        assertEquals(new Rectangle(0, 0, targetWidth, targetHeight),
-                mAmState.getTaskByActivityName(activityName).getBounds());
-    }
-
-    void assertValidBounds(boolean compareTaskAndStackBounds) {
-        // Cycle through the stacks and tasks to figure out if the home stack is resizable
-        final ActivityTask homeTask = mAmState.getHomeTask();
-        final boolean homeStackIsResizable = homeTask != null
-                && homeTask.getResizeMode().equals(RESIZE_MODE_RESIZEABLE);
-
-        for (ActivityStack aStack : mAmState.getStacks()) {
-            final int stackId = aStack.mStackId;
-            final WindowStack wStack = mWmState.getStack(stackId);
-            assertNotNull("stackId=" + stackId + " in AM but not in WM?", wStack);
-
-            assertEquals("Stack fullscreen state in AM and WM must be equal stackId=" + stackId,
-                    aStack.isFullscreen(), wStack.isFullscreen());
-
-            final Rectangle aStackBounds = aStack.getBounds();
-            final Rectangle wStackBounds = wStack.getBounds();
-
-            if (aStack.isFullscreen()) {
-                assertNull("Stack bounds in AM must be null stackId=" + stackId, aStackBounds);
-            } else {
-                assertEquals("Stack bounds in AM and WM must be equal stackId=" + stackId,
-                        aStackBounds, wStackBounds);
-            }
-
-            for (ActivityTask aTask : aStack.getTasks()) {
-                final int taskId = aTask.mTaskId;
-                final WindowTask wTask = wStack.getTask(taskId);
-                assertNotNull(
-                        "taskId=" + taskId + " in AM but not in WM? stackId=" + stackId, wTask);
-
-                final boolean aTaskIsFullscreen = aTask.isFullscreen();
-                final boolean wTaskIsFullscreen = wTask.isFullscreen();
-                assertEquals("Task fullscreen state in AM and WM must be equal taskId=" + taskId
-                        + ", stackId=" + stackId, aTaskIsFullscreen, wTaskIsFullscreen);
-
-                final Rectangle aTaskBounds = aTask.getBounds();
-                final Rectangle wTaskBounds = wTask.getBounds();
-                final Rectangle displayRect = mWmState.getDisplay(aStack.mDisplayId)
-                        .getDisplayRect();
-
-                if (aTaskIsFullscreen) {
-                    assertNull("Task bounds in AM must be null for fullscreen taskId=" + taskId,
-                            aTaskBounds);
-                } else if (!homeStackIsResizable && mWmState.isDockedStackMinimized()
-                        && displayRect.getWidth() > displayRect.getHeight()) {
-                    // When minimized using non-resizable launcher in landscape mode, it will move
-                    // the task offscreen in the negative x direction unlike portrait that crops.
-                    // The x value in the task bounds will not match the stack bounds since the
-                    // only the task was moved.
-                    assertEquals("Task bounds in AM and WM must match width taskId=" + taskId
-                            + ", stackId" + stackId, aTaskBounds.getWidth(),
-                            wTaskBounds.getWidth());
-                    assertEquals("Task bounds in AM and WM must match height taskId=" + taskId
-                                    + ", stackId" + stackId, aTaskBounds.getHeight(),
-                            wTaskBounds.getHeight());
-                    assertEquals("Task bounds must match stack bounds y taskId=" + taskId
-                                    + ", stackId" + stackId, aTaskBounds.getY(),
-                            wTaskBounds.getY());
-                    assertEquals("Task and stack bounds must match width taskId=" + taskId
-                                    + ", stackId" + stackId, aStackBounds.getWidth(),
-                            wTaskBounds.getWidth());
-                    assertEquals("Task and stack bounds must match height taskId=" + taskId
-                                    + ", stackId" + stackId, aStackBounds.getHeight(),
-                            wTaskBounds.getHeight());
-                    assertEquals("Task and stack bounds must match y taskId=" + taskId
-                                    + ", stackId" + stackId, aStackBounds.getY(),
-                            wTaskBounds.getY());
-                } else {
-                    assertEquals("Task bounds in AM and WM must be equal taskId=" + taskId
-                            + ", stackId=" + stackId, aTaskBounds, wTaskBounds);
-
-                    if (compareTaskAndStackBounds && stackId != FREEFORM_WORKSPACE_STACK_ID) {
-                        int aTaskMinWidth = aTask.getMinWidth();
-                        int aTaskMinHeight = aTask.getMinHeight();
-
-                        if (aTaskMinWidth == -1 || aTaskMinHeight == -1) {
-                            // Minimal dimension(s) not set for task - it should be using defaults.
-                            int defaultMinimalSize = (stackId == PINNED_STACK_ID)
-                                    ? defaultMinimalPinnedTaskSize(aStack.mDisplayId)
-                                    : defaultMinimalTaskSize(aStack.mDisplayId);
-
-                            if (aTaskMinWidth == -1) {
-                                aTaskMinWidth = defaultMinimalSize;
-                            }
-                            if (aTaskMinHeight == -1) {
-                                aTaskMinHeight = defaultMinimalSize;
-                            }
-                        }
-
-                        if (aStackBounds.getWidth() >= aTaskMinWidth
-                                && aStackBounds.getHeight() >= aTaskMinHeight
-                                || stackId == PINNED_STACK_ID) {
-                            // Bounds are not smaller then minimal possible, so stack and task
-                            // bounds must be equal.
-                            assertEquals("Task bounds must be equal to stack bounds taskId="
-                                    + taskId + ", stackId=" + stackId, aStackBounds, wTaskBounds);
-                        } else if (stackId == DOCKED_STACK_ID && homeStackIsResizable
-                                && mWmState.isDockedStackMinimized()) {
-                            // Portrait if the display height is larger than the width
-                            if (displayRect.getHeight() > displayRect.getWidth()) {
-                                assertEquals("Task width must be equal to stack width taskId="
-                                        + taskId + ", stackId=" + stackId,
-                                        aStackBounds.getWidth(), wTaskBounds.getWidth());
-                                assertTrue("Task height must be greater than stack height "
-                                        + "taskId=" + taskId + ", stackId=" + stackId,
-                                        aStackBounds.getHeight() < wTaskBounds.getHeight());
-                                assertEquals("Task and stack x position must be equal taskId="
-                                        + taskId + ", stackId=" + stackId,
-                                        wTaskBounds.getX(), wStackBounds.getX());
-                            } else {
-                                assertTrue("Task width must be greater than stack width taskId="
-                                        + taskId + ", stackId=" + stackId,
-                                        aStackBounds.getWidth() < wTaskBounds.getWidth());
-                                assertEquals("Task height must be equal to stack height taskId="
-                                        + taskId + ", stackId=" + stackId,
-                                        aStackBounds.getHeight(), wTaskBounds.getHeight());
-                                assertEquals("Task and stack y position must be equal taskId="
-                                        + taskId + ", stackId=" + stackId, wTaskBounds.getY(),
-                                        wStackBounds.getY());
-                            }
-                        } else {
-                            // Minimal dimensions affect task size, so bounds of task and stack must
-                            // be different - will compare dimensions instead.
-                            int targetWidth = (int) Math.max(aTaskMinWidth,
-                                    aStackBounds.getWidth());
-                            assertEquals("Task width must be set according to minimal width"
-                                            + " taskId=" + taskId + ", stackId=" + stackId,
-                                    targetWidth, (int) wTaskBounds.getWidth());
-                            int targetHeight = (int) Math.max(aTaskMinHeight,
-                                    aStackBounds.getHeight());
-                            assertEquals("Task height must be set according to minimal height"
-                                            + " taskId=" + taskId + ", stackId=" + stackId,
-                                    targetHeight, (int) wTaskBounds.getHeight());
-                        }
-                    }
-                }
-            }
-        }
-    }
-
-    static int dpToPx(float dp, int densityDpi){
-        return (int) (dp * densityDpi / DISPLAY_DENSITY_DEFAULT + 0.5f);
-    }
-
-    private int defaultMinimalTaskSize(int displayId) {
-        return dpToPx(DEFAULT_RESIZABLE_TASK_SIZE_DP, mWmState.getDisplay(displayId).getDpi());
-    }
-
-    private int defaultMinimalPinnedTaskSize(int displayId) {
-        return dpToPx(DEFAULT_PIP_RESIZABLE_TASK_SIZE_DP, mWmState.getDisplay(displayId).getDpi());
-    }
-}
diff --git a/hostsidetests/services/activityandwindowmanager/util/src/android/server/cts/ActivityManagerState.java b/hostsidetests/services/activityandwindowmanager/util/src/android/server/cts/ActivityManagerState.java
deleted file mode 100644
index fa79c5e..0000000
--- a/hostsidetests/services/activityandwindowmanager/util/src/android/server/cts/ActivityManagerState.java
+++ /dev/null
@@ -1,910 +0,0 @@
-/*
- * Copyright (C) 2016 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License
- */
-
-package android.server.cts;
-
-import com.android.tradefed.device.CollectingOutputReceiver;
-import com.android.tradefed.device.DeviceNotAvailableException;
-import com.android.tradefed.device.ITestDevice;
-
-import java.awt.Rectangle;
-import java.lang.Integer;
-import java.lang.String;
-import java.util.ArrayList;
-import java.util.Collections;
-import java.util.HashMap;
-import java.util.LinkedList;
-import java.util.List;
-
-import java.util.Map;
-import java.util.regex.Pattern;
-import java.util.regex.Matcher;
-
-import static android.server.cts.ActivityManagerTestBase.HOME_STACK_ID;
-import static android.server.cts.ActivityManagerTestBase.RECENTS_STACK_ID;
-import static android.server.cts.StateLogger.log;
-import static android.server.cts.StateLogger.logE;
-
-class ActivityManagerState {
-    public static final int DUMP_MODE_ACTIVITIES = 0;
-
-    public static final String STATE_RESUMED = "RESUMED";
-    public static final String STATE_PAUSED = "PAUSED";
-    public static final String STATE_STOPPED = "STOPPED";
-    public static final String STATE_DESTROYED = "DESTROYED";
-
-    public static final String RESIZE_MODE_RESIZEABLE = "RESIZE_MODE_RESIZEABLE";
-
-    private static final String DUMPSYS_ACTIVITY_ACTIVITIES = "dumpsys activity activities";
-
-    // Copied from ActivityRecord.java
-    private static final int APPLICATION_ACTIVITY_TYPE = 0;
-    private static final int HOME_ACTIVITY_TYPE = 1;
-    private static final int RECENTS_ACTIVITY_TYPE = 2;
-
-    private final Pattern mDisplayIdPattern = Pattern.compile("Display #(\\d+).*");
-    private final Pattern mStackIdPattern = Pattern.compile("Stack #(\\d+)\\:");
-    private final Pattern mResumedActivityPattern =
-            Pattern.compile("ResumedActivity\\: ActivityRecord\\{(.+) u(\\d+) (\\S+) (\\S+)\\}");
-    private final Pattern mFocusedStackPattern =
-            Pattern.compile("mFocusedStack=ActivityStack\\{(.+) stackId=(\\d+), (.+)\\}(.+)");
-
-    private final Pattern[] mExtractStackExitPatterns =
-            { mStackIdPattern, mResumedActivityPattern, mFocusedStackPattern, mDisplayIdPattern };
-
-    // 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();
-    // Stacks on all attached displays, in z-order with the top most at the front of the list.
-    private final Map<Integer, List<ActivityStack>> mDisplayStacks = new HashMap<>();
-    private KeyguardControllerState mKeyguardControllerState;
-    private int mFocusedStackId = -1;
-    private String mResumedActivityRecord = null;
-    private final List<String> mResumedActivities = new ArrayList();
-    private final LinkedList<String> mSysDump = new LinkedList();
-
-    void computeState(ITestDevice device) throws DeviceNotAvailableException {
-        computeState(device, DUMP_MODE_ACTIVITIES);
-    }
-
-    void computeState(ITestDevice device, int dumpMode) throws DeviceNotAvailableException {
-        // It is possible the system is in the middle of transition to the right state when we get
-        // the dump. We try a few times to get the information we need before giving up.
-        int retriesLeft = 3;
-        boolean retry = false;
-        String dump = null;
-
-        log("==============================");
-        log("     ActivityManagerState     ");
-        log("==============================");
-
-        do {
-            if (retry) {
-                log("***Incomplete AM state. Retrying...");
-                // Wait half a second between retries for activity manager to finish transitioning.
-                try {
-                    Thread.sleep(500);
-                } catch (InterruptedException e) {
-                    log(e.toString());
-                    // Well I guess we are not waiting...
-                }
-            }
-
-            final CollectingOutputReceiver outputReceiver = new CollectingOutputReceiver();
-            String dumpsysCmd = "";
-            switch (dumpMode) {
-                case DUMP_MODE_ACTIVITIES:
-                    dumpsysCmd = DUMPSYS_ACTIVITY_ACTIVITIES; break;
-            }
-            device.executeShellCommand(dumpsysCmd, outputReceiver);
-            dump = outputReceiver.getOutput();
-            parseSysDump(dump);
-
-            retry = mStacks.isEmpty() || mFocusedStackId == -1 || (mResumedActivityRecord == null
-                    || mResumedActivities.isEmpty()) && !mKeyguardControllerState.keyguardShowing;
-        } while (retry && retriesLeft-- > 0);
-
-        if (retry) {
-            log(dump);
-        }
-
-        if (mStacks.isEmpty()) {
-            logE("No stacks found...");
-        }
-        if (mFocusedStackId == -1) {
-            logE("No focused stack found...");
-        }
-        if (mResumedActivityRecord == null) {
-            logE("No focused activity found...");
-        }
-        if (mResumedActivities.isEmpty()) {
-            logE("No resumed activities found...");
-        }
-    }
-
-    private void parseSysDump(String sysDump) {
-        reset();
-
-        Collections.addAll(mSysDump, sysDump.split("\\n"));
-
-        int currentDisplayId = 0;
-        while (!mSysDump.isEmpty()) {
-            final ActivityStack stack = ActivityStack.create(mSysDump, mStackIdPattern,
-                    mExtractStackExitPatterns, currentDisplayId);
-
-            if (stack != null) {
-                mStacks.add(stack);
-                mDisplayStacks.get(currentDisplayId).add(stack);
-                if (stack.mResumedActivity != null) {
-                    mResumedActivities.add(stack.mResumedActivity);
-                }
-                continue;
-            }
-
-            KeyguardControllerState controller = KeyguardControllerState.create(
-                    mSysDump, new Pattern[0]);
-            if (controller != null) {
-                mKeyguardControllerState = controller;
-                continue;
-            }
-
-            final String line = mSysDump.pop().trim();
-
-            Matcher matcher = mFocusedStackPattern.matcher(line);
-            if (matcher.matches()) {
-                log(line);
-                final String stackId = matcher.group(2);
-                log(stackId);
-                mFocusedStackId = Integer.parseInt(stackId);
-                continue;
-            }
-
-            matcher = mResumedActivityPattern.matcher(line);
-            if (matcher.matches()) {
-                log(line);
-                mResumedActivityRecord = matcher.group(3);
-                log(mResumedActivityRecord);
-                continue;
-            }
-
-            matcher = mDisplayIdPattern.matcher(line);
-            if (matcher.matches()) {
-                log(line);
-                final String displayId = matcher.group(1);
-                log(displayId);
-                currentDisplayId = Integer.parseInt(displayId);
-                mDisplayStacks.put(currentDisplayId, new ArrayList<>());
-            }
-        }
-    }
-
-    private void reset() {
-        mStacks.clear();
-        mFocusedStackId = -1;
-        mResumedActivityRecord = null;
-        mResumedActivities.clear();
-        mSysDump.clear();
-        mKeyguardControllerState = null;
-    }
-
-    int getFrontStackId(int displayId) {
-        return mDisplayStacks.get(displayId).get(0).mStackId;
-    }
-
-    int getFocusedStackId() {
-        return mFocusedStackId;
-    }
-
-    String getFocusedActivity() {
-        return mResumedActivityRecord;
-    }
-
-    String getResumedActivity() {
-        return mResumedActivities.get(0);
-    }
-
-    int getResumedActivitiesCount() {
-        return mResumedActivities.size();
-    }
-
-    public KeyguardControllerState getKeyguardControllerState() {
-        return mKeyguardControllerState;
-    }
-
-    boolean containsStack(int stackId) {
-        return getStackById(stackId) != null;
-    }
-
-    ActivityStack getStackById(int stackId) {
-        for (ActivityStack stack : mStacks) {
-            if (stackId == stack.mStackId) {
-                return stack;
-            }
-        }
-        return null;
-    }
-
-    int getStackPosition(int stackId) {
-        for (Integer displayId : mDisplayStacks.keySet()) {
-            List<ActivityStack> stacks = mDisplayStacks.get(displayId);
-            for (int i = 0; i < stacks.size(); i++) {
-                if (stackId == stacks.get(i).mStackId) {
-                    return i;
-                }
-            }
-        }
-        return -1;
-    }
-
-    List<ActivityStack> getStacks() {
-        return new ArrayList(mStacks);
-    }
-
-    int getStackCount() {
-        return mStacks.size();
-    }
-
-    boolean containsActivity(String activityName) {
-        for (ActivityStack stack : mStacks) {
-            for (ActivityTask task : stack.mTasks) {
-                for (Activity activity : task.mActivities) {
-                    if (activity.name.equals(activityName)) {
-                        return true;
-                    }
-                }
-            }
-        }
-        return false;
-    }
-
-    boolean isActivityVisible(String activityName) {
-        for (ActivityStack stack : mStacks) {
-            for (ActivityTask task : stack.mTasks) {
-               for (Activity activity : task.mActivities) {
-                   if (activity.name.equals(activityName)) {
-                       return activity.visible;
-                   }
-               }
-            }
-        }
-        return false;
-    }
-
-    boolean containsStartedActivities() {
-        for (ActivityStack stack : mStacks) {
-            for (ActivityTask task : stack.mTasks) {
-                for (Activity activity : task.mActivities) {
-                    if (!activity.state.equals(STATE_STOPPED)
-                            && !activity.state.equals(STATE_DESTROYED)) {
-                        return true;
-                    }
-                }
-            }
-        }
-        return false;
-    }
-
-    boolean hasActivityState(String activityName, String activityState) {
-        String fullName = ActivityManagerTestBase.getActivityComponentName(activityName);
-        for (ActivityStack stack : mStacks) {
-            for (ActivityTask task : stack.mTasks) {
-                for (Activity activity : task.mActivities) {
-                    if (activity.name.equals(fullName)) {
-                        return activity.state.equals(activityState);
-                    }
-                }
-            }
-        }
-        return false;
-    }
-
-    int getActivityProcId(String activityName) {
-        for (ActivityStack stack : mStacks) {
-            for (ActivityTask task : stack.mTasks) {
-               for (Activity activity : task.mActivities) {
-                   if (activity.name.equals(activityName)) {
-                       return activity.procId;
-                   }
-               }
-            }
-        }
-        return -1;
-    }
-
-    boolean isHomeActivityVisible() {
-        final Activity homeActivity = getHomeActivity();
-        return homeActivity != null && homeActivity.visible;
-    }
-
-    boolean isRecentsActivityVisible() {
-        final Activity recentsActivity = getRecentsActivity();
-        return recentsActivity != null && recentsActivity.visible;
-    }
-
-    String getHomeActivityName() {
-        Activity activity = getHomeActivity();
-        if (activity == null) {
-            return null;
-        }
-        return activity.name;
-    }
-
-    ActivityTask getHomeTask() {
-        ActivityStack homeStack = getStackById(HOME_STACK_ID);
-        if (homeStack != null) {
-            for (ActivityTask task : homeStack.mTasks) {
-                if (task.mTaskType == HOME_ACTIVITY_TYPE) {
-                    return task;
-                }
-            }
-            return null;
-        }
-        return null;
-    }
-
-    ActivityTask getRecentsTask() {
-        ActivityStack recentsStack = getStackById(RECENTS_STACK_ID);
-        if (recentsStack != null) {
-            for (ActivityTask task : recentsStack.mTasks) {
-                if (task.mTaskType == RECENTS_ACTIVITY_TYPE) {
-                    return task;
-                }
-            }
-            return null;
-        }
-        return null;
-    }
-
-    private Activity getHomeActivity() {
-        final ActivityTask homeTask = getHomeTask();
-        return homeTask != null ? homeTask.mActivities.get(homeTask.mActivities.size() - 1) : null;
-    }
-
-    private Activity getRecentsActivity() {
-        final ActivityTask recentsTask = getRecentsTask();
-        return recentsTask != null ? recentsTask.mActivities.get(recentsTask.mActivities.size() - 1)
-                : null;
-    }
-
-    ActivityTask getTaskByActivityName(String activityName) {
-        return getTaskByActivityName(activityName, -1);
-    }
-
-    ActivityTask getTaskByActivityName(String activityName, int stackId) {
-        String fullName = ActivityManagerTestBase.getActivityComponentName(activityName);
-        for (ActivityStack stack : mStacks) {
-            if (stackId == -1 || stackId == stack.mStackId) {
-                for (ActivityTask task : stack.mTasks) {
-                    for (Activity activity : task.mActivities) {
-                        if (activity.name.equals(fullName)) {
-                            return task;
-                        }
-                    }
-                }
-            }
-        }
-        return null;
-    }
-
-    static class ActivityStack extends ActivityContainer {
-
-        private static final Pattern TASK_ID_PATTERN = Pattern.compile("Task id #(\\d+)");
-        private static final Pattern RESUMED_ACTIVITY_PATTERN = Pattern.compile(
-                "mResumedActivity\\: ActivityRecord\\{(.+) u(\\d+) (\\S+) (\\S+)\\}");
-        private static final Pattern SLEEPING_PATTERN = Pattern.compile("isSleeping=(\\S+)");
-
-        int mDisplayId;
-        int mStackId;
-        String mResumedActivity;
-        Boolean mSleeping; // A Boolean to trigger an NPE if it's not initialized
-        ArrayList<ActivityTask> mTasks = new ArrayList();
-
-        private ActivityStack() {
-        }
-
-        static ActivityStack create(LinkedList<String> dump, Pattern stackIdPattern,
-                                    Pattern[] exitPatterns, int displayId) {
-            final String line = dump.peek().trim();
-
-            final Matcher matcher = stackIdPattern.matcher(line);
-            if (!matcher.matches()) {
-                // Not a stack.
-                return null;
-            }
-            // For the stack Id line we just read.
-            dump.pop();
-
-            final ActivityStack stack = new ActivityStack();
-            stack.mDisplayId = displayId;
-            log(line);
-            final String stackId = matcher.group(1);
-            log(stackId);
-            stack.mStackId = Integer.parseInt(stackId);
-            stack.extract(dump, exitPatterns);
-            return stack;
-        }
-
-        private void extract(LinkedList<String> dump, Pattern[] exitPatterns) {
-
-            final List<Pattern> taskExitPatterns = new ArrayList();
-            Collections.addAll(taskExitPatterns, exitPatterns);
-            taskExitPatterns.add(TASK_ID_PATTERN);
-            taskExitPatterns.add(RESUMED_ACTIVITY_PATTERN);
-            final Pattern[] taskExitPatternsArray =
-                    taskExitPatterns.toArray(new Pattern[taskExitPatterns.size()]);
-
-            while (!doneExtracting(dump, exitPatterns)) {
-                final ActivityTask task =
-                        ActivityTask.create(dump, TASK_ID_PATTERN, taskExitPatternsArray);
-
-                if (task != null) {
-                    mTasks.add(task);
-                    continue;
-                }
-
-                final String line = dump.pop().trim();
-
-                if (extractFullscreen(line)) {
-                    continue;
-                }
-
-                if (extractBounds(line)) {
-                    continue;
-                }
-
-                Matcher matcher = RESUMED_ACTIVITY_PATTERN.matcher(line);
-                if (matcher.matches()) {
-                    log(line);
-                    mResumedActivity = matcher.group(3);
-                    log(mResumedActivity);
-                    continue;
-                }
-
-                matcher = SLEEPING_PATTERN.matcher(line);
-                if (matcher.matches()) {
-                    log(line);
-                    mSleeping = "true".equals(matcher.group(1));
-                    continue;
-                }
-            }
-        }
-
-        /**
-         * @return the bottom task in the stack.
-         */
-        ActivityTask getBottomTask() {
-            if (!mTasks.isEmpty()) {
-                // NOTE: Unlike the ActivityManager internals, we dump the state from top to bottom,
-                //       so the indices are inverted
-                return mTasks.get(mTasks.size() - 1);
-            }
-            return null;
-        }
-
-        /**
-         * @return the top task in the stack.
-         */
-        ActivityTask getTopTask() {
-            if (!mTasks.isEmpty()) {
-                // NOTE: Unlike the ActivityManager internals, we dump the state from top to bottom,
-                //       so the indices are inverted
-                return mTasks.get(0);
-            }
-            return null;
-        }
-
-        List<ActivityTask> getTasks() {
-            return new ArrayList(mTasks);
-        }
-
-        ActivityTask getTask(int taskId) {
-            for (ActivityTask task : mTasks) {
-                if (taskId == task.mTaskId) {
-                    return task;
-                }
-            }
-            return null;
-        }
-    }
-
-    static class ActivityTask extends ActivityContainer {
-        private static final Pattern TASK_RECORD_PATTERN = Pattern.compile("\\* TaskRecord\\"
-                + "{(\\S+) #(\\d+) (\\S+)=(\\S+) U=(\\d+) StackId=(\\d+) sz=(\\d+)\\}");
-
-        private static final Pattern LAST_NON_FULLSCREEN_BOUNDS_PATTERN = Pattern.compile(
-                "mLastNonFullscreenBounds=Rect\\((\\d+), (\\d+) - (\\d+), (\\d+)\\)");
-
-        private static final Pattern ORIG_ACTIVITY_PATTERN = Pattern.compile("origActivity=(\\S+)");
-        private static final Pattern REAL_ACTIVITY_PATTERN = Pattern.compile("realActivity=(\\S+)");
-
-        private static final Pattern ACTIVITY_NAME_PATTERN = Pattern.compile(
-                "\\* Hist #(\\d+)\\: ActivityRecord\\{(\\S+) u(\\d+) (\\S+) t(\\d+)\\}");
-
-        private static final Pattern TASK_TYPE_PATTERN = Pattern.compile("autoRemoveRecents=(\\S+) "
-                + "isPersistable=(\\S+) numFullscreen=(\\d+) taskType=(\\d+) "
-                + "mTaskToReturnTo=(\\d+)");
-
-        private static final Pattern RESIZABLE_PATTERN = Pattern.compile(
-                ".*mResizeMode=([^\\s]+).*");
-
-        int mTaskId;
-        int mStackId;
-        Rectangle mLastNonFullscreenBounds;
-        String mRealActivity;
-        String mOrigActivity;
-        ArrayList<Activity> mActivities = new ArrayList();
-        int mTaskType = -1;
-        int mReturnToType = -1;
-        private String mResizeMode;
-
-        private ActivityTask() {
-        }
-
-        static ActivityTask create(
-                LinkedList<String> dump, Pattern taskIdPattern, Pattern[] exitPatterns) {
-            final String line = dump.peek().trim();
-
-            final Matcher matcher = taskIdPattern.matcher(line);
-            if (!matcher.matches()) {
-                // Not a task.
-                return null;
-            }
-            // For the task Id line we just read.
-            dump.pop();
-
-            final ActivityTask task = new ActivityTask();
-            log(line);
-            final String taskId = matcher.group(1);
-            log(taskId);
-            task.mTaskId = Integer.parseInt(taskId);
-            task.extract(dump, exitPatterns);
-            return task;
-        }
-
-        private void extract(LinkedList<String> dump, Pattern[] exitPatterns) {
-            final List<Pattern> activityExitPatterns = new ArrayList();
-            Collections.addAll(activityExitPatterns, exitPatterns);
-            activityExitPatterns.add(ACTIVITY_NAME_PATTERN);
-            final Pattern[] activityExitPatternsArray =
-                    activityExitPatterns.toArray(new Pattern[activityExitPatterns.size()]);
-
-            while (!doneExtracting(dump, exitPatterns)) {
-                final Activity activity =
-                        Activity.create(dump, ACTIVITY_NAME_PATTERN, activityExitPatternsArray);
-
-                if (activity != null) {
-                    mActivities.add(activity);
-                    continue;
-                }
-
-                final String line = dump.pop().trim();
-
-                if (extractFullscreen(line)) {
-                    continue;
-                }
-
-                if (extractBounds(line)) {
-                    continue;
-                }
-
-                if (extractMinimalSize(line)) {
-                    continue;
-                }
-
-                Matcher matcher = TASK_RECORD_PATTERN.matcher(line);
-                if (matcher.matches()) {
-                    log(line);
-                    final String stackId = matcher.group(6);
-                    mStackId = Integer.valueOf(stackId);
-                    log(stackId);
-                    continue;
-                }
-
-                matcher = LAST_NON_FULLSCREEN_BOUNDS_PATTERN.matcher(line);
-                if (matcher.matches()) {
-                    log(line);
-                    mLastNonFullscreenBounds = extractBounds(matcher);
-                }
-
-                matcher = REAL_ACTIVITY_PATTERN.matcher(line);
-                if (matcher.matches()) {
-                    if (mRealActivity == null) {
-                        log(line);
-                        mRealActivity = matcher.group(1);
-                        log(mRealActivity);
-                    }
-                    continue;
-                }
-
-                matcher = ORIG_ACTIVITY_PATTERN.matcher(line);
-                if (matcher.matches()) {
-                    if (mOrigActivity == null) {
-                        log(line);
-                        mOrigActivity = matcher.group(1);
-                        log(mOrigActivity);
-                    }
-                    continue;
-                }
-
-                matcher = TASK_TYPE_PATTERN.matcher(line);
-                if (matcher.matches()) {
-                    log(line);
-                    mTaskType = Integer.valueOf(matcher.group(4));
-                    mReturnToType = Integer.valueOf(matcher.group(5));
-                    continue;
-                }
-
-                matcher = RESIZABLE_PATTERN.matcher(line);
-                if (matcher.matches()) {
-                    log(line);
-                    mResizeMode = matcher.group(1);
-                    log(mResizeMode);
-                    continue;
-                }
-            }
-        }
-
-        public String getResizeMode() {
-            return mResizeMode;
-        }
-
-        /**
-         * @return whether this task contains the given activity.
-         */
-        public boolean containsActivity(String activityName) {
-            for (Activity activity : mActivities) {
-                if (activity.name.equals(activityName)) {
-                    return true;
-                }
-            }
-            return false;
-        }
-    }
-
-    static class Activity {
-        private static final Pattern STATE_PATTERN = Pattern.compile("state=(\\S+).*");
-        private static final Pattern VISIBILITY_PATTERN = Pattern.compile("keysPaused=(\\S+) "
-                + "inHistory=(\\S+) visible=(\\S+) sleeping=(\\S+) idle=(\\S+) "
-                + "mStartingWindowState=(\\S+)");
-        private static final Pattern FRONT_OF_TASK_PATTERN = Pattern.compile("frontOfTask=(\\S+) "
-                + "task=TaskRecord\\{(\\S+) #(\\d+) A=(\\S+) U=(\\d+) StackId=(\\d+) sz=(\\d+)\\}");
-        private static final Pattern PROCESS_RECORD_PATTERN = Pattern.compile(
-                "app=ProcessRecord\\{(\\S+) (\\d+):(\\S+)/(.+)\\}");
-
-        String name;
-        String state;
-        boolean visible;
-        boolean frontOfTask;
-        int procId = -1;
-
-        private Activity() {
-        }
-
-        static Activity create(
-                LinkedList<String> dump, Pattern activityNamePattern, Pattern[] exitPatterns) {
-            final String line = dump.peek().trim();
-
-            final Matcher matcher = activityNamePattern.matcher(line);
-            if (!matcher.matches()) {
-                // Not an activity.
-                return null;
-            }
-            // For the activity name line we just read.
-            dump.pop();
-
-            final Activity activity = new Activity();
-            log(line);
-            activity.name = matcher.group(4);
-            log(activity.name);
-            activity.extract(dump, exitPatterns);
-            return activity;
-        }
-
-        private void extract(LinkedList<String> dump, Pattern[] exitPatterns) {
-
-            while (!doneExtracting(dump, exitPatterns)) {
-                final String line = dump.pop().trim();
-
-                // Break the activity extraction once we hit an empty line
-                if (line.isEmpty()) {
-                    break;
-                }
-
-                Matcher matcher = VISIBILITY_PATTERN.matcher(line);
-                if (matcher.matches()) {
-                    log(line);
-                    final String visibleString = matcher.group(3);
-                    visible = Boolean.valueOf(visibleString);
-                    log(visibleString);
-                    continue;
-                }
-
-                matcher = STATE_PATTERN.matcher(line);
-                if (matcher.matches()) {
-                    log(line);
-                    state = matcher.group(1);
-                    log(state);
-                    continue;
-                }
-
-                matcher = PROCESS_RECORD_PATTERN.matcher(line);
-                if (matcher.matches()) {
-                    log(line);
-                    final String procIdString = matcher.group(2);
-                    procId = Integer.valueOf(procIdString);
-                    log(procIdString);
-                    continue;
-                }
-
-                matcher = FRONT_OF_TASK_PATTERN.matcher(line);
-                if (matcher.matches()) {
-                    log(line);
-                    final String frontOfTaskString = matcher.group(1);
-                    frontOfTask = Boolean.valueOf(frontOfTaskString);
-                    log(frontOfTaskString);
-                    continue;
-                }
-            }
-        }
-    }
-
-    static abstract class ActivityContainer {
-        protected static final Pattern FULLSCREEN_PATTERN = Pattern.compile("mFullscreen=(\\S+)");
-        protected static final Pattern BOUNDS_PATTERN =
-                Pattern.compile("mBounds=Rect\\((\\d+), (\\d+) - (\\d+), (\\d+)\\)");
-        protected static final Pattern MIN_WIDTH_PATTERN =
-                Pattern.compile("mMinWidth=(\\d+)");
-        protected static final Pattern MIN_HEIGHT_PATTERN =
-                Pattern.compile("mMinHeight=(\\d+)");
-
-        protected boolean mFullscreen;
-        protected Rectangle mBounds;
-        protected int mMinWidth = -1;
-        protected int mMinHeight = -1;
-
-        boolean extractFullscreen(String line) {
-            final Matcher matcher = FULLSCREEN_PATTERN.matcher(line);
-            if (!matcher.matches()) {
-                return false;
-            }
-            log(line);
-            final String fullscreen = matcher.group(1);
-            log(fullscreen);
-            mFullscreen = Boolean.valueOf(fullscreen);
-            return true;
-        }
-
-        boolean extractBounds(String line) {
-            final Matcher matcher = BOUNDS_PATTERN.matcher(line);
-            if (!matcher.matches()) {
-                return false;
-            }
-            log(line);
-            mBounds = extractBounds(matcher);
-            return true;
-        }
-
-        static Rectangle extractBounds(Matcher matcher) {
-            final int left = Integer.valueOf(matcher.group(1));
-            final int top = Integer.valueOf(matcher.group(2));
-            final int right = Integer.valueOf(matcher.group(3));
-            final int bottom = Integer.valueOf(matcher.group(4));
-            final Rectangle rect = new Rectangle(left, top, right - left, bottom - top);
-
-            log(rect.toString());
-            return rect;
-        }
-
-        boolean extractMinimalSize(String line) {
-            final Matcher minWidthMatcher = MIN_WIDTH_PATTERN.matcher(line);
-            final Matcher minHeightMatcher = MIN_HEIGHT_PATTERN.matcher(line);
-
-            if (minWidthMatcher.matches()) {
-                log(line);
-                mMinWidth = Integer.valueOf(minWidthMatcher.group(1));
-            } else if (minHeightMatcher.matches()) {
-                log(line);
-                mMinHeight = Integer.valueOf(minHeightMatcher.group(1));
-            } else {
-                return false;
-            }
-            return true;
-        }
-
-        Rectangle getBounds() {
-            return mBounds;
-        }
-
-        boolean isFullscreen() {
-            return mFullscreen;
-        }
-
-        int getMinWidth() {
-            return mMinWidth;
-        }
-
-        int getMinHeight() {
-            return mMinHeight;
-        }
-    }
-
-    static class KeyguardControllerState {
-        private static final Pattern NAME_PATTERN = Pattern.compile("KeyguardController:");
-        private static final Pattern SHOWING_PATTERN = Pattern.compile("mKeyguardShowing=(\\S+)");
-        private static final Pattern OCCLUDED_PATTERN = Pattern.compile("mOccluded=(\\S+)");
-
-        boolean keyguardShowing;
-        boolean keyguardOccluded;
-
-        private KeyguardControllerState() {
-        }
-
-        static KeyguardControllerState create(LinkedList<String> dump, Pattern[] exitPatterns) {
-            final String line = dump.peek().trim();
-
-            final Matcher matcher = NAME_PATTERN.matcher(line);
-            if (!matcher.matches()) {
-                // Not KeyguardController
-                return null;
-            }
-
-            // For the KeyguardController line we just read.
-            dump.pop();
-
-            final KeyguardControllerState controller = new KeyguardControllerState();
-            controller.extract(dump, exitPatterns);
-            return controller;
-        }
-
-        private void extract(LinkedList<String> dump, Pattern[] exitPatterns) {
-
-            while (!doneExtracting(dump, exitPatterns)) {
-                final String line = dump.pop().trim();
-
-                Matcher matcher = SHOWING_PATTERN.matcher(line);
-                if (matcher.matches()) {
-                    log(line);
-                    final String showingString = matcher.group(1);
-                    keyguardShowing = Boolean.valueOf(showingString);
-                    log(showingString);
-                    continue;
-                }
-
-                matcher = OCCLUDED_PATTERN.matcher(line);
-                if (matcher.matches()) {
-                    log(line);
-                    final String occludedString = matcher.group(1);
-                    keyguardOccluded = Boolean.valueOf(occludedString);
-                    log(occludedString);
-                    continue;
-                }
-            }
-        }
-    }
-
-    static boolean doneExtracting(LinkedList<String> dump, Pattern[] exitPatterns) {
-        if (dump.isEmpty()) {
-            return true;
-        }
-        final String line = dump.peek().trim();
-
-        for (Pattern pattern : exitPatterns) {
-            if (pattern.matcher(line).matches()) {
-                return true;
-            }
-        }
-        return false;
-    }
-}
diff --git a/hostsidetests/services/activityandwindowmanager/util/src/android/server/cts/ActivityManagerTestBase.java b/hostsidetests/services/activityandwindowmanager/util/src/android/server/cts/ActivityManagerTestBase.java
deleted file mode 100644
index 9ba292f..0000000
--- a/hostsidetests/services/activityandwindowmanager/util/src/android/server/cts/ActivityManagerTestBase.java
+++ /dev/null
@@ -1,1459 +0,0 @@
-/*
- * Copyright (C) 2016 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License
- */
-
-package android.server.cts;
-
-import com.android.ddmlib.Log.LogLevel;
-import com.android.tradefed.device.CollectingOutputReceiver;
-import com.android.tradefed.device.DeviceNotAvailableException;
-import com.android.tradefed.device.ITestDevice;
-import com.android.tradefed.log.LogUtil.CLog;
-import com.android.tradefed.result.InputStreamSource;
-import com.android.tradefed.testtype.DeviceTestCase;
-
-import java.awt.image.BufferedImage;
-import java.lang.Exception;
-import java.lang.Integer;
-import java.lang.String;
-import java.util.HashSet;
-import java.util.List;
-import java.util.UUID;
-import java.util.regex.Matcher;
-import java.util.regex.Pattern;
-
-import static android.server.cts.StateLogger.log;
-import static android.server.cts.StateLogger.logE;
-
-import android.server.cts.ActivityManagerState.ActivityStack;
-
-import javax.imageio.ImageIO;
-
-public abstract class ActivityManagerTestBase extends DeviceTestCase {
-    private static final boolean PRETEND_DEVICE_SUPPORTS_PIP = false;
-    private static final boolean PRETEND_DEVICE_SUPPORTS_FREEFORM = false;
-    private static final String LOG_SEPARATOR = "LOG_SEPARATOR";
-
-    // Constants copied from ActivityManager.StackId. If they are changed there, these must be
-    // updated.
-    /** Invalid stack ID. */
-    public static final int INVALID_STACK_ID = -1;
-
-    /** First static stack ID. */
-    public static final int FIRST_STATIC_STACK_ID = 0;
-
-    /** Home activity stack ID. */
-    public static final int HOME_STACK_ID = FIRST_STATIC_STACK_ID;
-
-    /** ID of stack where fullscreen activities are normally launched into. */
-    public static final int FULLSCREEN_WORKSPACE_STACK_ID = 1;
-
-    /** ID of stack where freeform/resized activities are normally launched into. */
-    public static final int FREEFORM_WORKSPACE_STACK_ID = FULLSCREEN_WORKSPACE_STACK_ID + 1;
-
-    /** ID of stack that occupies a dedicated region of the screen. */
-    public static final int DOCKED_STACK_ID = FREEFORM_WORKSPACE_STACK_ID + 1;
-
-    /** ID of stack that always on top (always visible) when it exist. */
-    public static final int PINNED_STACK_ID = DOCKED_STACK_ID + 1;
-
-    /** Recents activity stack ID. */
-    public static final int RECENTS_STACK_ID = PINNED_STACK_ID + 1;
-
-    /** Assistant activity stack ID.  This stack is fullscreen and non-resizeable. */
-    public static final int ASSISTANT_STACK_ID = RECENTS_STACK_ID + 1;
-
-    protected static final int[] ALL_STACK_IDS_BUT_HOME = {
-            FULLSCREEN_WORKSPACE_STACK_ID, FREEFORM_WORKSPACE_STACK_ID, DOCKED_STACK_ID,
-            PINNED_STACK_ID, ASSISTANT_STACK_ID
-    };
-
-    protected static final int[] ALL_STACK_IDS_BUT_HOME_AND_FULLSCREEN = {
-            FREEFORM_WORKSPACE_STACK_ID, DOCKED_STACK_ID, PINNED_STACK_ID, ASSISTANT_STACK_ID
-    };
-
-    private static final String TASK_ID_PREFIX = "taskId";
-
-    private static final String AM_STACK_LIST = "am stack list";
-
-    private static final String AM_FORCE_STOP_TEST_PACKAGE = "am force-stop android.server.cts";
-    private static final String AM_FORCE_STOP_SECOND_TEST_PACKAGE
-            = "am force-stop android.server.cts.second";
-    private static final String AM_FORCE_STOP_THIRD_TEST_PACKAGE
-            = "am force-stop android.server.cts.third";
-
-    private static final String AM_REMOVE_STACK = "am stack remove ";
-
-    protected static final String AM_START_HOME_ACTIVITY_COMMAND =
-            "am start -a android.intent.action.MAIN -c android.intent.category.HOME";
-
-    protected static final String AM_MOVE_TOP_ACTIVITY_TO_PINNED_STACK_COMMAND =
-            "am stack move-top-activity-to-pinned-stack 1 0 0 500 500";
-
-    static final String LAUNCHING_ACTIVITY = "LaunchingActivity";
-    static final String ALT_LAUNCHING_ACTIVITY = "AltLaunchingActivity";
-    static final String BROADCAST_RECEIVER_ACTIVITY = "BroadcastReceiverActivity";
-
-    /** Broadcast shell command for finishing {@link BroadcastReceiverActivity}. */
-    static final String FINISH_ACTIVITY_BROADCAST
-            = "am broadcast -a trigger_broadcast --ez finish true";
-
-    /** Broadcast shell command for finishing {@link BroadcastReceiverActivity}. */
-    static final String MOVE_TASK_TO_BACK_BROADCAST
-            = "am broadcast -a trigger_broadcast --ez moveToBack true";
-
-    private static final String AM_RESIZE_DOCKED_STACK = "am stack resize-docked-stack ";
-    private static final String AM_RESIZE_STACK = "am stack resize ";
-
-    static final String AM_MOVE_TASK = "am stack move-task ";
-
-    private static final String AM_SUPPORTS_SPLIT_SCREEN_MULTIWINDOW =
-            "am supports-split-screen-multi-window";
-    private static final String AM_NO_HOME_SCREEN = "am no-home-screen";
-
-    private static final String INPUT_KEYEVENT_HOME = "input keyevent 3";
-    private static final String INPUT_KEYEVENT_BACK = "input keyevent 4";
-    private static final String INPUT_KEYEVENT_APP_SWITCH = "input keyevent 187";
-    public static final String INPUT_KEYEVENT_WINDOW = "input keyevent 171";
-
-    private static final String LOCK_CREDENTIAL = "1234";
-
-    private static final int INVALID_DISPLAY_ID = -1;
-
-    private static final String DEFAULT_COMPONENT_NAME = "android.server.cts";
-
-    private static final int UI_MODE_TYPE_MASK = 0x0f;
-    private static final int UI_MODE_TYPE_VR_HEADSET = 0x07;
-
-    static String componentName = DEFAULT_COMPONENT_NAME;
-
-    protected static final int INVALID_DEVICE_ROTATION = -1;
-
-    /** A reference to the device under test. */
-    protected ITestDevice mDevice;
-
-    private HashSet<String> mAvailableFeatures;
-
-    private boolean mLockCredentialsSet;
-
-    private boolean mLockDisabled;
-
-    protected static String getAmStartCmd(final String activityName) {
-        return "am start -n " + getActivityComponentName(activityName);
-    }
-
-    /**
-     * @return the am command to start the given activity with the following extra key/value pairs.
-     *         {@param keyValuePairs} must be a list of arguments defining each key/value extra.
-     */
-    protected static String getAmStartCmd(final String activityName,
-            final String... keyValuePairs) {
-        String base = getAmStartCmd(activityName);
-        if (keyValuePairs.length % 2 != 0) {
-            throw new RuntimeException("keyValuePairs must be pairs of key/value arguments");
-        }
-        for (int i = 0; i < keyValuePairs.length; i += 2) {
-            base += " --es " + keyValuePairs[i] + " " + keyValuePairs[i + 1];
-        }
-        return base;
-    }
-
-    protected static String getAmStartCmd(final String activityName, final int displayId) {
-        return "am start -n " + getActivityComponentName(activityName) + " -f 0x18000000"
-                + " --display " + displayId;
-    }
-
-    protected static String getAmStartCmdInNewTask(final String activityName) {
-        return "am start -n " + getActivityComponentName(activityName) + " -f 0x18000000";
-    }
-
-    protected static String getAmStartCmdOverHome(final String activityName) {
-        return "am start --activity-task-on-home -n " + getActivityComponentName(activityName);
-    }
-
-    protected static String getOrientationBroadcast(int orientation) {
-        return "am broadcast -a trigger_broadcast --ei orientation " + orientation;
-    }
-
-    static String getActivityComponentName(final String activityName) {
-        return getActivityComponentName(componentName, activityName);
-    }
-
-    private static boolean isFullyQualifiedActivityName(String name) {
-        return name != null && name.contains(".");
-    }
-
-    static String getActivityComponentName(final String packageName, final String activityName) {
-        return packageName + "/" + (isFullyQualifiedActivityName(activityName) ? "" : ".") +
-                activityName;
-    }
-
-    // A little ugly, but lets avoid having to strip static everywhere for
-    // now.
-    public static void setComponentName(String name) {
-        componentName = name;
-    }
-
-    protected static void setDefaultComponentName() {
-        setComponentName(DEFAULT_COMPONENT_NAME);
-    }
-
-    static String getBaseWindowName() {
-        return getBaseWindowName(componentName);
-    }
-
-    static String getBaseWindowName(final String packageName) {
-        return getBaseWindowName(packageName, true /*prependPackageName*/);
-    }
-
-    static String getBaseWindowName(final String packageName, boolean prependPackageName) {
-        return packageName + "/" + (prependPackageName ? packageName + "." : "");
-    }
-
-    static String getWindowName(final String activityName) {
-        return getWindowName(componentName, activityName);
-    }
-
-    static String getWindowName(final String packageName, final String activityName) {
-        return getBaseWindowName(packageName, !isFullyQualifiedActivityName(activityName))
-                + activityName;
-    }
-
-    protected ActivityAndWindowManagersState mAmWmState = new ActivityAndWindowManagersState();
-
-    private int mInitialAccelerometerRotation;
-    private int mUserRotation;
-    private float mFontScale;
-
-    private SurfaceTraceReceiver mSurfaceTraceReceiver;
-    private Thread mSurfaceTraceThread;
-
-    void installSurfaceObserver(SurfaceTraceReceiver.SurfaceObserver observer) {
-        mSurfaceTraceReceiver = new SurfaceTraceReceiver(observer);
-        mSurfaceTraceThread = new Thread() {
-            @Override
-            public void run() {
-                try {
-                    mDevice.executeShellCommand("wm surface-trace", mSurfaceTraceReceiver);
-                } catch (DeviceNotAvailableException e) {
-                    logE("Device not available: " + e.toString());
-                }
-            }
-        };
-        mSurfaceTraceThread.start();
-    }
-
-    void removeSurfaceObserver() {
-        mSurfaceTraceReceiver.cancel();
-        mSurfaceTraceThread.interrupt();
-    }
-
-    @Override
-    protected void setUp() throws Exception {
-        super.setUp();
-        setDefaultComponentName();
-
-        // Get the device, this gives a handle to run commands and install APKs.
-        mDevice = getDevice();
-        wakeUpAndUnlockDevice();
-        pressHomeButton();
-        // Remove special stacks.
-        removeStacks(ALL_STACK_IDS_BUT_HOME_AND_FULLSCREEN);
-        // Store rotation settings.
-        mInitialAccelerometerRotation = getAccelerometerRotation();
-        mUserRotation = getUserRotation();
-        mFontScale = getFontScale();
-        mLockCredentialsSet = false;
-        mLockDisabled = isLockDisabled();
-    }
-
-    @Override
-    protected void tearDown() throws Exception {
-        super.tearDown();
-        try {
-            setLockDisabled(mLockDisabled);
-            executeShellCommand(AM_FORCE_STOP_TEST_PACKAGE);
-            executeShellCommand(AM_FORCE_STOP_SECOND_TEST_PACKAGE);
-            executeShellCommand(AM_FORCE_STOP_THIRD_TEST_PACKAGE);
-            // Restore rotation settings to the state they were before test.
-            setAccelerometerRotation(mInitialAccelerometerRotation);
-            setUserRotation(mUserRotation);
-            setFontScale(mFontScale);
-            setWindowTransitionAnimationDurationScale(1);
-            // Remove special stacks.
-            removeStacks(ALL_STACK_IDS_BUT_HOME_AND_FULLSCREEN);
-            wakeUpAndUnlockDevice();
-            pressHomeButton();
-        } catch (DeviceNotAvailableException e) {
-        }
-    }
-
-    protected void removeStacks(int... stackIds) {
-        try {
-            for (Integer stackId : stackIds) {
-                executeShellCommand(AM_REMOVE_STACK + stackId);
-            }
-        } catch (DeviceNotAvailableException e) {
-        }
-    }
-
-    protected String executeShellCommand(String command) throws DeviceNotAvailableException {
-        return executeShellCommand(mDevice, command);
-    }
-
-    protected static String executeShellCommand(ITestDevice device, String command)
-            throws DeviceNotAvailableException {
-        log("adb shell " + command);
-        return device.executeShellCommand(command);
-    }
-
-    protected void executeShellCommand(String command, CollectingOutputReceiver outputReceiver)
-            throws DeviceNotAvailableException {
-        log("adb shell " + command);
-        mDevice.executeShellCommand(command, outputReceiver);
-    }
-
-    protected BufferedImage takeScreenshot() throws Exception {
-        final InputStreamSource stream = mDevice.getScreenshot("PNG", false /* rescale */);
-        if (stream == null) {
-            fail("Failed to take screenshot of device");
-        }
-        return ImageIO.read(stream.createInputStream());
-    }
-
-    protected void launchActivityInComponent(final String componentName,
-            final String targetActivityName, final String... keyValuePairs) throws Exception {
-        final String originalComponentName = ActivityManagerTestBase.componentName;
-        setComponentName(componentName);
-        launchActivity(targetActivityName, keyValuePairs);
-        setComponentName(originalComponentName);
-    }
-
-    protected void launchActivity(final String targetActivityName, final String... keyValuePairs)
-            throws Exception {
-        executeShellCommand(getAmStartCmd(targetActivityName, keyValuePairs));
-        mAmWmState.waitForValidState(mDevice, targetActivityName);
-    }
-
-    protected void launchActivityNoWait(final String targetActivityName,
-            final String... keyValuePairs) throws Exception {
-        executeShellCommand(getAmStartCmd(targetActivityName, keyValuePairs));
-    }
-
-    protected void launchActivityInNewTask(final String targetActivityName) throws Exception {
-        executeShellCommand(getAmStartCmdInNewTask(targetActivityName));
-        mAmWmState.waitForValidState(mDevice, targetActivityName);
-    }
-
-    /**
-     * Starts an activity in a new stack.
-     * @return the stack id of the newly created stack.
-     */
-    protected int launchActivityInNewDynamicStack(final String activityName) throws Exception {
-        HashSet<Integer> stackIds = getStackIds();
-        executeShellCommand("am stack start " + ActivityAndWindowManagersState.DEFAULT_DISPLAY_ID
-                + " " + getActivityComponentName(activityName));
-        HashSet<Integer> newStackIds = getStackIds();
-        newStackIds.removeAll(stackIds);
-        if (newStackIds.isEmpty()) {
-            return INVALID_STACK_ID;
-        } else {
-            assertTrue(newStackIds.size() == 1);
-            return newStackIds.iterator().next();
-        }
-    }
-
-    /**
-     * Returns the set of stack ids.
-     */
-    private HashSet<Integer> getStackIds() throws Exception {
-        mAmWmState.computeState(mDevice, null);
-        final List<ActivityStack> stacks = mAmWmState.getAmState().getStacks();
-        final HashSet<Integer> stackIds = new HashSet<>();
-        for (ActivityStack s : stacks) {
-            stackIds.add(s.mStackId);
-        }
-        return stackIds;
-    }
-
-    protected void launchHomeActivity()
-            throws Exception {
-        executeShellCommand(AM_START_HOME_ACTIVITY_COMMAND);
-        mAmWmState.waitForHomeActivityVisible(mDevice);
-    }
-
-    protected void launchActivityOnDisplay(String targetActivityName, int displayId)
-            throws Exception {
-        executeShellCommand(getAmStartCmd(targetActivityName, displayId));
-
-        mAmWmState.waitForValidState(mDevice, targetActivityName);
-    }
-
-    /**
-     * Launch specific target activity. It uses existing instance of {@link #LAUNCHING_ACTIVITY}, so
-     * that one should be started first.
-     * @param toSide Launch to side in split-screen.
-     * @param randomData Make intent URI random by generating random data.
-     * @param multipleTask Allow multiple task launch.
-     * @param targetActivityName Target activity to be launched. Only class name should be provided,
-     *                           package name of {@link #LAUNCHING_ACTIVITY} will be added
-     *                           automatically.
-     * @param displayId Display id where target activity should be launched.
-     * @throws Exception
-     */
-    protected void launchActivityFromLaunching(boolean toSide, boolean randomData,
-            boolean multipleTask, String targetActivityName, int displayId) throws Exception {
-        StringBuilder commandBuilder = new StringBuilder(getAmStartCmd(LAUNCHING_ACTIVITY));
-        commandBuilder.append(" -f 0x20000000");
-        if (toSide) {
-            commandBuilder.append(" --ez launch_to_the_side true");
-        }
-        if (randomData) {
-            commandBuilder.append(" --ez random_data true");
-        }
-        if (multipleTask) {
-            commandBuilder.append(" --ez multiple_task true");
-        }
-        if (targetActivityName != null) {
-            commandBuilder.append(" --es target_activity ").append(targetActivityName);
-        }
-        if (displayId != INVALID_DISPLAY_ID) {
-            commandBuilder.append(" --ei display_id ").append(displayId);
-        }
-        executeShellCommand(commandBuilder.toString());
-
-        mAmWmState.waitForValidState(mDevice, targetActivityName);
-    }
-
-    protected void launchActivityInStack(String activityName, int stackId,
-            final String... keyValuePairs) throws Exception {
-        executeShellCommand(getAmStartCmd(activityName, keyValuePairs) + " --stack " + stackId);
-
-        mAmWmState.waitForValidState(mDevice, activityName, stackId);
-    }
-
-    protected void launchActivityInDockStack(String activityName) throws Exception {
-        launchActivity(activityName);
-        // TODO(b/36279415): The way we launch an activity into the docked stack is different from
-        // what the user actually does. Long term we should use
-        // "adb shell input keyevent --longpress _app_swich_key_code_" to trigger a long press on
-        // the recents button which is consistent with what the user does. However, currently sys-ui
-        // does handle FLAG_LONG_PRESS for the app switch key. It just listens for long press on the
-        // view. We need to fix that in sys-ui before we can change this.
-        moveActivityToDockStack(activityName);
-
-        mAmWmState.waitForValidState(mDevice, activityName, DOCKED_STACK_ID);
-    }
-
-    protected void launchActivityToSide(boolean randomData, boolean multipleTaskFlag,
-            String targetActivity) throws Exception {
-        final String activityToLaunch = targetActivity != null ? targetActivity : "TestActivity";
-        getLaunchActivityBuilder().setToSide(true).setRandomData(randomData)
-                .setMultipleTask(multipleTaskFlag).setTargetActivityName(activityToLaunch)
-                .execute();
-
-        mAmWmState.waitForValidState(mDevice, activityToLaunch, FULLSCREEN_WORKSPACE_STACK_ID);
-    }
-
-    protected void moveActivityToDockStack(String activityName) throws Exception {
-        moveActivityToStack(activityName, DOCKED_STACK_ID);
-    }
-
-    protected void moveActivityToStack(String activityName, int stackId) throws Exception {
-        final int taskId = getActivityTaskId(activityName);
-        final String cmd = AM_MOVE_TASK + taskId + " " + stackId + " true";
-        executeShellCommand(cmd);
-
-        mAmWmState.waitForValidState(mDevice, activityName, stackId);
-    }
-
-    protected void resizeActivityTask(String activityName, int left, int top, int right, int bottom)
-            throws Exception {
-        final int taskId = getActivityTaskId(activityName);
-        final String cmd = "am task resize "
-                + taskId + " " + left + " " + top + " " + right + " " + bottom;
-        executeShellCommand(cmd);
-    }
-
-    protected void resizeDockedStack(
-            int stackWidth, int stackHeight, int taskWidth, int taskHeight)
-                    throws DeviceNotAvailableException {
-        executeShellCommand(AM_RESIZE_DOCKED_STACK
-                + "0 0 " + stackWidth + " " + stackHeight
-                + " 0 0 " + taskWidth + " " + taskHeight);
-    }
-
-    protected void resizeStack(int stackId, int stackLeft, int stackTop, int stackWidth,
-            int stackHeight) throws DeviceNotAvailableException {
-        executeShellCommand(AM_RESIZE_STACK + String.format("%d %d %d %d %d", stackId, stackLeft,
-                stackTop, stackWidth, stackHeight));
-    }
-
-    protected void pressHomeButton() throws DeviceNotAvailableException {
-        executeShellCommand(INPUT_KEYEVENT_HOME);
-    }
-
-    protected void pressBackButton() throws DeviceNotAvailableException {
-        executeShellCommand(INPUT_KEYEVENT_BACK);
-    }
-
-    protected void pressAppSwitchButton() throws DeviceNotAvailableException {
-        executeShellCommand(INPUT_KEYEVENT_APP_SWITCH);
-    }
-
-    // Utility method for debugging, not used directly here, but useful, so kept around.
-    protected void printStacksAndTasks() throws DeviceNotAvailableException {
-        CollectingOutputReceiver outputReceiver = new CollectingOutputReceiver();
-        executeShellCommand(AM_STACK_LIST, outputReceiver);
-        String output = outputReceiver.getOutput();
-        for (String line : output.split("\\n")) {
-            CLog.logAndDisplay(LogLevel.INFO, line);
-        }
-    }
-
-    protected int getActivityTaskId(String name) throws DeviceNotAvailableException {
-        CollectingOutputReceiver outputReceiver = new CollectingOutputReceiver();
-        executeShellCommand(AM_STACK_LIST, outputReceiver);
-        final String output = outputReceiver.getOutput();
-        final Pattern activityPattern = Pattern.compile("(.*) " + getWindowName(name) + " (.*)");
-        for (String line : output.split("\\n")) {
-            Matcher matcher = activityPattern.matcher(line);
-            if (matcher.matches()) {
-                for (String word : line.split("\\s+")) {
-                    if (word.startsWith(TASK_ID_PREFIX)) {
-                        final String withColon = word.split("=")[1];
-                        return Integer.parseInt(withColon.substring(0, withColon.length() - 1));
-                    }
-                }
-            }
-        }
-        return -1;
-    }
-
-    protected boolean supportsVrMode() throws DeviceNotAvailableException {
-        return hasDeviceFeature("android.software.vr.mode") &&
-                hasDeviceFeature("android.hardware.vr.high_performance");
-    }
-
-    protected boolean supportsPip() throws DeviceNotAvailableException {
-        return hasDeviceFeature("android.software.picture_in_picture")
-                || PRETEND_DEVICE_SUPPORTS_PIP;
-    }
-
-    protected boolean supportsFreeform() throws DeviceNotAvailableException {
-        return hasDeviceFeature("android.software.freeform_window_management")
-                || PRETEND_DEVICE_SUPPORTS_FREEFORM;
-    }
-
-    protected boolean isHandheld() throws DeviceNotAvailableException {
-        return !hasDeviceFeature("android.software.leanback")
-                && !hasDeviceFeature("android.hardware.type.watch")
-                && !hasDeviceFeature("android.hardware.type.embedded");
-    }
-
-    // TODO: Switch to using a feature flag, when available.
-    protected boolean isUiModeLockedToVrHeadset() throws DeviceNotAvailableException {
-        final String output = runCommandAndPrintOutput("dumpsys uimode");
-
-        Integer curUiMode = null;
-        Boolean uiModeLocked = null;
-        for (String line : output.split("\\n")) {
-            line = line.trim();
-            Matcher matcher = sCurrentUiModePattern.matcher(line);
-            if (matcher.find()) {
-                curUiMode = Integer.parseInt(matcher.group(1), 16);
-            }
-            matcher = sUiModeLockedPattern.matcher(line);
-            if (matcher.find()) {
-                uiModeLocked = matcher.group(1).equals("true");
-            }
-        }
-
-        boolean uiModeLockedToVrHeadset = (curUiMode != null) && (uiModeLocked != null)
-                && ((curUiMode & UI_MODE_TYPE_MASK) == UI_MODE_TYPE_VR_HEADSET) && uiModeLocked;
-
-        if (uiModeLockedToVrHeadset) {
-            CLog.logAndDisplay(LogLevel.INFO, "UI mode is locked to VR headset");
-        }
-
-        return uiModeLockedToVrHeadset;
-    }
-
-    protected boolean supportsSplitScreenMultiWindow() throws DeviceNotAvailableException {
-        CollectingOutputReceiver outputReceiver = new CollectingOutputReceiver();
-        executeShellCommand(AM_SUPPORTS_SPLIT_SCREEN_MULTIWINDOW, outputReceiver);
-        String output = outputReceiver.getOutput();
-        return !output.startsWith("false");
-    }
-
-    protected boolean noHomeScreen() throws DeviceNotAvailableException {
-        CollectingOutputReceiver outputReceiver = new CollectingOutputReceiver();
-        executeShellCommand(AM_NO_HOME_SCREEN, outputReceiver);
-        String output = outputReceiver.getOutput();
-        return output.startsWith("true");
-    }
-
-    /**
-     * Rotation support is indicated by explicitly having both landscape and portrait
-     * features or not listing either at all.
-     */
-    protected boolean supportsRotation() throws DeviceNotAvailableException {
-        return (hasDeviceFeature("android.hardware.screen.landscape")
-                    && hasDeviceFeature("android.hardware.screen.portrait"))
-            || (!hasDeviceFeature("android.hardware.screen.landscape")
-                    && !hasDeviceFeature("android.hardware.screen.portrait"));
-    }
-
-    protected boolean hasDeviceFeature(String requiredFeature) throws DeviceNotAvailableException {
-        if (mAvailableFeatures == null) {
-            // TODO: Move this logic to ITestDevice.
-            final String output = runCommandAndPrintOutput("pm list features");
-
-            // Extract the id of the new user.
-            mAvailableFeatures = new HashSet<>();
-            for (String feature: output.split("\\s+")) {
-                // Each line in the output of the command has the format "feature:{FEATURE_VALUE}".
-                String[] tokens = feature.split(":");
-                assertTrue("\"" + feature + "\" expected to have format feature:{FEATURE_VALUE}",
-                        tokens.length > 1);
-                assertEquals(feature, "feature", tokens[0]);
-                mAvailableFeatures.add(tokens[1]);
-            }
-        }
-        boolean result = mAvailableFeatures.contains(requiredFeature);
-        if (!result) {
-            CLog.logAndDisplay(LogLevel.INFO, "Device doesn't support " + requiredFeature);
-        }
-        return result;
-    }
-
-    protected boolean isDisplayOn() throws DeviceNotAvailableException {
-        final CollectingOutputReceiver outputReceiver = new CollectingOutputReceiver();
-        mDevice.executeShellCommand("dumpsys power", outputReceiver);
-
-        for (String line : outputReceiver.getOutput().split("\\n")) {
-            line = line.trim();
-
-            final Matcher matcher = sDisplayStatePattern.matcher(line);
-            if (matcher.matches()) {
-                final String state = matcher.group(1);
-                log("power state=" + state);
-                return "ON".equals(state);
-            }
-        }
-        log("power state :(");
-        return false;
-    }
-
-    protected void sleepDevice() throws DeviceNotAvailableException {
-        int retriesLeft = 5;
-        runCommandAndPrintOutput("input keyevent SLEEP");
-        do {
-            if (isDisplayOn()) {
-                log("***Waiting for display to turn off...");
-                try {
-                    Thread.sleep(1000);
-                } catch (InterruptedException e) {
-                    log(e.toString());
-                    // Well I guess we are not waiting...
-                }
-            } else {
-                break;
-            }
-        } while (retriesLeft-- > 0);
-    }
-
-    protected void wakeUpAndUnlockDevice() throws DeviceNotAvailableException {
-        wakeUpDevice();
-        unlockDevice();
-    }
-
-    protected void wakeUpAndRemoveLock() throws DeviceNotAvailableException {
-        wakeUpDevice();
-        setLockDisabled(true);
-    }
-
-    protected void wakeUpDevice() throws DeviceNotAvailableException {
-        runCommandAndPrintOutput("input keyevent WAKEUP");
-    }
-
-    protected void unlockDevice() throws DeviceNotAvailableException {
-        runCommandAndPrintOutput("input keyevent 82");
-    }
-
-    protected void unlockDeviceWithCredential() throws Exception {
-        runCommandAndPrintOutput("input keyevent 82");
-        try {
-            Thread.sleep(3000);
-        } catch (InterruptedException e) {
-            //ignored
-        }
-        enterAndConfirmLockCredential();
-    }
-
-    protected void enterAndConfirmLockCredential() throws Exception {
-        // TODO: This should use waitForIdle..but there ain't such a thing on hostside tests, boo :(
-        Thread.sleep(500);
-
-        runCommandAndPrintOutput("input text " + LOCK_CREDENTIAL);
-        runCommandAndPrintOutput("input keyevent KEYCODE_ENTER");
-    }
-
-    protected void gotoKeyguard() throws Exception {
-        sleepDevice();
-        wakeUpDevice();
-        mAmWmState.waitForKeyguardShowingAndNotOccluded(mDevice);
-    }
-
-    protected void setLockCredential() throws DeviceNotAvailableException {
-        mLockCredentialsSet = true;
-        runCommandAndPrintOutput("locksettings set-pin " + LOCK_CREDENTIAL);
-    }
-
-    private void removeLockCredential() throws DeviceNotAvailableException {
-        runCommandAndPrintOutput("locksettings clear --old " + LOCK_CREDENTIAL);
-    }
-
-    /**
-     * Returns whether the lock screen is disabled.
-     * @return true if the lock screen is disabled, false otherwise.
-     */
-    private boolean isLockDisabled() throws DeviceNotAvailableException {
-        final String isLockDisabled = runCommandAndPrintOutput("locksettings get-disabled").trim();
-        if ("null".equals(isLockDisabled)) {
-            return false;
-        }
-        return Boolean.parseBoolean(isLockDisabled);
-
-    }
-
-    /**
-     * Disable the lock screen.
-     * @param lockDisabled true if should disable, false otherwise.
-     */
-    void setLockDisabled(boolean lockDisabled) throws DeviceNotAvailableException {
-        runCommandAndPrintOutput("locksettings set-disabled " + lockDisabled);
-    }
-
-    /**
-     * Sets the device rotation, value corresponds to one of {@link Surface.ROTATION_0},
-     * {@link Surface.ROTATION_90}, {@link Surface.ROTATION_180}, {@link Surface.ROTATION_270}.
-     */
-    protected void setDeviceRotation(int rotation) throws Exception {
-        setAccelerometerRotation(0);
-        setUserRotation(rotation);
-        mAmWmState.waitForRotation(mDevice, rotation);
-    }
-
-    protected int getDeviceRotation(int displayId) throws DeviceNotAvailableException {
-        final String displays = runCommandAndPrintOutput("dumpsys display displays").trim();
-        Pattern pattern = Pattern.compile(
-                "(mDisplayId=" + displayId + ")([\\s\\S]*)(mOverrideDisplayInfo)(.*)"
-                        + "(rotation)(\\s+)(\\d+)");
-        Matcher matcher = pattern.matcher(displays);
-        while (matcher.find()) {
-            final String match = matcher.group(7);
-            return Integer.parseInt(match);
-        }
-
-        return INVALID_DEVICE_ROTATION;
-    }
-
-    private int getAccelerometerRotation() throws DeviceNotAvailableException {
-        final String rotation =
-                runCommandAndPrintOutput("settings get system accelerometer_rotation");
-        return Integer.parseInt(rotation.trim());
-    }
-
-    private void setAccelerometerRotation(int rotation) throws DeviceNotAvailableException {
-        runCommandAndPrintOutput(
-                "settings put system accelerometer_rotation " + rotation);
-    }
-
-    protected int getUserRotation() throws DeviceNotAvailableException {
-        final String rotation =
-                runCommandAndPrintOutput("settings get system user_rotation").trim();
-        if ("null".equals(rotation)) {
-            return -1;
-        }
-        return Integer.parseInt(rotation);
-    }
-
-    private void setUserRotation(int rotation) throws DeviceNotAvailableException {
-        if (rotation == -1) {
-            runCommandAndPrintOutput(
-                    "settings delete system user_rotation");
-        } else {
-            runCommandAndPrintOutput(
-                    "settings put system user_rotation " + rotation);
-        }
-    }
-
-    protected void setFontScale(float fontScale) throws DeviceNotAvailableException {
-        if (fontScale == 0.0f) {
-            runCommandAndPrintOutput(
-                    "settings delete system font_scale");
-        } else {
-            runCommandAndPrintOutput(
-                    "settings put system font_scale " + fontScale);
-        }
-    }
-
-    protected void setWindowTransitionAnimationDurationScale(float animDurationScale)
-            throws DeviceNotAvailableException {
-        runCommandAndPrintOutput(
-                "settings put global transition_animation_scale " + animDurationScale);
-    }
-
-    protected float getFontScale() throws DeviceNotAvailableException {
-        try {
-            final String fontScale =
-                    runCommandAndPrintOutput("settings get system font_scale").trim();
-            return Float.parseFloat(fontScale);
-        } catch (NumberFormatException e) {
-            // If we don't have a valid font scale key, return 0.0f now so
-            // that we delete the key in tearDown().
-            return 0.0f;
-        }
-    }
-
-    protected String runCommandAndPrintOutput(String command) throws DeviceNotAvailableException {
-        final String output = executeShellCommand(command);
-        log(output);
-        return output;
-    }
-
-    /**
-     * Tries to clear logcat and inserts log separator in case clearing didn't succeed, so we can
-     * always find the starting point from where to evaluate following logs.
-     * @return Unique log separator.
-     */
-    protected String clearLogcat() throws DeviceNotAvailableException {
-        mDevice.executeAdbCommand("logcat", "-c");
-        final String uniqueString = UUID.randomUUID().toString();
-        executeShellCommand("log -t " + LOG_SEPARATOR + " " + uniqueString);
-        return uniqueString;
-    }
-
-    void assertActivityLifecycle(String activityName, boolean relaunched,
-            String logSeparator) throws DeviceNotAvailableException {
-        int retriesLeft = 5;
-        String resultString;
-        do {
-            resultString = verifyLifecycleCondition(activityName, logSeparator, relaunched);
-            if (resultString != null) {
-                log("***Waiting for valid lifecycle state: " + resultString);
-                try {
-                    Thread.sleep(1000);
-                } catch (InterruptedException e) {
-                    log(e.toString());
-                }
-            } else {
-                break;
-            }
-        } while (retriesLeft-- > 0);
-
-        assertNull(resultString, resultString);
-    }
-
-    /** @return Error string if lifecycle counts don't match, null if everything is fine. */
-    private String verifyLifecycleCondition(String activityName, String logSeparator,
-            boolean relaunched) throws DeviceNotAvailableException {
-        final ActivityLifecycleCounts lifecycleCounts = new ActivityLifecycleCounts(activityName,
-                logSeparator);
-        if (relaunched) {
-            if (lifecycleCounts.mDestroyCount < 1) {
-                return activityName + " must have been destroyed. mDestroyCount="
-                        + lifecycleCounts.mDestroyCount;
-            }
-            if (lifecycleCounts.mCreateCount < 1) {
-                return activityName + " must have been (re)created. mCreateCount="
-                        + lifecycleCounts.mCreateCount;
-            }
-        } else {
-            if (lifecycleCounts.mDestroyCount > 0) {
-                return activityName + " must *NOT* have been destroyed. mDestroyCount="
-                        + lifecycleCounts.mDestroyCount;
-            }
-            if (lifecycleCounts.mCreateCount > 0) {
-                return activityName + " must *NOT* have been (re)created. mCreateCount="
-                        + lifecycleCounts.mCreateCount;
-            }
-            if (lifecycleCounts.mConfigurationChangedCount < 1) {
-                return activityName + " must have received configuration changed. "
-                        + "mConfigurationChangedCount="
-                        + lifecycleCounts.mConfigurationChangedCount;
-            }
-        }
-        return null;
-    }
-
-    protected void assertRelaunchOrConfigChanged(
-            String activityName, int numRelaunch, int numConfigChange, String logSeparator)
-            throws DeviceNotAvailableException {
-        int retriesLeft = 5;
-        String resultString;
-        do {
-            resultString = verifyRelaunchOrConfigChanged(activityName, numRelaunch, numConfigChange,
-                    logSeparator);
-            if (resultString != null) {
-                log("***Waiting for relaunch or config changed: " + resultString);
-                try {
-                    Thread.sleep(1000);
-                } catch (InterruptedException e) {
-                    log(e.toString());
-                }
-            } else {
-                break;
-            }
-        } while (retriesLeft-- > 0);
-
-        assertNull(resultString, resultString);
-    }
-
-    /** @return Error string if lifecycle counts don't match, null if everything is fine. */
-    private String verifyRelaunchOrConfigChanged(String activityName, int numRelaunch,
-            int numConfigChange, String logSeparator) throws DeviceNotAvailableException {
-        final ActivityLifecycleCounts lifecycleCounts = new ActivityLifecycleCounts(activityName,
-                logSeparator);
-
-        if (lifecycleCounts.mDestroyCount != numRelaunch) {
-            return activityName + " has been destroyed " + lifecycleCounts.mDestroyCount
-                    + " time(s), expecting " + numRelaunch;
-        } else if (lifecycleCounts.mCreateCount != numRelaunch) {
-            return activityName + " has been (re)created " + lifecycleCounts.mCreateCount
-                    + " time(s), expecting " + numRelaunch;
-        } else if (lifecycleCounts.mConfigurationChangedCount != numConfigChange) {
-            return activityName + " has received " + lifecycleCounts.mConfigurationChangedCount
-                    + " onConfigurationChanged() calls, expecting " + numConfigChange;
-        }
-        return null;
-    }
-
-    protected void assertActivityDestroyed(String activityName, String logSeparator)
-            throws DeviceNotAvailableException {
-        int retriesLeft = 5;
-        String resultString;
-        do {
-            resultString = verifyActivityDestroyed(activityName, logSeparator);
-            if (resultString != null) {
-                log("***Waiting for activity destroyed: " + resultString);
-                try {
-                    Thread.sleep(1000);
-                } catch (InterruptedException e) {
-                    log(e.toString());
-                }
-            } else {
-                break;
-            }
-        } while (retriesLeft-- > 0);
-
-        assertNull(resultString, resultString);
-    }
-
-    /** @return Error string if lifecycle counts don't match, null if everything is fine. */
-    private String verifyActivityDestroyed(String activityName, String logSeparator)
-            throws DeviceNotAvailableException {
-        final ActivityLifecycleCounts lifecycleCounts = new ActivityLifecycleCounts(activityName,
-                logSeparator);
-
-        if (lifecycleCounts.mDestroyCount != 1) {
-            return activityName + " has been destroyed " + lifecycleCounts.mDestroyCount
-                    + " time(s), expecting single destruction.";
-        } else if (lifecycleCounts.mCreateCount != 0) {
-            return activityName + " has been (re)created " + lifecycleCounts.mCreateCount
-                    + " time(s), not expecting any.";
-        } else if (lifecycleCounts.mConfigurationChangedCount != 0) {
-            return activityName + " has received " + lifecycleCounts.mConfigurationChangedCount
-                    + " onConfigurationChanged() calls, not expecting any.";
-        }
-        return null;
-    }
-
-    protected String[] getDeviceLogsForComponent(String componentName, String logSeparator)
-            throws DeviceNotAvailableException {
-        return getDeviceLogsForComponents(new String[]{componentName}, logSeparator);
-    }
-
-    protected String[] getDeviceLogsForComponents(final String[] componentNames,
-            String logSeparator) throws DeviceNotAvailableException {
-        String filters = LOG_SEPARATOR + ":I ";
-        for (String component : componentNames) {
-            filters += component + ":I ";
-        }
-        final String[] result = mDevice.executeAdbCommand(
-                "logcat", "-v", "brief", "-d", filters, "*:S").split("\\n");
-        if (logSeparator == null) {
-            return result;
-        }
-
-        // Make sure that we only check logs after the separator.
-        int i = 0;
-        boolean lookingForSeparator = true;
-        while (i < result.length && lookingForSeparator) {
-            if (result[i].contains(logSeparator)) {
-                lookingForSeparator = false;
-            }
-            i++;
-        }
-        final String[] filteredResult = new String[result.length - i];
-        for (int curPos = 0; i < result.length; curPos++, i++) {
-            filteredResult[curPos] = result[i];
-        }
-        return filteredResult;
-    }
-
-    void assertSingleLaunch(String activityName, String logSeparator) throws DeviceNotAvailableException {
-        int retriesLeft = 5;
-        String resultString;
-        do {
-            resultString = validateLifecycleCounts(activityName, logSeparator, 1 /* createCount */,
-                    1 /* startCount */, 1 /* resumeCount */, 0 /* pauseCount */, 0 /* stopCount */,
-                    0 /* destroyCount */);
-            if (resultString != null) {
-                log("***Waiting for valid lifecycle state: " + resultString);
-                try {
-                    Thread.sleep(1000);
-                } catch (InterruptedException e) {
-                    log(e.toString());
-                }
-            } else {
-                break;
-            }
-        } while (retriesLeft-- > 0);
-
-        assertNull(resultString, resultString);
-    }
-
-    public void assertSingleLaunchAndStop(String activityName, String logSeparator) throws DeviceNotAvailableException {
-        int retriesLeft = 5;
-        String resultString;
-        do {
-            resultString = validateLifecycleCounts(activityName, logSeparator, 1 /* createCount */,
-                    1 /* startCount */, 1 /* resumeCount */, 1 /* pauseCount */, 1 /* stopCount */,
-                    0 /* destroyCount */);
-            if (resultString != null) {
-                log("***Waiting for valid lifecycle state: " + resultString);
-                try {
-                    Thread.sleep(1000);
-                } catch (InterruptedException e) {
-                    log(e.toString());
-                }
-            } else {
-                break;
-            }
-        } while (retriesLeft-- > 0);
-
-        assertNull(resultString, resultString);
-    }
-
-    public void assertSingleStartAndStop(String activityName, String logSeparator) throws DeviceNotAvailableException {
-        int retriesLeft = 5;
-        String resultString;
-        do {
-            resultString =  validateLifecycleCounts(activityName, logSeparator, 0 /* createCount */,
-                    1 /* startCount */, 1 /* resumeCount */, 1 /* pauseCount */, 1 /* stopCount */,
-                    0 /* destroyCount */);
-            if (resultString != null) {
-                log("***Waiting for valid lifecycle state: " + resultString);
-                try {
-                    Thread.sleep(1000);
-                } catch (InterruptedException e) {
-                    log(e.toString());
-                }
-            } else {
-                break;
-            }
-        } while (retriesLeft-- > 0);
-
-        assertNull(resultString, resultString);
-    }
-
-    void assertSingleStart(String activityName, String logSeparator) throws DeviceNotAvailableException {
-        int retriesLeft = 5;
-        String resultString;
-        do {
-            resultString = validateLifecycleCounts(activityName, logSeparator, 0 /* createCount */,
-                    1 /* startCount */, 1 /* resumeCount */, 0 /* pauseCount */, 0 /* stopCount */,
-                    0 /* destroyCount */);
-            if (resultString != null) {
-                log("***Waiting for valid lifecycle state: " + resultString);
-                try {
-                    Thread.sleep(1000);
-                } catch (InterruptedException e) {
-                    log(e.toString());
-                }
-            } else {
-                break;
-            }
-        } while (retriesLeft-- > 0);
-
-        assertNull(resultString, resultString);
-    }
-
-    private String validateLifecycleCounts(String activityName, String logSeparator,
-            int createCount, int startCount, int resumeCount, int pauseCount, int stopCount,
-            int destroyCount) throws DeviceNotAvailableException {
-
-        final ActivityLifecycleCounts lifecycleCounts = new ActivityLifecycleCounts(activityName,
-                logSeparator);
-
-        if (lifecycleCounts.mCreateCount != createCount) {
-            return activityName + " created " + lifecycleCounts.mCreateCount + " times.";
-        }
-        if (lifecycleCounts.mStartCount != startCount) {
-            return activityName + " started " + lifecycleCounts.mStartCount + " times.";
-        }
-        if (lifecycleCounts.mResumeCount != resumeCount) {
-            return activityName + " resumed " + lifecycleCounts.mResumeCount + " times.";
-        }
-        if (lifecycleCounts.mPauseCount != pauseCount) {
-            return activityName + " paused " + lifecycleCounts.mPauseCount + " times.";
-        }
-        if (lifecycleCounts.mStopCount != stopCount) {
-            return activityName + " stopped " + lifecycleCounts.mStopCount + " times.";
-        }
-        if (lifecycleCounts.mDestroyCount != destroyCount) {
-            return activityName + " destroyed " + lifecycleCounts.mDestroyCount + " times.";
-        }
-        return null;
-    }
-
-    private static final Pattern sCreatePattern = Pattern.compile("(.+): onCreate");
-    private static final Pattern sStartPattern = Pattern.compile("(.+): onStart");
-    private static final Pattern sResumePattern = Pattern.compile("(.+): onResume");
-    private static final Pattern sPausePattern = Pattern.compile("(.+): onPause");
-    private static final Pattern sConfigurationChangedPattern =
-            Pattern.compile("(.+): onConfigurationChanged");
-    private static final Pattern sMovedToDisplayPattern =
-            Pattern.compile("(.+): onMovedToDisplay");
-    private static final Pattern sStopPattern = Pattern.compile("(.+): onStop");
-    private static final Pattern sDestroyPattern = Pattern.compile("(.+): onDestroy");
-    private static final Pattern sMultiWindowModeChangedPattern =
-            Pattern.compile("(.+): onMultiWindowModeChanged");
-    private static final Pattern sPictureInPictureModeChangedPattern =
-            Pattern.compile("(.+): onPictureInPictureModeChanged");
-    private static final Pattern sNewConfigPattern = Pattern.compile(
-            "(.+): config size=\\((\\d+),(\\d+)\\) displaySize=\\((\\d+),(\\d+)\\)"
-            + " metricsSize=\\((\\d+),(\\d+)\\) smallestScreenWidth=(\\d+) densityDpi=(\\d+)"
-            + " orientation=(\\d+)");
-    private static final Pattern sDisplayStatePattern =
-            Pattern.compile("Display Power: state=(.+)");
-    private static final Pattern sCurrentUiModePattern = Pattern.compile("mCurUiMode=0x(\\d+)");
-    private static final Pattern sUiModeLockedPattern =
-            Pattern.compile("mUiModeLocked=(true|false)");
-
-    class ReportedSizes {
-        int widthDp;
-        int heightDp;
-        int displayWidth;
-        int displayHeight;
-        int metricsWidth;
-        int metricsHeight;
-        int smallestWidthDp;
-        int densityDpi;
-        int orientation;
-
-        @Override
-        public String toString() {
-            return "ReportedSizes: {widthDp=" + widthDp + " heightDp=" + heightDp
-                    + " displayWidth=" + displayWidth + " displayHeight=" + displayHeight
-                    + " metricsWidth=" + metricsWidth + " metricsHeight=" + metricsHeight
-                    + " smallestWidthDp=" + smallestWidthDp + " densityDpi=" + densityDpi
-                    + " orientation=" + orientation + "}";
-        }
-
-        @Override
-        public boolean equals(Object obj) {
-            if ( this == obj ) return true;
-            if ( !(obj instanceof ReportedSizes) ) return false;
-            ReportedSizes that = (ReportedSizes) obj;
-            return widthDp == that.widthDp
-                    && heightDp == that.heightDp
-                    && displayWidth == that.displayWidth
-                    && displayHeight == that.displayHeight
-                    && metricsWidth == that.metricsWidth
-                    && metricsHeight == that.metricsHeight
-                    && smallestWidthDp == that.smallestWidthDp
-                    && densityDpi == that.densityDpi
-                    && orientation == that.orientation;
-        }
-    }
-
-    ReportedSizes getLastReportedSizesForActivity(String activityName, String logSeparator)
-            throws DeviceNotAvailableException {
-        int retriesLeft = 5;
-        ReportedSizes result;
-        do {
-            result = readLastReportedSizes(activityName, logSeparator);
-            if (result == null) {
-                log("***Waiting for sizes to be reported...");
-                try {
-                    Thread.sleep(1000);
-                } catch (InterruptedException e) {
-                    log(e.toString());
-                    // Well I guess we are not waiting...
-                }
-            } else {
-                break;
-            }
-        } while (retriesLeft-- > 0);
-        return result;
-    }
-
-    private ReportedSizes readLastReportedSizes(String activityName, String logSeparator)
-            throws DeviceNotAvailableException {
-        final String[] lines = getDeviceLogsForComponent(activityName, logSeparator);
-        for (int i = lines.length - 1; i >= 0; i--) {
-            final String line = lines[i].trim();
-            final Matcher matcher = sNewConfigPattern.matcher(line);
-            if (matcher.matches()) {
-                ReportedSizes details = new ReportedSizes();
-                details.widthDp = Integer.parseInt(matcher.group(2));
-                details.heightDp = Integer.parseInt(matcher.group(3));
-                details.displayWidth = Integer.parseInt(matcher.group(4));
-                details.displayHeight = Integer.parseInt(matcher.group(5));
-                details.metricsWidth = Integer.parseInt(matcher.group(6));
-                details.metricsHeight = Integer.parseInt(matcher.group(7));
-                details.smallestWidthDp = Integer.parseInt(matcher.group(8));
-                details.densityDpi = Integer.parseInt(matcher.group(9));
-                details.orientation = Integer.parseInt(matcher.group(10));
-                return details;
-            }
-        }
-        return null;
-    }
-
-    class ActivityLifecycleCounts {
-        int mCreateCount;
-        int mStartCount;
-        int mResumeCount;
-        int mConfigurationChangedCount;
-        int mLastConfigurationChangedLineIndex;
-        int mMovedToDisplayCount;
-        int mMultiWindowModeChangedCount;
-        int mLastMultiWindowModeChangedLineIndex;
-        int mPictureInPictureModeChangedCount;
-        int mLastPictureInPictureModeChangedLineIndex;
-        int mPauseCount;
-        int mStopCount;
-        int mLastStopLineIndex;
-        int mDestroyCount;
-
-        public ActivityLifecycleCounts(String activityName, String logSeparator)
-                throws DeviceNotAvailableException {
-            int lineIndex = 0;
-            for (String line : getDeviceLogsForComponent(activityName, logSeparator)) {
-                line = line.trim();
-                lineIndex++;
-
-                Matcher matcher = sCreatePattern.matcher(line);
-                if (matcher.matches()) {
-                    mCreateCount++;
-                    continue;
-                }
-
-                matcher = sStartPattern.matcher(line);
-                if (matcher.matches()) {
-                    mStartCount++;
-                    continue;
-                }
-
-                matcher = sResumePattern.matcher(line);
-                if (matcher.matches()) {
-                    mResumeCount++;
-                    continue;
-                }
-
-                matcher = sConfigurationChangedPattern.matcher(line);
-                if (matcher.matches()) {
-                    mConfigurationChangedCount++;
-                    mLastConfigurationChangedLineIndex = lineIndex;
-                    continue;
-                }
-
-                matcher = sMovedToDisplayPattern.matcher(line);
-                if (matcher.matches()) {
-                    mMovedToDisplayCount++;
-                    continue;
-                }
-
-                matcher = sMultiWindowModeChangedPattern.matcher(line);
-                if (matcher.matches()) {
-                    mMultiWindowModeChangedCount++;
-                    mLastMultiWindowModeChangedLineIndex = lineIndex;
-                    continue;
-                }
-
-                matcher = sPictureInPictureModeChangedPattern.matcher(line);
-                if (matcher.matches()) {
-                    mPictureInPictureModeChangedCount++;
-                    mLastPictureInPictureModeChangedLineIndex = lineIndex;
-                    continue;
-                }
-
-                matcher = sPausePattern.matcher(line);
-                if (matcher.matches()) {
-                    mPauseCount++;
-                    continue;
-                }
-
-                matcher = sStopPattern.matcher(line);
-                if (matcher.matches()) {
-                    mStopCount++;
-                    mLastStopLineIndex = lineIndex;
-                    continue;
-                }
-
-                matcher = sDestroyPattern.matcher(line);
-                if (matcher.matches()) {
-                    mDestroyCount++;
-                    continue;
-                }
-            }
-        }
-    }
-
-    protected void stopTestCase() throws Exception {
-        executeShellCommand("am force-stop " + componentName);
-    }
-
-    protected LaunchActivityBuilder getLaunchActivityBuilder() {
-        return new LaunchActivityBuilder(mAmWmState, mDevice);
-    }
-
-    protected static class LaunchActivityBuilder {
-        private final ActivityAndWindowManagersState mAmWmState;
-        private final ITestDevice mDevice;
-
-        private String mTargetActivityName;
-        private String mTargetPackage = componentName;
-        private boolean mToSide;
-        private boolean mRandomData;
-        private boolean mNewTask;
-        private boolean mMultipleTask;
-        private int mDisplayId = INVALID_DISPLAY_ID;
-        private String mLaunchingActivityName = LAUNCHING_ACTIVITY;
-        private boolean mReorderToFront;
-        private boolean mWaitForLaunched;
-
-        public LaunchActivityBuilder(ActivityAndWindowManagersState amWmState,
-                                     ITestDevice device) {
-            mAmWmState = amWmState;
-            mDevice = device;
-            mWaitForLaunched = true;
-        }
-
-        public LaunchActivityBuilder setToSide(boolean toSide) {
-            mToSide = toSide;
-            return this;
-        }
-
-        public LaunchActivityBuilder setRandomData(boolean randomData) {
-            mRandomData = randomData;
-            return this;
-        }
-
-        public LaunchActivityBuilder setNewTask(boolean newTask) {
-            mNewTask = newTask;
-            return this;
-        }
-
-        public LaunchActivityBuilder setMultipleTask(boolean multipleTask) {
-            mMultipleTask = multipleTask;
-            return this;
-        }
-
-        public LaunchActivityBuilder setReorderToFront(boolean reorderToFront) {
-            mReorderToFront = reorderToFront;
-            return this;
-        }
-
-        public LaunchActivityBuilder setTargetActivityName(String name) {
-            mTargetActivityName = name;
-            return this;
-        }
-
-        public LaunchActivityBuilder setTargetPackage(String pkg) {
-            mTargetPackage = pkg;
-            return this;
-        }
-
-        public LaunchActivityBuilder setDisplayId(int id) {
-            mDisplayId = id;
-            return this;
-        }
-
-        public LaunchActivityBuilder setLaunchingActivityName(String name) {
-            mLaunchingActivityName = name;
-            return this;
-        }
-
-        public LaunchActivityBuilder setWaitForLaunched(boolean shouldWait) {
-            mWaitForLaunched = shouldWait;
-            return this;
-        }
-
-        public void execute() throws Exception {
-            StringBuilder commandBuilder = new StringBuilder(getAmStartCmd(mLaunchingActivityName));
-            commandBuilder.append(" -f 0x20000000");
-
-            // Add a flag to ensure we actually mean to launch an activity.
-            commandBuilder.append(" --ez launch_activity true");
-
-            if (mToSide) {
-                commandBuilder.append(" --ez launch_to_the_side true");
-            }
-            if (mRandomData) {
-                commandBuilder.append(" --ez random_data true");
-            }
-            if (mNewTask) {
-                commandBuilder.append(" --ez new_task true");
-            }
-            if (mMultipleTask) {
-                commandBuilder.append(" --ez multiple_task true");
-            }
-            if (mReorderToFront) {
-                commandBuilder.append(" --ez reorder_to_front true");
-            }
-            if (mTargetActivityName != null) {
-                commandBuilder.append(" --es target_activity ").append(mTargetActivityName);
-                commandBuilder.append(" --es package_name ").append(mTargetPackage);
-            }
-            if (mDisplayId != INVALID_DISPLAY_ID) {
-                commandBuilder.append(" --ei display_id ").append(mDisplayId);
-            }
-            executeShellCommand(mDevice, commandBuilder.toString());
-
-            if (mWaitForLaunched) {
-                mAmWmState.waitForValidState(mDevice, new String[]{mTargetActivityName},
-                        null /* stackIds */, false /* compareTaskAndStackBounds */, mTargetPackage);
-            }
-        }
-    }
-
-    void tearDownLockCredentials() throws Exception {
-        if (!mLockCredentialsSet) {
-            return;
-        }
-
-        removeLockCredential();
-        // Dismiss active keyguard after credential is cleared, so
-        // keyguard doesn't ask for the stale credential.
-        pressBackButton();
-        sleepDevice();
-        wakeUpAndUnlockDevice();
-    }
-}
diff --git a/hostsidetests/services/activityandwindowmanager/util/src/android/server/cts/StateLogger.java b/hostsidetests/services/activityandwindowmanager/util/src/android/server/cts/StateLogger.java
deleted file mode 100644
index 335f26c..0000000
--- a/hostsidetests/services/activityandwindowmanager/util/src/android/server/cts/StateLogger.java
+++ /dev/null
@@ -1,40 +0,0 @@
-/*
- * Copyright (C) 2016 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License
- */
-
-package android.server.cts;
-
-import com.android.tradefed.log.LogUtil.CLog;
-
-import static com.android.ddmlib.Log.LogLevel.INFO;
-import static com.android.ddmlib.Log.LogLevel.ERROR;
-
-/**
- * Util class to perform simple state logging.
- */
-public class StateLogger {
-    private static final boolean DEBUG = false;
-
-    /** Simple info-level logging gated by {@link #DEBUG} flag */
-    public static void log(String logText) {
-        if (DEBUG) {
-            CLog.logAndDisplay(INFO, logText);
-        }
-    }
-
-    public static void logE(String logText) {
-        CLog.logAndDisplay(ERROR, logText);
-    }
-}
diff --git a/hostsidetests/services/activityandwindowmanager/util/src/android/server/cts/SurfaceTraceReceiver.java b/hostsidetests/services/activityandwindowmanager/util/src/android/server/cts/SurfaceTraceReceiver.java
deleted file mode 100644
index 8026e80..0000000
--- a/hostsidetests/services/activityandwindowmanager/util/src/android/server/cts/SurfaceTraceReceiver.java
+++ /dev/null
@@ -1,380 +0,0 @@
-/*
- * Copyright (C) 2016 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License
- */
-
-package android.server.cts;
-
-import com.android.ddmlib.IShellOutputReceiver;
-import com.android.tradefed.device.DeviceNotAvailableException;
-import com.android.tradefed.device.ITestDevice;
-import com.android.tradefed.log.LogUtil.CLog;
-
-import java.awt.Rectangle;
-import java.io.ByteArrayInputStream;
-import java.io.DataInputStream;
-import java.io.IOException;
-import java.lang.System;
-import junit.framework.Assert;
-
-import static android.server.cts.StateLogger.logE;
-
-// Parses a trace of surface commands from the WM (in real time)
-// and dispenses them via the SurfaceObserver interface.
-//
-// Data enters through addOutput
-public class SurfaceTraceReceiver implements IShellOutputReceiver {
-    final SurfaceObserver mObserver;
-
-    private State mState = State.CMD;
-    private String mCurrentWindowName = null;
-    private int mArgPosition = 0;
-    private float[] mTmpFloats = new float[10];
-    private int[] mTmpInts = new int[10];
-    private Rectangle.Float mTmpRect = new Rectangle.Float();
-    private byte[] mUnprocessedBytes = new byte[16384];
-    private byte[] mFullData = new byte[32768];
-    private int mUnprocessedBytesLength;
-
-    private boolean mCancelled = false;
-
-    interface SurfaceObserver {
-        default void setAlpha(String windowName, float alpha) {}
-        default void setLayer(String windowName, int layer) {}
-        default void setPosition(String windowName, float x, float y) {}
-        default void setSize(String widnowName, int width, int height) {}
-        default void setLayerStack(String windowName, int layerStack) {}
-        default void setMatrix(String windowName, float dsdx, float dtdx, float dsdy, float dtdy) {}
-        default void setCrop(String windowName, Rectangle.Float crop) {}
-        default void setFinalCrop(String windowName, Rectangle.Float finalCrop) {}
-        default void hide(String windowName) {}
-        default void show(String windowName) {}
-        default void setGeometryAppliesWithResize(String windowName) {}
-        default void openTransaction() {}
-        default void closeTransaction() {}
-    };
-
-    enum State {
-        CMD,
-        SET_ALPHA,
-        SET_LAYER,
-        SET_POSITION,
-        SET_SIZE,
-        SET_CROP,
-        SET_FINAL_CROP,
-        SET_LAYER_STACK,
-        SET_MATRIX,
-        HIDE,
-        SHOW,
-        GEOMETRY_APPLIES_WITH_RESIZE
-    };
-
-    SurfaceTraceReceiver(SurfaceObserver observer) {
-        mObserver = observer;
-    }
-
-    // Reset state and prepare to accept a new command.
-    void nextCmd(DataInputStream d) {
-        mState = State.CMD;
-        mCurrentWindowName = null;
-        mArgPosition = 0;
-
-        try {
-            // Consume the sigil
-            d.readByte();
-            d.readByte();
-            d.readByte();
-            d.readByte();
-        } catch (Exception e) {
-            logE("Exception consuming sigil: " + e);
-        }
-    }
-
-    // When the command parsing functions below are called, the window name
-    // will already be parsed. The responsibility of these functions
-    // is to parse other arguments 1 by 1 and accumlate them until the appropriate number
-    // is reached. At that point the parser should emit an event to the observer and
-    // call nextCmd
-    void parseAlpha(DataInputStream d) throws IOException {
-        float alpha = d.readFloat();
-        mObserver.setAlpha(mCurrentWindowName, alpha);
-        nextCmd(d);
-    }
-
-    void parseLayer(DataInputStream d) throws IOException {
-        int layer = d.readInt();
-        mObserver.setLayer(mCurrentWindowName, layer);
-        nextCmd(d);
-    }
-
-    void parsePosition(DataInputStream d) throws IOException {
-        mTmpFloats[mArgPosition] = d.readFloat();
-        mArgPosition++;
-        if (mArgPosition == 2)  {
-            mObserver.setPosition(mCurrentWindowName, mTmpFloats[0], mTmpFloats[1]);
-            nextCmd(d);
-        }
-    }
-
-    void parseSize(DataInputStream d) throws IOException {
-        mTmpInts[mArgPosition] = d.readInt();
-        mArgPosition++;
-        if (mArgPosition == 2) {
-            mObserver.setSize(mCurrentWindowName, mTmpInts[0], mTmpInts[1]);
-            nextCmd(d);
-        }
-    }
-
-    // Careful Android rectangle rep is top-left-right-bottom awt is top-left-width-height
-    void parseCrop(DataInputStream d) throws IOException {
-        mTmpFloats[mArgPosition] = d.readFloat();
-        mArgPosition++;
-        if (mArgPosition == 4) {
-            mTmpRect.setRect(mTmpFloats[0], mTmpFloats[1], mTmpFloats[2]-mTmpFloats[0],
-                    mTmpFloats[3]-mTmpFloats[1]);
-            mObserver.setCrop(mCurrentWindowName, mTmpRect);
-            nextCmd(d);
-        }
-    }
-
-    void parseFinalCrop(DataInputStream d) throws IOException {
-        mTmpFloats[mArgPosition] = d.readInt();
-        mArgPosition++;
-        if (mArgPosition == 4) {
-            mTmpRect.setRect(mTmpFloats[0], mTmpFloats[1], mTmpFloats[2]-mTmpFloats[0],
-                    mTmpFloats[3]-mTmpFloats[1]);
-            mObserver.setFinalCrop(mCurrentWindowName, mTmpRect);
-            nextCmd(d);
-        }
-    }
-
-    void parseLayerStack(DataInputStream d) throws IOException {
-        int layerStack = d.readInt();
-        mObserver.setLayerStack(mCurrentWindowName, layerStack);
-        nextCmd(d);
-    }
-
-    void parseSetMatrix(DataInputStream d) throws IOException {
-        mTmpFloats[mArgPosition] = d.readFloat();
-        mArgPosition++;
-        if (mArgPosition == 4) {
-            mObserver.setMatrix(mCurrentWindowName, mTmpFloats[0],
-                    mTmpFloats[1], mTmpFloats[2], mTmpFloats[3]);
-            nextCmd(d);
-        }
-    }
-
-    void parseHide(DataInputStream d) throws IOException {
-        mObserver.hide(mCurrentWindowName);
-        nextCmd(d);
-    }
-
-    void parseShow(DataInputStream d) throws IOException {
-        mObserver.show(mCurrentWindowName);
-        nextCmd(d);
-    }
-
-    void parseGeometryAppliesWithResize(DataInputStream d) throws IOException {
-        mObserver.setGeometryAppliesWithResize(mCurrentWindowName);
-        nextCmd(d);
-    }
-
-    public int indexAfterLastSigil(byte[] data, int offset, int length) {
-        int idx = offset + length - 1;
-        int sigilsNeeded = 4;
-        byte sigil = (byte)0xfc;
-        while (idx > offset) {
-            if (data[idx] == sigil) {
-                sigilsNeeded--;
-                if (sigilsNeeded == 0) {
-                    return idx+4;
-                }
-            } else {
-                sigilsNeeded = 4;
-            }
-            idx--;
-        }
-        return idx; // idx == offset at this point
-    }
-
-    // The tricky bit here is ADB may break up our words, and not send us complete messages,
-    // or even complete integers! To ensure we process the data in appropciate chunks,
-    // We look for a sigil (0xfcfcfcfc) and only process data when it ends in as igil.
-    // Otherwise we save it and wait to receive a sigil, then process the merged data.
-    public void addOutput(byte[] data, int offset, int length) {
-        byte[] combinedData = data;
-
-        // First we have to merge any unprocessed bytes from the last call in to
-        // a combined array.
-        if (mUnprocessedBytesLength > 0) {
-            System.arraycopy(mUnprocessedBytes, 0, mFullData, 0, mUnprocessedBytesLength);
-            System.arraycopy(data, offset, mFullData, mUnprocessedBytesLength, length);
-            combinedData = mFullData;
-            length = mUnprocessedBytesLength + length;
-            offset = 0;
-            mUnprocessedBytesLength = 0;
-        }
-
-        // Now we find the last sigil in our combined array. Everything before this index is
-        // a properly terminated message ready to be parsed.
-        int completedIndex = indexAfterLastSigil(combinedData, offset, length);
-        // If there are any bytes left after the last sigil, save them for next time.
-        if (completedIndex != length + offset) {
-            mUnprocessedBytesLength = (length+offset)-(completedIndex);
-            System.arraycopy(combinedData, completedIndex,
-                    mUnprocessedBytes, 0, mUnprocessedBytesLength);
-        }
-        //  If there was no sigil, we have nothing to process yet.
-        if (completedIndex <= offset) {
-            return;
-        }
-        ByteArrayInputStream b = new ByteArrayInputStream(combinedData, offset, completedIndex - offset);
-        DataInputStream d = new DataInputStream(b);
-
-        // We may not receive an entire message at once (for example we may receive
-        // a command without its arguments), so we track our current state, over multiple
-        // addOutput calls. When we are in State.CMD it means we next expect a new command.
-        // If we are not expecting a command, then all commands with arguments, begin with
-        // a window name. Once we have the window name, individual parseAlpha,
-        // parseLayer, etc...statements will parse command arguments one at a time. Once
-        // the appropriate number of arguments is collected the observer will be invoked
-        // and the state reset. For commands which have no arguments (e.g. open/close transaction),
-        // parseCmd can emit the observer event and call nextCmd() right away.
-        try {
-            while (b.available() > 0) {
-                if (mState != State.CMD && mCurrentWindowName == null) {
-                    mCurrentWindowName = d.readUTF();
-                    if (b.available() == 0) {
-                        return;
-                    }
-                }
-                switch (mState) {
-                case CMD: {
-                    String cmd = d.readUTF();
-                    parseCmd(d, cmd);
-                    break;
-                }
-                case SET_ALPHA: {
-                    parseAlpha(d);
-                    break;
-                }
-                case SET_LAYER: {
-                    parseLayer(d);
-                    break;
-                }
-                case SET_POSITION: {
-                    parsePosition(d);
-                    break;
-                }
-                case SET_SIZE: {
-                    parseSize(d);
-                    break;
-                }
-                case SET_CROP: {
-                    parseCrop(d);
-                    break;
-                }
-                case SET_FINAL_CROP: {
-                    parseFinalCrop(d);
-                    break;
-                }
-                case SET_LAYER_STACK: {
-                    parseLayerStack(d);
-                    break;
-                }
-                case SET_MATRIX: {
-                    parseSetMatrix(d);
-                    break;
-                }
-                case HIDE: {
-                    parseHide(d);
-                    break;
-                }
-                case SHOW: {
-                    parseShow(d);
-                    break;
-                }
-                case GEOMETRY_APPLIES_WITH_RESIZE: {
-                    parseGeometryAppliesWithResize(d);
-                    break;
-                }
-                }
-            }
-        } catch (Exception e) {
-            logE("Error in surface trace receiver: " + e.toString());
-        }
-    }
-
-    void parseCmd(DataInputStream d, String cmd) {
-        switch (cmd) {
-        case "Alpha":
-            mState = State.SET_ALPHA;
-            break;
-        case "Layer":
-            mState = State.SET_LAYER;
-            break;
-        case "Position":
-            mState = State.SET_POSITION;
-            break;
-        case "Size":
-            mState = State.SET_SIZE;
-            break;
-        case "Crop":
-            mState = State.SET_CROP;
-            break;
-        case "FinalCrop":
-            mState = State.SET_FINAL_CROP;
-            break;
-        case "LayerStack":
-            mState = State.SET_LAYER_STACK;
-            break;
-        case "Matrix":
-            mState = State.SET_MATRIX;
-            break;
-        case "Hide":
-            mState = State.HIDE;
-            break;
-        case "Show":
-            mState = State.SHOW;
-            break;
-        case "GeometryAppliesWithResize":
-            mState = State.GEOMETRY_APPLIES_WITH_RESIZE;
-            break;
-        case "OpenTransaction":
-            mObserver.openTransaction();
-            nextCmd(d);
-            break;
-        case "CloseTransaction":
-            mObserver.closeTransaction();
-            nextCmd(d);
-            break;
-        default:
-            Assert.fail("Unexpected surface command: " + cmd);
-            break;
-        }
-    }
-
-    @Override
-    public void flush() {
-    }
-
-    void cancel() {
-        mCancelled = true;
-    }
-
-    @Override
-    public boolean isCancelled() {
-        return mCancelled;
-    }
-}
diff --git a/hostsidetests/services/activityandwindowmanager/util/src/android/server/cts/WindowManagerState.java b/hostsidetests/services/activityandwindowmanager/util/src/android/server/cts/WindowManagerState.java
deleted file mode 100644
index c112a14..0000000
--- a/hostsidetests/services/activityandwindowmanager/util/src/android/server/cts/WindowManagerState.java
+++ /dev/null
@@ -1,1152 +0,0 @@
-/*
- * Copyright (C) 2016 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License
- */
-
-package android.server.cts;
-
-import static android.server.cts.ActivityAndWindowManagersState.DEFAULT_DISPLAY_ID;
-import static android.server.cts.StateLogger.log;
-import static android.server.cts.StateLogger.logE;
-
-import com.android.tradefed.device.CollectingOutputReceiver;
-import com.android.tradefed.device.DeviceNotAvailableException;
-import com.android.tradefed.device.ITestDevice;
-
-import java.awt.*;
-import java.util.ArrayList;
-import java.util.Collections;
-import java.util.Comparator;
-import java.util.HashMap;
-import java.util.LinkedList;
-import java.util.List;
-import java.util.Map;
-import java.util.regex.Matcher;
-import java.util.regex.Pattern;
-
-public class WindowManagerState {
-
-    public static final String TRANSIT_ACTIVITY_OPEN = "TRANSIT_ACTIVITY_OPEN";
-    public static final String TRANSIT_ACTIVITY_CLOSE = "TRANSIT_ACTIVITY_CLOSE";
-    public static final String TRANSIT_TASK_OPEN = "TRANSIT_TASK_OPEN";
-    public static final String TRANSIT_TASK_CLOSE = "TRANSIT_TASK_CLOSE";
-
-    public static final String TRANSIT_WALLPAPER_OPEN = "TRANSIT_WALLPAPER_OPEN";
-    public static final String TRANSIT_WALLPAPER_CLOSE = "TRANSIT_WALLPAPER_CLOSE";
-    public static final String TRANSIT_WALLPAPER_INTRA_OPEN = "TRANSIT_WALLPAPER_INTRA_OPEN";
-    public static final String TRANSIT_WALLPAPER_INTRA_CLOSE = "TRANSIT_WALLPAPER_INTRA_CLOSE";
-
-    public static final String TRANSIT_KEYGUARD_GOING_AWAY = "TRANSIT_KEYGUARD_GOING_AWAY";
-    public static final String TRANSIT_KEYGUARD_GOING_AWAY_ON_WALLPAPER =
-            "TRANSIT_KEYGUARD_GOING_AWAY_ON_WALLPAPER";
-    public static final String TRANSIT_KEYGUARD_OCCLUDE = "TRANSIT_KEYGUARD_OCCLUDE";
-    public static final String TRANSIT_KEYGUARD_UNOCCLUDE = "TRANSIT_KEYGUARD_UNOCCLUDE";
-
-    public static final String APP_STATE_IDLE = "APP_STATE_IDLE";
-
-    private static final String DUMPSYS_WINDOW = "dumpsys window -a";
-
-    private static final Pattern sWindowPattern =
-            Pattern.compile("Window #(\\d+) Window\\{([0-9a-fA-F]+) u(\\d+) (.+)\\}\\:");
-    private static final Pattern sStartingWindowPattern =
-            Pattern.compile("Window #(\\d+) Window\\{([0-9a-fA-F]+) u(\\d+) Starting (.+)\\}\\:");
-    private static final Pattern sExitingWindowPattern =
-            Pattern.compile("Window #(\\d+) Window\\{([0-9a-fA-F]+) u(\\d+) (.+) EXITING\\}\\:");
-    private static final Pattern sDebuggerWindowPattern =
-            Pattern.compile("Window #(\\d+) Window\\{([0-9a-fA-F]+) u(\\d+) Waiting For Debugger: (.+)\\}\\:");
-
-    private static final Pattern sFocusedWindowPattern = Pattern.compile(
-            "mCurrentFocus=Window\\{([0-9a-fA-F]+) u(\\d+) (\\S+)\\}");
-    private static final Pattern sAppErrorFocusedWindowPattern = Pattern.compile(
-            "mCurrentFocus=Window\\{([0-9a-fA-F]+) u(\\d+) Application Error\\: (\\S+)\\}");
-    private static final Pattern sWaitingForDebuggerFocusedWindowPattern = Pattern.compile(
-            "mCurrentFocus=Window\\{([0-9a-fA-F]+) u(\\d+) Waiting For Debugger\\: (\\S+)\\}");
-
-    private static final Pattern sFocusedAppPattern =
-            Pattern.compile("mFocusedApp=AppWindowToken\\{(.+) token=Token\\{(.+) "
-                    + "ActivityRecord\\{(.+) u(\\d+) (\\S+) (\\S+)");
-    private static final Pattern sStableBoundsPattern = Pattern.compile(
-            "mStable=\\((\\d+),(\\d+)\\)-\\((\\d+),(\\d+)\\)");
-    private static final Pattern sDefaultPinnedStackBoundsPattern = Pattern.compile(
-            "defaultBounds=\\[(\\d+),(\\d+)\\]\\[(\\d+),(\\d+)\\]");
-    private static final Pattern sPinnedStackMovementBoundsPattern = Pattern.compile(
-            "movementBounds=\\[(\\d+),(\\d+)\\]\\[(\\d+),(\\d+)\\]");
-    private static final Pattern sRotationPattern = Pattern.compile(
-            "mRotation=(\\d).*");
-    private static final Pattern sLastOrientationPattern = Pattern.compile(
-            ".*mLastOrientation=(\\d)");
-
-    private static final Pattern sLastAppTransitionPattern =
-            Pattern.compile("mLastUsedAppTransition=(.+)");
-    private static final Pattern sAppTransitionStatePattern =
-            Pattern.compile("mAppTransitionState=(.+)");
-
-    private static final Pattern sStackIdPattern = Pattern.compile("mStackId=(\\d+)");
-
-    private static final Pattern sInputMethodWindowPattern =
-            Pattern.compile("mInputMethodWindow=Window\\{([0-9a-fA-F]+) u\\d+ .+\\}.*");
-
-    private static final Pattern sDisplayIdPattern =
-            Pattern.compile("Display: mDisplayId=(\\d+)");
-
-    private static final Pattern sDisplayFrozenPattern =
-            Pattern.compile("mDisplayFrozen=([a-z]*) .*");
-
-    private static final Pattern sDockedStackMinimizedPattern =
-            Pattern.compile("mMinimizedDock=([a-z]*)");
-
-    private static final Pattern[] sExtractStackExitPatterns = {
-            sStackIdPattern, sWindowPattern, sStartingWindowPattern, sExitingWindowPattern,
-            sDebuggerWindowPattern, sFocusedWindowPattern, sAppErrorFocusedWindowPattern,
-            sWaitingForDebuggerFocusedWindowPattern,
-            sFocusedAppPattern, sLastAppTransitionPattern, sDefaultPinnedStackBoundsPattern,
-            sPinnedStackMovementBoundsPattern, sDisplayIdPattern, sDockedStackMinimizedPattern};
-
-    // Windows in z-order with the top most at the front of the list.
-    private List<WindowState> mWindowStates = new ArrayList();
-    // Stacks in z-order with the top most at the front of the list, starting with primary display.
-    private final List<WindowStack> mStacks = new ArrayList();
-    // Stacks on all attached displays, in z-order with the top most at the front of the list.
-    private final Map<Integer, List<WindowStack>> mDisplayStacks
-            = new HashMap<>();
-    private List<Display> mDisplays = new ArrayList();
-    private String mFocusedWindow = null;
-    private String mFocusedApp = null;
-    private String mLastTransition = null;
-    private String mAppTransitionState = null;
-    private String mInputMethodWindowAppToken = null;
-    private Rectangle mStableBounds = new Rectangle();
-    private final Rectangle mDefaultPinnedStackBounds = new Rectangle();
-    private final Rectangle mPinnedStackMovementBounds = new Rectangle();
-    private final LinkedList<String> mSysDump = new LinkedList();
-    private int mRotation;
-    private int mLastOrientation;
-    private boolean mDisplayFrozen;
-    private boolean mIsDockedStackMinimized;
-
-    void computeState(ITestDevice device) throws DeviceNotAvailableException {
-        // It is possible the system is in the middle of transition to the right state when we get
-        // the dump. We try a few times to get the information we need before giving up.
-        int retriesLeft = 3;
-        boolean retry = false;
-        String dump = null;
-
-        log("==============================");
-        log("      WindowManagerState      ");
-        log("==============================");
-        do {
-            if (retry) {
-                log("***Incomplete WM state. Retrying...");
-                // Wait half a second between retries for window manager to finish transitioning...
-                try {
-                    Thread.sleep(500);
-                } catch (InterruptedException e) {
-                    log(e.toString());
-                    // Well I guess we are not waiting...
-                }
-            }
-
-            final CollectingOutputReceiver outputReceiver = new CollectingOutputReceiver();
-            device.executeShellCommand(DUMPSYS_WINDOW, outputReceiver);
-            dump = outputReceiver.getOutput();
-            parseSysDump(dump);
-
-            retry = mWindowStates.isEmpty() || mFocusedApp == null;
-        } while (retry && retriesLeft-- > 0);
-
-        if (retry) {
-            log(dump);
-        }
-
-        if (mWindowStates.isEmpty()) {
-            logE("No Windows found...");
-        }
-        if (mFocusedWindow == null) {
-            logE("No Focused Window...");
-        }
-        if (mFocusedApp == null) {
-            logE("No Focused App...");
-        }
-    }
-
-    private void parseSysDump(String sysDump) {
-        reset();
-
-        Collections.addAll(mSysDump, sysDump.split("\\n"));
-
-        int currentDisplayId = DEFAULT_DISPLAY_ID;
-        while (!mSysDump.isEmpty()) {
-            final Display display =
-                    Display.create(mSysDump, sExtractStackExitPatterns);
-            if (display != null) {
-                log(display.toString());
-                mDisplays.add(display);
-                currentDisplayId = display.mDisplayId;
-                mDisplayStacks.put(currentDisplayId, new ArrayList<>());
-                continue;
-            }
-
-            final WindowStack stack =
-                    WindowStack.create(mSysDump, sStackIdPattern, sExtractStackExitPatterns);
-
-            if (stack != null) {
-                mStacks.add(stack);
-                mDisplayStacks.get(currentDisplayId).add(stack);
-                continue;
-            }
-
-
-            final WindowState ws = WindowState.create(mSysDump, sExtractStackExitPatterns);
-            if (ws != null) {
-                log(ws.toString());
-
-                // Check to see if we are in the middle of transitioning. If we are, we want to
-                // skip dumping until window manager is done transitioning windows.
-                if (ws.isStartingWindow()) {
-                    log("Skipping dump due to starting window transition...");
-                    return;
-                }
-
-                if (ws.isExitingWindow()) {
-                    log("Skipping dump due to exiting window transition...");
-                    return;
-                }
-
-                mWindowStates.add(ws);
-                continue;
-            }
-
-            final String line = mSysDump.pop().trim();
-
-            Matcher matcher = sFocusedWindowPattern.matcher(line);
-            if (matcher.matches()) {
-                log(line);
-                final String focusedWindow = matcher.group(3);
-                log(focusedWindow);
-                mFocusedWindow = focusedWindow;
-                continue;
-            }
-
-            matcher = sAppErrorFocusedWindowPattern.matcher(line);
-            if (matcher.matches()) {
-                log(line);
-                final String focusedWindow = matcher.group(3);
-                log(focusedWindow);
-                mFocusedWindow = focusedWindow;
-                continue;
-            }
-
-            matcher = sWaitingForDebuggerFocusedWindowPattern.matcher(line);
-            if (matcher.matches()) {
-                log(line);
-                final String focusedWindow = matcher.group(3);
-                log(focusedWindow);
-                mFocusedWindow = focusedWindow;
-                continue;
-            }
-
-            matcher = sFocusedAppPattern.matcher(line);
-            if (matcher.matches()) {
-                log(line);
-                final String focusedApp = matcher.group(5);
-                log(focusedApp);
-                mFocusedApp = focusedApp;
-                continue;
-            }
-
-            matcher = sAppTransitionStatePattern.matcher(line);
-            if (matcher.matches()) {
-                log(line);
-                final String appTransitionState = matcher.group(1);
-                log(appTransitionState);
-                mAppTransitionState = appTransitionState;
-                continue;
-            }
-
-            matcher = sLastAppTransitionPattern.matcher(line);
-            if (matcher.matches()) {
-                log(line);
-                final String lastAppTransitionPattern = matcher.group(1);
-                log(lastAppTransitionPattern);
-                mLastTransition = lastAppTransitionPattern;
-                continue;
-            }
-
-            matcher = sStableBoundsPattern.matcher(line);
-            if (matcher.matches()) {
-                log(line);
-                int left = Integer.parseInt(matcher.group(1));
-                int top = Integer.parseInt(matcher.group(2));
-                int right = Integer.parseInt(matcher.group(3));
-                int bottom = Integer.parseInt(matcher.group(4));
-                mStableBounds.setBounds(left, top, right - left, bottom - top);
-                log(mStableBounds.toString());
-                continue;
-            }
-
-            matcher = sDefaultPinnedStackBoundsPattern.matcher(line);
-            if (matcher.matches()) {
-                log(line);
-                int left = Integer.parseInt(matcher.group(1));
-                int top = Integer.parseInt(matcher.group(2));
-                int right = Integer.parseInt(matcher.group(3));
-                int bottom = Integer.parseInt(matcher.group(4));
-                mDefaultPinnedStackBounds.setBounds(left, top, right - left, bottom - top);
-                log(mDefaultPinnedStackBounds.toString());
-                continue;
-            }
-
-            matcher = sPinnedStackMovementBoundsPattern.matcher(line);
-            if (matcher.matches()) {
-                log(line);
-                int left = Integer.parseInt(matcher.group(1));
-                int top = Integer.parseInt(matcher.group(2));
-                int right = Integer.parseInt(matcher.group(3));
-                int bottom = Integer.parseInt(matcher.group(4));
-                mPinnedStackMovementBounds.setBounds(left, top, right - left, bottom - top);
-                log(mPinnedStackMovementBounds.toString());
-                continue;
-            }
-
-            matcher = sInputMethodWindowPattern.matcher(line);
-            if (matcher.matches()) {
-                log(line);
-                mInputMethodWindowAppToken = matcher.group(1);
-                log(mInputMethodWindowAppToken);
-                continue;
-            }
-
-            matcher = sRotationPattern.matcher(line);
-            if (matcher.matches()) {
-                log(line);
-                mRotation = Integer.parseInt(matcher.group(1));
-                continue;
-            }
-
-            matcher = sLastOrientationPattern.matcher(line);
-            if (matcher.matches()) {
-                log(line);
-                mLastOrientation = Integer.parseInt(matcher.group(1));
-                continue;
-            }
-
-            matcher = sDisplayFrozenPattern.matcher(line);
-            if (matcher.matches()) {
-                log(line);
-                mDisplayFrozen = Boolean.parseBoolean(matcher.group(1));
-                continue;
-            }
-
-            matcher = sDockedStackMinimizedPattern.matcher(line);
-            if (matcher.matches()) {
-                log(line);
-                mIsDockedStackMinimized = Boolean.parseBoolean(matcher.group(1));
-                continue;
-            }
-        }
-    }
-
-    void getMatchingWindowTokens(final String windowName, List<String> tokenList) {
-        tokenList.clear();
-
-        for (WindowState ws : mWindowStates) {
-            if (windowName.equals(ws.getName())) {
-                tokenList.add(ws.getToken());
-            }
-        }
-    }
-
-    void getMatchingVisibleWindowState(final String windowName, List<WindowState> windowList) {
-        windowList.clear();
-        for (WindowState ws : mWindowStates) {
-            if (ws.isShown() && windowName.equals(ws.getName())) {
-                windowList.add(ws);
-            }
-        }
-    }
-
-    void getPrefixMatchingVisibleWindowState(final String windowName, List<WindowState> windowList) {
-        windowList.clear();
-        for (WindowState ws : mWindowStates) {
-            if (ws.isShown() && ws.getName().startsWith(windowName)) {
-                windowList.add(ws);
-            }
-        }
-    }
-
-    WindowState getWindowByPackageName(String packageName, int windowType) {
-        for (WindowState ws : mWindowStates) {
-            final String name = ws.getName();
-            if (name == null || !name.contains(packageName)) {
-                continue;
-            }
-            if (windowType != ws.getType()) {
-                continue;
-            }
-            return ws;
-        }
-
-        return null;
-    }
-
-    void getWindowsByPackageName(String packageName, List<Integer> restrictToTypeList,
-            List<WindowState> outWindowList) {
-        outWindowList.clear();
-        for (WindowState ws : mWindowStates) {
-            final String name = ws.getName();
-            if (name == null || !name.contains(packageName)) {
-                continue;
-            }
-            if (restrictToTypeList != null && !restrictToTypeList.contains(ws.getType())) {
-                continue;
-            }
-            outWindowList.add(ws);
-        }
-    }
-
-    void sortWindowsByLayer(List<WindowState> windows) {
-        windows.sort(Comparator.comparingInt(WindowState::getLayer));
-    }
-
-    WindowState getWindowStateForAppToken(String appToken) {
-        for (WindowState ws : mWindowStates) {
-            if (ws.getToken().equals(appToken)) {
-                return ws;
-            }
-        }
-        return null;
-    }
-
-    Display getDisplay(int displayId) {
-        for (Display display : mDisplays) {
-            if (displayId == display.getDisplayId()) {
-                return display;
-            }
-        }
-        return null;
-    }
-
-    String getFrontWindow() {
-        if (mWindowStates == null || mWindowStates.isEmpty()) {
-            return null;
-        }
-        return mWindowStates.get(0).getName();
-    }
-
-    String getFocusedWindow() {
-        return mFocusedWindow;
-    }
-
-    String getFocusedApp() {
-        return mFocusedApp;
-    }
-
-    String getLastTransition() {
-        return mLastTransition;
-    }
-
-    String getAppTransitionState() {
-        return mAppTransitionState;
-    }
-
-    int getFrontStackId(int displayId) {
-        return mDisplayStacks.get(displayId).get(0).mStackId;
-    }
-
-    public int getRotation() {
-        return mRotation;
-    }
-
-    int getLastOrientation() {
-        return mLastOrientation;
-    }
-
-    boolean containsStack(int stackId) {
-        for (WindowStack stack : mStacks) {
-            if (stackId == stack.mStackId) {
-                return true;
-            }
-        }
-        return false;
-    }
-
-    /** Check if there exists a window record with matching windowName. */
-    boolean containsWindow(String windowName) {
-        for (WindowState window : mWindowStates) {
-            if (window.getName().equals(windowName)) {
-                return true;
-            }
-        }
-        return false;
-    }
-
-    /** Check if at least one window which matches provided window name is visible. */
-    boolean isWindowVisible(String windowName) {
-        for (WindowState window : mWindowStates) {
-            if (window.getName().equals(windowName)) {
-                if (window.isShown()) {
-                    return true;
-                }
-            }
-        }
-        return false;
-    }
-
-    boolean allWindowsVisible(String windowName) {
-        boolean allVisible = false;
-        for (WindowState window : mWindowStates) {
-            if (window.getName().equals(windowName)) {
-                if (!window.isShown()) {
-                    log("[VISIBLE] not visible" + windowName);
-                    return false;
-                }
-                log("[VISIBLE] visible" + windowName);
-                allVisible = true;
-            }
-        }
-        return allVisible;
-    }
-
-    WindowStack getStack(int stackId) {
-        for (WindowStack stack : mStacks) {
-            if (stackId == stack.mStackId) {
-                return stack;
-            }
-        }
-        return null;
-    }
-
-
-    int getStackPosition(int stackId) {
-        for (Integer displayId : mDisplayStacks.keySet()) {
-            List<WindowStack> stacks = mDisplayStacks.get(displayId);
-            for (int i = 0; i < stacks.size(); i++) {
-                if (stackId == stacks.get(i).mStackId) {
-                    return i;
-                }
-            }
-        }
-        return -1;
-    }
-
-    WindowState getInputMethodWindowState() {
-        return getWindowStateForAppToken(mInputMethodWindowAppToken);
-    }
-
-    Rectangle getStableBounds() {
-        return mStableBounds;
-    }
-
-    Rectangle getDefaultPinnedStackBounds() {
-        return mDefaultPinnedStackBounds;
-    }
-
-    Rectangle getPinnedStackMomentBounds() {
-        return mPinnedStackMovementBounds;
-    }
-
-    WindowState findFirstWindowWithType(int type) {
-        for (WindowState window : mWindowStates) {
-            if (window.getType() == type) {
-                return window;
-            }
-        }
-        return null;
-    }
-
-    public boolean isDisplayFrozen() {
-        return mDisplayFrozen;
-    }
-
-    public boolean isDockedStackMinimized() {
-        return mIsDockedStackMinimized;
-    }
-
-    private void reset() {
-        mSysDump.clear();
-        mStacks.clear();
-        mDisplays.clear();
-        mWindowStates.clear();
-        mFocusedWindow = null;
-        mFocusedApp = null;
-        mInputMethodWindowAppToken = null;
-    }
-
-    static class WindowStack extends WindowContainer {
-
-        private static final Pattern sTaskIdPattern = Pattern.compile("taskId=(\\d+)");
-        private static final Pattern sWindowAnimationBackgroundSurfacePattern =
-                Pattern.compile("mWindowAnimationBackgroundSurface:");
-
-        int mStackId;
-        ArrayList<WindowTask> mTasks = new ArrayList();
-        boolean mWindowAnimationBackgroundSurfaceShowing;
-
-        private WindowStack() {
-
-        }
-
-        static WindowStack create(
-                LinkedList<String> dump, Pattern stackIdPattern, Pattern[] exitPatterns) {
-            final String line = dump.peek().trim();
-
-            final Matcher matcher = stackIdPattern.matcher(line);
-            if (!matcher.matches()) {
-                // Not a stack.
-                return null;
-            }
-            // For the stack Id line we just read.
-            dump.pop();
-
-            final WindowStack stack = new WindowStack();
-            log(line);
-            final String stackId = matcher.group(1);
-            log(stackId);
-            stack.mStackId = Integer.parseInt(stackId);
-            stack.extract(dump, exitPatterns);
-            return stack;
-        }
-
-        void extract(LinkedList<String> dump, Pattern[] exitPatterns) {
-
-            final List<Pattern> taskExitPatterns = new ArrayList();
-            Collections.addAll(taskExitPatterns, exitPatterns);
-            taskExitPatterns.add(sTaskIdPattern);
-            taskExitPatterns.add(sWindowAnimationBackgroundSurfacePattern);
-            final Pattern[] taskExitPatternsArray =
-                    taskExitPatterns.toArray(new Pattern[taskExitPatterns.size()]);
-
-            while (!doneExtracting(dump, exitPatterns)) {
-                final WindowTask task =
-                        WindowTask.create(dump, sTaskIdPattern, taskExitPatternsArray);
-
-                if (task != null) {
-                    mTasks.add(task);
-                    continue;
-                }
-
-                final String line = dump.pop().trim();
-
-                if (extractFullscreen(line)) {
-                    continue;
-                }
-
-                if (extractBounds(line)) {
-                    continue;
-                }
-
-                if (extractWindowAnimationBackgroundSurface(line)) {
-                    continue;
-                }
-            }
-        }
-
-        boolean extractWindowAnimationBackgroundSurface(String line) {
-            if (sWindowAnimationBackgroundSurfacePattern.matcher(line).matches()) {
-                log(line);
-                mWindowAnimationBackgroundSurfaceShowing = true;
-                return true;
-            }
-            return false;
-        }
-
-        WindowTask getTask(int taskId) {
-            for (WindowTask task : mTasks) {
-                if (taskId == task.mTaskId) {
-                    return task;
-                }
-            }
-            return null;
-        }
-
-        boolean isWindowAnimationBackgroundSurfaceShowing() {
-            return mWindowAnimationBackgroundSurfaceShowing;
-        }
-    }
-
-    static class WindowTask extends WindowContainer {
-        private static final Pattern sTempInsetBoundsPattern =
-                Pattern.compile("mTempInsetBounds=\\[(\\d+),(\\d+)\\]\\[(\\d+),(\\d+)\\]");
-
-        private static final Pattern sAppTokenPattern = Pattern.compile(
-                "Activity #(\\d+) AppWindowToken\\{(\\S+) token=Token\\{(\\S+) "
-                + "ActivityRecord\\{(\\S+) u(\\d+) (\\S+) t(\\d+)\\}\\}\\}");
-
-
-        int mTaskId;
-        Rectangle mTempInsetBounds;
-        List<String> mAppTokens = new ArrayList();
-
-        private WindowTask() {
-        }
-
-        static WindowTask create(
-                LinkedList<String> dump, Pattern taskIdPattern, Pattern[] exitPatterns) {
-            final String line = dump.peek().trim();
-
-            final Matcher matcher = taskIdPattern.matcher(line);
-            if (!matcher.matches()) {
-                // Not a task.
-                return null;
-            }
-            // For the task Id line we just read.
-            dump.pop();
-
-            final WindowTask task = new WindowTask();
-            log(line);
-            final String taskId = matcher.group(1);
-            log(taskId);
-            task.mTaskId = Integer.parseInt(taskId);
-            task.extract(dump, exitPatterns);
-            return task;
-        }
-
-        private void extract(LinkedList<String> dump, Pattern[] exitPatterns) {
-            while (!doneExtracting(dump, exitPatterns)) {
-                final String line = dump.pop().trim();
-
-                if (extractFullscreen(line)) {
-                    continue;
-                }
-
-                if (extractBounds(line)) {
-                    continue;
-                }
-
-                Matcher matcher = sTempInsetBoundsPattern.matcher(line);
-                if (matcher.matches()) {
-                    log(line);
-                    mTempInsetBounds = extractBounds(matcher);
-                }
-
-                matcher = sAppTokenPattern.matcher(line);
-                if (matcher.matches()) {
-                    log(line);
-                    final String appToken = matcher.group(6);
-                    log(appToken);
-                    mAppTokens.add(appToken);
-                    continue;
-                }
-            }
-        }
-    }
-
-    static abstract class WindowContainer {
-        protected static final Pattern sFullscreenPattern = Pattern.compile("mFillsParent=(\\S+)");
-        protected static final Pattern sBoundsPattern =
-                Pattern.compile("mBounds=\\[(-?\\d+),(-?\\d+)\\]\\[(-?\\d+),(-?\\d+)\\]");
-
-        protected boolean mFullscreen;
-        protected Rectangle mBounds;
-
-        static boolean doneExtracting(LinkedList<String> dump, Pattern[] exitPatterns) {
-            if (dump.isEmpty()) {
-                return true;
-            }
-            final String line = dump.peek().trim();
-
-            for (Pattern pattern : exitPatterns) {
-                if (pattern.matcher(line).matches()) {
-                    return true;
-                }
-            }
-            return false;
-        }
-
-        boolean extractFullscreen(String line) {
-            final Matcher matcher = sFullscreenPattern.matcher(line);
-            if (!matcher.matches()) {
-                return false;
-            }
-            log(line);
-            final String fullscreen = matcher.group(1);
-            log(fullscreen);
-            mFullscreen = Boolean.valueOf(fullscreen);
-            return true;
-        }
-
-        boolean extractBounds(String line) {
-            final Matcher matcher = sBoundsPattern.matcher(line);
-            if (!matcher.matches()) {
-                return false;
-            }
-            log(line);
-            mBounds = extractBounds(matcher);
-            return true;
-        }
-
-        static Rectangle extractBounds(Matcher matcher) {
-            final int left = Integer.valueOf(matcher.group(1));
-            final int top = Integer.valueOf(matcher.group(2));
-            final int right = Integer.valueOf(matcher.group(3));
-            final int bottom = Integer.valueOf(matcher.group(4));
-            final Rectangle rect = new Rectangle(left, top, right - left, bottom - top);
-
-            log(rect.toString());
-            return rect;
-        }
-
-        static void extractMultipleBounds(Matcher matcher, int groupIndex, Rectangle... rectList) {
-            for (Rectangle rect : rectList) {
-                if (rect == null) {
-                    return;
-                }
-                final int left = Integer.valueOf(matcher.group(groupIndex++));
-                final int top = Integer.valueOf(matcher.group(groupIndex++));
-                final int right = Integer.valueOf(matcher.group(groupIndex++));
-                final int bottom = Integer.valueOf(matcher.group(groupIndex++));
-                rect.setBounds(left, top, right - left, bottom - top);
-            }
-        }
-
-        Rectangle getBounds() {
-            return mBounds;
-        }
-
-        boolean isFullscreen() {
-            return mFullscreen;
-        }
-    }
-
-    static class Display extends WindowContainer {
-        private static final String TAG = "[Display] ";
-
-        private static final Pattern sDisplayInfoPattern =
-                Pattern.compile("(.+) (\\d+)dpi cur=(\\d+)x(\\d+) app=(\\d+)x(\\d+) (.+)");
-
-        private final int mDisplayId;
-        private Rectangle mDisplayRect = new Rectangle();
-        private Rectangle mAppRect = new Rectangle();
-        private int mDpi;
-
-        private Display(int displayId) {
-            mDisplayId = displayId;
-        }
-
-        int getDisplayId() {
-            return mDisplayId;
-        }
-
-        int getDpi() {
-            return mDpi;
-        }
-
-        Rectangle getDisplayRect() {
-            return mDisplayRect;
-        }
-
-        Rectangle getAppRect() {
-            return mAppRect;
-        }
-
-        static Display create(LinkedList<String> dump, Pattern[] exitPatterns) {
-            // TODO: exit pattern for displays?
-            final String line = dump.peek().trim();
-
-            Matcher matcher = sDisplayIdPattern.matcher(line);
-            if (!matcher.matches()) {
-                return null;
-            }
-
-            log(TAG + "DISPLAY_ID: " + line);
-            dump.pop();
-
-            final int displayId = Integer.valueOf(matcher.group(1));
-            final Display display = new Display(displayId);
-            display.extract(dump, exitPatterns);
-            return display;
-        }
-
-        private void extract(LinkedList<String> dump, Pattern[] exitPatterns) {
-            while (!doneExtracting(dump, exitPatterns)) {
-                final String line = dump.pop().trim();
-
-                final Matcher matcher = sDisplayInfoPattern.matcher(line);
-                if (matcher.matches()) {
-                    log(TAG + "DISPLAY_INFO: " + line);
-                    mDpi = Integer.valueOf(matcher.group(2));
-
-                    final int displayWidth = Integer.valueOf(matcher.group(3));
-                    final int displayHeight = Integer.valueOf(matcher.group(4));
-                    mDisplayRect.setBounds(0, 0, displayWidth, displayHeight);
-
-                    final int appWidth = Integer.valueOf(matcher.group(5));
-                    final int appHeight = Integer.valueOf(matcher.group(6));
-                    mAppRect.setBounds(0, 0, appWidth, appHeight);
-
-                    // break as we don't need other info for now
-                    break;
-                }
-                // Extract other info here if needed
-            }
-        }
-
-        @Override
-        public String toString() {
-            return "Display #" + mDisplayId + ": mDisplayRect=" + mDisplayRect
-                    + " mAppRect=" + mAppRect;
-        }
-    }
-
-    public static class WindowState extends WindowContainer {
-        private static final String TAG = "[WindowState] ";
-
-        public static final int TYPE_WALLPAPER = 2013;
-
-        private static final int WINDOW_TYPE_NORMAL   = 0;
-        private static final int WINDOW_TYPE_STARTING = 1;
-        private static final int WINDOW_TYPE_EXITING  = 2;
-        private static final int WINDOW_TYPE_DEBUGGER = 3;
-
-        private static final String RECT_STR = "\\[(\\d+),(\\d+)\\]\\[(\\d+),(\\d+)\\]";
-        private static final String NEGATIVE_VALUES_ALLOWED_RECT_STR =
-                "\\[([-\\d]+),([-\\d]+)\\]\\[([-\\d]+),([-\\d]+)\\]";
-        private static final Pattern sMainFramePattern = Pattern.compile("mFrame=" + RECT_STR + ".+");
-        private static final Pattern sFramePattern =
-                Pattern.compile("Frames: containing=" + RECT_STR + " parent=" + RECT_STR);
-        private static final Pattern sContentFramePattern =
-            Pattern.compile("content=" + RECT_STR + " .+");
-        private static final Pattern sWindowAssociationPattern =
-                Pattern.compile("mDisplayId=(\\d+) stackId=(\\d+) (.+)");
-        private static final Pattern sSurfaceInsetsPattern =
-            Pattern.compile("Cur insets.+surface=" + RECT_STR + ".+");
-        private static final Pattern sContentInsetsPattern =
-                Pattern.compile("Cur insets.+content=" + NEGATIVE_VALUES_ALLOWED_RECT_STR + ".+");
-        private static final Pattern sGivenContentInsetsPattern =
-                Pattern.compile("mGivenContentInsets=" + RECT_STR + ".+");
-        private static final Pattern sCropPattern =
-            Pattern.compile(".+mLastClipRect=" + RECT_STR + ".*");
-        private static final Pattern sSurfacePattern =
-                Pattern.compile("Surface: shown=(\\S+) layer=(\\d+) alpha=[\\d.]+ rect=\\([\\d.-]+,[\\d.-]+\\) [\\d.]+ x [\\d.]+.*");
-        private static final Pattern sAttrsPattern=
-                Pattern.compile("mAttrs=WM\\.LayoutParams\\{.*ty=(\\d+).*\\}");
-
-
-        private final String mName;
-        private final String mAppToken;
-        private final int mWindowType;
-        private int mType;
-        private int mDisplayId;
-        private int mStackId;
-        private int mLayer;
-        private boolean mShown;
-        private Rectangle mContainingFrame = new Rectangle();
-        private Rectangle mParentFrame = new Rectangle();
-        private Rectangle mContentFrame = new Rectangle();
-        private Rectangle mFrame = new Rectangle();
-        private Rectangle mSurfaceInsets = new Rectangle();
-        private Rectangle mContentInsets = new Rectangle();
-        private Rectangle mGivenContentInsets = new Rectangle();
-        private Rectangle mCrop = new Rectangle();
-
-
-        private WindowState(Matcher matcher, int windowType) {
-            mName = matcher.group(4);
-            mAppToken = matcher.group(2);
-            mWindowType = windowType;
-        }
-
-        public String getName() {
-            return mName;
-        }
-
-        String getToken() {
-            return mAppToken;
-        }
-
-        boolean isStartingWindow() {
-            return mWindowType == WINDOW_TYPE_STARTING;
-        }
-
-        boolean isExitingWindow() {
-            return mWindowType == WINDOW_TYPE_EXITING;
-        }
-
-        boolean isDebuggerWindow() {
-            return mWindowType == WINDOW_TYPE_DEBUGGER;
-        }
-
-        int getDisplayId() {
-            return mDisplayId;
-        }
-
-        int getStackId() {
-            return mStackId;
-        }
-
-        int getLayer() {
-            return mLayer;
-        }
-
-        Rectangle getContainingFrame() {
-            return mContainingFrame;
-        }
-
-        Rectangle getFrame() {
-            return mFrame;
-        }
-
-        Rectangle getSurfaceInsets() {
-            return mSurfaceInsets;
-        }
-
-        Rectangle getContentInsets() {
-            return mContentInsets;
-        }
-
-        Rectangle getGivenContentInsets() {
-            return mGivenContentInsets;
-        }
-
-        Rectangle getContentFrame() {
-            return mContentFrame;
-        }
-
-        Rectangle getParentFrame() {
-            return mParentFrame;
-        }
-
-        Rectangle getCrop() {
-            return mCrop;
-        }
-
-        boolean isShown() {
-            return mShown;
-        }
-
-        int getType() {
-            return mType;
-        }
-
-        static WindowState create(LinkedList<String> dump, Pattern[] exitPatterns) {
-            final String line = dump.peek().trim();
-
-            Matcher matcher = sWindowPattern.matcher(line);
-            if (!matcher.matches()) {
-                return null;
-            }
-
-            log(TAG + "WINDOW: " + line);
-            dump.pop();
-
-            final WindowState window;
-            Matcher specialMatcher;
-            if ((specialMatcher = sStartingWindowPattern.matcher(line)).matches()) {
-                log(TAG + "STARTING: " + line);
-                window = new WindowState(specialMatcher, WINDOW_TYPE_STARTING);
-            } else if ((specialMatcher = sExitingWindowPattern.matcher(line)).matches()) {
-                log(TAG + "EXITING: " + line);
-                window = new WindowState(specialMatcher, WINDOW_TYPE_EXITING);
-            } else if ((specialMatcher = sDebuggerWindowPattern.matcher(line)).matches()) {
-                log(TAG + "DEBUGGER: " + line);
-                window = new WindowState(specialMatcher, WINDOW_TYPE_DEBUGGER);
-            } else {
-                window = new WindowState(matcher, WINDOW_TYPE_NORMAL);
-            }
-
-            window.extract(dump, exitPatterns);
-            return window;
-        }
-
-        private void extract(LinkedList<String> dump, Pattern[] exitPatterns) {
-            while (!doneExtracting(dump, exitPatterns)) {
-                final String line = dump.pop().trim();
-
-                Matcher matcher = sWindowAssociationPattern.matcher(line);
-                if (matcher.matches()) {
-                    log(TAG + "WINDOW_ASSOCIATION: " + line);
-                    mDisplayId = Integer.valueOf(matcher.group(1));
-                    mStackId = Integer.valueOf(matcher.group(2));
-                    continue;
-                }
-
-                matcher = sMainFramePattern.matcher(line);
-                if (matcher.matches()) {
-                    log(TAG + "MAIN WINDOW FRAME: " + line);
-                    mFrame = extractBounds(matcher);
-                    continue;
-                }
-
-                matcher = sFramePattern.matcher(line);
-                if (matcher.matches()) {
-                    log(TAG + "FRAME: " + line);
-                    extractMultipleBounds(matcher, 1, mContainingFrame, mParentFrame);
-                    continue;
-                }
-
-                matcher = sContentFramePattern.matcher(line);
-                if (matcher.matches()) {
-                    log(TAG + "CONTENT FRAME: " + line);
-                    mContentFrame = extractBounds(matcher);
-                }
-
-                matcher = sSurfaceInsetsPattern.matcher(line);
-                if (matcher.matches()) {
-                    log(TAG + "INSETS: " + line);
-                    mSurfaceInsets = extractBounds(matcher);
-                }
-
-                matcher = sContentInsetsPattern.matcher(line);
-                if (matcher.matches()) {
-                    log(TAG + "CONTENT INSETS: " + line);
-                    mContentInsets = extractBounds(matcher);
-                }
-
-                matcher = sCropPattern.matcher(line);
-                if (matcher.matches()) {
-                    log(TAG + "CROP: " + line);
-                    mCrop = extractBounds(matcher);
-                }
-
-                matcher = sSurfacePattern.matcher(line);
-                if (matcher.matches()) {
-                    log(TAG + "SURFACE: " + line);
-                    mShown = Boolean.valueOf(matcher.group(1));
-                    mLayer = Integer.valueOf(matcher.group(2));
-                }
-
-                matcher = sAttrsPattern.matcher(line);
-                if (matcher.matches()) {
-                    log(TAG + "ATTRS: " + line);
-                    mType = Integer.valueOf(matcher.group(1));
-                }
-
-                matcher = sGivenContentInsetsPattern.matcher(line);
-                if (matcher.matches()) {
-                    log(TAG + "GIVEN CONTENT INSETS: " + line);
-                    mGivenContentInsets = extractBounds(matcher);
-                }
-
-                // Extract other info here if needed
-            }
-        }
-
-        private static String getWindowTypeSuffix(int windowType) {
-            switch (windowType) {
-            case WINDOW_TYPE_STARTING: return " STARTING";
-            case WINDOW_TYPE_EXITING: return " EXITING";
-            case WINDOW_TYPE_DEBUGGER: return " DEBUGGER";
-            default: break;
-            }
-            return "";
-        }
-
-        @Override
-        public String toString() {
-            return "WindowState: {" + mAppToken + " " + mName
-                    + getWindowTypeSuffix(mWindowType) + "}" + " type=" + mType
-                    + " cf=" + mContainingFrame + " pf=" + mParentFrame;
-        }
-    }
-}
diff --git a/hostsidetests/services/activityandwindowmanager/windowmanager/Android.mk b/hostsidetests/services/activityandwindowmanager/windowmanager/Android.mk
deleted file mode 100644
index 0739743..0000000
--- a/hostsidetests/services/activityandwindowmanager/windowmanager/Android.mk
+++ /dev/null
@@ -1,38 +0,0 @@
-# Copyright (C) 2015 The Android Open Source Project
-#
-# Licensed under the Apache License, Version 2.0 (the "License");
-# you may not use this file except in compliance with the License.
-# You may obtain a copy of the License at
-#
-#      http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing, software
-# distributed under the License is distributed on an "AS IS" BASIS,
-# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-# See the License for the specific language governing permissions and
-# limitations under the License.
-
-LOCAL_PATH := $(call my-dir)
-
-include $(CLEAR_VARS)
-
-LOCAL_MODULE_TAGS := tests
-
-LOCAL_MODULE := CtsWindowManagerHostTestCases
-
-LOCAL_SRC_FILES := $(call all-java-files-under, src)
-
-LOCAL_JAVA_LIBRARIES := cts-tradefed tradefed CtsServicesHostTestCases
-LOCAL_STATIC_JAVA_LIBRARIES := \
-    cts-amwm-util \
-    platform-test-annotations-host
-
-LOCAL_CTS_TEST_PACKAGE := android.server.cts
-
-# Tag this module as a cts test artifact
-LOCAL_COMPATIBILITY_SUITE := cts vts general-tests
-
-include $(BUILD_CTS_HOST_JAVA_LIBRARY)
-
-# Build the test APKs using their own makefiles
-include $(call all-makefiles-under,$(LOCAL_PATH))
diff --git a/hostsidetests/services/activityandwindowmanager/windowmanager/AndroidTest.xml b/hostsidetests/services/activityandwindowmanager/windowmanager/AndroidTest.xml
deleted file mode 100644
index 82f0099..0000000
--- a/hostsidetests/services/activityandwindowmanager/windowmanager/AndroidTest.xml
+++ /dev/null
@@ -1,31 +0,0 @@
-<?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 window manager host test cases">
-    <option name="config-descriptor:metadata" key="component" value="framework" />
-    <target_preparer class="com.android.tradefed.targetprep.suite.SuiteApkInstaller">
-        <option name="cleanup-apks" value="true" />
-        <option name="test-file-name" value="CtsDragAndDropSourceApp.apk" />
-        <option name="test-file-name" value="CtsDragAndDropTargetApp.apk" />
-        <option name="test-file-name" value="CtsDragAndDropTargetAppSdk23.apk" />
-        <option name="test-file-name" value="CtsDeviceWindowFramesTestApp.apk" />
-        <option name="test-file-name" value="CtsDeviceAlertWindowTestApp.apk" />
-        <option name="test-file-name" value="CtsDeviceAlertWindowTestAppSdk25.apk" />
-    </target_preparer>
-    <test class="com.android.compatibility.common.tradefed.testtype.JarHostTest" >
-        <option name="jar" value="CtsWindowManagerHostTestCases.jar" />
-        <option name="runtime-hint" value="20m40s" />
-    </test>
-</configuration>
diff --git a/hostsidetests/services/activityandwindowmanager/windowmanager/alertwindowapp/AndroidManifest.xml b/hostsidetests/services/activityandwindowmanager/windowmanager/alertwindowapp/AndroidManifest.xml
deleted file mode 100755
index 9c6a6ad..0000000
--- a/hostsidetests/services/activityandwindowmanager/windowmanager/alertwindowapp/AndroidManifest.xml
+++ /dev/null
@@ -1,33 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
- * Copyright (C) 2017 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT 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"
-          xmlns:androidprv="http://schemas.android.com/apk/prv/res/android"
-          package="android.server.alertwindowapp">
-    <uses-permission android:name="android.permission.SYSTEM_ALERT_WINDOW" />
-
-    <application android:label="CtsAlertWindow">
-        <activity android:name=".AlertWindowTestActivity"
-                  android:exported="true" android:windowSoftInputMode="stateAlwaysVisible">
-            <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/services/activityandwindowmanager/windowmanager/alertwindowapp/src/android/server/alertwindowapp/AlertWindowTestActivity.java b/hostsidetests/services/activityandwindowmanager/windowmanager/alertwindowapp/src/android/server/alertwindowapp/AlertWindowTestActivity.java
deleted file mode 100644
index db007e2..0000000
--- a/hostsidetests/services/activityandwindowmanager/windowmanager/alertwindowapp/src/android/server/alertwindowapp/AlertWindowTestActivity.java
+++ /dev/null
@@ -1,49 +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.server.alertwindowapp;
-
-import android.os.Bundle;
-import android.server.alertwindowappsdk25.AlertWindowTestBaseActivity;
-
-import static android.view.WindowManager.LayoutParams.TYPE_APPLICATION_OVERLAY;
-import static android.view.WindowManager.LayoutParams.TYPE_PHONE;
-import static android.view.WindowManager.LayoutParams.TYPE_PRIORITY_PHONE;
-import static android.view.WindowManager.LayoutParams.TYPE_SYSTEM_ALERT;
-import static android.view.WindowManager.LayoutParams.TYPE_SYSTEM_ERROR;
-import static android.view.WindowManager.LayoutParams.TYPE_SYSTEM_OVERLAY;
-
-public class AlertWindowTestActivity extends AlertWindowTestBaseActivity {
-    private static final int[] ALERT_WINDOW_TYPES = {
-            TYPE_PHONE,
-            TYPE_PRIORITY_PHONE,
-            TYPE_SYSTEM_ALERT,
-            TYPE_SYSTEM_ERROR,
-            TYPE_SYSTEM_OVERLAY,
-            TYPE_APPLICATION_OVERLAY
-    };
-
-    @Override
-    protected void onCreate(Bundle icicle) {
-        super.onCreate(icicle);
-        createAllAlertWindows(getPackageName());
-    }
-
-    @Override
-    protected int[] getAlertWindowTypes() {
-        return ALERT_WINDOW_TYPES;
-    }
-}
diff --git a/hostsidetests/services/activityandwindowmanager/windowmanager/alertwindowappsdk25/AndroidManifest.xml b/hostsidetests/services/activityandwindowmanager/windowmanager/alertwindowappsdk25/AndroidManifest.xml
deleted file mode 100755
index efc80ea..0000000
--- a/hostsidetests/services/activityandwindowmanager/windowmanager/alertwindowappsdk25/AndroidManifest.xml
+++ /dev/null
@@ -1,32 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
-  ~ Copyright (C) 2017 The Android Open Source Project
-  ~
-  ~ Licensed under the Apache License, Version 2.0 (the "License");
-  ~ you may not use this file except in compliance with the License.
-  ~ You may obtain a copy of the License at
-  ~
-  ~      http://www.apache.org/licenses/LICENSE-2.0
-  ~
-  ~ Unless required by applicable law or agreed to in writing, software
-  ~ distributed under the License is distributed on an "AS IS" BASIS,
-  ~ WITHOUT 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.server.alertwindowappsdk25">
-    <uses-permission android:name="android.permission.SYSTEM_ALERT_WINDOW" />
-
-    <application android:label="CtsAlertWindowSdk25">
-        <activity android:name=".AlertWindowTestActivitySdk25"
-                  android:exported="true" android:windowSoftInputMode="stateAlwaysVisible">
-            <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/services/activityandwindowmanager/windowmanager/alertwindowappsdk25/src/android/server/alertwindowappsdk25/AlertWindowTestActivitySdk25.java b/hostsidetests/services/activityandwindowmanager/windowmanager/alertwindowappsdk25/src/android/server/alertwindowappsdk25/AlertWindowTestActivitySdk25.java
deleted file mode 100644
index 046879f..0000000
--- a/hostsidetests/services/activityandwindowmanager/windowmanager/alertwindowappsdk25/src/android/server/alertwindowappsdk25/AlertWindowTestActivitySdk25.java
+++ /dev/null
@@ -1,46 +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.server.alertwindowappsdk25;
-
-import android.os.Bundle;
-
-import static android.view.WindowManager.LayoutParams.TYPE_PHONE;
-import static android.view.WindowManager.LayoutParams.TYPE_PRIORITY_PHONE;
-import static android.view.WindowManager.LayoutParams.TYPE_SYSTEM_ALERT;
-import static android.view.WindowManager.LayoutParams.TYPE_SYSTEM_ERROR;
-import static android.view.WindowManager.LayoutParams.TYPE_SYSTEM_OVERLAY;
-
-public class AlertWindowTestActivitySdk25 extends AlertWindowTestBaseActivity {
-    private static final int[] ALERT_WINDOW_TYPES = {
-            TYPE_PHONE,
-            TYPE_PRIORITY_PHONE,
-            TYPE_SYSTEM_ALERT,
-            TYPE_SYSTEM_ERROR,
-            TYPE_SYSTEM_OVERLAY
-    };
-
-    @Override
-    protected void onCreate(Bundle icicle) {
-        super.onCreate(icicle);
-        createAllAlertWindows(getPackageName());
-    }
-
-    @Override
-    protected int[] getAlertWindowTypes() {
-        return ALERT_WINDOW_TYPES;
-    }
-}
diff --git a/hostsidetests/services/activityandwindowmanager/windowmanager/alertwindowappsdk25/src/android/server/alertwindowappsdk25/AlertWindowTestBaseActivity.java b/hostsidetests/services/activityandwindowmanager/windowmanager/alertwindowappsdk25/src/android/server/alertwindowappsdk25/AlertWindowTestBaseActivity.java
deleted file mode 100644
index 2d0dad0..0000000
--- a/hostsidetests/services/activityandwindowmanager/windowmanager/alertwindowappsdk25/src/android/server/alertwindowappsdk25/AlertWindowTestBaseActivity.java
+++ /dev/null
@@ -1,82 +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.server.alertwindowappsdk25;
-
-import android.app.Activity;
-import android.graphics.Color;
-import android.graphics.Point;
-import android.util.Log;
-import android.view.WindowManager;
-import android.widget.TextView;
-
-import static android.view.Gravity.LEFT;
-import static android.view.Gravity.TOP;
-import static android.view.WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE;
-import static android.view.WindowManager.LayoutParams.FLAG_NOT_TOUCHABLE;
-import static android.view.WindowManager.LayoutParams.FLAG_WATCH_OUTSIDE_TOUCH;
-
-public abstract class AlertWindowTestBaseActivity extends Activity {
-
-    protected void createAllAlertWindows(String windowName) {
-        final int[] alertWindowTypes = getAlertWindowTypes();
-        for (int type : alertWindowTypes) {
-            try {
-                createAlertWindow(type, windowName);
-            } catch (Exception e) {
-                Log.e("AlertWindowTestBaseActivity", "Can't create type=" + type, e);
-            }
-        }
-    }
-
-    protected void createAlertWindow(int type) {
-        createAlertWindow(type, getPackageName());
-    }
-
-    protected void createAlertWindow(int type, String windowName) {
-        if (!isSystemAlertWindowType(type)) {
-            throw new IllegalArgumentException("Well...you are not an alert window type=" + type);
-        }
-
-        final Point size = new Point();
-        final WindowManager wm = getSystemService(WindowManager.class);
-        wm.getDefaultDisplay().getSize(size);
-
-        WindowManager.LayoutParams params = new WindowManager.LayoutParams(
-                type, FLAG_NOT_FOCUSABLE | FLAG_WATCH_OUTSIDE_TOUCH | FLAG_NOT_TOUCHABLE);
-        params.width = size.x / 3;
-        params.height = size.y / 3;
-        params.gravity = TOP | LEFT;
-        params.setTitle(windowName);
-
-        final TextView view = new TextView(this);
-        view.setText(windowName + "   type=" + type);
-        view.setBackgroundColor(Color.RED);
-        wm.addView(view, params);
-    }
-
-    private boolean isSystemAlertWindowType(int type) {
-        final int[] alertWindowTypes = getAlertWindowTypes();
-        for (int current : alertWindowTypes) {
-            if (current == type) {
-                return true;
-            }
-        }
-        return false;
-    }
-
-    protected abstract int[] getAlertWindowTypes();
-}
diff --git a/hostsidetests/services/activityandwindowmanager/windowmanager/dndsourceapp/Android.mk b/hostsidetests/services/activityandwindowmanager/windowmanager/dndsourceapp/Android.mk
deleted file mode 100644
index 12a04ed..0000000
--- a/hostsidetests/services/activityandwindowmanager/windowmanager/dndsourceapp/Android.mk
+++ /dev/null
@@ -1,31 +0,0 @@
-# Copyright (C) 2016 The Android Open Source Project
-#
-# Licensed under the Apache License, Version 2.0 (the "License");
-# you may not use this file except in compliance with the License.
-# You may obtain a copy of the License at
-#
-#      http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing, software
-# distributed under the License is distributed on an "AS IS" BASIS,
-# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-# See the License for the specific language governing permissions and
-# limitations under the License.
-
-LOCAL_PATH:= $(call my-dir)
-
-include $(CLEAR_VARS)
-
-# Don't include this package in any target.
-LOCAL_MODULE_TAGS := tests
-
-LOCAL_SRC_FILES := $(call all-java-files-under, src)
-
-LOCAL_SDK_VERSION := current
-
-# Tag this module as a cts test artifact
-LOCAL_COMPATIBILITY_SUITE := cts vts general-tests
-
-LOCAL_PACKAGE_NAME := CtsDragAndDropSourceApp
-
-include $(BUILD_CTS_SUPPORT_PACKAGE)
diff --git a/hostsidetests/services/activityandwindowmanager/windowmanager/dndsourceapp/AndroidManifest.xml b/hostsidetests/services/activityandwindowmanager/windowmanager/dndsourceapp/AndroidManifest.xml
deleted file mode 100644
index 296a979..0000000
--- a/hostsidetests/services/activityandwindowmanager/windowmanager/dndsourceapp/AndroidManifest.xml
+++ /dev/null
@@ -1,31 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!-- 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.
--->
-
-<manifest xmlns:android="http://schemas.android.com/apk/res/android"
-        package="android.wm.cts.dndsourceapp">
-    <application android:label="CtsDnDSource">
-        <activity android:name="android.wm.cts.dndsourceapp.DragSource">
-            <intent-filter>
-                <action android:name="android.intent.action.MAIN"/>
-                <category android:name="android.intent.category.LAUNCHER"/>
-            </intent-filter>
-        </activity>
-
-        <provider android:name="android.wm.cts.dndsourceapp.DragSourceContentProvider"
-                  android:authorities="android.wm.cts.dndsource.contentprovider"
-                  android:grantUriPermissions="true"/>
-    </application>
-</manifest>
diff --git a/hostsidetests/services/activityandwindowmanager/windowmanager/dndsourceapp/src/android/wm/cts/dndsourceapp/DragSource.java b/hostsidetests/services/activityandwindowmanager/windowmanager/dndsourceapp/src/android/wm/cts/dndsourceapp/DragSource.java
deleted file mode 100644
index 7cb7b8b..0000000
--- a/hostsidetests/services/activityandwindowmanager/windowmanager/dndsourceapp/src/android/wm/cts/dndsourceapp/DragSource.java
+++ /dev/null
@@ -1,131 +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.wm.cts.dndsourceapp;
-
-import android.app.Activity;
-import android.content.ClipData;
-import android.content.ClipDescription;
-import android.net.Uri;
-import android.os.Bundle;
-import android.os.Handler;
-import android.os.FileUriExposedException;
-import android.os.PersistableBundle;
-import android.util.Log;
-import android.view.MotionEvent;
-import android.view.View;
-import android.widget.TextView;
-
-import java.io.File;
-
-public class DragSource extends Activity{
-    private static final String LOG_TAG = "DragSource";
-
-    private static final String RESULT_KEY_START_DRAG = "START_DRAG";
-    private static final String RESULT_KEY_DETAILS = "DETAILS";
-    private static final String RESULT_OK = "OK";
-    private static final String RESULT_EXCEPTION = "Exception";
-
-    private static final String URI_PREFIX =
-            "content://" + DragSourceContentProvider.AUTHORITY + "/data";
-
-    private static final String MAGIC_VALUE = "42";
-    private static final long TIMEOUT_CANCEL = 150;
-
-    private TextView mTextView;
-
-    @Override
-    public void onCreate(Bundle savedInstanceState) {
-        super.onCreate(savedInstanceState);
-
-        View view = getLayoutInflater().inflate(R.layout.source_activity, null);
-        setContentView(view);
-
-        final Uri plainUri = Uri.parse(URI_PREFIX + "/" + MAGIC_VALUE);
-
-        setUpDragSource("disallow_global", plainUri, 0);
-        setUpDragSource("cancel_soon", plainUri, View.DRAG_FLAG_GLOBAL);
-
-        setUpDragSource("grant_none", plainUri, View.DRAG_FLAG_GLOBAL);
-        setUpDragSource("grant_read", plainUri,
-                View.DRAG_FLAG_GLOBAL | View.DRAG_FLAG_GLOBAL_URI_READ);
-        setUpDragSource("grant_write", plainUri,
-                View.DRAG_FLAG_GLOBAL | View.DRAG_FLAG_GLOBAL_URI_WRITE);
-        setUpDragSource("grant_read_persistable", plainUri,
-                View.DRAG_FLAG_GLOBAL | View.DRAG_FLAG_GLOBAL_URI_READ |
-                        View.DRAG_FLAG_GLOBAL_PERSISTABLE_URI_PERMISSION);
-
-        final Uri prefixUri = Uri.parse(URI_PREFIX);
-
-        setUpDragSource("grant_read_prefix", prefixUri,
-                View.DRAG_FLAG_GLOBAL | View.DRAG_FLAG_GLOBAL_URI_READ |
-                        View.DRAG_FLAG_GLOBAL_PREFIX_URI_PERMISSION);
-        setUpDragSource("grant_read_noprefix", prefixUri,
-                View.DRAG_FLAG_GLOBAL | View.DRAG_FLAG_GLOBAL_URI_READ);
-
-        final Uri fileUri = Uri.fromFile(new File("/sdcard/sample.jpg"));
-
-        setUpDragSource("file_local", fileUri, 0);
-        setUpDragSource("file_global", fileUri, View.DRAG_FLAG_GLOBAL);
-    }
-
-    private void setUpDragSource(String mode, final Uri uri, final int flags) {
-        if (!mode.equals(getIntent().getStringExtra("mode"))) {
-            return;
-        }
-        mTextView = (TextView) findViewById(R.id.drag_source);
-        mTextView.setText(mode);
-        mTextView.setOnTouchListener(new View.OnTouchListener() {
-            @Override
-            public boolean onTouch(View v, MotionEvent event) {
-                if (event.getAction() != MotionEvent.ACTION_DOWN) {
-                    return false;
-                }
-                try {
-                    final ClipDescription clipDescription = new ClipDescription("", new String[] {
-                            ClipDescription.MIMETYPE_TEXT_URILIST });
-                    PersistableBundle extras = new PersistableBundle(1);
-                    extras.putString("extraKey", "extraValue");
-                    clipDescription.setExtras(extras);
-                    final ClipData clipData = new ClipData(clipDescription, new ClipData.Item(uri));
-                    v.startDragAndDrop(
-                            clipData,
-                            new View.DragShadowBuilder(v),
-                            null,
-                            flags);
-                    logResult(RESULT_KEY_START_DRAG, RESULT_OK);
-                } catch (FileUriExposedException e) {
-                    logResult(RESULT_KEY_DETAILS, e.getMessage());
-                    logResult(RESULT_KEY_START_DRAG, RESULT_EXCEPTION);
-                }
-                if (mode.equals("cancel_soon")) {
-                    new Handler().postDelayed(new Runnable() {
-                        @Override
-                        public void run() {
-                            v.cancelDragAndDrop();
-                        }
-                    }, TIMEOUT_CANCEL);
-                }
-                return true;
-            }
-        });
-    }
-
-    private void logResult(String key, String value) {
-        Log.i(LOG_TAG, key + "=" + value);
-        mTextView.setText(mTextView.getText() + "\n" + key + "=" + value);
-    }
-}
diff --git a/hostsidetests/services/activityandwindowmanager/windowmanager/dndsourceapp/src/android/wm/cts/dndsourceapp/DragSourceContentProvider.java b/hostsidetests/services/activityandwindowmanager/windowmanager/dndsourceapp/src/android/wm/cts/dndsourceapp/DragSourceContentProvider.java
deleted file mode 100644
index 06a94aa..0000000
--- a/hostsidetests/services/activityandwindowmanager/windowmanager/dndsourceapp/src/android/wm/cts/dndsourceapp/DragSourceContentProvider.java
+++ /dev/null
@@ -1,71 +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.wm.cts.dndsourceapp;
-
-import android.content.ContentProvider;
-import android.content.ContentValues;
-import android.content.UriMatcher;
-import android.database.Cursor;
-import android.net.Uri;
-
-public class DragSourceContentProvider extends ContentProvider {
-
-    public static final String AUTHORITY = "android.wm.cts.dndsource.contentprovider";
-
-    private static final UriMatcher sMatcher = new UriMatcher(UriMatcher.NO_MATCH);
-
-    private static final int URI_DATA = 1;
-
-    static {
-        sMatcher.addURI(AUTHORITY, "data/#", URI_DATA);
-    }
-
-    @Override
-    public boolean onCreate() {
-        return false;
-    }
-
-    @Override
-    public Cursor query(Uri uri, String[] projection, String selection, String[] selectionArgs,
-                        String sortOrder) {
-        switch (sMatcher.match(uri)) {
-            case URI_DATA:
-                return new DragSourceCursor(uri.getLastPathSegment());
-        }
-        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/services/activityandwindowmanager/windowmanager/dndsourceapp/src/android/wm/cts/dndsourceapp/DragSourceCursor.java b/hostsidetests/services/activityandwindowmanager/windowmanager/dndsourceapp/src/android/wm/cts/dndsourceapp/DragSourceCursor.java
deleted file mode 100644
index c54df65..0000000
--- a/hostsidetests/services/activityandwindowmanager/windowmanager/dndsourceapp/src/android/wm/cts/dndsourceapp/DragSourceCursor.java
+++ /dev/null
@@ -1,80 +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.wm.cts.dndsourceapp;
-
-import android.database.AbstractCursor;
-
-public class DragSourceCursor extends AbstractCursor {
-    private static final String COLUMN_KEY = "key";
-
-    private final String mValue;
-
-    public DragSourceCursor(String value) {
-        mValue = value;
-    }
-
-    @Override
-    public int getCount() {
-        return 1;
-    }
-
-    @Override
-    public String[] getColumnNames() {
-        return new String[] {COLUMN_KEY};
-    }
-
-    @Override
-    public String getString(int column) {
-        if (getPosition() != 0) {
-            throw new IllegalArgumentException("Incorrect position: " + getPosition());
-        }
-        if (column != 0) {
-            throw new IllegalArgumentException("Incorrect column: " + column);
-        }
-        return mValue;
-    }
-
-    @Override
-    public short getShort(int column) {
-        return 0;
-    }
-
-    @Override
-    public int getInt(int column) {
-        return 0;
-    }
-
-    @Override
-    public long getLong(int column) {
-        return 0;
-    }
-
-    @Override
-    public float getFloat(int column) {
-        return 0;
-    }
-
-    @Override
-    public double getDouble(int column) {
-        return 0;
-    }
-
-    @Override
-    public boolean isNull(int column) {
-        return false;
-    }
-}
diff --git a/hostsidetests/services/activityandwindowmanager/windowmanager/dndtargetapp/Android.mk b/hostsidetests/services/activityandwindowmanager/windowmanager/dndtargetapp/Android.mk
deleted file mode 100644
index cb43a8b..0000000
--- a/hostsidetests/services/activityandwindowmanager/windowmanager/dndtargetapp/Android.mk
+++ /dev/null
@@ -1,31 +0,0 @@
-# Copyright (C) 2016 The Android Open Source Project
-#
-# Licensed under the Apache License, Version 2.0 (the "License");
-# you may not use this file except in compliance with the License.
-# You may obtain a copy of the License at
-#
-#      http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing, software
-# distributed under the License is distributed on an "AS IS" BASIS,
-# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-# See the License for the specific language governing permissions and
-# limitations under the License.
-
-LOCAL_PATH:= $(call my-dir)
-
-include $(CLEAR_VARS)
-
-# Don't include this package in any target.
-LOCAL_MODULE_TAGS := tests
-
-LOCAL_SRC_FILES := $(call all-java-files-under, src)
-
-LOCAL_SDK_VERSION := current
-
-# Tag this module as a cts test artifact
-LOCAL_COMPATIBILITY_SUITE := cts vts general-tests
-
-LOCAL_PACKAGE_NAME := CtsDragAndDropTargetApp
-
-include $(BUILD_CTS_SUPPORT_PACKAGE)
diff --git a/hostsidetests/services/activityandwindowmanager/windowmanager/dndtargetapp/AndroidManifest.xml b/hostsidetests/services/activityandwindowmanager/windowmanager/dndtargetapp/AndroidManifest.xml
deleted file mode 100644
index ed7b9c2..0000000
--- a/hostsidetests/services/activityandwindowmanager/windowmanager/dndtargetapp/AndroidManifest.xml
+++ /dev/null
@@ -1,27 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!-- 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.
--->
-
-<manifest xmlns:android="http://schemas.android.com/apk/res/android"
-        package="android.wm.cts.dndtargetapp">
-    <application android:label="CtsDnDTarget">
-        <activity android:name="android.wm.cts.dndtargetapp.DropTarget">
-            <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/services/activityandwindowmanager/windowmanager/dndtargetapp/src/android/wm/cts/dndtargetapp/DropTarget.java b/hostsidetests/services/activityandwindowmanager/windowmanager/dndtargetapp/src/android/wm/cts/dndtargetapp/DropTarget.java
deleted file mode 100644
index 8892c6f..0000000
--- a/hostsidetests/services/activityandwindowmanager/windowmanager/dndtargetapp/src/android/wm/cts/dndtargetapp/DropTarget.java
+++ /dev/null
@@ -1,308 +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.wm.cts.dndtargetapp;
-
-import android.app.Activity;
-import android.content.ClipData;
-import android.content.ClipDescription;
-import android.content.ContentValues;
-import android.database.Cursor;
-import android.net.Uri;
-import android.os.Bundle;
-import android.os.PersistableBundle;
-import android.util.Log;
-import android.view.DragAndDropPermissions;
-import android.view.DragEvent;
-import android.view.View;
-import android.widget.TextView;
-
-public class DropTarget extends Activity {
-    public static final String LOG_TAG = "DropTarget";
-
-    private static final String RESULT_KEY_DRAG_STARTED = "DRAG_STARTED";
-    private static final String RESULT_KEY_DRAG_ENDED = "DRAG_ENDED";
-    private static final String RESULT_KEY_EXTRAS = "EXTRAS";
-    private static final String RESULT_KEY_DROP_RESULT = "DROP";
-    private static final String RESULT_KEY_DETAILS = "DETAILS";
-    private static final String RESULT_KEY_ACCESS_AFTER = "AFTER";
-    private static final String RESULT_KEY_ACCESS_BEFORE = "BEFORE";
-    private static final String RESULT_KEY_CLIP_DATA_ERROR = "CLIP_DATA_ERROR";
-    private static final String RESULT_KEY_CLIP_DESCR_ERROR = "CLIP_DESCR_ERROR";
-    private static final String RESULT_KEY_LOCAL_STATE_ERROR = "LOCAL_STATE_ERROR";
-
-    public static final String RESULT_OK = "OK";
-    public static final String RESULT_EXCEPTION = "Exception";
-    public static final String RESULT_MISSING = "MISSING";
-    public static final String RESULT_LEAKING = "LEAKING";
-
-    protected static final String MAGIC_VALUE = "42";
-
-    private TextView mTextView;
-
-    @Override
-    public void onCreate(Bundle savedInstanceState) {
-        super.onCreate(savedInstanceState);
-
-        View view = getLayoutInflater().inflate(R.layout.target_activity, null);
-        setContentView(view);
-
-        setUpDropTarget("request_none", new OnDragUriReadListener(false));
-        setUpDropTarget("request_read", new OnDragUriReadListener());
-        setUpDropTarget("request_write", new OnDragUriWriteListener());
-        setUpDropTarget("request_read_nested", new OnDragUriReadPrefixListener());
-        setUpDropTarget("request_take_persistable", new OnDragUriTakePersistableListener());
-    }
-
-    private void setUpDropTarget(String mode, OnDragUriListener listener) {
-        if (!mode.equals(getIntent().getStringExtra("mode"))) {
-            return;
-        }
-        mTextView = (TextView)findViewById(R.id.drag_target);
-        mTextView.setText(mode);
-        mTextView.setOnDragListener(listener);
-    }
-
-    private String checkExtraValue(DragEvent event) {
-        PersistableBundle extras = event.getClipDescription().getExtras();
-        if (extras == null) {
-            return "Null";
-        }
-
-        final String value = extras.getString("extraKey");
-        if ("extraValue".equals(value)) {
-            return RESULT_OK;
-        }
-        return value;
-    }
-
-    private void logResult(String key, String value) {
-        Log.i(LOG_TAG, key + "=" + value);
-        mTextView.setText(mTextView.getText() + "\n" + key + "=" + value);
-    }
-
-    private abstract class OnDragUriListener implements View.OnDragListener {
-        private final boolean requestPermissions;
-
-        public OnDragUriListener(boolean requestPermissions) {
-            this.requestPermissions = requestPermissions;
-        }
-
-        @Override
-        public boolean onDrag(View v, DragEvent event) {
-            checkDragEvent(event);
-
-            switch (event.getAction()) {
-                case DragEvent.ACTION_DRAG_STARTED:
-                    logResult(RESULT_KEY_DRAG_STARTED, RESULT_OK);
-                    logResult(RESULT_KEY_EXTRAS, checkExtraValue(event));
-                    return true;
-
-                case DragEvent.ACTION_DRAG_ENTERED:
-                    return true;
-
-                case DragEvent.ACTION_DRAG_LOCATION:
-                    return true;
-
-                case DragEvent.ACTION_DRAG_EXITED:
-                    return true;
-
-                case DragEvent.ACTION_DROP:
-                    // Try accessing the Uri without the permissions grant.
-                    accessContent(event, RESULT_KEY_ACCESS_BEFORE, false);
-
-                    // Try accessing the Uri with the permission grant (if required);
-                    accessContent(event, RESULT_KEY_DROP_RESULT, requestPermissions);
-
-                    // Try accessing the Uri after the permissions have been released.
-                    accessContent(event, RESULT_KEY_ACCESS_AFTER, false);
-                    return true;
-
-                case DragEvent.ACTION_DRAG_ENDED:
-                    logResult(RESULT_KEY_DRAG_ENDED, RESULT_OK);
-                    return true;
-
-                default:
-                    return false;
-            }
-        }
-
-        private void accessContent(DragEvent event, String resultKey, boolean requestPermissions) {
-            String result;
-            try {
-                result = processDrop(event, requestPermissions);
-            } catch (SecurityException e) {
-                result = RESULT_EXCEPTION;
-                if (resultKey.equals(RESULT_KEY_DROP_RESULT)) {
-                    logResult(RESULT_KEY_DETAILS, e.getMessage());
-                }
-            }
-            logResult(resultKey, result);
-        }
-
-        private String processDrop(DragEvent event, boolean requestPermissions) {
-            final ClipData clipData = event.getClipData();
-            if (clipData == null) {
-                return "Null ClipData";
-            }
-            if (clipData.getItemCount() == 0) {
-                return "Empty ClipData";
-            }
-            ClipData.Item item = clipData.getItemAt(0);
-            if (item == null) {
-                return "Null ClipData.Item";
-            }
-            Uri uri = item.getUri();
-            if (uri == null) {
-                return "Null Uri";
-            }
-
-            DragAndDropPermissions permissions = null;
-            if (requestPermissions) {
-                permissions = requestDragAndDropPermissions(event);
-                if (permissions == null) {
-                    return "Null DragAndDropPermissions";
-                }
-            }
-
-            try {
-                return processUri(uri);
-            } finally {
-                if (permissions != null) {
-                    permissions.release();
-                }
-            }
-        }
-
-        abstract protected String processUri(Uri uri);
-    }
-
-    private void checkDragEvent(DragEvent event) {
-        final int action = event.getAction();
-
-        // ClipData should be available for ACTION_DROP only.
-        final ClipData clipData = event.getClipData();
-        if (action == DragEvent.ACTION_DROP) {
-            if (clipData == null) {
-                logResult(RESULT_KEY_CLIP_DATA_ERROR, RESULT_MISSING);
-            }
-        } else {
-            if (clipData != null) {
-                logResult(RESULT_KEY_CLIP_DATA_ERROR, RESULT_LEAKING + action);
-            }
-        }
-
-        // ClipDescription should be always available except for ACTION_DRAG_ENDED.
-        final ClipDescription clipDescription = event.getClipDescription();
-        if (action != DragEvent.ACTION_DRAG_ENDED) {
-            if (clipDescription == null) {
-                logResult(RESULT_KEY_CLIP_DESCR_ERROR, RESULT_MISSING + action);
-            }
-        } else {
-            if (clipDescription != null) {
-                logResult(RESULT_KEY_CLIP_DESCR_ERROR, RESULT_LEAKING);
-            }
-        }
-
-        // Local state should be always null for cross-app drags.
-        final Object localState = event.getLocalState();
-        if (localState != null) {
-            logResult(RESULT_KEY_LOCAL_STATE_ERROR, RESULT_LEAKING + action);
-        }
-    }
-
-    private class OnDragUriReadListener extends OnDragUriListener {
-        OnDragUriReadListener(boolean requestPermissions) {
-            super(requestPermissions);
-        }
-
-        OnDragUriReadListener() {
-            super(true);
-        }
-
-        protected String processUri(Uri uri) {
-            return checkQueryResult(uri, MAGIC_VALUE);
-        }
-
-        protected String checkQueryResult(Uri uri, String expectedValue) {
-            Cursor cursor = null;
-            try {
-                cursor = getContentResolver().query(uri, null, null, null, null);
-                if (cursor == null) {
-                    return "Null Cursor";
-                }
-                cursor.moveToPosition(0);
-                String value = cursor.getString(0);
-                if (!expectedValue.equals(value)) {
-                    return "Wrong value: " + value;
-                }
-                return RESULT_OK;
-            } finally {
-                if (cursor != null) {
-                    cursor.close();
-                }
-            }
-        }
-    }
-
-    private class OnDragUriWriteListener extends OnDragUriListener {
-        OnDragUriWriteListener() {
-            super(true);
-        }
-
-        protected String processUri(Uri uri) {
-            ContentValues values = new ContentValues();
-            values.put("key", 100);
-            getContentResolver().update(uri, values, null, null);
-            return RESULT_OK;
-        }
-    }
-
-    private class OnDragUriReadPrefixListener extends OnDragUriReadListener {
-        @Override
-        protected String processUri(Uri uri) {
-            final String result1 = queryPrefixed(uri, "1");
-            if (!result1.equals(RESULT_OK)) {
-                return result1;
-            }
-            final String result2 = queryPrefixed(uri, "2");
-            if (!result2.equals(RESULT_OK)) {
-                return result2;
-            }
-            return queryPrefixed(uri, "3");
-        }
-
-        private String queryPrefixed(Uri uri, String selector) {
-            final Uri prefixedUri = Uri.parse(uri.toString() + "/" + selector);
-            return checkQueryResult(prefixedUri, selector);
-        }
-    }
-
-    private class OnDragUriTakePersistableListener extends OnDragUriListener {
-        OnDragUriTakePersistableListener() {
-            super(true);
-        }
-
-        @Override
-        protected String processUri(Uri uri) {
-            getContentResolver().takePersistableUriPermission(
-                    uri, View.DRAG_FLAG_GLOBAL_URI_READ);
-            getContentResolver().releasePersistableUriPermission(
-                    uri, View.DRAG_FLAG_GLOBAL_URI_READ);
-            return RESULT_OK;
-        }
-    }
-}
diff --git a/hostsidetests/services/activityandwindowmanager/windowmanager/dndtargetappsdk23/Android.mk b/hostsidetests/services/activityandwindowmanager/windowmanager/dndtargetappsdk23/Android.mk
deleted file mode 100644
index a33e1bc..0000000
--- a/hostsidetests/services/activityandwindowmanager/windowmanager/dndtargetappsdk23/Android.mk
+++ /dev/null
@@ -1,31 +0,0 @@
-# Copyright (C) 2016 The Android Open Source Project
-#
-# Licensed under the Apache License, Version 2.0 (the "License");
-# you may not use this file except in compliance with the License.
-# You may obtain a copy of the License at
-#
-#      http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing, software
-# distributed under the License is distributed on an "AS IS" BASIS,
-# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-# See the License for the specific language governing permissions and
-# limitations under the License.
-
-LOCAL_PATH:= $(call my-dir)
-
-include $(CLEAR_VARS)
-
-# Don't include this package in any target.
-LOCAL_MODULE_TAGS := tests
-
-LOCAL_SRC_FILES := $(call all-java-files-under, src)
-
-LOCAL_SDK_VERSION := 23
-
-# Tag this module as a cts test artifact
-LOCAL_COMPATIBILITY_SUITE := cts vts general-tests
-
-LOCAL_PACKAGE_NAME := CtsDragAndDropTargetAppSdk23
-
-include $(BUILD_CTS_SUPPORT_PACKAGE)
diff --git a/hostsidetests/services/activityandwindowmanager/windowmanager/dndtargetappsdk23/AndroidManifest.xml b/hostsidetests/services/activityandwindowmanager/windowmanager/dndtargetappsdk23/AndroidManifest.xml
deleted file mode 100644
index ea636a8..0000000
--- a/hostsidetests/services/activityandwindowmanager/windowmanager/dndtargetappsdk23/AndroidManifest.xml
+++ /dev/null
@@ -1,27 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!-- 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.
--->
-
-<manifest xmlns:android="http://schemas.android.com/apk/res/android"
-        package="android.wm.cts.dndtargetappsdk23">
-    <application android:label="CtsDnDTarget">
-        <activity android:name="android.wm.cts.dndtargetappsdk23.DropTarget">
-            <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/services/activityandwindowmanager/windowmanager/dndtargetappsdk23/src/android/wm/cts/dndtargetappsdk23/DropTarget.java b/hostsidetests/services/activityandwindowmanager/windowmanager/dndtargetappsdk23/src/android/wm/cts/dndtargetappsdk23/DropTarget.java
deleted file mode 100644
index 2cb7779..0000000
--- a/hostsidetests/services/activityandwindowmanager/windowmanager/dndtargetappsdk23/src/android/wm/cts/dndtargetappsdk23/DropTarget.java
+++ /dev/null
@@ -1,86 +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.wm.cts.dndtargetappsdk23;
-
-import android.app.Activity;
-import android.os.Bundle;
-import android.util.Log;
-import android.view.DragEvent;
-import android.view.View;
-import android.widget.TextView;
-
-/**
- * This application is compiled against SDK 23 and used to verify that apps targeting SDK 23 and
- * below do not receive global drags.
- */
-public class DropTarget extends Activity {
-    public static final String LOG_TAG = "DropTarget";
-
-    private static final String RESULT_KEY_DRAG_STARTED = "DRAG_STARTED";
-    private static final String RESULT_KEY_DROP_RESULT = "DROP";
-
-    public static final String RESULT_OK = "OK";
-
-    private TextView mTextView;
-
-    @Override
-    public void onCreate(Bundle savedInstanceState) {
-        super.onCreate(savedInstanceState);
-
-        View view = getLayoutInflater().inflate(R.layout.target_activity, null);
-        setContentView(view);
-
-        mTextView = (TextView) findViewById(R.id.drag_target);
-        mTextView.setOnDragListener(new OnDragListener());
-    }
-
-    private void logResult(String key, String value) {
-        String result = key + "=" + value;
-        Log.i(LOG_TAG, result);
-        mTextView.setText(result);
-    }
-
-    private class OnDragListener implements View.OnDragListener {
-        @Override
-        public boolean onDrag(View v, DragEvent event) {
-            switch (event.getAction()) {
-                case DragEvent.ACTION_DRAG_STARTED:
-                    logResult(RESULT_KEY_DRAG_STARTED, RESULT_OK);
-                    return true;
-
-                case DragEvent.ACTION_DRAG_ENTERED:
-                    return true;
-
-                case DragEvent.ACTION_DRAG_LOCATION:
-                    return true;
-
-                case DragEvent.ACTION_DRAG_EXITED:
-                    return true;
-
-                case DragEvent.ACTION_DROP:
-                    logResult(RESULT_KEY_DROP_RESULT, RESULT_OK);
-                    return true;
-
-                case DragEvent.ACTION_DRAG_ENDED:
-                    return true;
-
-                default:
-                    return false;
-            }
-        }
-    }
-}
diff --git a/hostsidetests/services/activityandwindowmanager/windowmanager/frametestapp/AndroidManifest.xml b/hostsidetests/services/activityandwindowmanager/windowmanager/frametestapp/AndroidManifest.xml
deleted file mode 100755
index 7f899c2..0000000
--- a/hostsidetests/services/activityandwindowmanager/windowmanager/frametestapp/AndroidManifest.xml
+++ /dev/null
@@ -1,30 +0,0 @@
-<?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.
- -->
-
-<manifest xmlns:android="http://schemas.android.com/apk/res/android"
-          xmlns:androidprv="http://schemas.android.com/apk/prv/res/android"
-          package="android.server.FrameTestApp">
-    <application android:theme="@android:style/Theme.Material">
-        <activity android:name=".DialogTestActivity"
-                android:exported="true"
-        />
-        <activity android:name=".MovingChildTestActivity"
-                  android:exported="true"
-        />
-    </application>
-</manifest>
-
diff --git a/hostsidetests/services/activityandwindowmanager/windowmanager/frametestapp/src/android/server/frametestapp/DialogTestActivity.java b/hostsidetests/services/activityandwindowmanager/windowmanager/frametestapp/src/android/server/frametestapp/DialogTestActivity.java
deleted file mode 100644
index 593cf34..0000000
--- a/hostsidetests/services/activityandwindowmanager/windowmanager/frametestapp/src/android/server/frametestapp/DialogTestActivity.java
+++ /dev/null
@@ -1,206 +0,0 @@
-/*
- * Copyright (C) 2015 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package android.server.FrameTestApp;
-
-import android.app.Activity;
-import android.app.AlertDialog;
-import android.content.Intent;
-import android.os.Bundle;
-import android.view.WindowManager;
-import android.view.Window;
-import android.view.Gravity;
-
-public class DialogTestActivity extends Activity {
-
-    AlertDialog mDialog;
-
-    protected void onCreate(Bundle icicle) {
-        super.onCreate(icicle);
-    }
-
-    protected void onStop() {
-        super.onStop();
-        mDialog.dismiss();
-    }
-    protected void onResume() {
-        super.onResume();
-        setupTest(getIntent());
-    }
-
-    private void setupTest(Intent intent) {
-        String testCase = intent.getStringExtra(
-                "android.server.FrameTestApp.DialogTestCase");
-        switch (testCase) {
-           case "MatchParent": {
-               testMatchParent();
-               break;
-           } case "MatchParentLayoutInOverscan": {
-               testMatchParentLayoutInOverscan();
-           }  break;
-           case "ExplicitSize": {
-               testExplicitSize();
-               break;
-           }
-           case "ExplicitSizeTopLeftGravity": {
-               testExplicitSizeTopLeftGravity();
-               break;
-           }
-           case "ExplicitSizeBottomRightGravity": {
-               testExplicitSizeBottomRightGravity();
-               break;
-           }
-           case "OversizedDimensions": {
-               testOversizedDimensions();
-               break;
-           }
-           case "OversizedDimensionsNoLimits": {
-               testOversizedDimensionsNoLimits();
-               break;
-           }
-           case "ExplicitPositionMatchParent": {
-               testExplicitPositionMatchParent();
-               break;
-           }
-           case "ExplicitPositionMatchParentNoLimits": {
-               testExplicitPositionMatchParentNoLimits();
-               break;
-           }
-           case "NoFocus": {
-               testNoFocus();
-               break;
-           }
-           case "WithMargins": {
-               testWithMargins();
-               break;
-           }
-           default:
-               break;
-        }
-    }
-
-    interface DialogLayoutParamsTest {
-        void doSetup(WindowManager.LayoutParams p);
-    }
-
-    private void doLayoutParamTest(DialogLayoutParamsTest t) {
-        mDialog = new AlertDialog.Builder(this).create();
-
-        mDialog.setMessage("Testing is fun!");
-        mDialog.setTitle("android.server.FrameTestApp/android.server.FrameTestApp.TestDialog");
-        mDialog.create();
-
-        Window w = mDialog.getWindow();
-        final WindowManager.LayoutParams params = w.getAttributes();
-        t.doSetup(params);
-        w.setAttributes(params);
-
-        mDialog.show();
-    }
-
-    private void testMatchParent() {
-        doLayoutParamTest((WindowManager.LayoutParams params) -> {
-            params.width = WindowManager.LayoutParams.MATCH_PARENT;
-            params.height = WindowManager.LayoutParams.MATCH_PARENT;
-        });
-    }
-
-    private void testMatchParentLayoutInOverscan() {
-        doLayoutParamTest((WindowManager.LayoutParams params) -> {
-            params.width = WindowManager.LayoutParams.MATCH_PARENT;
-            params.height = WindowManager.LayoutParams.MATCH_PARENT;
-            params.flags |= WindowManager.LayoutParams.FLAG_LAYOUT_IN_SCREEN;
-            params.flags |= WindowManager.LayoutParams.FLAG_LAYOUT_IN_OVERSCAN;
-        });
-    }
-
-    private void testExplicitSize() {
-        doLayoutParamTest((WindowManager.LayoutParams params) -> {
-            params.width = 200;
-            params.height = 200;
-        });
-    }
-
-    private void testExplicitSizeTopLeftGravity() {
-        doLayoutParamTest((WindowManager.LayoutParams params) -> {
-            params.width = 200;
-            params.height = 200;
-            params.gravity = Gravity.TOP | Gravity.LEFT;
-        });
-    }
-
-    private void testExplicitSizeBottomRightGravity() {
-        doLayoutParamTest((WindowManager.LayoutParams params) -> {
-            params.width = 200;
-            params.height = 200;
-            params.gravity = Gravity.BOTTOM | Gravity.RIGHT;
-        });
-    }
-
-    private void testOversizedDimensions() {
-        doLayoutParamTest((WindowManager.LayoutParams params) -> {
-            params.width = 100000;
-            params.height = 100000;
-        });
-    }
-
-    private void testOversizedDimensionsNoLimits() {
-        doLayoutParamTest((WindowManager.LayoutParams params) -> {
-            params.width = 5000;
-            params.height = 5000;
-            params.flags |= WindowManager.LayoutParams.FLAG_LAYOUT_NO_LIMITS;
-            params.gravity = Gravity.LEFT | Gravity.TOP;
-        });
-    }
-
-    private void testExplicitPositionMatchParent() {
-        doLayoutParamTest((WindowManager.LayoutParams params) -> {
-            params.width = WindowManager.LayoutParams.MATCH_PARENT;
-            params.height = WindowManager.LayoutParams.MATCH_PARENT;
-            params.x = 100;
-            params.y = 100;
-        });
-    }
-
-    private void testExplicitPositionMatchParentNoLimits() {
-        doLayoutParamTest((WindowManager.LayoutParams params) -> {
-            params.width = WindowManager.LayoutParams.MATCH_PARENT;
-            params.height = WindowManager.LayoutParams.MATCH_PARENT;
-            params.gravity = Gravity.LEFT | Gravity.TOP;
-            params.flags |= WindowManager.LayoutParams.FLAG_LAYOUT_NO_LIMITS;
-            params.x = 100;
-            params.y = 100;
-        });
-    }
-
-    private void testNoFocus() {
-        doLayoutParamTest((WindowManager.LayoutParams params) -> {
-            params.flags |= WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE;
-        });
-    }
-
-    private void testWithMargins() {
-        doLayoutParamTest((WindowManager.LayoutParams params) -> {
-            params.gravity = Gravity.LEFT | Gravity.TOP;
-            params.horizontalMargin = .25f;
-            params.verticalMargin = .35f;
-            params.width = 200;
-            params.height = 200;
-            params.x = 0;
-            params.y = 0;
-        });
-    }
-}
diff --git a/hostsidetests/services/activityandwindowmanager/windowmanager/frametestapp/src/android/server/frametestapp/MovingChildTestActivity.java b/hostsidetests/services/activityandwindowmanager/windowmanager/frametestapp/src/android/server/frametestapp/MovingChildTestActivity.java
deleted file mode 100644
index de6f597..0000000
--- a/hostsidetests/services/activityandwindowmanager/windowmanager/frametestapp/src/android/server/frametestapp/MovingChildTestActivity.java
+++ /dev/null
@@ -1,85 +0,0 @@
-/*
- * Copyright (C) 2015 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package android.server.FrameTestApp;
-
-import android.app.Activity;
-import android.content.Intent;
-import android.content.Context;
-import android.os.Bundle;
-import android.view.WindowManager;
-import android.view.Window;
-import android.view.Gravity;
-import android.view.View;
-import android.widget.Space;
-import android.widget.Button;
-import android.view.ViewGroup.LayoutParams;
-import android.view.WindowManager;
-import android.widget.FrameLayout;
-
-// This activity will parent a Child to the main window, and then move
-// the main window around. We can use this to verify the Child
-// is properly updated.
-public class MovingChildTestActivity extends Activity {
-    Space mView;
-    int mX = 0;
-    int mY = 0;
-
-    final Runnable moveWindow = new Runnable() {
-            @Override
-            public void run() {
-                final Window w = getWindow();
-                final WindowManager.LayoutParams attribs = w.getAttributes();
-                attribs.privateFlags = WindowManager.LayoutParams.PRIVATE_FLAG_NO_MOVE_ANIMATION;
-                attribs.x = mX % 1000;
-                attribs.y = mY % 1000;
-                w.setAttributes(attribs);
-                mX += 5;
-                mY += 5;
-                mView.postDelayed(this, 50);
-            }
-    };
-
-    final Runnable makeChild = new Runnable() {
-            @Override
-            public void run() {
-                Button b = new Button(MovingChildTestActivity.this);
-                WindowManager.LayoutParams p = new WindowManager.LayoutParams(
-                        WindowManager.LayoutParams.TYPE_APPLICATION_PANEL);
-                p.privateFlags = WindowManager.LayoutParams.PRIVATE_FLAG_NO_MOVE_ANIMATION;
-                p.x = 0;
-                p.y = 0;
-                p.token = mView.getWindowToken();
-                p.setTitle("ChildWindow");
-
-                ((WindowManager)getSystemService(Context.WINDOW_SERVICE)).addView(b, p);
-
-                mView.postDelayed(moveWindow, 50);
-            }
-    };
-
-    protected void onCreate(Bundle icicle) {
-        super.onCreate(icicle);
-
-        final LayoutParams p = new LayoutParams(100, 100);
-        final Window w = getWindow();
-        w.setLayout(100, 100);
-        mView = new Space(this);
-
-        setContentView(mView, p);
-        mView.post(makeChild);
-    }
-}
diff --git a/hostsidetests/services/activityandwindowmanager/windowmanager/src/android/server/cts/AlertWindowsTests.java b/hostsidetests/services/activityandwindowmanager/windowmanager/src/android/server/cts/AlertWindowsTests.java
deleted file mode 100644
index bfed6d5..0000000
--- a/hostsidetests/services/activityandwindowmanager/windowmanager/src/android/server/cts/AlertWindowsTests.java
+++ /dev/null
@@ -1,168 +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.server.cts;
-
-import android.platform.test.annotations.Presubmit;
-import com.android.tradefed.device.DeviceNotAvailableException;
-
-import java.util.ArrayList;
-import java.util.Arrays;
-import java.util.List;
-
-/**
- * Build: mmma -j32 cts/hostsidetests/services
- * Run: cts/hostsidetests/services/activityandwindowmanager/util/run-test CtsWindowManagerHostTestCases android.server.cts.AlertWindowsTests
- */
-@Presubmit
-public class AlertWindowsTests extends ActivityManagerTestBase {
-
-    private static final String PACKAGE_NAME = "android.server.alertwindowapp";
-    private static final String ACTIVITY_NAME = "AlertWindowTestActivity";
-    private static final String SDK_25_PACKAGE_NAME = "android.server.alertwindowappsdk25";
-    private static final String SDK_25_ACTIVITY_NAME = "AlertWindowTestActivitySdk25";
-
-    // From WindowManager.java
-    private static final int TYPE_BASE_APPLICATION      = 1;
-    private static final int FIRST_SYSTEM_WINDOW        = 2000;
-
-    private static final int TYPE_PHONE                 = FIRST_SYSTEM_WINDOW + 2;
-    private static final int TYPE_SYSTEM_ALERT          = FIRST_SYSTEM_WINDOW + 3;
-    private static final int TYPE_SYSTEM_OVERLAY        = FIRST_SYSTEM_WINDOW + 6;
-    private static final int TYPE_PRIORITY_PHONE        = FIRST_SYSTEM_WINDOW + 7;
-    private static final int TYPE_SYSTEM_ERROR          = FIRST_SYSTEM_WINDOW + 10;
-    private static final int TYPE_APPLICATION_OVERLAY   = FIRST_SYSTEM_WINDOW + 38;
-
-    private static final int TYPE_STATUS_BAR            = FIRST_SYSTEM_WINDOW;
-    private static final int TYPE_INPUT_METHOD          = FIRST_SYSTEM_WINDOW + 11;
-    private static final int TYPE_NAVIGATION_BAR        = FIRST_SYSTEM_WINDOW + 19;
-
-    private final List<Integer> mAlertWindowTypes = Arrays.asList(
-            TYPE_PHONE,
-            TYPE_PRIORITY_PHONE,
-            TYPE_SYSTEM_ALERT,
-            TYPE_SYSTEM_ERROR,
-            TYPE_SYSTEM_OVERLAY,
-            TYPE_APPLICATION_OVERLAY);
-    private final List<Integer> mSystemWindowTypes = Arrays.asList(
-            TYPE_STATUS_BAR,
-            TYPE_INPUT_METHOD,
-            TYPE_NAVIGATION_BAR);
-
-    @Override
-    protected void tearDown() throws Exception {
-        super.tearDown();
-        try {
-            setAlertWindowPermission(PACKAGE_NAME, false);
-            setAlertWindowPermission(SDK_25_PACKAGE_NAME, false);
-            executeShellCommand("am force-stop " + PACKAGE_NAME);
-            executeShellCommand("am force-stop " + SDK_25_PACKAGE_NAME);
-        } catch (DeviceNotAvailableException e) {
-        }
-    }
-
-    public void testAlertWindowAllowed() throws Exception {
-        runAlertWindowTest(PACKAGE_NAME, ACTIVITY_NAME, true /* hasAlertWindowPermission */,
-                true /* atLeastO */);
-    }
-
-    public void testAlertWindowDisallowed() throws Exception {
-        runAlertWindowTest(PACKAGE_NAME, ACTIVITY_NAME, false /* hasAlertWindowPermission */,
-                true /* atLeastO */);
-    }
-
-    public void testAlertWindowAllowedSdk25() throws Exception {
-        runAlertWindowTest(SDK_25_PACKAGE_NAME, SDK_25_ACTIVITY_NAME,
-                true /* hasAlertWindowPermission */, false /* atLeastO */);
-    }
-
-    public void testAlertWindowDisallowedSdk25() throws Exception {
-        runAlertWindowTest(SDK_25_PACKAGE_NAME, SDK_25_ACTIVITY_NAME,
-                false /* hasAlertWindowPermission */, false /* atLeastO */);
-    }
-
-    private void runAlertWindowTest(String packageName, String activityName,
-            boolean hasAlertWindowPermission, boolean atLeastO) throws Exception {
-        setComponentName(packageName);
-        setAlertWindowPermission(packageName, hasAlertWindowPermission);
-
-        executeShellCommand(getAmStartCmd(activityName));
-        mAmWmState.computeState(mDevice, new String[] { activityName });
-        mAmWmState.assertVisibility(activityName, true);
-
-        assertAlertWindows(packageName, hasAlertWindowPermission, atLeastO);
-    }
-
-    private void assertAlertWindows(String packageName, boolean hasAlertWindowPermission,
-            boolean atLeastO) {
-        final WindowManagerState wMState = mAmWmState.getWmState();
-
-        final ArrayList<WindowManagerState.WindowState> alertWindows = new ArrayList();
-        wMState.getWindowsByPackageName(packageName, mAlertWindowTypes, alertWindows);
-
-        if (!hasAlertWindowPermission) {
-            assertTrue("Should be empty alertWindows=" + alertWindows, alertWindows.isEmpty());
-            return;
-        }
-
-        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);
-            }
-        }
-
-        final WindowManagerState.WindowState mainAppWindow =
-                wMState.getWindowByPackageName(packageName, TYPE_BASE_APPLICATION);
-
-        assertNotNull(mainAppWindow);
-
-        wMState.sortWindowsByLayer(alertWindows);
-        final WindowManagerState.WindowState lowestAlertWindow = alertWindows.get(0);
-        final WindowManagerState.WindowState highestAlertWindow =
-                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, lowestAlertWindow.getLayer() > mainAppWindow.getLayer());
-
-        // 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,
-                    highestAlertWindow.getLayer() < appOverlayWindow.getLayer());
-        }
-
-        // Assert that alert windows are below key system windows.
-        final ArrayList<WindowManagerState.WindowState> systemWindows = new ArrayList();
-        wMState.getWindowsByPackageName(packageName, mSystemWindowTypes, systemWindows);
-        if (!systemWindows.isEmpty()) {
-            wMState.sortWindowsByLayer(systemWindows);
-            final WindowManagerState.WindowState lowestSystemWindow = alertWindows.get(0);
-            assertTrue("highestAlertWindow=" + highestAlertWindow
-                    + " greater than lowestSystemWindow=" + lowestSystemWindow,
-                    highestAlertWindow.getLayer() < lowestSystemWindow.getLayer());
-        }
-    }
-
-    private void setAlertWindowPermission(String packageName, boolean allow) throws Exception {
-        executeShellCommand("appops set " + packageName + " android:system_alert_window "
-                + (allow ? "allow" : "deny"));
-    }
-}
diff --git a/hostsidetests/services/activityandwindowmanager/windowmanager/src/android/server/cts/ChildMovementTests.java b/hostsidetests/services/activityandwindowmanager/windowmanager/src/android/server/cts/ChildMovementTests.java
deleted file mode 100644
index 218fcfe..0000000
--- a/hostsidetests/services/activityandwindowmanager/windowmanager/src/android/server/cts/ChildMovementTests.java
+++ /dev/null
@@ -1,154 +0,0 @@
-/*
- * Copyright (C) 2016 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package android.server.cts;
-
-import static android.server.cts.StateLogger.logE;
-
-import java.util.List;
-import java.util.ArrayList;
-import java.awt.Rectangle;
-
-import com.android.ddmlib.Log.LogLevel;
-import com.android.tradefed.device.CollectingOutputReceiver;
-import com.android.tradefed.device.DeviceNotAvailableException;
-import com.android.tradefed.device.ITestDevice;
-import com.android.tradefed.log.LogUtil.CLog;
-import com.android.tradefed.testtype.DeviceTestCase;
-
-import android.server.cts.ActivityManagerTestBase;
-import android.server.cts.WindowManagerState.WindowState;
-
-public class ChildMovementTests extends ParentChildTestBase {
-    private List<WindowState> mWindowList = new ArrayList();
-
-    @Override
-    String intentKey() {
-        return "android.server.FrameTestApp.ChildTestCase";
-    }
-
-    @Override
-    String activityName() {
-        return "MovingChildTestActivity";
-    }
-
-    WindowState getSingleWindow(String fullWindowName) {
-        try {
-            mAmWmState.getWmState().getMatchingVisibleWindowState(fullWindowName, mWindowList);
-            return mWindowList.get(0);
-        } catch (Exception e) {
-            CLog.logAndDisplay(LogLevel.INFO, "Couldn't find window: " + fullWindowName);
-            return null;
-        }
-    }
-
-    WindowState getSingleWindowByPrefix(String prefix) {
-        try {
-            mAmWmState.getWmState().getPrefixMatchingVisibleWindowState(prefix, mWindowList);
-            return mWindowList.get(0);
-        } catch (Exception e) {
-            CLog.logAndDisplay(LogLevel.INFO, "Couldn't find window: " + prefix);
-            return null;
-        }
-    }
-
-    void doSingleTest(ParentChildTest t) throws Exception {
-        String popupName = "ChildWindow";
-        final String[] waitForVisible = new String[] { popupName };
-
-        mAmWmState.setUseActivityNamesForWindowNames(false);
-        mAmWmState.computeState(mDevice, waitForVisible);
-        WindowState popup = getSingleWindowByPrefix(popupName);
-        WindowState parent = getSingleWindow(getBaseWindowName() + activityName());
-
-        t.doTest(parent, popup);
-    }
-
-
-    Object monitor = new Object();
-    boolean testPassed = false;
-    String popupName = null;
-    String mainName = null;
-
-    SurfaceTraceReceiver.SurfaceObserver observer = new SurfaceTraceReceiver.SurfaceObserver() {
-        int transactionCount = 0;
-        boolean sawChildMove = false;
-        boolean sawMainMove = false;
-        int timesSeen = 0;
-
-        @Override
-        public void openTransaction() {
-            transactionCount++;
-            if (transactionCount == 1) {
-                sawChildMove = false;
-                sawMainMove = false;
-            }
-        }
-
-        @Override
-        public void closeTransaction() {
-            transactionCount--;
-            if (transactionCount != 0) {
-                return;
-            }
-            synchronized (monitor) {
-                if (sawChildMove ^ sawMainMove ) {
-                    monitor.notifyAll();
-                    return;
-                }
-                if (timesSeen > 10) {
-                    testPassed = true;
-                    monitor.notifyAll();
-                }
-            }
-        }
-
-        @Override
-        public void setPosition(String windowName, float x, float y) {
-            if (windowName.equals(popupName)) {
-                sawChildMove = true;
-                timesSeen++;
-            } else if (windowName.equals(mainName)) {
-                sawMainMove = true;
-            }
-        }
-    };
-
-    /**
-     * Here we test that a Child moves in the same transaction
-     * as its parent. We launch an activity with a Child which will
-     * move around its own main window. Then we listen to WindowManager transactions.
-     * Since the Child is static within the window, if we ever see one of
-     * them move xor the other one we have a problem!
-     */
-    public void testSurfaceMovesWithParent() throws Exception {
-        doFullscreenTest("MovesWithParent",
-            (WindowState parent, WindowState popup) -> {
-                    popupName = popup.getName();
-                    mainName = parent.getName();
-                    installSurfaceObserver(observer);
-                    try {
-                        synchronized (monitor) {
-                            monitor.wait(5000);
-                        }
-                    } catch (InterruptedException e) {
-                    } finally {
-                        assertTrue(testPassed);
-                        removeSurfaceObserver();
-                    }
-            });
-    }
-}
diff --git a/hostsidetests/services/activityandwindowmanager/windowmanager/src/android/server/cts/CrossAppDragAndDropTests.java b/hostsidetests/services/activityandwindowmanager/windowmanager/src/android/server/cts/CrossAppDragAndDropTests.java
deleted file mode 100644
index 17ede35..0000000
--- a/hostsidetests/services/activityandwindowmanager/windowmanager/src/android/server/cts/CrossAppDragAndDropTests.java
+++ /dev/null
@@ -1,547 +0,0 @@
-/*
- * Copyright (C) 2016 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package android.server.cts;
-
-import com.android.tradefed.device.CollectingOutputReceiver;
-import com.android.tradefed.device.DeviceNotAvailableException;
-import com.android.tradefed.device.ITestDevice;
-import com.android.tradefed.log.LogUtil.CLog;
-import com.android.tradefed.testtype.DeviceTestCase;
-
-import java.util.HashMap;
-import java.util.Map;
-import java.util.regex.Pattern;
-
-public class CrossAppDragAndDropTests extends DeviceTestCase {
-    // Constants copied from ActivityManager.StackId. If they are changed there, these must be
-    // updated.
-    /** ID of stack where fullscreen activities are normally launched into. */
-    private static final int FULLSCREEN_WORKSPACE_STACK_ID = 1;
-
-    /** ID of stack where freeform/resized activities are normally launched into. */
-    private static final int FREEFORM_WORKSPACE_STACK_ID = FULLSCREEN_WORKSPACE_STACK_ID + 1;
-
-    /** ID of stack that occupies a dedicated region of the screen. */
-    private static final int DOCKED_STACK_ID = FREEFORM_WORKSPACE_STACK_ID + 1;
-
-    /** ID of stack that always on top (always visible) when it exists. */
-    private static final int PINNED_STACK_ID = DOCKED_STACK_ID + 1;
-
-    private static final String AM_FORCE_STOP = "am force-stop ";
-    private static final String AM_MOVE_TASK = "am stack move-task ";
-    private static final String AM_RESIZE_TASK = "am task resize ";
-    private static final String AM_REMOVE_STACK = "am stack remove ";
-    private static final String AM_START_N = "am start -n ";
-    private static final String AM_STACK_LIST = "am stack list";
-    private static final String INPUT_MOUSE_SWIPE = "input mouse swipe ";
-    private static final String TASK_ID_PREFIX = "taskId";
-
-    // Regex pattern to match adb shell am stack list output of the form:
-    // taskId=<TASK_ID>: <componentName> bounds=[LEFT,TOP][RIGHT,BOTTOM]
-    private static final String TASK_REGEX_PATTERN_STRING =
-            "taskId=[0-9]+: %s bounds=\\[[0-9]+,[0-9]+\\]\\[[0-9]+,[0-9]+\\]";
-
-    private static final int SWIPE_DURATION_MS = 500;
-
-    private static final String SOURCE_PACKAGE_NAME = "android.wm.cts.dndsourceapp";
-    private static final String TARGET_PACKAGE_NAME = "android.wm.cts.dndtargetapp";
-    private static final String TARGET_23_PACKAGE_NAME = "android.wm.cts.dndtargetappsdk23";
-
-
-    private static final String SOURCE_ACTIVITY_NAME = "DragSource";
-    private static final String TARGET_ACTIVITY_NAME = "DropTarget";
-
-    private static final String FILE_GLOBAL = "file_global";
-    private static final String FILE_LOCAL = "file_local";
-    private static final String DISALLOW_GLOBAL = "disallow_global";
-    private static final String CANCEL_SOON = "cancel_soon";
-    private static final String GRANT_NONE = "grant_none";
-    private static final String GRANT_READ = "grant_read";
-    private static final String GRANT_WRITE = "grant_write";
-    private static final String GRANT_READ_PREFIX = "grant_read_prefix";
-    private static final String GRANT_READ_NOPREFIX = "grant_read_noprefix";
-    private static final String GRANT_READ_PERSISTABLE = "grant_read_persistable";
-
-    private static final String REQUEST_NONE = "request_none";
-    private static final String REQUEST_READ = "request_read";
-    private static final String REQUEST_READ_NESTED = "request_read_nested";
-    private static final String REQUEST_TAKE_PERSISTABLE = "request_take_persistable";
-    private static final String REQUEST_WRITE = "request_write";
-
-    private static final String SOURCE_LOG_TAG = "DragSource";
-    private static final String TARGET_LOG_TAG = "DropTarget";
-
-    private static final String RESULT_KEY_START_DRAG = "START_DRAG";
-    private static final String RESULT_KEY_DRAG_STARTED = "DRAG_STARTED";
-    private static final String RESULT_KEY_DRAG_ENDED = "DRAG_ENDED";
-    private static final String RESULT_KEY_EXTRAS = "EXTRAS";
-    private static final String RESULT_KEY_DROP_RESULT = "DROP";
-    private static final String RESULT_KEY_ACCESS_BEFORE = "BEFORE";
-    private static final String RESULT_KEY_ACCESS_AFTER = "AFTER";
-    private static final String RESULT_KEY_CLIP_DATA_ERROR = "CLIP_DATA_ERROR";
-    private static final String RESULT_KEY_CLIP_DESCR_ERROR = "CLIP_DESCR_ERROR";
-    private static final String RESULT_KEY_LOCAL_STATE_ERROR = "LOCAL_STATE_ERROR";
-
-    private static final String RESULT_MISSING = "Missing";
-    private static final String RESULT_OK = "OK";
-    private static final String RESULT_EXCEPTION = "Exception";
-    private static final String RESULT_NULL_DROP_PERMISSIONS = "Null DragAndDropPermissions";
-
-    private static final String AM_SUPPORTS_SPLIT_SCREEN_MULTIWINDOW =
-            "am supports-split-screen-multi-window";
-
-    private ITestDevice mDevice;
-
-    private Map<String, String> mSourceResults;
-    private Map<String, String> mTargetResults;
-
-    private String mSourcePackageName;
-    private String mTargetPackageName;
-
-    @Override
-    protected void setUp() throws Exception {
-        super.setUp();
-
-        mDevice = getDevice();
-
-        if (!supportsDragAndDrop()) {
-            return;
-        }
-
-        mSourcePackageName = SOURCE_PACKAGE_NAME;
-        mTargetPackageName = TARGET_PACKAGE_NAME;
-        cleanupState();
-    }
-
-    @Override
-    protected void tearDown() throws Exception {
-        super.tearDown();
-
-        if (!supportsDragAndDrop()) {
-            return;
-        }
-
-        mDevice.executeShellCommand(AM_FORCE_STOP + mSourcePackageName);
-        mDevice.executeShellCommand(AM_FORCE_STOP + mTargetPackageName);
-    }
-
-    private String executeShellCommand(String command) throws DeviceNotAvailableException {
-        return mDevice.executeShellCommand(command);
-    }
-
-    private void clearLogs() throws DeviceNotAvailableException {
-        executeShellCommand("logcat -c");
-    }
-
-    private String getStartCommand(String componentName, String modeExtra) {
-        return AM_START_N + componentName + " -e mode " + modeExtra;
-    }
-
-    private String getMoveTaskCommand(int taskId, int stackId) throws Exception {
-        return AM_MOVE_TASK + taskId + " " + stackId + " true";
-    }
-
-    private String getResizeTaskCommand(int taskId, Point topLeft, Point bottomRight)
-            throws Exception {
-        return AM_RESIZE_TASK + taskId + " " + topLeft.x + " " + topLeft.y + " " + bottomRight.x
-                + " " + bottomRight.y;
-    }
-
-    private String getComponentName(String packageName, String activityName) {
-        return packageName + "/" + packageName + "." + activityName;
-    }
-
-    /**
-     * Make sure that the special activity stacks are removed and the ActivityManager/WindowManager
-     * is in a good state.
-     */
-    private void cleanupState() throws Exception {
-        executeShellCommand(AM_FORCE_STOP + SOURCE_PACKAGE_NAME);
-        executeShellCommand(AM_FORCE_STOP + TARGET_PACKAGE_NAME);
-        executeShellCommand(AM_FORCE_STOP + TARGET_23_PACKAGE_NAME);
-        unlockDevice();
-
-        // Reinitialize the docked stack to force the window manager to reset its default bounds.
-        // See b/29068935.
-        clearLogs();
-        final String componentName = getComponentName(mSourcePackageName, SOURCE_ACTIVITY_NAME);
-        executeShellCommand(getStartCommand(componentName, null) + " --stack " +
-                FULLSCREEN_WORKSPACE_STACK_ID);
-        final int taskId = getActivityTaskId(componentName);
-        // Moving a task from the full screen stack to the docked stack resets
-        // WindowManagerService#mDockedStackCreateBounds.
-        executeShellCommand(getMoveTaskCommand(taskId, DOCKED_STACK_ID));
-        waitForResume(mSourcePackageName, SOURCE_ACTIVITY_NAME);
-        executeShellCommand(AM_FORCE_STOP + SOURCE_PACKAGE_NAME);
-
-        // Remove special stacks.
-        executeShellCommand(AM_REMOVE_STACK + PINNED_STACK_ID);
-        executeShellCommand(AM_REMOVE_STACK + DOCKED_STACK_ID);
-        executeShellCommand(AM_REMOVE_STACK + FREEFORM_WORKSPACE_STACK_ID);
-    }
-
-    private void launchDockedActivity(String packageName, String activityName, String mode)
-            throws Exception {
-        clearLogs();
-        final String componentName = getComponentName(packageName, activityName);
-        executeShellCommand(getStartCommand(componentName, mode) + " --stack " + DOCKED_STACK_ID);
-        waitForResume(packageName, activityName);
-    }
-
-    private void launchFullscreenActivity(String packageName, String activityName, String mode)
-            throws Exception {
-        clearLogs();
-        final String componentName = getComponentName(packageName, activityName);
-        executeShellCommand(getStartCommand(componentName, mode) + " --stack "
-                + FULLSCREEN_WORKSPACE_STACK_ID);
-        waitForResume(packageName, activityName);
-    }
-
-    /**
-     * @param displaySize size of the display
-     * @param leftSide {@code true} to launch the app taking up the left half of the display,
-     *         {@code false} to launch the app taking up the right half of the display.
-     */
-    private void launchFreeformActivity(String packageName, String activityName, String mode,
-            Point displaySize, boolean leftSide) throws Exception{
-        clearLogs();
-        final String componentName = getComponentName(packageName, activityName);
-        executeShellCommand(getStartCommand(componentName, mode) + " --stack "
-                + FREEFORM_WORKSPACE_STACK_ID);
-        waitForResume(packageName, activityName);
-        Point topLeft = new Point(leftSide ? 0 : displaySize.x / 2, 0);
-        Point bottomRight = new Point(leftSide ? displaySize.x / 2 : displaySize.x, displaySize.y);
-        executeShellCommand(getResizeTaskCommand(getActivityTaskId(componentName), topLeft,
-                bottomRight));
-    }
-
-    private void waitForResume(String packageName, String activityName) throws Exception {
-        final String fullActivityName = packageName + "." + activityName;
-        int retryCount = 3;
-        do {
-            Thread.sleep(500);
-            String logs = executeShellCommand("logcat -d -b events");
-            for (String line : logs.split("\\n")) {
-                if(line.contains("am_on_resume_called") && line.contains(fullActivityName)) {
-                    return;
-                }
-            }
-        } while (retryCount-- > 0);
-
-        throw new Exception(fullActivityName + " has failed to start");
-    }
-
-    private void injectInput(Point from, Point to, int durationMs) throws Exception {
-        executeShellCommand(
-                INPUT_MOUSE_SWIPE + from.x + " " + from.y + " " + to.x + " " + to.y + " " +
-                durationMs);
-    }
-
-    static class Point {
-        public int x, y;
-
-        public Point(int _x, int _y) {
-            x=_x;
-            y=_y;
-        }
-
-        public Point() {}
-    }
-
-    private String findTaskInfo(String name) throws Exception {
-        CollectingOutputReceiver outputReceiver = new CollectingOutputReceiver();
-        mDevice.executeShellCommand(AM_STACK_LIST, outputReceiver);
-        final String output = outputReceiver.getOutput();
-        final StringBuilder builder = new StringBuilder();
-        builder.append("Finding task info for task: ");
-        builder.append(name);
-        builder.append("\nParsing adb shell am output: " );
-        builder.append(output);
-        CLog.i(builder.toString());
-        final Pattern pattern = Pattern.compile(String.format(TASK_REGEX_PATTERN_STRING, name));
-        for (String line : output.split("\\n")) {
-            final String truncatedLine;
-            // Only look for the activity name before the "topActivity" string.
-            final int pos = line.indexOf("topActivity");
-            if (pos > 0) {
-                truncatedLine = line.substring(0, pos);
-            } else {
-                truncatedLine = line;
-            }
-            if (pattern.matcher(truncatedLine).find()) {
-                return truncatedLine;
-            }
-        }
-        return "";
-    }
-
-    private boolean getWindowBounds(String name, Point from, Point to) throws Exception {
-        final String taskInfo = findTaskInfo(name);
-        final String[] sections = taskInfo.split("\\[");
-        if (sections.length > 2) {
-            try {
-                parsePoint(sections[1], from);
-                parsePoint(sections[2], to);
-                return true;
-            } catch (Exception e) {
-                return false;
-            }
-        }
-        return false;
-    }
-
-    private int getActivityTaskId(String name) throws Exception {
-        final String taskInfo = findTaskInfo(name);
-        for (String word : taskInfo.split("\\s+")) {
-            if (word.startsWith(TASK_ID_PREFIX)) {
-                final String withColon = word.split("=")[1];
-                return Integer.parseInt(withColon.substring(0, withColon.length() - 1));
-            }
-        }
-        return -1;
-    }
-
-    private Point getDisplaySize() throws Exception {
-        final String output = executeShellCommand("wm size");
-        final String[] sizes = output.split(" ")[2].split("x");
-        return new Point(Integer.valueOf(sizes[0].trim()), Integer.valueOf(sizes[1].trim()));
-    }
-
-    private Point getWindowCenter(String name) throws Exception {
-        Point p1 = new Point();
-        Point p2 = new Point();
-        if (getWindowBounds(name, p1, p2)) {
-            return new Point((p1.x + p2.x) / 2, (p1.y + p2.y) / 2);
-        }
-        return null;
-    }
-
-    private void parsePoint(String string, Point point) {
-        final String[] parts = string.split("[,|\\]]");
-        point.x = Integer.parseInt(parts[0]);
-        point.y = Integer.parseInt(parts[1]);
-    }
-
-    private void unlockDevice() throws DeviceNotAvailableException {
-        // Wake up the device, if necessary.
-        executeShellCommand("input keyevent 224");
-        // Unlock the screen.
-        executeShellCommand("input keyevent 82");
-    }
-
-    private Map<String, String> getLogResults(String className, String lastResultKey)
-            throws Exception {
-        int retryCount = 10;
-        Map<String, String> output = new HashMap<String, String>();
-        do {
-
-            String logs = executeShellCommand("logcat -v brief -d " + className + ":I" + " *:S");
-            for (String line : logs.split("\\n")) {
-                if (line.startsWith("I/" + className)) {
-                    String payload = line.split(":")[1].trim();
-                    final String[] split = payload.split("=");
-                    if (split.length > 1) {
-                        output.put(split[0], split[1]);
-                    }
-                }
-            }
-            if (output.containsKey(lastResultKey)) {
-                return output;
-            }
-        } while (retryCount-- > 0);
-        return output;
-    }
-
-    private void assertDropResult(String sourceMode, String targetMode, String expectedDropResult)
-            throws Exception {
-        assertDragAndDropResults(sourceMode, targetMode, RESULT_OK, expectedDropResult, RESULT_OK);
-    }
-
-    private void assertNoGlobalDragEvents(String sourceMode, String expectedStartDragResult)
-            throws Exception {
-        assertDragAndDropResults(
-                sourceMode, REQUEST_NONE, expectedStartDragResult, RESULT_MISSING, RESULT_MISSING);
-    }
-
-    private void assertDragAndDropResults(String sourceMode, String targetMode,
-                                          String expectedStartDragResult, String expectedDropResult,
-                                          String expectedListenerResults) throws Exception {
-        if (!supportsDragAndDrop()) {
-            return;
-        }
-
-        if (supportsSplitScreenMultiWindow()) {
-            launchDockedActivity(mSourcePackageName, SOURCE_ACTIVITY_NAME, sourceMode);
-            launchFullscreenActivity(mTargetPackageName, TARGET_ACTIVITY_NAME, targetMode);
-        } else if (supportsFreeformMultiWindow()) {
-            // Fallback to try to launch two freeform windows side by side.
-            Point displaySize = getDisplaySize();
-            launchFreeformActivity(mSourcePackageName, SOURCE_ACTIVITY_NAME, sourceMode,
-                    displaySize, true /* leftSide */);
-            launchFreeformActivity(mTargetPackageName, TARGET_ACTIVITY_NAME, targetMode,
-                    displaySize, false /* leftSide */);
-        } else {
-            return;
-        }
-
-        clearLogs();
-
-        injectInput(
-                getWindowCenter(getComponentName(mSourcePackageName, SOURCE_ACTIVITY_NAME)),
-                getWindowCenter(getComponentName(mTargetPackageName, TARGET_ACTIVITY_NAME)),
-                SWIPE_DURATION_MS);
-
-        mSourceResults = getLogResults(SOURCE_LOG_TAG, RESULT_KEY_START_DRAG);
-        assertSourceResult(RESULT_KEY_START_DRAG, expectedStartDragResult);
-
-        mTargetResults = getLogResults(TARGET_LOG_TAG, RESULT_KEY_DRAG_ENDED);
-        assertTargetResult(RESULT_KEY_DROP_RESULT, expectedDropResult);
-        if (!RESULT_MISSING.equals(expectedDropResult)) {
-            assertTargetResult(RESULT_KEY_ACCESS_BEFORE, RESULT_EXCEPTION);
-            assertTargetResult(RESULT_KEY_ACCESS_AFTER, RESULT_EXCEPTION);
-        }
-        assertListenerResults(expectedListenerResults);
-    }
-
-    private void assertListenerResults(String expectedResult) throws Exception {
-        assertTargetResult(RESULT_KEY_DRAG_STARTED, expectedResult);
-        assertTargetResult(RESULT_KEY_DRAG_ENDED, expectedResult);
-        assertTargetResult(RESULT_KEY_EXTRAS, expectedResult);
-
-        assertTargetResult(RESULT_KEY_CLIP_DATA_ERROR, RESULT_MISSING);
-        assertTargetResult(RESULT_KEY_CLIP_DESCR_ERROR, RESULT_MISSING);
-        assertTargetResult(RESULT_KEY_LOCAL_STATE_ERROR, RESULT_MISSING);
-    }
-
-    private void assertSourceResult(String resultKey, String expectedResult) throws Exception {
-        assertResult(mSourceResults, resultKey, expectedResult);
-    }
-
-    private void assertTargetResult(String resultKey, String expectedResult) throws Exception {
-        assertResult(mTargetResults, resultKey, expectedResult);
-    }
-
-    private void assertResult(Map<String, String> results, String resultKey, String expectedResult)
-            throws Exception {
-        if (!supportsDragAndDrop()) {
-            return;
-        }
-
-        if (RESULT_MISSING.equals(expectedResult)) {
-            if (results.containsKey(resultKey)) {
-                fail("Unexpected " + resultKey + "=" + results.get(resultKey));
-            }
-        } else {
-            assertTrue("Missing " + resultKey, results.containsKey(resultKey));
-            assertEquals(resultKey + " result mismatch,", expectedResult,
-                    results.get(resultKey));
-        }
-    }
-
-    private boolean supportsDragAndDrop() throws Exception {
-        String supportsMultiwindow = mDevice.executeShellCommand("am supports-multiwindow").trim();
-        if ("true".equals(supportsMultiwindow)) {
-            return true;
-        } else if ("false".equals(supportsMultiwindow)) {
-            return false;
-        } else {
-            throw new Exception(
-                    "device does not support \"am supports-multiwindow\" shell command.");
-        }
-    }
-
-    private boolean supportsSplitScreenMultiWindow() throws DeviceNotAvailableException {
-        return !executeShellCommand(AM_SUPPORTS_SPLIT_SCREEN_MULTIWINDOW).startsWith("false");
-    }
-
-    private boolean supportsFreeformMultiWindow() throws DeviceNotAvailableException {
-        return mDevice.hasFeature("feature:android.software.freeform_window_management");
-    }
-
-    public void testCancelSoon() throws Exception {
-        assertDropResult(CANCEL_SOON, REQUEST_NONE, RESULT_MISSING);
-    }
-
-    public void testDisallowGlobal() throws Exception {
-        assertNoGlobalDragEvents(DISALLOW_GLOBAL, RESULT_OK);
-    }
-
-    public void testDisallowGlobalBelowSdk24() throws Exception {
-        mTargetPackageName = TARGET_23_PACKAGE_NAME;
-        assertNoGlobalDragEvents(GRANT_NONE, RESULT_OK);
-    }
-
-    public void testFileUriLocal() throws Exception {
-        assertNoGlobalDragEvents(FILE_LOCAL, RESULT_OK);
-    }
-
-    public void testFileUriGlobal() throws Exception {
-        assertNoGlobalDragEvents(FILE_GLOBAL, RESULT_EXCEPTION);
-    }
-
-    public void testGrantNoneRequestNone() throws Exception {
-        assertDropResult(GRANT_NONE, REQUEST_NONE, RESULT_EXCEPTION);
-    }
-
-    public void testGrantNoneRequestRead() throws Exception {
-        assertDropResult(GRANT_NONE, REQUEST_READ, RESULT_NULL_DROP_PERMISSIONS);
-    }
-
-    public void testGrantNoneRequestWrite() throws Exception {
-        assertDropResult(GRANT_NONE, REQUEST_WRITE, RESULT_NULL_DROP_PERMISSIONS);
-    }
-
-    public void testGrantReadRequestNone() throws Exception {
-        assertDropResult(GRANT_READ, REQUEST_NONE, RESULT_EXCEPTION);
-    }
-
-    public void testGrantReadRequestRead() throws Exception {
-        assertDropResult(GRANT_READ, REQUEST_READ, RESULT_OK);
-    }
-
-    public void testGrantReadRequestWrite() throws Exception {
-        assertDropResult(GRANT_READ, REQUEST_WRITE, RESULT_EXCEPTION);
-    }
-
-    public void testGrantReadNoPrefixRequestReadNested() throws Exception {
-        assertDropResult(GRANT_READ_NOPREFIX, REQUEST_READ_NESTED, RESULT_EXCEPTION);
-    }
-
-    public void testGrantReadPrefixRequestReadNested() throws Exception {
-        assertDropResult(GRANT_READ_PREFIX, REQUEST_READ_NESTED, RESULT_OK);
-    }
-
-    public void testGrantPersistableRequestTakePersistable() throws Exception {
-        assertDropResult(GRANT_READ_PERSISTABLE, REQUEST_TAKE_PERSISTABLE, RESULT_OK);
-    }
-
-    public void testGrantReadRequestTakePersistable() throws Exception {
-        assertDropResult(GRANT_READ, REQUEST_TAKE_PERSISTABLE, RESULT_EXCEPTION);
-    }
-
-    public void testGrantWriteRequestNone() throws Exception {
-        assertDropResult(GRANT_WRITE, REQUEST_NONE, RESULT_EXCEPTION);
-    }
-
-    public void testGrantWriteRequestRead() throws Exception {
-        assertDropResult(GRANT_WRITE, REQUEST_READ, RESULT_EXCEPTION);
-    }
-
-    public void testGrantWriteRequestWrite() throws Exception {
-        assertDropResult(GRANT_WRITE, REQUEST_WRITE, RESULT_OK);
-    }
-}
diff --git a/hostsidetests/services/activityandwindowmanager/windowmanager/src/android/server/cts/DialogFrameTests.java b/hostsidetests/services/activityandwindowmanager/windowmanager/src/android/server/cts/DialogFrameTests.java
deleted file mode 100644
index c99f001..0000000
--- a/hostsidetests/services/activityandwindowmanager/windowmanager/src/android/server/cts/DialogFrameTests.java
+++ /dev/null
@@ -1,215 +0,0 @@
-/*
- * Copyright (C) 2016 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package android.server.cts;
-
-import java.util.List;
-import java.util.ArrayList;
-import java.awt.Rectangle;
-
-import com.android.ddmlib.Log.LogLevel;
-import com.android.tradefed.log.LogUtil.CLog;
-
-import android.server.cts.WindowManagerState.WindowState;
-
-public class DialogFrameTests extends ParentChildTestBase {
-    private List<WindowState> mWindowList = new ArrayList();
-
-    @Override
-    String intentKey() {
-        return "android.server.FrameTestApp.DialogTestCase";
-    }
-
-    @Override
-    String activityName() {
-        return "DialogTestActivity";
-    }
-
-    WindowState getSingleWindow(String windowName) {
-        try {
-            mAmWmState.getWmState().getMatchingVisibleWindowState(
-                    getBaseWindowName() + windowName, mWindowList);
-            return mWindowList.get(0);
-        } catch (Exception e) {
-            CLog.logAndDisplay(LogLevel.INFO, "Couldn't find window: " + windowName);
-            return null;
-        }
-    }
-
-    void doSingleTest(ParentChildTest t) throws Exception {
-        final String[] waitForVisible = new String[] { "TestDialog" };
-
-        mAmWmState.computeState(mDevice, waitForVisible);
-        WindowState dialog = getSingleWindow("TestDialog");
-        WindowState parent = getSingleWindow("DialogTestActivity");
-
-        t.doTest(parent, dialog);
-    }
-
-    // With Width and Height as MATCH_PARENT we should fill
-    // the same content frame as the main activity window
-    public void testMatchParentDialog() throws Exception {
-        doParentChildTest("MatchParent",
-            (WindowState parent, WindowState dialog) -> {
-                assertEquals(parent.getContentFrame(), dialog.getFrame());
-            });
-    }
-
-    // If we have LAYOUT_IN_SCREEN and LAYOUT_IN_OVERSCAN with MATCH_PARENT,
-    // we will not be constrained to the insets and so we will be the same size
-    // as the main window main frame.
-    public void testMatchParentDialogLayoutInOverscan() throws Exception {
-        doParentChildTest("MatchParentLayoutInOverscan",
-            (WindowState parent, WindowState dialog) -> {
-                assertEquals(parent.getFrame(), dialog.getFrame());
-            });
-    }
-
-    static final int explicitDimension = 200;
-
-    // The default gravity for dialogs should center them.
-    public void testExplicitSizeDefaultGravity() throws Exception {
-        doParentChildTest("ExplicitSize",
-            (WindowState parent, WindowState dialog) -> {
-                Rectangle contentFrame = parent.getContentFrame();
-                Rectangle expectedFrame = new Rectangle(
-                        contentFrame.x + (contentFrame.width - explicitDimension)/2,
-                        contentFrame.y + (contentFrame.height - explicitDimension)/2,
-                        explicitDimension, explicitDimension);
-                assertEquals(expectedFrame, dialog.getFrame());
-            });
-    }
-
-    public void testExplicitSizeTopLeftGravity() throws Exception {
-        doParentChildTest("ExplicitSizeTopLeftGravity",
-            (WindowState parent, WindowState dialog) -> {
-                Rectangle contentFrame = parent.getContentFrame();
-                Rectangle expectedFrame = new Rectangle(
-                        contentFrame.x,
-                        contentFrame.y,
-                        explicitDimension,
-                        explicitDimension);
-                assertEquals(expectedFrame, dialog.getFrame());
-            });
-    }
-
-    public void testExplicitSizeBottomRightGravity() throws Exception {
-        doParentChildTest("ExplicitSizeBottomRightGravity",
-            (WindowState parent, WindowState dialog) -> {
-                Rectangle contentFrame = parent.getContentFrame();
-                Rectangle expectedFrame = new Rectangle(
-                        contentFrame.x + contentFrame.width - explicitDimension,
-                        contentFrame.y + contentFrame.height - explicitDimension,
-                        explicitDimension, explicitDimension);
-                assertEquals(expectedFrame, dialog.getFrame());
-            });
-    }
-
-    // TODO: Commented out for now because it doesn't work. We end up
-    // insetting the decor on the bottom. I think this is a bug
-    // probably in the default dialog flags:
-    // b/30127373
-    //    public void testOversizedDimensions() throws Exception {
-    //        doParentChildTest("OversizedDimensions",
-    //            (WindowState parent, WindowState dialog) -> {
-    // With the default flags oversize should result in clipping to
-    // parent frame.
-    //                assertEquals(parent.getContentFrame(), dialog.getFrame());
-    //         });
-    //    }
-
-    // TODO(b/63993863) : Disabled pending public API to fetch maximum surface size.
-    // static final int oversizedDimension = 5000;
-    // With FLAG_LAYOUT_NO_LIMITS  we should get the size we request, even if its much
-    // larger than the screen.
-    // public void testOversizedDimensionsNoLimits() throws Exception {
-    // TODO(b/36890978): We only run this in fullscreen because of the
-    // unclear status of NO_LIMITS for non-child surfaces in MW modes
-    //     doFullscreenTest("OversizedDimensionsNoLimits",
-    //        (WindowState parent, WindowState dialog) -> {
-    //            Rectangle contentFrame = parent.getContentFrame();
-    //            Rectangle expectedFrame = new Rectangle(contentFrame.x, contentFrame.y,
-    //                    oversizedDimension, oversizedDimension);
-    //            assertEquals(expectedFrame, dialog.getFrame());
-    //        });
-    // }
-
-    // If we request the MATCH_PARENT and a non-zero position, we wouldn't be
-    // able to fit all of our content, so we should be adjusted to just fit the
-    // content frame.
-    public void testExplicitPositionMatchParent() throws Exception {
-        doParentChildTest("ExplicitPositionMatchParent",
-             (WindowState parent, WindowState dialog) -> {
-                    assertEquals(parent.getContentFrame(),
-                            dialog.getFrame());
-             });
-    }
-
-    // Unless we pass NO_LIMITS in which case our requested position should
-    // be honored.
-    public void testExplicitPositionMatchParentNoLimits() throws Exception {
-        final int explicitPosition = 100;
-        doParentChildTest("ExplicitPositionMatchParentNoLimits",
-            (WindowState parent, WindowState dialog) -> {
-                Rectangle contentFrame = parent.getContentFrame();
-                Rectangle expectedFrame = new Rectangle(contentFrame.x + explicitPosition,
-                        contentFrame.y + explicitPosition,
-                        contentFrame.width,
-                        contentFrame.height);
-            });
-    }
-
-    // We run the two focus tests fullscreen only because switching to the
-    // docked stack will strip away focus from the task anyway.
-    public void testDialogReceivesFocus() throws Exception {
-        doFullscreenTest("MatchParent",
-            (WindowState parent, WindowState dialog) -> {
-                assertEquals(dialog.getName(), mAmWmState.getWmState().getFocusedWindow());
-        });
-    }
-
-    public void testNoFocusDialog() throws Exception {
-        doFullscreenTest("NoFocus",
-            (WindowState parent, WindowState dialog) -> {
-                assertEquals(parent.getName(), mAmWmState.getWmState().getFocusedWindow());
-        });
-    }
-
-    public void testMarginsArePercentagesOfContentFrame() throws Exception {
-        float horizontalMargin = .25f;
-        float verticalMargin = .35f;
-        doParentChildTest("WithMargins",
-            (WindowState parent, WindowState dialog) -> {
-                Rectangle frame = parent.getContentFrame();
-                Rectangle expectedFrame = new Rectangle(
-                        (int)(horizontalMargin*frame.width + frame.x),
-                        (int)(verticalMargin*frame.height + frame.y),
-                        explicitDimension,
-                        explicitDimension);
-                assertEquals(expectedFrame, dialog.getFrame());
-                });
-    }
-
-    public void testDialogPlacedAboveParent() throws Exception {
-        doParentChildTest("MatchParent",
-            (WindowState parent, WindowState dialog) -> {
-                // Not only should the dialog be higher, but it should be
-                // leave multiple layers of space inbetween for DimLayers,
-                // etc...
-                assertTrue(dialog.getLayer() - parent.getLayer() >= 5);
-        });
-    }
-}
diff --git a/hostsidetests/services/activityandwindowmanager/windowmanager/src/android/server/cts/ParentChildTestBase.java b/hostsidetests/services/activityandwindowmanager/windowmanager/src/android/server/cts/ParentChildTestBase.java
deleted file mode 100644
index c8ca9f4..0000000
--- a/hostsidetests/services/activityandwindowmanager/windowmanager/src/android/server/cts/ParentChildTestBase.java
+++ /dev/null
@@ -1,75 +0,0 @@
-/*
- * Copyright (C) 2016 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package android.server.cts;
-
-import com.android.ddmlib.Log.LogLevel;
-import com.android.tradefed.log.LogUtil.CLog;
-import com.android.tradefed.testtype.DeviceTestCase;
-
-import android.server.cts.WindowManagerState.WindowState;
-import android.server.cts.ActivityManagerTestBase;
-
-public abstract class ParentChildTestBase extends ActivityManagerTestBase {
-    private static final String COMPONENT_NAME = "android.server.FrameTestApp";
-
-    interface ParentChildTest {
-        void doTest(WindowState parent, WindowState child);
-    }
-
-    public void startTestCase(String testCase) throws Exception {
-        setComponentName(COMPONENT_NAME);
-        String cmd = getAmStartCmd(activityName(), intentKey(), testCase);
-        CLog.logAndDisplay(LogLevel.INFO, cmd);
-        executeShellCommand(cmd);
-    }
-
-    public void startTestCaseDocked(String testCase) throws Exception {
-        setComponentName(COMPONENT_NAME);
-        String cmd = getAmStartCmd(activityName(), intentKey(), testCase);
-        CLog.logAndDisplay(LogLevel.INFO, cmd);
-        executeShellCommand(cmd);
-        moveActivityToDockStack(activityName());
-    }
-
-    abstract String intentKey();
-    abstract String activityName();
-
-    abstract void doSingleTest(ParentChildTest t) throws Exception;
-
-    void doFullscreenTest(String testCase, ParentChildTest t) throws Exception {
-        CLog.logAndDisplay(LogLevel.INFO, "Running test fullscreen");
-        startTestCase(testCase);
-        doSingleTest(t);
-        stopTestCase();
-    }
-
-    void doDockedTest(String testCase, ParentChildTest t) throws Exception {
-        CLog.logAndDisplay(LogLevel.INFO, "Running test docked");
-        if (!supportsSplitScreenMultiWindow()) {
-            CLog.logAndDisplay(LogLevel.INFO, "Skipping test: no split multi-window support");
-            return;
-        }
-        startTestCaseDocked(testCase);
-        doSingleTest(t);
-        stopTestCase();
-    }
-
-    void doParentChildTest(String testCase, ParentChildTest t) throws Exception {
-        doFullscreenTest(testCase, t);
-        doDockedTest(testCase, t);
-    }
-}
diff --git a/hostsidetests/shortcuts/deviceside/backup/launcher1/Android.mk b/hostsidetests/shortcuts/deviceside/backup/launcher1/Android.mk
index d6077a4..d7517cd 100644
--- a/hostsidetests/shortcuts/deviceside/backup/launcher1/Android.mk
+++ b/hostsidetests/shortcuts/deviceside/backup/launcher1/Android.mk
@@ -37,6 +37,8 @@
     ub-uiautomator \
     ShortcutManagerTestUtils
 
+LOCAL_JAVA_LIBRARIES := android.test.base.stubs
+
 LOCAL_SDK_VERSION := current
 
 include $(BUILD_CTS_PACKAGE)
diff --git a/hostsidetests/shortcuts/deviceside/backup/launcher1/AndroidManifest.xml b/hostsidetests/shortcuts/deviceside/backup/launcher1/AndroidManifest.xml
index 75b3ca0..d84c7b5 100644
--- a/hostsidetests/shortcuts/deviceside/backup/launcher1/AndroidManifest.xml
+++ b/hostsidetests/shortcuts/deviceside/backup/launcher1/AndroidManifest.xml
@@ -19,6 +19,8 @@
     package="android.content.pm.cts.shortcut.backup.launcher1">
 
     <application>
+        <uses-library android:name="android.test.runner" />
+
         <activity android:name="MainActivity" android:exported="true" >
             <intent-filter>
                 <action android:name="android.intent.action.MAIN" />
diff --git a/hostsidetests/shortcuts/deviceside/backup/launcher1/src/android/content/pm/cts/shortcut/backup/launcher1/ShortcutManagerPostBackupTest.java b/hostsidetests/shortcuts/deviceside/backup/launcher1/src/android/content/pm/cts/shortcut/backup/launcher1/ShortcutManagerPostBackupTest.java
index b82bb1d..e76cf28 100644
--- a/hostsidetests/shortcuts/deviceside/backup/launcher1/src/android/content/pm/cts/shortcut/backup/launcher1/ShortcutManagerPostBackupTest.java
+++ b/hostsidetests/shortcuts/deviceside/backup/launcher1/src/android/content/pm/cts/shortcut/backup/launcher1/ShortcutManagerPostBackupTest.java
@@ -17,6 +17,7 @@
 
 import static com.android.server.pm.shortcutmanagertest.ShortcutManagerTestUtils.assertWith;
 
+import android.content.pm.ShortcutInfo;
 import android.content.pm.cts.shortcut.device.common.ShortcutManagerDeviceTestBase;
 
 public class ShortcutManagerPostBackupTest extends ShortcutManagerDeviceTestBase {
@@ -57,9 +58,22 @@
                 .selectByIds("ms1")
                 .areAllNotPinned();
 
+        // Package3 doesn't support backup&restore.
+        // However, the manifest-shortcuts will be republished anyway, so they're still pinned.
+        // The dynamic shortcuts can't be restored, but we'll still restore them as disabled
+        // shortcuts that are not visible to the publisher.
         assertWith(getPackageShortcuts(ShortcutManagerPreBackupTest.PUBLISHER3_PKG))
-                .haveIds("ms1", "ms2")
+                .haveIds("ms1", "ms2", "s1", "s2")
+                .areAllPinned()
+
+                .selectByIds("ms1", "ms2")
                 .areAllEnabled()
-                .areAllNotPinned(); // P3 doesn't get backed up, so no longer pinned.
+                .areAllWithDisabledReason(ShortcutInfo.DISABLED_REASON_NOT_DISABLED)
+
+                .revertToOriginalList()
+                .selectByIds("s1", "s2")
+                .areAllDisabled()
+                .areAllWithDisabledReason(ShortcutInfo.DISABLED_REASON_BACKUP_NOT_SUPPORTED)
+                ;
     }
 }
diff --git a/hostsidetests/shortcuts/deviceside/backup/launcher2/Android.mk b/hostsidetests/shortcuts/deviceside/backup/launcher2/Android.mk
index 8f124a0..8a6440d 100644
--- a/hostsidetests/shortcuts/deviceside/backup/launcher2/Android.mk
+++ b/hostsidetests/shortcuts/deviceside/backup/launcher2/Android.mk
@@ -37,6 +37,8 @@
     ub-uiautomator \
     ShortcutManagerTestUtils
 
+LOCAL_JAVA_LIBRARIES := android.test.base.stubs
+
 LOCAL_SDK_VERSION := current
 
 include $(BUILD_CTS_PACKAGE)
diff --git a/hostsidetests/shortcuts/deviceside/backup/launcher2/AndroidManifest.xml b/hostsidetests/shortcuts/deviceside/backup/launcher2/AndroidManifest.xml
index 71ffc61..9297220 100644
--- a/hostsidetests/shortcuts/deviceside/backup/launcher2/AndroidManifest.xml
+++ b/hostsidetests/shortcuts/deviceside/backup/launcher2/AndroidManifest.xml
@@ -19,6 +19,8 @@
     package="android.content.pm.cts.shortcut.backup.launcher2">
 
     <application>
+        <uses-library android:name="android.test.runner" />
+
         <activity android:name="MainActivity" android:exported="true" >
             <intent-filter>
                 <action android:name="android.intent.action.MAIN" />
diff --git a/hostsidetests/shortcuts/deviceside/backup/launcher2/src/android/content/pm/cts/shortcut/backup/launcher2/ShortcutManagerPostBackupTest.java b/hostsidetests/shortcuts/deviceside/backup/launcher2/src/android/content/pm/cts/shortcut/backup/launcher2/ShortcutManagerPostBackupTest.java
index 75c79c5..115c476 100644
--- a/hostsidetests/shortcuts/deviceside/backup/launcher2/src/android/content/pm/cts/shortcut/backup/launcher2/ShortcutManagerPostBackupTest.java
+++ b/hostsidetests/shortcuts/deviceside/backup/launcher2/src/android/content/pm/cts/shortcut/backup/launcher2/ShortcutManagerPostBackupTest.java
@@ -17,6 +17,7 @@
 
 import static com.android.server.pm.shortcutmanagertest.ShortcutManagerTestUtils.assertWith;
 
+import android.content.pm.ShortcutInfo;
 import android.content.pm.cts.shortcut.device.common.ShortcutManagerDeviceTestBase;
 
 public class ShortcutManagerPostBackupTest extends ShortcutManagerDeviceTestBase {
@@ -51,8 +52,22 @@
                 .selectByIds("ms2")
                 .areAllNotPinned();
 
+        // Package3 doesn't support backup&restore.
+        // However, the manifest-shortcuts will be republished anyway, so they're still pinned.
+        // The dynamic shortcuts can't be restored, but we'll still restore them as disabled
+        // shortcuts that are not visible to the publisher.
         assertWith(getPackageShortcuts(ShortcutManagerPreBackupTest.PUBLISHER3_PKG))
-                .haveIds("ms1", "ms2")
-                .areAllEnabled();
+                .haveIds("ms1", "ms2", "s2", "s3")
+                .areAllPinned()
+
+                .selectByIds("ms1", "ms2")
+                .areAllEnabled()
+                .areAllWithDisabledReason(ShortcutInfo.DISABLED_REASON_NOT_DISABLED)
+
+                .revertToOriginalList()
+                .selectByIds("s2", "s3")
+                .areAllDisabled()
+                .areAllWithDisabledReason(ShortcutInfo.DISABLED_REASON_BACKUP_NOT_SUPPORTED)
+        ;
     }
 }
diff --git a/hostsidetests/shortcuts/deviceside/backup/launcher3/Android.mk b/hostsidetests/shortcuts/deviceside/backup/launcher3/Android.mk
index 9883e5c..be8edb3 100644
--- a/hostsidetests/shortcuts/deviceside/backup/launcher3/Android.mk
+++ b/hostsidetests/shortcuts/deviceside/backup/launcher3/Android.mk
@@ -37,6 +37,8 @@
     ub-uiautomator \
     ShortcutManagerTestUtils
 
+LOCAL_JAVA_LIBRARIES := android.test.base.stubs
+
 LOCAL_SDK_VERSION := current
 
 include $(BUILD_CTS_PACKAGE)
diff --git a/hostsidetests/shortcuts/deviceside/backup/launcher3/AndroidManifest.xml b/hostsidetests/shortcuts/deviceside/backup/launcher3/AndroidManifest.xml
index 82dc28f..8f1c1a7 100644
--- a/hostsidetests/shortcuts/deviceside/backup/launcher3/AndroidManifest.xml
+++ b/hostsidetests/shortcuts/deviceside/backup/launcher3/AndroidManifest.xml
@@ -19,6 +19,8 @@
     package="android.content.pm.cts.shortcut.backup.launcher3">
 
     <application android:allowBackup="false">
+        <uses-library android:name="android.test.runner" />
+
         <activity android:name="MainActivity" android:exported="true" >
             <intent-filter>
                 <action android:name="android.intent.action.MAIN" />
diff --git a/hostsidetests/shortcuts/deviceside/backup/launcher4new/Android.mk b/hostsidetests/shortcuts/deviceside/backup/launcher4new/Android.mk
new file mode 100644
index 0000000..97287fb
--- /dev/null
+++ b/hostsidetests/shortcuts/deviceside/backup/launcher4new/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)
+
+# Tag this module as a cts test artifact
+LOCAL_COMPATIBILITY_SUITE := cts vts general-tests
+
+LOCAL_PACKAGE_NAME := CtsShortcutBackupLauncher4new
+
+LOCAL_MODULE_TAGS := optional
+
+LOCAL_MODULE_PATH := $(TARGET_OUT_DATA_APPS)
+
+LOCAL_SRC_FILES := $(call all-java-files-under, ../launcher4old/src) \
+    $(call all-java-files-under, ../../common/src)
+
+LOCAL_STATIC_JAVA_LIBRARIES := \
+    android-support-test \
+    android-support-v4 \
+    mockito-target-minus-junit4 \
+    compatibility-device-util \
+    ctstestrunner \
+    ub-uiautomator \
+    ShortcutManagerTestUtils
+
+LOCAL_JAVA_LIBRARIES := android.test.base.stubs
+
+LOCAL_SDK_VERSION := current
+
+include $(BUILD_CTS_PACKAGE)
diff --git a/hostsidetests/shortcuts/deviceside/backup/launcher4new/AndroidManifest.xml b/hostsidetests/shortcuts/deviceside/backup/launcher4new/AndroidManifest.xml
new file mode 100644
index 0000000..1032971
--- /dev/null
+++ b/hostsidetests/shortcuts/deviceside/backup/launcher4new/AndroidManifest.xml
@@ -0,0 +1,41 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ -->
+
+<manifest xmlns:android="http://schemas.android.com/apk/res/android"
+    package="android.content.pm.cts.shortcut.backup.launcher4"
+    android:versionCode="11">
+
+    <application>
+        <uses-library android:name="android.test.runner" />
+
+        <activity android:name="MainActivity" android:exported="true" >
+            <intent-filter>
+                <action android:name="android.intent.action.MAIN" />
+                <category android:name="android.intent.category.DEFAULT" />
+                <category android:name="android.intent.category.HOME" />
+            </intent-filter>
+            <intent-filter>
+                <action android:name="android.content.pm.action.CONFIRM_PIN_SHORTCUT" />
+            </intent-filter>
+        </activity>
+    </application>
+
+    <instrumentation
+        android:name="android.support.test.runner.AndroidJUnitRunner"
+        android:targetPackage="android.content.pm.cts.shortcut.backup.launcher4"  />
+</manifest>
+
diff --git a/hostsidetests/shortcuts/deviceside/backup/launcher4old/Android.mk b/hostsidetests/shortcuts/deviceside/backup/launcher4old/Android.mk
new file mode 100644
index 0000000..6389d22
--- /dev/null
+++ b/hostsidetests/shortcuts/deviceside/backup/launcher4old/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)
+
+# Tag this module as a cts test artifact
+LOCAL_COMPATIBILITY_SUITE := cts vts general-tests
+
+LOCAL_PACKAGE_NAME := CtsShortcutBackupLauncher4old
+
+LOCAL_MODULE_TAGS := optional
+
+LOCAL_MODULE_PATH := $(TARGET_OUT_DATA_APPS)
+
+LOCAL_SRC_FILES := $(call all-java-files-under, src) \
+    $(call all-java-files-under, ../../common/src)
+
+LOCAL_STATIC_JAVA_LIBRARIES := \
+    android-support-test \
+    android-support-v4 \
+    mockito-target-minus-junit4 \
+    compatibility-device-util \
+    ctstestrunner \
+    ub-uiautomator \
+    ShortcutManagerTestUtils
+
+LOCAL_JAVA_LIBRARIES := android.test.base.stubs
+
+LOCAL_SDK_VERSION := current
+
+include $(BUILD_CTS_PACKAGE)
diff --git a/hostsidetests/shortcuts/deviceside/backup/launcher4old/AndroidManifest.xml b/hostsidetests/shortcuts/deviceside/backup/launcher4old/AndroidManifest.xml
new file mode 100644
index 0000000..e7c81b3
--- /dev/null
+++ b/hostsidetests/shortcuts/deviceside/backup/launcher4old/AndroidManifest.xml
@@ -0,0 +1,41 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ -->
+
+<manifest xmlns:android="http://schemas.android.com/apk/res/android"
+    package="android.content.pm.cts.shortcut.backup.launcher4"
+    android:versionCode="10">
+
+    <application>
+        <uses-library android:name="android.test.runner" />
+
+        <activity android:name="MainActivity" android:exported="true" >
+            <intent-filter>
+                <action android:name="android.intent.action.MAIN" />
+                <category android:name="android.intent.category.DEFAULT" />
+                <category android:name="android.intent.category.HOME" />
+            </intent-filter>
+            <intent-filter>
+                <action android:name="android.content.pm.action.CONFIRM_PIN_SHORTCUT" />
+            </intent-filter>
+        </activity>
+    </application>
+
+    <instrumentation
+        android:name="android.support.test.runner.AndroidJUnitRunner"
+        android:targetPackage="android.content.pm.cts.shortcut.backup.launcher4"  />
+</manifest>
+
diff --git a/hostsidetests/shortcuts/deviceside/backup/launcher4old/src/android/content/pm/cts/shortcut/backup/launcher4/MainActivity.java b/hostsidetests/shortcuts/deviceside/backup/launcher4old/src/android/content/pm/cts/shortcut/backup/launcher4/MainActivity.java
new file mode 100644
index 0000000..33215d6
--- /dev/null
+++ b/hostsidetests/shortcuts/deviceside/backup/launcher4old/src/android/content/pm/cts/shortcut/backup/launcher4/MainActivity.java
@@ -0,0 +1,39 @@
+/*
+ * 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.content.pm.cts.shortcut.backup.launcher4;
+
+import android.app.Activity;
+import android.content.pm.LauncherApps;
+import android.os.Bundle;
+import android.os.PersistableBundle;
+
+public class MainActivity extends Activity {
+    @Override
+    protected void onCreate(Bundle savedInstanceState) {
+        super.onCreate(savedInstanceState);
+
+        // Just accept all requests.
+        if (LauncherApps.ACTION_CONFIRM_PIN_SHORTCUT.equals(getIntent().getAction())) {
+            LauncherApps.PinItemRequest request = getSystemService(LauncherApps.class)
+                    .getPinItemRequest(getIntent());
+            final PersistableBundle extras = request.getShortcutInfo().getExtras();
+            if (extras != null && extras.getBoolean("acceptit")) {
+                request.accept();
+            }
+        }
+        finish();
+    }
+}
diff --git a/hostsidetests/shortcuts/deviceside/backup/launcher4old/src/android/content/pm/cts/shortcut/backup/launcher4/ShortcutManagerPostBackupTest.java b/hostsidetests/shortcuts/deviceside/backup/launcher4old/src/android/content/pm/cts/shortcut/backup/launcher4/ShortcutManagerPostBackupTest.java
new file mode 100644
index 0000000..f9ecfef
--- /dev/null
+++ b/hostsidetests/shortcuts/deviceside/backup/launcher4old/src/android/content/pm/cts/shortcut/backup/launcher4/ShortcutManagerPostBackupTest.java
@@ -0,0 +1,181 @@
+/*
+ * 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.content.pm.cts.shortcut.backup.launcher4;
+
+import static com.android.server.pm.shortcutmanagertest.ShortcutManagerTestUtils.assertWith;
+
+import android.content.pm.ShortcutInfo;
+import android.content.pm.cts.shortcut.device.common.ShortcutManagerDeviceTestBase;
+
+public class ShortcutManagerPostBackupTest extends ShortcutManagerDeviceTestBase {
+    @Override
+    protected void setUp() throws Exception {
+        super.setUp();
+
+        setAsDefaultLauncher(MainActivity.class);
+    }
+
+    public void testRestoredOnOldVersion() {
+        assertWith(getPackageShortcuts(ShortcutManagerPreBackupTest.PUBLISHER4_PKG))
+                .haveIds("ms1", "ms2", "s1", "s2")
+                .areAllPinned()
+
+                .selectByIds("ms1", "ms2")
+                .areAllEnabled()
+                .areAllManifest()
+
+                // s1 is re-published, so it's enabled and dynamic.
+                .revertToOriginalList()
+                .selectByIds("s1")
+                .areAllEnabled()
+                .areAllDynamic()
+                .forAllShortcuts(si -> {
+                    assertEquals("shortlabel1_new_one", si.getShortLabel());
+
+                    // The app re-published the shortcut, not updated, so the fields that existed
+                    // in the original shortcut is gone now.
+                    assertNull(si.getExtras());
+                })
+
+                .revertToOriginalList()
+                .selectByIds("s2")
+                .areAllDisabled()
+                .areAllWithDisabledReason(ShortcutInfo.DISABLED_REASON_VERSION_LOWER)
+                .forAllShortcuts(si -> {
+                    // Note the label shouldn't be updated with the updateShortcuts() call.
+                    assertEquals("shortlabel2", si.getShortLabel());
+                })
+                .areAllNotDynamic();
+    }
+
+    public void testRestoredOnNewVersion() {
+        assertWith(getPackageShortcuts(ShortcutManagerPreBackupTest.PUBLISHER4_PKG))
+                .haveIds("ms1", "ms2", "s1", "s2")
+                .areAllPinned()
+
+                .selectByIds("ms1", "ms2")
+                .areAllEnabled()
+                .areAllManifest()
+
+                // s1 is re-published, so it's enabled and dynamic.
+                .revertToOriginalList()
+                .selectByIds("s1", "s2")
+                .areAllEnabled()
+
+                .revertToOriginalList()
+                .selectByIds("s1")
+                .areAllDynamic()
+                .forAllShortcuts(si -> {
+                    assertEquals("shortlabel1_new_one", si.getShortLabel());
+
+                    // The app re-published the shortcut, not updated, so the fields that existed
+                    // in the original shortcut is gone now.
+                    assertNull(si.getExtras());
+                })
+
+                .revertToOriginalList()
+                .selectByIds("s2")
+                .areAllNotDynamic()
+                .forAllShortcuts(si -> {
+                    assertEquals("shortlabel2_updated", si.getShortLabel());
+                })
+                ;
+    }
+
+
+    public void testRestoreWrongKey() {
+        assertWith(getPackageShortcuts(ShortcutManagerPreBackupTest.PUBLISHER4_PKG))
+                .haveIds("ms1", "ms2", "s1", "s2")
+                .areAllPinned()
+
+                .selectByIds("ms1", "ms2")
+                .areAllEnabled()
+                .areAllManifest()
+
+                // s1 is re-published, so it's enabled and dynamic.
+                .revertToOriginalList()
+                .selectByIds("s1")
+                .areAllEnabled()
+                .areAllDynamic()
+                .forAllShortcuts(si -> {
+                    assertEquals("shortlabel1_new_one", si.getShortLabel());
+
+                    // The app re-published the shortcut, not updated, so the fields that existed
+                    // in the original shortcut is gone now.
+                    assertNull(si.getExtras());
+                })
+
+
+                // updateShortcuts() shouldn't work on it, so it keeps the original label.
+                .revertToOriginalList()
+                .selectByIds("s2")
+                .areAllNotDynamic()
+                .forAllShortcuts(si -> {
+                    assertEquals("shortlabel2", si.getShortLabel());
+                })
+        ;
+    }
+
+    public void testRestoreNoManifestOnOldVersion() {
+        assertWith(getPackageShortcuts(ShortcutManagerPreBackupTest.PUBLISHER4_PKG))
+                .haveIds("ms1", "ms2", "s1", "s2")
+                .areAllPinned()
+
+                .selectByIds("s1", "ms1")
+                .areAllEnabled()
+                .areAllDynamic()
+                .areAllMutable()
+
+                .revertToOriginalList()
+                .selectByIds("s2", "ms2")
+                .areAllDisabled();
+    }
+
+    public void testRestoreNoManifestOnNewVersion() {
+        assertWith(getPackageShortcuts(ShortcutManagerPreBackupTest.PUBLISHER4_PKG))
+                .haveIds("ms1", "ms2", "s1", "s2")
+                .areAllPinned()
+
+                .selectByIds("ms1", "ms2")
+                .areAllDisabled()
+                .areAllImmutable()
+                .areAllWithDisabledReason(ShortcutInfo.DISABLED_REASON_APP_CHANGED)
+
+                .revertToOriginalList()
+                .selectByIds("s1", "s2")
+                .areAllEnabled()
+                .areAllMutable();
+    }
+
+    public void testInvisibleIgnored() {
+        assertWith(getPackageShortcuts(ShortcutManagerPreBackupTest.PUBLISHER4_PKG))
+                .haveIds("ms1", "ms2", "s1", "s2")
+
+                .selectByIds("ms1", "s1", "s2")
+                .areAllPinned()
+                .areAllDisabled()
+                .areAllWithDisabledReason(ShortcutInfo.DISABLED_REASON_VERSION_LOWER)
+                .areAllNotDynamic()
+                .areAllNotManifest()
+
+                .revertToOriginalList()
+                .selectByIds("ms2")
+                .areAllEnabled()
+                .areAllPinned()
+                .areAllNotDynamic()
+                .areAllNotManifest();
+    }
+}
diff --git a/hostsidetests/shortcuts/deviceside/backup/launcher4old/src/android/content/pm/cts/shortcut/backup/launcher4/ShortcutManagerPreBackupTest.java b/hostsidetests/shortcuts/deviceside/backup/launcher4old/src/android/content/pm/cts/shortcut/backup/launcher4/ShortcutManagerPreBackupTest.java
new file mode 100644
index 0000000..8d99255
--- /dev/null
+++ b/hostsidetests/shortcuts/deviceside/backup/launcher4old/src/android/content/pm/cts/shortcut/backup/launcher4/ShortcutManagerPreBackupTest.java
@@ -0,0 +1,40 @@
+/*
+ * 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.content.pm.cts.shortcut.backup.launcher4;
+
+import static com.android.server.pm.shortcutmanagertest.ShortcutManagerTestUtils.list;
+
+import android.content.pm.cts.shortcut.device.common.ShortcutManagerDeviceTestBase;
+import android.test.suitebuilder.annotation.SmallTest;
+
+@SmallTest
+public class ShortcutManagerPreBackupTest extends ShortcutManagerDeviceTestBase {
+    static final String PUBLISHER4_PKG =
+            "android.content.pm.cts.shortcut.backup.publisher4";
+
+    @Override
+    protected void setUp() throws Exception {
+        super.setUp();
+
+        setAsDefaultLauncher(MainActivity.class);
+    }
+
+    public void testPreBackup() {
+        // Pin all the shortcuts.
+        getLauncherApps().pinShortcuts(PUBLISHER4_PKG, list("s1", "s2", "ms1", "ms2"),
+                getUserHandle());
+    }
+}
diff --git a/hostsidetests/shortcuts/deviceside/backup/publisher1/Android.mk b/hostsidetests/shortcuts/deviceside/backup/publisher1/Android.mk
index 72da903..ac91c82 100644
--- a/hostsidetests/shortcuts/deviceside/backup/publisher1/Android.mk
+++ b/hostsidetests/shortcuts/deviceside/backup/publisher1/Android.mk
@@ -37,6 +37,8 @@
     ub-uiautomator \
     ShortcutManagerTestUtils
 
+LOCAL_JAVA_LIBRARIES := android.test.base.stubs
+
 LOCAL_SDK_VERSION := current
 
 include $(BUILD_CTS_PACKAGE)
diff --git a/hostsidetests/shortcuts/deviceside/backup/publisher1/AndroidManifest.xml b/hostsidetests/shortcuts/deviceside/backup/publisher1/AndroidManifest.xml
index 3d23c72..f35fcef 100644
--- a/hostsidetests/shortcuts/deviceside/backup/publisher1/AndroidManifest.xml
+++ b/hostsidetests/shortcuts/deviceside/backup/publisher1/AndroidManifest.xml
@@ -18,6 +18,8 @@
     package="android.content.pm.cts.shortcut.backup.publisher1">
 
     <application>
+        <uses-library android:name="android.test.runner" />
+
         <activity android:name="MainActivity" android:exported="true" >
             <intent-filter>
                 <action android:name="android.intent.action.MAIN" />
diff --git a/hostsidetests/shortcuts/deviceside/backup/publisher2/Android.mk b/hostsidetests/shortcuts/deviceside/backup/publisher2/Android.mk
index 286dded..2bb7f06 100644
--- a/hostsidetests/shortcuts/deviceside/backup/publisher2/Android.mk
+++ b/hostsidetests/shortcuts/deviceside/backup/publisher2/Android.mk
@@ -37,6 +37,8 @@
     ub-uiautomator \
     ShortcutManagerTestUtils
 
+LOCAL_JAVA_LIBRARIES := android.test.base.stubs
+
 LOCAL_SDK_VERSION := current
 
 include $(BUILD_CTS_PACKAGE)
diff --git a/hostsidetests/shortcuts/deviceside/backup/publisher2/AndroidManifest.xml b/hostsidetests/shortcuts/deviceside/backup/publisher2/AndroidManifest.xml
index c3271b4..e4c5720 100644
--- a/hostsidetests/shortcuts/deviceside/backup/publisher2/AndroidManifest.xml
+++ b/hostsidetests/shortcuts/deviceside/backup/publisher2/AndroidManifest.xml
@@ -18,6 +18,8 @@
     package="android.content.pm.cts.shortcut.backup.publisher2">
 
     <application>
+        <uses-library android:name="android.test.runner" />
+
         <activity android:name="MainActivity" android:exported="true" >
             <intent-filter>
                 <action android:name="android.intent.action.MAIN" />
diff --git a/hostsidetests/shortcuts/deviceside/backup/publisher3/Android.mk b/hostsidetests/shortcuts/deviceside/backup/publisher3/Android.mk
index f1421d1..9a5f530 100644
--- a/hostsidetests/shortcuts/deviceside/backup/publisher3/Android.mk
+++ b/hostsidetests/shortcuts/deviceside/backup/publisher3/Android.mk
@@ -37,6 +37,8 @@
     ub-uiautomator \
     ShortcutManagerTestUtils
 
+LOCAL_JAVA_LIBRARIES := android.test.base.stubs
+
 LOCAL_SDK_VERSION := current
 
 include $(BUILD_CTS_PACKAGE)
diff --git a/hostsidetests/shortcuts/deviceside/backup/publisher3/AndroidManifest.xml b/hostsidetests/shortcuts/deviceside/backup/publisher3/AndroidManifest.xml
index 5e03ff8..9f47f2a 100644
--- a/hostsidetests/shortcuts/deviceside/backup/publisher3/AndroidManifest.xml
+++ b/hostsidetests/shortcuts/deviceside/backup/publisher3/AndroidManifest.xml
@@ -18,6 +18,8 @@
     package="android.content.pm.cts.shortcut.backup.publisher3">
 
     <application android:allowBackup="false">
+        <uses-library android:name="android.test.runner" />
+
         <activity android:name="MainActivity" android:exported="true" >
             <intent-filter>
                 <action android:name="android.intent.action.MAIN" />
diff --git a/hostsidetests/shortcuts/deviceside/backup/publisher3/src/android/content/pm/cts/shortcut/backup/publisher3/ShortcutManagerPostBackupTest.java b/hostsidetests/shortcuts/deviceside/backup/publisher3/src/android/content/pm/cts/shortcut/backup/publisher3/ShortcutManagerPostBackupTest.java
index 870dab9..33c8f82 100644
--- a/hostsidetests/shortcuts/deviceside/backup/publisher3/src/android/content/pm/cts/shortcut/backup/publisher3/ShortcutManagerPostBackupTest.java
+++ b/hostsidetests/shortcuts/deviceside/backup/publisher3/src/android/content/pm/cts/shortcut/backup/publisher3/ShortcutManagerPostBackupTest.java
@@ -21,16 +21,17 @@
 
 public class ShortcutManagerPostBackupTest extends ShortcutManagerDeviceTestBase {
     public void testWithUninstall() {
+        // backup = false, so dynamic shouldn't be restored.
         assertWith(getManager().getDynamicShortcuts())
                 .isEmpty();
 
-        // backup = false, so no pinned shortcuts should be restored.
+        // But manifest shortcuts will be restored anyway, so they'll restored as pinned.
         assertWith(getManager().getPinnedShortcuts())
-                .isEmpty();
+                .haveIds("ms1", "ms2");
 
         assertWith(getManager().getManifestShortcuts())
                 .haveIds("ms1", "ms2")
-                .areAllNotPinned();
+                .areAllPinned();
     }
 
     public void testWithNoUninstall() {
@@ -39,8 +40,8 @@
                 .haveIds("s1", "s2", "s3")
                 .areAllNotPinned();
 
+        // Manifest shortcuts should still be published.
         assertWith(getManager().getManifestShortcuts())
-                .haveIds("ms1", "ms2")
-                .areAllNotPinned();
+                .haveIds("ms1", "ms2");
     }
 }
diff --git a/hostsidetests/shortcuts/deviceside/backup/publisher4new/Android.mk b/hostsidetests/shortcuts/deviceside/backup/publisher4new/Android.mk
new file mode 100644
index 0000000..71a545c
--- /dev/null
+++ b/hostsidetests/shortcuts/deviceside/backup/publisher4new/Android.mk
@@ -0,0 +1,46 @@
+# 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)
+
+# Tag this module as a cts test artifact
+LOCAL_COMPATIBILITY_SUITE := cts vts general-tests
+
+LOCAL_PACKAGE_NAME := CtsShortcutBackupPublisher4new
+
+LOCAL_MODULE_TAGS := optional
+
+LOCAL_MODULE_PATH := $(TARGET_OUT_DATA_APPS)
+
+LOCAL_SRC_FILES := $(call all-java-files-under, ../publisher4old/src) \
+    $(call all-java-files-under, ../../common/src)
+
+LOCAL_RESOURCE_DIR := $(LOCAL_PATH)/../publisher4old/res
+
+LOCAL_STATIC_JAVA_LIBRARIES := \
+    android-support-test \
+    android-support-v4 \
+    mockito-target-minus-junit4 \
+    compatibility-device-util \
+    ctstestrunner \
+    ub-uiautomator \
+    ShortcutManagerTestUtils
+
+LOCAL_JAVA_LIBRARIES := android.test.base.stubs
+
+LOCAL_SDK_VERSION := current
+
+include $(BUILD_CTS_PACKAGE)
diff --git a/hostsidetests/shortcuts/deviceside/backup/publisher4new/AndroidManifest.xml b/hostsidetests/shortcuts/deviceside/backup/publisher4new/AndroidManifest.xml
new file mode 100644
index 0000000..c671d95
--- /dev/null
+++ b/hostsidetests/shortcuts/deviceside/backup/publisher4new/AndroidManifest.xml
@@ -0,0 +1,47 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ -->
+<manifest xmlns:android="http://schemas.android.com/apk/res/android"
+    package="android.content.pm.cts.shortcut.backup.publisher4"
+    android:versionCode="11">
+
+    <application>
+        <uses-library android:name="android.test.runner" />
+
+        <activity android:name="MainActivity" android:exported="true" >
+            <intent-filter>
+                <action android:name="android.intent.action.MAIN" />
+                <category android:name="android.intent.category.DEFAULT" />
+                <category android:name="android.intent.category.LAUNCHER" />
+            </intent-filter>
+            <meta-data android:name="android.app.shortcuts" android:resource="@xml/shortcuts"/>
+        </activity>
+        <activity-alias android:name="MainActivity2"
+            android:targetActivity="android.content.pm.cts.shortcut.backup.publisher4.MainActivity"
+            android:exported="true" >
+            <intent-filter>
+                <action android:name="android.intent.action.MAIN" />
+                <category android:name="android.intent.category.DEFAULT" />
+                <category android:name="android.intent.category.LAUNCHER" />
+            </intent-filter>
+        </activity-alias>
+    </application>
+
+    <instrumentation
+        android:name="android.support.test.runner.AndroidJUnitRunner"
+        android:targetPackage="android.content.pm.cts.shortcut.backup.publisher4"  />
+</manifest>
+
diff --git a/hostsidetests/shortcuts/deviceside/backup/publisher4new_nobackup/Android.mk b/hostsidetests/shortcuts/deviceside/backup/publisher4new_nobackup/Android.mk
new file mode 100644
index 0000000..1e35a01
--- /dev/null
+++ b/hostsidetests/shortcuts/deviceside/backup/publisher4new_nobackup/Android.mk
@@ -0,0 +1,46 @@
+# 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)
+
+# Tag this module as a cts test artifact
+LOCAL_COMPATIBILITY_SUITE := cts vts general-tests
+
+LOCAL_PACKAGE_NAME := CtsShortcutBackupPublisher4new_nobackup
+
+LOCAL_MODULE_TAGS := optional
+
+LOCAL_MODULE_PATH := $(TARGET_OUT_DATA_APPS)
+
+LOCAL_SRC_FILES := $(call all-java-files-under, ../publisher4old/src) \
+    $(call all-java-files-under, ../../common/src)
+
+LOCAL_RESOURCE_DIR := $(LOCAL_PATH)/../publisher4old/res
+
+LOCAL_STATIC_JAVA_LIBRARIES := \
+    android-support-test \
+    android-support-v4 \
+    mockito-target-minus-junit4 \
+    compatibility-device-util \
+    ctstestrunner \
+    ub-uiautomator \
+    ShortcutManagerTestUtils
+
+LOCAL_JAVA_LIBRARIES := android.test.base.stubs
+
+LOCAL_SDK_VERSION := current
+
+include $(BUILD_CTS_PACKAGE)
diff --git a/hostsidetests/shortcuts/deviceside/backup/publisher4new_nobackup/AndroidManifest.xml b/hostsidetests/shortcuts/deviceside/backup/publisher4new_nobackup/AndroidManifest.xml
new file mode 100644
index 0000000..e176c81
--- /dev/null
+++ b/hostsidetests/shortcuts/deviceside/backup/publisher4new_nobackup/AndroidManifest.xml
@@ -0,0 +1,47 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ -->
+<manifest xmlns:android="http://schemas.android.com/apk/res/android"
+    package="android.content.pm.cts.shortcut.backup.publisher4"
+    android:versionCode="11">
+
+    <application android:allowBackup="false">
+        <uses-library android:name="android.test.runner" />
+
+        <activity android:name="MainActivity" android:exported="true" >
+            <intent-filter>
+                <action android:name="android.intent.action.MAIN" />
+                <category android:name="android.intent.category.DEFAULT" />
+                <category android:name="android.intent.category.LAUNCHER" />
+            </intent-filter>
+            <meta-data android:name="android.app.shortcuts" android:resource="@xml/shortcuts"/>
+        </activity>
+        <activity-alias android:name="MainActivity2"
+            android:targetActivity="android.content.pm.cts.shortcut.backup.publisher4.MainActivity"
+            android:exported="true" >
+            <intent-filter>
+                <action android:name="android.intent.action.MAIN" />
+                <category android:name="android.intent.category.DEFAULT" />
+                <category android:name="android.intent.category.LAUNCHER" />
+            </intent-filter>
+        </activity-alias>
+    </application>
+
+    <instrumentation
+        android:name="android.support.test.runner.AndroidJUnitRunner"
+        android:targetPackage="android.content.pm.cts.shortcut.backup.publisher4"  />
+</manifest>
+
diff --git a/hostsidetests/shortcuts/deviceside/backup/publisher4new_nomanifest/Android.mk b/hostsidetests/shortcuts/deviceside/backup/publisher4new_nomanifest/Android.mk
new file mode 100644
index 0000000..321377e
--- /dev/null
+++ b/hostsidetests/shortcuts/deviceside/backup/publisher4new_nomanifest/Android.mk
@@ -0,0 +1,46 @@
+# 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)
+
+# Tag this module as a cts test artifact
+LOCAL_COMPATIBILITY_SUITE := cts vts general-tests
+
+LOCAL_PACKAGE_NAME := CtsShortcutBackupPublisher4new_nomanifest
+
+LOCAL_MODULE_TAGS := optional
+
+LOCAL_MODULE_PATH := $(TARGET_OUT_DATA_APPS)
+
+LOCAL_SRC_FILES := $(call all-java-files-under, ../publisher4old/src) \
+    $(call all-java-files-under, ../../common/src)
+
+LOCAL_RESOURCE_DIR := $(LOCAL_PATH)/../publisher4old/res
+
+LOCAL_STATIC_JAVA_LIBRARIES := \
+    android-support-test \
+    android-support-v4 \
+    mockito-target-minus-junit4 \
+    compatibility-device-util \
+    ctstestrunner \
+    ub-uiautomator \
+    ShortcutManagerTestUtils
+
+LOCAL_JAVA_LIBRARIES := android.test.base.stubs
+
+LOCAL_SDK_VERSION := current
+
+include $(BUILD_CTS_PACKAGE)
diff --git a/hostsidetests/shortcuts/deviceside/backup/publisher4new_nomanifest/AndroidManifest.xml b/hostsidetests/shortcuts/deviceside/backup/publisher4new_nomanifest/AndroidManifest.xml
new file mode 100644
index 0000000..a08611a
--- /dev/null
+++ b/hostsidetests/shortcuts/deviceside/backup/publisher4new_nomanifest/AndroidManifest.xml
@@ -0,0 +1,37 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ -->
+<manifest xmlns:android="http://schemas.android.com/apk/res/android"
+    package="android.content.pm.cts.shortcut.backup.publisher4"
+    android:versionCode="11">
+
+    <application>
+        <uses-library android:name="android.test.runner" />
+
+        <activity android:name="MainActivity" android:exported="true" >
+            <intent-filter>
+                <action android:name="android.intent.action.MAIN" />
+                <category android:name="android.intent.category.DEFAULT" />
+                <category android:name="android.intent.category.LAUNCHER" />
+            </intent-filter>
+        </activity>
+    </application>
+
+    <instrumentation
+        android:name="android.support.test.runner.AndroidJUnitRunner"
+        android:targetPackage="android.content.pm.cts.shortcut.backup.publisher4"  />
+</manifest>
+
diff --git a/hostsidetests/shortcuts/deviceside/backup/publisher4new_wrongkey/Android.mk b/hostsidetests/shortcuts/deviceside/backup/publisher4new_wrongkey/Android.mk
new file mode 100644
index 0000000..76410e8
--- /dev/null
+++ b/hostsidetests/shortcuts/deviceside/backup/publisher4new_wrongkey/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)
+
+# Tag this module as a cts test artifact
+LOCAL_COMPATIBILITY_SUITE := cts vts general-tests
+
+LOCAL_PACKAGE_NAME := CtsShortcutBackupPublisher4new_wrongkey
+
+LOCAL_MODULE_TAGS := optional
+
+LOCAL_MODULE_PATH := $(TARGET_OUT_DATA_APPS)
+
+LOCAL_SRC_FILES := $(call all-java-files-under, ../publisher4old/src) \
+    $(call all-java-files-under, ../../common/src)
+
+LOCAL_RESOURCE_DIR := $(LOCAL_PATH)/../publisher4old/res
+
+LOCAL_STATIC_JAVA_LIBRARIES := \
+    android-support-test \
+    android-support-v4 \
+    mockito-target-minus-junit4 \
+    compatibility-device-util \
+    ctstestrunner \
+    ub-uiautomator \
+    ShortcutManagerTestUtils
+
+LOCAL_JAVA_LIBRARIES := android.test.base.stubs
+
+LOCAL_CERTIFICATE := cts/hostsidetests/appsecurity/certs/cts-testkey1
+
+LOCAL_SDK_VERSION := current
+
+include $(BUILD_CTS_PACKAGE)
diff --git a/hostsidetests/shortcuts/deviceside/backup/publisher4new_wrongkey/AndroidManifest.xml b/hostsidetests/shortcuts/deviceside/backup/publisher4new_wrongkey/AndroidManifest.xml
new file mode 100644
index 0000000..e176c81
--- /dev/null
+++ b/hostsidetests/shortcuts/deviceside/backup/publisher4new_wrongkey/AndroidManifest.xml
@@ -0,0 +1,47 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ -->
+<manifest xmlns:android="http://schemas.android.com/apk/res/android"
+    package="android.content.pm.cts.shortcut.backup.publisher4"
+    android:versionCode="11">
+
+    <application android:allowBackup="false">
+        <uses-library android:name="android.test.runner" />
+
+        <activity android:name="MainActivity" android:exported="true" >
+            <intent-filter>
+                <action android:name="android.intent.action.MAIN" />
+                <category android:name="android.intent.category.DEFAULT" />
+                <category android:name="android.intent.category.LAUNCHER" />
+            </intent-filter>
+            <meta-data android:name="android.app.shortcuts" android:resource="@xml/shortcuts"/>
+        </activity>
+        <activity-alias android:name="MainActivity2"
+            android:targetActivity="android.content.pm.cts.shortcut.backup.publisher4.MainActivity"
+            android:exported="true" >
+            <intent-filter>
+                <action android:name="android.intent.action.MAIN" />
+                <category android:name="android.intent.category.DEFAULT" />
+                <category android:name="android.intent.category.LAUNCHER" />
+            </intent-filter>
+        </activity-alias>
+    </application>
+
+    <instrumentation
+        android:name="android.support.test.runner.AndroidJUnitRunner"
+        android:targetPackage="android.content.pm.cts.shortcut.backup.publisher4"  />
+</manifest>
+
diff --git a/hostsidetests/shortcuts/deviceside/backup/publisher4old/Android.mk b/hostsidetests/shortcuts/deviceside/backup/publisher4old/Android.mk
new file mode 100644
index 0000000..7341d66
--- /dev/null
+++ b/hostsidetests/shortcuts/deviceside/backup/publisher4old/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)
+
+# Tag this module as a cts test artifact
+LOCAL_COMPATIBILITY_SUITE := cts vts general-tests
+
+LOCAL_PACKAGE_NAME := CtsShortcutBackupPublisher4old
+
+LOCAL_MODULE_TAGS := optional
+
+LOCAL_MODULE_PATH := $(TARGET_OUT_DATA_APPS)
+
+LOCAL_SRC_FILES := $(call all-java-files-under, src) \
+    $(call all-java-files-under, ../../common/src)
+
+LOCAL_STATIC_JAVA_LIBRARIES := \
+    android-support-test \
+    android-support-v4 \
+    mockito-target-minus-junit4 \
+    compatibility-device-util \
+    ctstestrunner \
+    ub-uiautomator \
+    ShortcutManagerTestUtils
+
+LOCAL_JAVA_LIBRARIES := android.test.base.stubs
+
+LOCAL_SDK_VERSION := current
+
+include $(BUILD_CTS_PACKAGE)
diff --git a/hostsidetests/shortcuts/deviceside/backup/publisher4old/AndroidManifest.xml b/hostsidetests/shortcuts/deviceside/backup/publisher4old/AndroidManifest.xml
new file mode 100644
index 0000000..0c9f4ab
--- /dev/null
+++ b/hostsidetests/shortcuts/deviceside/backup/publisher4old/AndroidManifest.xml
@@ -0,0 +1,47 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ -->
+<manifest xmlns:android="http://schemas.android.com/apk/res/android"
+    package="android.content.pm.cts.shortcut.backup.publisher4"
+    android:versionCode="10">
+
+    <application>
+        <uses-library android:name="android.test.runner" />
+
+        <activity android:name="MainActivity" android:exported="true" >
+            <intent-filter>
+                <action android:name="android.intent.action.MAIN" />
+                <category android:name="android.intent.category.DEFAULT" />
+                <category android:name="android.intent.category.LAUNCHER" />
+            </intent-filter>
+            <meta-data android:name="android.app.shortcuts" android:resource="@xml/shortcuts"/>
+        </activity>
+        <activity-alias android:name="MainActivity2"
+            android:targetActivity="android.content.pm.cts.shortcut.backup.publisher4.MainActivity"
+            android:exported="true" >
+            <intent-filter>
+                <action android:name="android.intent.action.MAIN" />
+                <category android:name="android.intent.category.DEFAULT" />
+                <category android:name="android.intent.category.LAUNCHER" />
+            </intent-filter>
+        </activity-alias>
+    </application>
+
+    <instrumentation
+        android:name="android.support.test.runner.AndroidJUnitRunner"
+        android:targetPackage="android.content.pm.cts.shortcut.backup.publisher4"  />
+</manifest>
+
diff --git a/hostsidetests/shortcuts/deviceside/backup/publisher4old/res/drawable-nodpi/black_16x16.png b/hostsidetests/shortcuts/deviceside/backup/publisher4old/res/drawable-nodpi/black_16x16.png
new file mode 100644
index 0000000..a26da5c
--- /dev/null
+++ b/hostsidetests/shortcuts/deviceside/backup/publisher4old/res/drawable-nodpi/black_16x16.png
Binary files differ
diff --git a/hostsidetests/shortcuts/deviceside/backup/publisher4old/res/drawable-nodpi/black_16x64.png b/hostsidetests/shortcuts/deviceside/backup/publisher4old/res/drawable-nodpi/black_16x64.png
new file mode 100644
index 0000000..ed049fa
--- /dev/null
+++ b/hostsidetests/shortcuts/deviceside/backup/publisher4old/res/drawable-nodpi/black_16x64.png
Binary files differ
diff --git a/hostsidetests/shortcuts/deviceside/backup/publisher4old/res/drawable-nodpi/black_64x16.png b/hostsidetests/shortcuts/deviceside/backup/publisher4old/res/drawable-nodpi/black_64x16.png
new file mode 100644
index 0000000..a0983c7
--- /dev/null
+++ b/hostsidetests/shortcuts/deviceside/backup/publisher4old/res/drawable-nodpi/black_64x16.png
Binary files differ
diff --git a/hostsidetests/shortcuts/deviceside/backup/publisher4old/res/values/strings.xml b/hostsidetests/shortcuts/deviceside/backup/publisher4old/res/values/strings.xml
new file mode 100644
index 0000000..5872ed1
--- /dev/null
+++ b/hostsidetests/shortcuts/deviceside/backup/publisher4old/res/values/strings.xml
@@ -0,0 +1,37 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2017 The Android Open Source Project
+
+     Licensed under the Apache License, Version 2.0 (the "License");
+     you may not use this file except in compliance with the License.
+     You may obtain a copy of the License at
+
+          http://www.apache.org/licenses/LICENSE-2.0
+
+     Unless required by applicable law or agreed to in writing, software
+     distributed under the License is distributed on an "AS IS" BASIS,
+     WITHOUT 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="label1">Shortcut 1</string>
+    <string name="long_label1">Long shortcut label1</string>
+    <string name="disabled_message1">Shortcut 1 is disabled</string>
+
+    <string name="label2">Shortcut 2</string>
+    <string name="long_label2">Long shortcut label2</string>
+    <string name="disabled_message2">Shortcut 2 is disabled</string>
+
+    <string name="label3">Shortcut 3</string>
+    <string name="long_label3">Long shortcut label3</string>
+    <string name="disabled_message3">Shortcut 3 is disabled</string>
+
+    <string name="label4">Shortcut 4</string>
+    <string name="long_label4">Long shortcut label4</string>
+    <string name="disabled_message4">Shortcut 4 is disabled</string>
+
+    <string name="label5">Shortcut 5</string>
+    <string name="long_label5">Long shortcut label5</string>
+    <string name="disabled_message5">Shortcut 5 is disabled</string>
+</resources>
diff --git a/hostsidetests/shortcuts/deviceside/backup/publisher4old/res/xml/shortcuts.xml b/hostsidetests/shortcuts/deviceside/backup/publisher4old/res/xml/shortcuts.xml
new file mode 100644
index 0000000..11cede6
--- /dev/null
+++ b/hostsidetests/shortcuts/deviceside/backup/publisher4old/res/xml/shortcuts.xml
@@ -0,0 +1,46 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2017 The Android Open Source Project
+
+     Licensed under the Apache License, Version 2.0 (the "License");
+     you may not use this file except in compliance with the License.
+     You may obtain a copy of the License at
+
+          http://www.apache.org/licenses/LICENSE-2.0
+
+     Unless required by applicable law or agreed to in writing, software
+     distributed under the License is distributed on an "AS IS" BASIS,
+     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+     See the License for the specific language governing permissions and
+     limitations under the License.
+-->
+<shortcuts xmlns:android="http://schemas.android.com/apk/res/android" >
+    <shortcut
+        android:shortcutId="ms1"
+        android:icon="@drawable/black_16x16"
+        android:shortcutShortLabel="@string/label1"
+        android:shortcutLongLabel="@string/long_label1"
+        android:shortcutDisabledMessage="@string/disabled_message1">
+        <intent
+            android:action="android.intent.action.VIEW" />
+        <categories android:name="android.shortcut.conversation" />
+        <categories android:name="android.shortcut.media" />
+    </shortcut>
+    <shortcut
+        android:shortcutId="ms2"
+        android:shortcutShortLabel="@string/label2">
+        android:shortcutLongLabel="@string/long_label2">
+        <intent android:action="action" />
+        <intent android:action="action2"
+            android:data="data"
+            android:mimeType="a/b"
+            android:targetPackage="pkg"
+            android:targetClass="pkg.class"
+            >
+            <categories android:name="icat1"/>
+            <categories android:name="icat2"/>
+            <extra android:name="key1" android:value="value1" />
+            <extra android:name="key2" android:value="123" />
+            <extra android:name="key3" android:value="true" />
+        </intent>
+    </shortcut>
+</shortcuts>
diff --git a/hostsidetests/shortcuts/deviceside/backup/publisher4old/src/android/content/pm/cts/shortcut/backup/publisher4/MainActivity.java b/hostsidetests/shortcuts/deviceside/backup/publisher4old/src/android/content/pm/cts/shortcut/backup/publisher4/MainActivity.java
new file mode 100644
index 0000000..62acf9d
--- /dev/null
+++ b/hostsidetests/shortcuts/deviceside/backup/publisher4old/src/android/content/pm/cts/shortcut/backup/publisher4/MainActivity.java
@@ -0,0 +1,21 @@
+/*
+ * 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.content.pm.cts.shortcut.backup.publisher4;
+
+import android.app.Activity;
+
+public class MainActivity extends Activity {
+}
diff --git a/hostsidetests/shortcuts/deviceside/backup/publisher4old/src/android/content/pm/cts/shortcut/backup/publisher4/ShortcutManagerPostBackupTest.java b/hostsidetests/shortcuts/deviceside/backup/publisher4old/src/android/content/pm/cts/shortcut/backup/publisher4/ShortcutManagerPostBackupTest.java
new file mode 100644
index 0000000..224c8ba
--- /dev/null
+++ b/hostsidetests/shortcuts/deviceside/backup/publisher4old/src/android/content/pm/cts/shortcut/backup/publisher4/ShortcutManagerPostBackupTest.java
@@ -0,0 +1,339 @@
+/*
+ * 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.content.pm.cts.shortcut.backup.publisher4;
+
+import static com.android.server.pm.shortcutmanagertest.ShortcutManagerTestUtils.assertWith;
+import static com.android.server.pm.shortcutmanagertest.ShortcutManagerTestUtils.list;
+import static com.android.server.pm.shortcutmanagertest.ShortcutManagerTestUtils.setDefaultLauncher;
+
+import android.app.PendingIntent;
+import android.content.BroadcastReceiver;
+import android.content.Context;
+import android.content.Intent;
+import android.content.IntentFilter;
+import android.content.pm.ShortcutInfo;
+import android.content.pm.cts.shortcut.device.common.ShortcutManagerDeviceTestBase;
+import android.os.PersistableBundle;
+import android.text.TextUtils;
+
+import java.security.SecureRandom;
+import java.util.concurrent.CountDownLatch;
+import java.util.concurrent.TimeUnit;
+
+public class ShortcutManagerPostBackupTest extends ShortcutManagerDeviceTestBase {
+    public void testRestoredOnOldVersion() {
+        assertWith(getManager().getDynamicShortcuts())
+                .isEmpty();
+
+        assertWith(getManager().getPinnedShortcuts())
+                .haveIds("ms1", "ms2")
+                .areAllEnabled();
+
+        assertWith(getManager().getManifestShortcuts())
+                .haveIds("ms1", "ms2")
+                .areAllPinned()
+                .areAllEnabled();
+
+        // At this point, s1 and s2 don't look to exist to the publisher, so it can publish a
+        // dynamic shortcut and that should work.
+        // But updateShortcuts() don't.
+
+        final ShortcutInfo s1 = new ShortcutInfo.Builder(getContext(), "s1")
+                .setShortLabel("shortlabel1_new_one")
+                .setActivity(getActivity("MainActivity"))
+                .setIntents(new Intent[]{new Intent("main")})
+                .build();
+
+        assertTrue(getManager().addDynamicShortcuts(list(s1)));
+
+        final ShortcutInfo s2 = new ShortcutInfo.Builder(getContext(), "s2")
+                .setShortLabel("shortlabel2_updated")
+                .build();
+        assertTrue(getManager().updateShortcuts(list(s2)));
+
+        assertWith(getManager().getDynamicShortcuts())
+                .haveIds("s1") // s2 not in the list.
+                .areAllEnabled();
+
+        assertWith(getManager().getPinnedShortcuts())
+                .haveIds("ms1", "ms2", "s1") // s2 not in the list.
+                .areAllEnabled();
+
+    }
+
+    public void testRestoredOnNewVersion() {
+        assertWith(getManager().getDynamicShortcuts())
+                .haveIds("s1")
+                .areAllEnabled()
+                .areAllPinned();
+
+        assertWith(getManager().getManifestShortcuts())
+                .haveIds("ms1", "ms2")
+                .areAllPinned()
+                .areAllEnabled();
+
+        assertWith(getManager().getPinnedShortcuts())
+                .haveIds("ms1", "ms2", "s1", "s2")
+                .areAllEnabled();
+
+        // This time, update should work.
+        final ShortcutInfo s2 = new ShortcutInfo.Builder(getContext(), "s2")
+                .setShortLabel("shortlabel2_updated")
+                .build();
+        assertTrue(getManager().updateShortcuts(list(s2)));
+
+        assertWith(getManager().getPinnedShortcuts())
+                .selectByIds("s2")
+                .forAllShortcuts(si -> {
+                    assertEquals("shortlabel2_updated", si.getShortLabel());
+                });
+    }
+
+    public void testBackupDisabled() {
+        assertWith(getManager().getDynamicShortcuts())
+                .isEmpty();
+
+        assertWith(getManager().getPinnedShortcuts())
+                .haveIds("ms1", "ms2")
+                .areAllEnabled();
+
+        assertWith(getManager().getManifestShortcuts())
+                .haveIds("ms1", "ms2")
+                .areAllPinned()
+                .areAllEnabled();
+
+        // Backup was disabled, so either s1 nor s2 look to be restored to the app.
+        // So when the app publishes s1, it'll be published as new.
+        // But it'll still be pinned.
+        final ShortcutInfo s1 = new ShortcutInfo.Builder(getContext(), "s1")
+                .setShortLabel("shortlabel1_new_one")
+                .setActivity(getActivity("MainActivity"))
+                .setIntents(new Intent[]{new Intent("main")})
+                .build();
+
+        assertTrue(getManager().addDynamicShortcuts(list(s1)));
+
+        assertWith(getManager().getDynamicShortcuts())
+                .haveIds("s1") // s2 not in the list.
+                .areAllPinned()
+                .areAllEnabled()
+                .forAllShortcuts(si -> {
+                    // The app re-published the shortcut, not updated, so the fields that existed
+                    // in the original shortcut is gone now.
+                    assertNull(si.getExtras());
+                });
+    }
+
+    public void testRestoreWrongKey() {
+        // Restored pinned shortcuts are from a package with a different signature, so the dynamic
+        // pinned shortcuts should be disabled-invisible.
+
+        assertWith(getManager().getDynamicShortcuts())
+                .isEmpty();
+
+        assertWith(getManager().getPinnedShortcuts())
+                .haveIds("ms1", "ms2");
+
+        assertWith(getManager().getManifestShortcuts())
+                .haveIds("ms1", "ms2")
+                .areAllPinned()
+                .areAllEnabled();
+
+        final ShortcutInfo s1 = new ShortcutInfo.Builder(getContext(), "s1")
+                .setShortLabel("shortlabel1_new_one")
+                .setActivity(getActivity("MainActivity"))
+                .setIntents(new Intent[]{new Intent("main")})
+                .build();
+
+        assertTrue(getManager().addDynamicShortcuts(list(s1)));
+
+        final ShortcutInfo s2 = new ShortcutInfo.Builder(getContext(), "s2")
+                .setShortLabel("shortlabel2_updated")
+                .build();
+        assertTrue(getManager().updateShortcuts(list(s2)));
+
+        assertWith(getManager().getDynamicShortcuts())
+                .haveIds("s1") // s2 not in the list.
+                .areAllEnabled();
+    }
+
+    /**
+     * Restored on an older version that have no manifest shortcuts.
+     *
+     * In this case, the publisher wouldn't see the manifest shortcuts, and they're overwritable.
+     */
+    public void testRestoreNoManifestOnOldVersion() {
+        assertWith(getManager().getManifestShortcuts())
+                .isEmpty();
+
+        assertWith(getManager().getDynamicShortcuts())
+                .isEmpty();
+
+        assertWith(getManager().getPinnedShortcuts())
+                .isEmpty();
+
+        // ms1 was manifest/immutable, but can be overwritten.
+        final ShortcutInfo ms1 = new ShortcutInfo.Builder(getContext(), "ms1")
+                .setShortLabel("ms1_new_one")
+                .setActivity(getActivity("MainActivity"))
+                .setIntents(new Intent[]{new Intent("main")})
+                .build();
+
+        assertTrue(getManager().setDynamicShortcuts(list(ms1)));
+
+        assertWith(getManager().getDynamicShortcuts())
+                .haveIds("ms1");
+        assertWith(getManager().getPinnedShortcuts())
+                .haveIds("ms1");
+
+        // Adding s1 should also work.
+        final ShortcutInfo s1 = new ShortcutInfo.Builder(getContext(), "s1")
+                .setShortLabel("s1_new_one")
+                .setActivity(getActivity("MainActivity"))
+                .setIntents(new Intent[]{new Intent("main")})
+                .build();
+
+        assertTrue(getManager().addDynamicShortcuts(list(s1)));
+
+        assertWith(getManager().getDynamicShortcuts())
+                .haveIds("s1", "ms1");
+        assertWith(getManager().getPinnedShortcuts())
+                .haveIds("s1", "ms1");
+
+        // Update on ms2 should be no-op.
+        final ShortcutInfo ms2 = new ShortcutInfo.Builder(getContext(), "ms2")
+                .setShortLabel("ms2-updated")
+                .build();
+        assertTrue(getManager().updateShortcuts(list(ms2)));
+
+        assertWith(getManager().getManifestShortcuts())
+                .isEmpty();
+        assertWith(getManager().getDynamicShortcuts())
+                .haveIds("s1", "ms1")
+                .areAllEnabled()
+                .areAllPinned()
+                .areAllMutable()
+
+                .selectByIds("s1")
+                .forAllShortcuts(si -> {
+                    assertEquals("s1_new_one", si.getShortLabel());
+                })
+
+                .revertToOriginalList()
+                .selectByIds("ms1")
+                .forAllShortcuts(si -> {
+                    assertEquals("ms1_new_one", si.getShortLabel());
+                });
+    }
+
+    /**
+     * Restored on the same (or newer) version that have no manifest shortcuts.
+     *
+     * In this case, the publisher will see the manifest shortcuts (as immutable pinned),
+     * and they *cannot* be overwritten.
+     */
+    public void testRestoreNoManifestOnNewVersion() {
+        assertWith(getManager().getManifestShortcuts())
+                .isEmpty();
+
+        assertWith(getManager().getDynamicShortcuts())
+                .isEmpty();
+
+        assertWith(getManager().getPinnedShortcuts())
+                .haveIds("ms1", "ms2", "s1", "s2")
+                .selectByIds("ms1", "ms2")
+                .areAllDisabled()
+
+                .revertToOriginalList()
+                .selectByIds("s1", "s2")
+                .areAllEnabled();
+    }
+
+    private void assertNoShortcuts() {
+        assertWith(getManager().getDynamicShortcuts()).isEmpty();
+        assertWith(getManager().getPinnedShortcuts()).isEmpty();
+        assertWith(getManager().getManifestShortcuts()).isEmpty();
+    }
+
+    public void testInvisibleIgnored() throws Exception {
+        assertNoShortcuts();
+
+        // Make sure "disable" won't change the disabled reason. Also make sure "enable" won't
+        // enable them.
+        getManager().disableShortcuts(list("s1", "s2", "ms1"));
+        assertNoShortcuts();
+
+        getManager().enableShortcuts(list("ms1", "s2"));
+        assertNoShortcuts();
+
+        getManager().enableShortcuts(list("ms1"));
+        assertNoShortcuts();
+
+        getManager().removeDynamicShortcuts(list("s1", "ms1"));
+        assertNoShortcuts();
+
+        getManager().removeAllDynamicShortcuts();
+        assertNoShortcuts();
+
+
+        // Force launcher 4 to be the default launcher so it'll receive the pin request.
+        setDefaultLauncher(getInstrumentation(),
+                "android.content.pm.cts.shortcut.backup.launcher4/.MainActivity");
+
+        // Update, set and add have been tested already, so let's test "pin".
+
+        final CountDownLatch latch = new CountDownLatch(1);
+
+        PersistableBundle pb = new PersistableBundle();
+        pb.putBoolean("acceptit", true);
+
+        final ShortcutInfo ms2 = new ShortcutInfo.Builder(getContext(), "ms2")
+                .setShortLabel("ms2_new_one")
+                .setActivity(getActivity("MainActivity"))
+                .setIntents(new Intent[]{new Intent("main2")})
+                .setExtras(pb)
+                .build();
+
+        final String myIntentAction = "cts-shortcut-intent_" + new SecureRandom().nextInt();
+        final IntentFilter myFilter = new IntentFilter(myIntentAction);
+
+        final BroadcastReceiver onResult = new BroadcastReceiver() {
+            @Override
+            public void onReceive(Context context, Intent intent) {
+                latch.countDown();
+            }
+        };
+        getContext().registerReceiver(onResult, myFilter);
+        assertTrue(getManager().requestPinShortcut(ms2,
+                PendingIntent.getBroadcast(getContext(), 0, new Intent(myIntentAction),
+                        PendingIntent.FLAG_CANCEL_CURRENT).getIntentSender()));
+
+        assertTrue("Didn't receive requestPinShortcut() callback.",
+                latch.await(30, TimeUnit.SECONDS));
+
+        assertWith(getManager().getPinnedShortcuts())
+                .haveIds("ms2")
+                .areAllNotDynamic()
+                .areAllNotManifest()
+                .areAllMutable()
+                .areAllPinned()
+                .forAllShortcuts(si -> {
+                    // requestPinShortcut() acts as an update in this case, so even though
+                    // the original shortcut hada  long label, this one does not.
+                    assertTrue(TextUtils.isEmpty(si.getLongLabel()));
+                });
+    }
+}
diff --git a/hostsidetests/shortcuts/deviceside/backup/publisher4old/src/android/content/pm/cts/shortcut/backup/publisher4/ShortcutManagerPreBackupTest.java b/hostsidetests/shortcuts/deviceside/backup/publisher4old/src/android/content/pm/cts/shortcut/backup/publisher4/ShortcutManagerPreBackupTest.java
new file mode 100644
index 0000000..15b3853
--- /dev/null
+++ b/hostsidetests/shortcuts/deviceside/backup/publisher4old/src/android/content/pm/cts/shortcut/backup/publisher4/ShortcutManagerPreBackupTest.java
@@ -0,0 +1,71 @@
+/*
+ * 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.content.pm.cts.shortcut.backup.publisher4;
+
+import static com.android.server.pm.shortcutmanagertest.ShortcutManagerTestUtils.assertWith;
+import static com.android.server.pm.shortcutmanagertest.ShortcutManagerTestUtils.list;
+import static com.android.server.pm.shortcutmanagertest.ShortcutManagerTestUtils.makePersistableBundle;
+import static com.android.server.pm.shortcutmanagertest.ShortcutManagerTestUtils.set;
+
+import android.content.Intent;
+import android.content.pm.ShortcutInfo;
+import android.content.pm.cts.shortcut.device.common.ShortcutManagerDeviceTestBase;
+import android.graphics.BitmapFactory;
+import android.graphics.drawable.Icon;
+import android.test.suitebuilder.annotation.SmallTest;
+
+@SmallTest
+public class ShortcutManagerPreBackupTest extends ShortcutManagerDeviceTestBase {
+    public void testPreBackup() {
+        final Icon icon1 = Icon.createWithBitmap(BitmapFactory.decodeResource(
+                getContext().getResources(), R.drawable.black_16x64));
+        final Icon icon3 = Icon.createWithResource(getContext(), R.drawable.black_64x16);
+
+        final ShortcutInfo s1 = new ShortcutInfo.Builder(getContext(), "s1")
+                .setShortLabel("shortlabel1")
+                .setLongLabel("longlabel1")
+                .setIcon(icon1)
+                .setActivity(getActivity("MainActivity"))
+                .setDisabledMessage("disabledmessage1")
+                .setIntents(new Intent[]{new Intent("view").putExtra("k1", "v1")})
+                .setExtras(makePersistableBundle("ek1", "ev1"))
+                .setCategories(set("cat1"))
+                .build();
+
+        final ShortcutInfo s2 = new ShortcutInfo.Builder(getContext(), "s2")
+                .setShortLabel("shortlabel2")
+                .setActivity(getActivity("MainActivity2"))
+                .setIntents(new Intent[]{new Intent("main")})
+                .build();
+
+        final ShortcutInfo s3 = new ShortcutInfo.Builder(getContext(), "s3")
+                .setShortLabel("shortlabel3")
+                .setIcon(icon3)
+                .setActivity(getActivity("MainActivity2"))
+                .setIntents(new Intent[]{new Intent("main")})
+                .build();
+
+        assertTrue(getManager().setDynamicShortcuts(list(s1, s2, s3)));
+
+        assertWith(getManager().getDynamicShortcuts())
+                .haveIds("s1", "s2", "s3")
+                .areAllNotPinned();
+
+        assertWith(getManager().getManifestShortcuts())
+                .haveIds("ms1", "ms2")
+                .areAllNotPinned();
+   }
+}
diff --git a/hostsidetests/shortcuts/deviceside/backup/publisher4old_nomanifest/Android.mk b/hostsidetests/shortcuts/deviceside/backup/publisher4old_nomanifest/Android.mk
new file mode 100644
index 0000000..35e2152
--- /dev/null
+++ b/hostsidetests/shortcuts/deviceside/backup/publisher4old_nomanifest/Android.mk
@@ -0,0 +1,46 @@
+# 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)
+
+# Tag this module as a cts test artifact
+LOCAL_COMPATIBILITY_SUITE := cts vts general-tests
+
+LOCAL_PACKAGE_NAME := CtsShortcutBackupPublisher4old_nomanifest
+
+LOCAL_MODULE_TAGS := optional
+
+LOCAL_MODULE_PATH := $(TARGET_OUT_DATA_APPS)
+
+LOCAL_SRC_FILES := $(call all-java-files-under, ../publisher4old/src) \
+    $(call all-java-files-under, ../../common/src)
+
+LOCAL_RESOURCE_DIR := $(LOCAL_PATH)/../publisher4old/res
+
+LOCAL_STATIC_JAVA_LIBRARIES := \
+    android-support-test \
+    android-support-v4 \
+    mockito-target-minus-junit4 \
+    compatibility-device-util \
+    ctstestrunner \
+    ub-uiautomator \
+    ShortcutManagerTestUtils
+
+LOCAL_JAVA_LIBRARIES := android.test.base.stubs
+
+LOCAL_SDK_VERSION := current
+
+include $(BUILD_CTS_PACKAGE)
diff --git a/hostsidetests/shortcuts/deviceside/backup/publisher4old_nomanifest/AndroidManifest.xml b/hostsidetests/shortcuts/deviceside/backup/publisher4old_nomanifest/AndroidManifest.xml
new file mode 100644
index 0000000..af47b93
--- /dev/null
+++ b/hostsidetests/shortcuts/deviceside/backup/publisher4old_nomanifest/AndroidManifest.xml
@@ -0,0 +1,37 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ -->
+<manifest xmlns:android="http://schemas.android.com/apk/res/android"
+    package="android.content.pm.cts.shortcut.backup.publisher4"
+    android:versionCode="10">
+
+    <application>
+        <uses-library android:name="android.test.runner" />
+
+        <activity android:name="MainActivity" android:exported="true" >
+            <intent-filter>
+                <action android:name="android.intent.action.MAIN" />
+                <category android:name="android.intent.category.DEFAULT" />
+                <category android:name="android.intent.category.LAUNCHER" />
+            </intent-filter>
+        </activity>
+    </application>
+
+    <instrumentation
+        android:name="android.support.test.runner.AndroidJUnitRunner"
+        android:targetPackage="android.content.pm.cts.shortcut.backup.publisher4"  />
+</manifest>
+
diff --git a/hostsidetests/shortcuts/deviceside/multiuser/Android.mk b/hostsidetests/shortcuts/deviceside/multiuser/Android.mk
index 619bdfe..dc1d018 100644
--- a/hostsidetests/shortcuts/deviceside/multiuser/Android.mk
+++ b/hostsidetests/shortcuts/deviceside/multiuser/Android.mk
@@ -39,6 +39,8 @@
     ub-uiautomator \
     ShortcutManagerTestUtils
 
+LOCAL_JAVA_LIBRARIES := android.test.base.stubs
+
 LOCAL_SDK_VERSION := current
 
 include $(BUILD_CTS_PACKAGE)
diff --git a/hostsidetests/shortcuts/deviceside/multiuser/AndroidManifest.xml b/hostsidetests/shortcuts/deviceside/multiuser/AndroidManifest.xml
index 3fbed5f..886aded 100644
--- a/hostsidetests/shortcuts/deviceside/multiuser/AndroidManifest.xml
+++ b/hostsidetests/shortcuts/deviceside/multiuser/AndroidManifest.xml
@@ -18,6 +18,8 @@
     package="android.content.pm.cts.shortcut.multiuser">
 
     <application>
+        <uses-library android:name="android.test.runner" />
+
         <activity android:name="Launcher" android:exported="true" >
             <intent-filter>
                 <action android:name="android.intent.action.MAIN" />
diff --git a/hostsidetests/shortcuts/deviceside/upgrade/Android.mk b/hostsidetests/shortcuts/deviceside/upgrade/Android.mk
index ae0bf75..d7e13b6 100644
--- a/hostsidetests/shortcuts/deviceside/upgrade/Android.mk
+++ b/hostsidetests/shortcuts/deviceside/upgrade/Android.mk
@@ -43,6 +43,8 @@
     ub-uiautomator \
     ShortcutManagerTestUtils
 
+LOCAL_JAVA_LIBRARIES := android.test.base.stubs
+
 LOCAL_SDK_VERSION := current
 
 include $(BUILD_CTS_PACKAGE)
@@ -74,6 +76,8 @@
     ub-uiautomator \
     ShortcutManagerTestUtils
 
+LOCAL_JAVA_LIBRARIES := android.test.base.stubs
+
 LOCAL_SDK_VERSION := current
 
 include $(BUILD_CTS_PACKAGE)
diff --git a/hostsidetests/shortcuts/deviceside/upgrade/AndroidManifest.xml b/hostsidetests/shortcuts/deviceside/upgrade/AndroidManifest.xml
index 1b88d5e..72d7dfc 100644
--- a/hostsidetests/shortcuts/deviceside/upgrade/AndroidManifest.xml
+++ b/hostsidetests/shortcuts/deviceside/upgrade/AndroidManifest.xml
@@ -18,6 +18,8 @@
     package="android.content.pm.cts.shortcut.upgrade">
 
     <application>
+        <uses-library android:name="android.test.runner" />
+
         <activity android:name="Launcher" android:exported="true" >
             <intent-filter>
                 <action android:name="android.intent.action.MAIN" />
diff --git a/hostsidetests/shortcuts/hostside/AndroidTest.xml b/hostsidetests/shortcuts/hostside/AndroidTest.xml
index 8b53b2e..b060940 100644
--- a/hostsidetests/shortcuts/hostside/AndroidTest.xml
+++ b/hostsidetests/shortcuts/hostside/AndroidTest.xml
@@ -14,6 +14,7 @@
      limitations under the License.
 -->
 <configuration description="Config for the CTS ShortcutManager host tests">
+    <option name="test-suite-tag" value="cts" />
     <option name="config-descriptor:metadata" key="component" value="framework" />
     <target_preparer class="android.cts.backup.BackupPreparer">
         <option name="enable-backup-if-needed" value="true" />
diff --git a/hostsidetests/shortcuts/hostside/src/android/content/pm/cts/shortcuthost/BaseShortcutManagerHostTest.java b/hostsidetests/shortcuts/hostside/src/android/content/pm/cts/shortcuthost/BaseShortcutManagerHostTest.java
index c703e7f..9b8d98a 100644
--- a/hostsidetests/shortcuts/hostside/src/android/content/pm/cts/shortcuthost/BaseShortcutManagerHostTest.java
+++ b/hostsidetests/shortcuts/hostside/src/android/content/pm/cts/shortcuthost/BaseShortcutManagerHostTest.java
@@ -41,6 +41,8 @@
 abstract public class BaseShortcutManagerHostTest extends DeviceTestCase implements IBuildReceiver {
     protected static final boolean DUMPSYS_IN_TEARDOWN = false; // DO NOT SUBMIT WITH TRUE
 
+    protected static final boolean NO_UNINSTALL_IN_TEARDOWN = false; // DO NOT SUBMIT WITH TRUE
+
     private static final String RUNNER = "android.support.test.runner.AndroidJUnitRunner";
 
     private IBuildInfo mCtsBuild;
diff --git a/hostsidetests/shortcuts/hostside/src/android/content/pm/cts/shortcuthost/ShortcutManagerBackupTest.java b/hostsidetests/shortcuts/hostside/src/android/content/pm/cts/shortcuthost/ShortcutManagerBackupTest.java
index 960b41f..3caacad 100644
--- a/hostsidetests/shortcuts/hostside/src/android/content/pm/cts/shortcuthost/ShortcutManagerBackupTest.java
+++ b/hostsidetests/shortcuts/hostside/src/android/content/pm/cts/shortcuthost/ShortcutManagerBackupTest.java
@@ -26,9 +26,22 @@
     private static final String LAUNCHER1_APK = "CtsShortcutBackupLauncher1.apk";
     private static final String LAUNCHER2_APK = "CtsShortcutBackupLauncher2.apk";
     private static final String LAUNCHER3_APK = "CtsShortcutBackupLauncher3.apk";
+    private static final String LAUNCHER4_OLD_APK = "CtsShortcutBackupLauncher4old.apk";
+    private static final String LAUNCHER4_NEW_APK = "CtsShortcutBackupLauncher4new.apk";
+
     private static final String PUBLISHER1_APK = "CtsShortcutBackupPublisher1.apk";
     private static final String PUBLISHER2_APK = "CtsShortcutBackupPublisher2.apk";
     private static final String PUBLISHER3_APK = "CtsShortcutBackupPublisher3.apk";
+    private static final String PUBLISHER4_OLD_APK = "CtsShortcutBackupPublisher4old.apk";
+    private static final String PUBLISHER4_NEW_APK = "CtsShortcutBackupPublisher4new.apk";
+    private static final String PUBLISHER4_NEW_NOBACKUP_APK
+            = "CtsShortcutBackupPublisher4new_nobackup.apk";
+    private static final String PUBLISHER4_NEW_WRONGKEY_APK
+            = "CtsShortcutBackupPublisher4new_wrongkey.apk";
+    private static final String PUBLISHER4_OLD_NO_MANIFST_APK
+            = "CtsShortcutBackupPublisher4old_nomanifest.apk";
+    private static final String PUBLISHER4_NEW_NO_MANIFST_APK
+            = "CtsShortcutBackupPublisher4new_nomanifest.apk";
 
     private static final String LAUNCHER1_PKG =
             "android.content.pm.cts.shortcut.backup.launcher1";
@@ -36,12 +49,17 @@
             "android.content.pm.cts.shortcut.backup.launcher2";
     private static final String LAUNCHER3_PKG =
             "android.content.pm.cts.shortcut.backup.launcher3";
+    private static final String LAUNCHER4_PKG =
+            "android.content.pm.cts.shortcut.backup.launcher4";
+
     private static final String PUBLISHER1_PKG =
             "android.content.pm.cts.shortcut.backup.publisher1";
     private static final String PUBLISHER2_PKG =
             "android.content.pm.cts.shortcut.backup.publisher2";
     private static final String PUBLISHER3_PKG =
             "android.content.pm.cts.shortcut.backup.publisher3";
+    private static final String PUBLISHER4_PKG =
+            "android.content.pm.cts.shortcut.backup.publisher4";
 
     private static final int BROADCAST_TIMEOUT_SECONDS = 120;
 
@@ -59,16 +77,22 @@
             clearShortcuts(LAUNCHER1_PKG, getPrimaryUserId());
             clearShortcuts(LAUNCHER2_PKG, getPrimaryUserId());
             clearShortcuts(LAUNCHER3_PKG, getPrimaryUserId());
+            clearShortcuts(LAUNCHER4_PKG, getPrimaryUserId());
+
             clearShortcuts(PUBLISHER1_PKG, getPrimaryUserId());
             clearShortcuts(PUBLISHER2_PKG, getPrimaryUserId());
             clearShortcuts(PUBLISHER3_PKG, getPrimaryUserId());
+            clearShortcuts(PUBLISHER4_PKG, getPrimaryUserId());
 
             uninstallPackageAndWaitUntilBroadcastsDrain(LAUNCHER1_PKG);
             uninstallPackageAndWaitUntilBroadcastsDrain(LAUNCHER2_PKG);
             uninstallPackageAndWaitUntilBroadcastsDrain(LAUNCHER3_PKG);
+            uninstallPackageAndWaitUntilBroadcastsDrain(LAUNCHER4_PKG);
+
             uninstallPackageAndWaitUntilBroadcastsDrain(PUBLISHER1_PKG);
             uninstallPackageAndWaitUntilBroadcastsDrain(PUBLISHER2_PKG);
             uninstallPackageAndWaitUntilBroadcastsDrain(PUBLISHER3_PKG);
+            uninstallPackageAndWaitUntilBroadcastsDrain(PUBLISHER4_PKG);
 
             waitUntilPackagesGone();
         }
@@ -80,14 +104,16 @@
             dumpsys("tearDown");
         }
 
-        if (mSupportsBackup) {
+        if (mSupportsBackup && !NO_UNINSTALL_IN_TEARDOWN) {
             getDevice().uninstallPackage(LAUNCHER1_PKG);
             getDevice().uninstallPackage(LAUNCHER2_PKG);
             getDevice().uninstallPackage(LAUNCHER3_PKG);
+            getDevice().uninstallPackage(LAUNCHER4_PKG);
 
             getDevice().uninstallPackage(PUBLISHER1_PKG);
             getDevice().uninstallPackage(PUBLISHER2_PKG);
             getDevice().uninstallPackage(PUBLISHER3_PKG);
+            getDevice().uninstallPackage(PUBLISHER4_PKG);
         }
 
         super.tearDown();
@@ -169,8 +195,8 @@
         CLog.i("Waiting until all packages are removed from shortcut manager...");
 
         final String packages[] = {
-                LAUNCHER1_PKG,  LAUNCHER2_PKG, LAUNCHER3_PKG,
-                PUBLISHER1_PKG, PUBLISHER2_PKG, PUBLISHER3_PKG,
+                LAUNCHER1_PKG,  LAUNCHER2_PKG, LAUNCHER3_PKG, LAUNCHER4_PKG,
+                PUBLISHER1_PKG, PUBLISHER2_PKG, PUBLISHER3_PKG, PUBLISHER4_PKG,
         };
 
         String dumpsys = "";
@@ -184,9 +210,11 @@
             if (dumpsys.contains("Launcher: " + LAUNCHER1_PKG)) continue;
             if (dumpsys.contains("Launcher: " + LAUNCHER2_PKG)) continue;
             if (dumpsys.contains("Launcher: " + LAUNCHER3_PKG)) continue;
+            if (dumpsys.contains("Launcher: " + LAUNCHER4_PKG)) continue;
             if (dumpsys.contains("Package: " + PUBLISHER1_PKG)) continue;
             if (dumpsys.contains("Package: " + PUBLISHER2_PKG)) continue;
             if (dumpsys.contains("Package: " + PUBLISHER3_PKG)) continue;
+            if (dumpsys.contains("Package: " + PUBLISHER4_PKG)) continue;
 
             dumpsys("Shortcut manager handled broadcasts");
 
@@ -298,6 +326,353 @@
                 getPrimaryUserId());
     }
 
+    public void testBackupAndRestore_downgrade() throws Exception {
+        if (!mSupportsBackup) {
+            return;
+        }
+        dumpsys("Test start");
+
+        // First, publish shortcuts from the new version and pin them.
+
+        installAppAsUser(PUBLISHER4_NEW_APK, getPrimaryUserId());
+        installAppAsUser(LAUNCHER4_NEW_APK, getPrimaryUserId());
+
+        runDeviceTestsAsUser(PUBLISHER4_PKG, ".ShortcutManagerPreBackupTest", getPrimaryUserId());
+        runDeviceTestsAsUser(LAUNCHER4_PKG, ".ShortcutManagerPreBackupTest", getPrimaryUserId());
+
+        dumpsys("Before backup");
+
+        // Backup
+        doBackup();
+
+        // Uninstall all apps
+        uninstallPackageAndWaitUntilBroadcastsDrain(LAUNCHER4_PKG);
+        uninstallPackageAndWaitUntilBroadcastsDrain(PUBLISHER4_PKG);
+
+        // Make sure the shortcut service handled all the uninstall broadcasts.
+        waitUntilPackagesGone();
+
+        // Do it one more time just in case...
+        waitUntilBroadcastsDrain();
+
+        // Then restore
+        doRestore();
+
+        dumpsys("After restore");
+
+        // Restore the old version of the app, and the launcher.
+        // (But we don't check the launcher's version, so using old is fine.)
+        installAppAsUser(LAUNCHER4_OLD_APK, getPrimaryUserId());
+        installAppAsUser(PUBLISHER4_OLD_APK, getPrimaryUserId());
+        waitUntilBroadcastsDrain();
+
+        runDeviceTestsAsUser(PUBLISHER4_PKG, ".ShortcutManagerPostBackupTest",
+                "testRestoredOnOldVersion",
+                getPrimaryUserId());
+
+        runDeviceTestsAsUser(LAUNCHER4_PKG, ".ShortcutManagerPostBackupTest",
+                "testRestoredOnOldVersion",
+                getPrimaryUserId());
+
+        // New install the original version. All blocked shortcuts should re-appear.
+        installAppAsUser(PUBLISHER4_NEW_APK, getPrimaryUserId());
+        waitUntilBroadcastsDrain();
+
+        runDeviceTestsAsUser(PUBLISHER4_PKG, ".ShortcutManagerPostBackupTest",
+                "testRestoredOnNewVersion",
+                getPrimaryUserId());
+
+        runDeviceTestsAsUser(LAUNCHER4_PKG, ".ShortcutManagerPostBackupTest",
+                "testRestoredOnNewVersion",
+                getPrimaryUserId());
+
+    }
+
+    public void testBackupAndRestore_backupWasDisabled() throws Exception {
+        if (!mSupportsBackup) {
+            return;
+        }
+        dumpsys("Test start");
+
+        // First, publish shortcuts from "nobackup" version.
+
+        installAppAsUser(PUBLISHER4_NEW_NOBACKUP_APK, getPrimaryUserId());
+        installAppAsUser(LAUNCHER4_NEW_APK, getPrimaryUserId());
+
+        runDeviceTestsAsUser(PUBLISHER4_PKG, ".ShortcutManagerPreBackupTest", getPrimaryUserId());
+        runDeviceTestsAsUser(LAUNCHER4_PKG, ".ShortcutManagerPreBackupTest", getPrimaryUserId());
+
+        dumpsys("Before backup");
+
+        // Backup
+        doBackup();
+
+        // Uninstall all apps
+        uninstallPackageAndWaitUntilBroadcastsDrain(LAUNCHER4_PKG);
+        uninstallPackageAndWaitUntilBroadcastsDrain(PUBLISHER4_PKG);
+
+        // Make sure the shortcut service handled all the uninstall broadcasts.
+        waitUntilPackagesGone();
+
+        // Do it one more time just in case...
+        waitUntilBroadcastsDrain();
+
+        // Then restore
+        doRestore();
+
+        dumpsys("After restore");
+
+        // Install the "backup-ok" version. But restoration is limited.
+        installAppAsUser(LAUNCHER4_NEW_APK, getPrimaryUserId());
+        installAppAsUser(PUBLISHER4_NEW_APK, getPrimaryUserId());
+        waitUntilBroadcastsDrain();
+
+        runDeviceTestsAsUser(PUBLISHER4_PKG, ".ShortcutManagerPostBackupTest",
+                "testBackupDisabled",
+                getPrimaryUserId());
+    }
+
+    public void testBackupAndRestore_backupIsDisabled() throws Exception {
+        if (!mSupportsBackup) {
+            return;
+        }
+        dumpsys("Test start");
+
+        // First, publish shortcuts from backup-ok version.
+
+        installAppAsUser(PUBLISHER4_NEW_APK, getPrimaryUserId());
+        installAppAsUser(LAUNCHER4_NEW_APK, getPrimaryUserId());
+
+        runDeviceTestsAsUser(PUBLISHER4_PKG, ".ShortcutManagerPreBackupTest", getPrimaryUserId());
+        runDeviceTestsAsUser(LAUNCHER4_PKG, ".ShortcutManagerPreBackupTest", getPrimaryUserId());
+
+        dumpsys("Before backup");
+
+        // Backup
+        doBackup();
+
+        // Uninstall all apps
+        uninstallPackageAndWaitUntilBroadcastsDrain(LAUNCHER4_PKG);
+        uninstallPackageAndWaitUntilBroadcastsDrain(PUBLISHER4_PKG);
+
+        // Make sure the shortcut service handled all the uninstall broadcasts.
+        waitUntilPackagesGone();
+
+        // Do it one more time just in case...
+        waitUntilBroadcastsDrain();
+
+        // Then restore
+        doRestore();
+
+        dumpsys("After restore");
+
+        // Install the nobackup version. Restoration is limited.
+        installAppAsUser(LAUNCHER4_NEW_APK, getPrimaryUserId());
+        installAppAsUser(PUBLISHER4_NEW_NOBACKUP_APK, getPrimaryUserId());
+        waitUntilBroadcastsDrain();
+
+        runDeviceTestsAsUser(PUBLISHER4_PKG, ".ShortcutManagerPostBackupTest",
+                "testBackupDisabled",
+                getPrimaryUserId());
+    }
+
+    public void testBackupAndRestore_wrongKey() throws Exception {
+        if (!mSupportsBackup) {
+            return;
+        }
+        dumpsys("Test start");
+
+        // First, publish shortcuts from backup-ok version.
+
+        installAppAsUser(PUBLISHER4_NEW_APK, getPrimaryUserId());
+        installAppAsUser(LAUNCHER4_NEW_APK, getPrimaryUserId());
+
+        runDeviceTestsAsUser(PUBLISHER4_PKG, ".ShortcutManagerPreBackupTest", getPrimaryUserId());
+        runDeviceTestsAsUser(LAUNCHER4_PKG, ".ShortcutManagerPreBackupTest", getPrimaryUserId());
+
+        dumpsys("Before backup");
+
+        // Backup
+        doBackup();
+
+        // Uninstall all apps
+        uninstallPackageAndWaitUntilBroadcastsDrain(LAUNCHER4_PKG);
+        uninstallPackageAndWaitUntilBroadcastsDrain(PUBLISHER4_PKG);
+
+        // Make sure the shortcut service handled all the uninstall broadcasts.
+        waitUntilPackagesGone();
+
+        // Do it one more time just in case...
+        waitUntilBroadcastsDrain();
+
+        // Then restore
+        doRestore();
+
+        dumpsys("After restore");
+
+        // Install the nobackup version. Restoration is limited.
+        installAppAsUser(LAUNCHER4_NEW_APK, getPrimaryUserId());
+        installAppAsUser(PUBLISHER4_NEW_WRONGKEY_APK, getPrimaryUserId());
+        waitUntilBroadcastsDrain();
+
+        runDeviceTestsAsUser(PUBLISHER4_PKG, ".ShortcutManagerPostBackupTest",
+                "testRestoreWrongKey",
+                getPrimaryUserId());
+
+        runDeviceTestsAsUser(LAUNCHER4_PKG, ".ShortcutManagerPostBackupTest",
+                "testRestoreWrongKey",
+                getPrimaryUserId());
+    }
+
+    public void testBackupAndRestore_noManifestOnOldVersion() throws Exception {
+        if (!mSupportsBackup) {
+            return;
+        }
+        dumpsys("Test start");
+
+        // First, publish shortcuts from backup-ok version.
+
+        installAppAsUser(PUBLISHER4_NEW_APK, getPrimaryUserId());
+        installAppAsUser(LAUNCHER4_NEW_APK, getPrimaryUserId());
+
+        runDeviceTestsAsUser(PUBLISHER4_PKG, ".ShortcutManagerPreBackupTest", getPrimaryUserId());
+        runDeviceTestsAsUser(LAUNCHER4_PKG, ".ShortcutManagerPreBackupTest", getPrimaryUserId());
+
+        dumpsys("Before backup");
+
+        // Backup
+        doBackup();
+
+        // Uninstall all apps
+        uninstallPackageAndWaitUntilBroadcastsDrain(LAUNCHER4_PKG);
+        uninstallPackageAndWaitUntilBroadcastsDrain(PUBLISHER4_PKG);
+
+        // Make sure the shortcut service handled all the uninstall broadcasts.
+        waitUntilPackagesGone();
+
+        // Do it one more time just in case...
+        waitUntilBroadcastsDrain();
+
+        // Then restore
+        doRestore();
+
+        dumpsys("After restore");
+
+        // Install the nobackup version. Restoration is limited.
+        installAppAsUser(LAUNCHER4_NEW_APK, getPrimaryUserId());
+        installAppAsUser(PUBLISHER4_OLD_NO_MANIFST_APK, getPrimaryUserId());
+        waitUntilBroadcastsDrain();
+
+        runDeviceTestsAsUser(PUBLISHER4_PKG, ".ShortcutManagerPostBackupTest",
+                "testRestoreNoManifestOnOldVersion",
+                getPrimaryUserId());
+
+        runDeviceTestsAsUser(LAUNCHER4_PKG, ".ShortcutManagerPostBackupTest",
+                "testRestoreNoManifestOnOldVersion",
+                getPrimaryUserId());
+    }
+
+    public void testBackupAndRestore_noManifestOnNewVersion() throws Exception {
+        if (!mSupportsBackup) {
+            return;
+        }
+        dumpsys("Test start");
+
+        // First, publish shortcuts from backup-ok version.
+
+        installAppAsUser(PUBLISHER4_NEW_APK, getPrimaryUserId());
+        installAppAsUser(LAUNCHER4_NEW_APK, getPrimaryUserId());
+
+        runDeviceTestsAsUser(PUBLISHER4_PKG, ".ShortcutManagerPreBackupTest", getPrimaryUserId());
+        runDeviceTestsAsUser(LAUNCHER4_PKG, ".ShortcutManagerPreBackupTest", getPrimaryUserId());
+
+        dumpsys("Before backup");
+
+        // Backup
+        doBackup();
+
+        // Uninstall all apps
+        uninstallPackageAndWaitUntilBroadcastsDrain(LAUNCHER4_PKG);
+        uninstallPackageAndWaitUntilBroadcastsDrain(PUBLISHER4_PKG);
+
+        // Make sure the shortcut service handled all the uninstall broadcasts.
+        waitUntilPackagesGone();
+
+        // Do it one more time just in case...
+        waitUntilBroadcastsDrain();
+
+        // Then restore
+        doRestore();
+
+        dumpsys("After restore");
+
+        // Install the nobackup version. Restoration is limited.
+        installAppAsUser(LAUNCHER4_NEW_APK, getPrimaryUserId());
+        installAppAsUser(PUBLISHER4_NEW_NO_MANIFST_APK, getPrimaryUserId());
+        waitUntilBroadcastsDrain();
+
+        runDeviceTestsAsUser(PUBLISHER4_PKG, ".ShortcutManagerPostBackupTest",
+                "testRestoreNoManifestOnNewVersion",
+                getPrimaryUserId());
+
+        runDeviceTestsAsUser(LAUNCHER4_PKG, ".ShortcutManagerPostBackupTest",
+                "testRestoreNoManifestOnNewVersion",
+                getPrimaryUserId());
+    }
+
+    /**
+     * Make sure invisible shortcuts are ignored by all API calls.
+     *
+     * (Restore from new to old-nomanifest)
+     */
+    public void testBackupAndRestore_invisibleIgnored() throws Exception {
+        if (!mSupportsBackup) {
+            return;
+        }
+        dumpsys("Test start");
+
+        // First, publish shortcuts from backup-ok version.
+
+        installAppAsUser(PUBLISHER4_NEW_APK, getPrimaryUserId());
+        installAppAsUser(LAUNCHER4_NEW_APK, getPrimaryUserId());
+
+        runDeviceTestsAsUser(PUBLISHER4_PKG, ".ShortcutManagerPreBackupTest", getPrimaryUserId());
+        runDeviceTestsAsUser(LAUNCHER4_PKG, ".ShortcutManagerPreBackupTest", getPrimaryUserId());
+
+        dumpsys("Before backup");
+
+        // Backup
+        doBackup();
+
+        // Uninstall all apps
+        uninstallPackageAndWaitUntilBroadcastsDrain(LAUNCHER4_PKG);
+        uninstallPackageAndWaitUntilBroadcastsDrain(PUBLISHER4_PKG);
+
+        // Make sure the shortcut service handled all the uninstall broadcasts.
+        waitUntilPackagesGone();
+
+        // Do it one more time just in case...
+        waitUntilBroadcastsDrain();
+
+        // Then restore
+        doRestore();
+
+        dumpsys("After restore");
+
+        // Install the nobackup version. Restoration is limited.
+        installAppAsUser(LAUNCHER4_NEW_APK, getPrimaryUserId());
+        installAppAsUser(PUBLISHER4_OLD_NO_MANIFST_APK, getPrimaryUserId());
+        waitUntilBroadcastsDrain();
+
+        runDeviceTestsAsUser(PUBLISHER4_PKG, ".ShortcutManagerPostBackupTest",
+                "testInvisibleIgnored",
+                getPrimaryUserId());
+
+        runDeviceTestsAsUser(LAUNCHER4_PKG, ".ShortcutManagerPostBackupTest",
+                "testInvisibleIgnored",
+                getPrimaryUserId());
+    }
+
     public void testBackupAndRestore_withNoUninstall() throws Exception {
         if (!mSupportsBackup) {
             return;
diff --git a/hostsidetests/statsd/Android.mk b/hostsidetests/statsd/Android.mk
new file mode 100644
index 0000000..b614cb1
--- /dev/null
+++ b/hostsidetests/statsd/Android.mk
@@ -0,0 +1,33 @@
+# 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)
+
+# tag this module as a cts test artifact
+LOCAL_MODULE_TAGS := tests
+LOCAL_COMPATIBILITY_SUITE := cts vts general-tests
+
+LOCAL_MODULE := CtsStatsdHostTestCases
+
+LOCAL_PROTOC_OPTIMIZE_TYPE := full
+LOCAL_STATIC_JAVA_LIBRARIES := platformprotos
+LOCAL_JAVA_LIBRARIES := cts-tradefed tradefed compatibility-host-util host-libprotobuf-java-full
+
+include $(BUILD_HOST_JAVA_LIBRARY)
+
+include $(call all-makefiles-under,$(LOCAL_PATH))
diff --git a/hostsidetests/statsd/AndroidTest.xml b/hostsidetests/statsd/AndroidTest.xml
new file mode 100644
index 0000000..4a0f150
--- /dev/null
+++ b/hostsidetests/statsd/AndroidTest.xml
@@ -0,0 +1,22 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2017 The Android Open Source Project
+
+     Licensed under the Apache License, Version 2.0 (the "License");
+     you may not use this file except in compliance with the License.
+     You may obtain a copy of the License at
+
+          http://www.apache.org/licenses/LICENSE-2.0
+
+     Unless required by applicable law or agreed to in writing, software
+     distributed under the License is distributed on an "AS IS" BASIS,
+     WITHOUT 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 Statsd host test cases">
+    <option name="test-suite-tag" value="cts" />
+    <option name="config-descriptor:metadata" key="component" value="statsd" />
+    <test class="com.android.compatibility.common.tradefed.testtype.JarHostTest" >
+        <option name="jar" value="CtsStatsdHostTestCases.jar" />
+    </test>
+</configuration>
diff --git a/hostsidetests/statsd/apps/Android.mk b/hostsidetests/statsd/apps/Android.mk
new file mode 100644
index 0000000..4a74e80
--- /dev/null
+++ b/hostsidetests/statsd/apps/Android.mk
@@ -0,0 +1,20 @@
+# Copyright (C) 2017 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#      http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+LOCAL_PATH := $(call my-dir)
+
+include $(CLEAR_VARS)
+
+# Build the test APKs using their own makefiles
+include $(call all-makefiles-under,$(LOCAL_PATH))
diff --git a/hostsidetests/statsd/apps/statsdapp/Android.mk b/hostsidetests/statsd/apps/statsdapp/Android.mk
new file mode 100644
index 0000000..58e509e
--- /dev/null
+++ b/hostsidetests/statsd/apps/statsdapp/Android.mk
@@ -0,0 +1,46 @@
+# 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_PACKAGE_NAME := CtsStatsdAtomsApp
+
+# 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_SRC_FILES := \
+    $(call all-java-files-under, src) \
+
+LOCAL_JAVA_LIBRARIES := android.test.runner cts-junit org.apache.http.legacy
+
+LOCAL_PRIVILEGED_MODULE := true
+LOCAL_PROTOC_OPTIMIZE_TYPE := lite-static
+
+LOCAL_STATIC_JAVA_LIBRARIES := \
+    ctstestrunner \
+    compatibility-device-util \
+    android-support-v4 \
+    legacy-android-test \
+    android-support-test
+
+LOCAL_SDK_VERSION := test_current
+
+# tag this module as a cts test artifact
+LOCAL_COMPATIBILITY_SUITE := cts vts general-tests
+
+include $(BUILD_CTS_PACKAGE)
\ No newline at end of file
diff --git a/hostsidetests/statsd/apps/statsdapp/AndroidManifest.xml b/hostsidetests/statsd/apps/statsdapp/AndroidManifest.xml
new file mode 100644
index 0000000..5544294
--- /dev/null
+++ b/hostsidetests/statsd/apps/statsdapp/AndroidManifest.xml
@@ -0,0 +1,53 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2017 The Android Open Source Project
+
+     Licensed under the Apache License, Version 2.0 (the "License");
+     you may not use this file except in compliance with the License.
+     You may obtain a copy of the License at
+
+          http://www.apache.org/licenses/LICENSE-2.0
+
+     Unless required by applicable law or agreed to in writing, software
+     distributed under the License is distributed on an "AS IS" BASIS,
+     WITHOUT 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.server.cts.device.statsd" >
+    <uses-permission android:name="android.permission.ACCESS_WIFI_STATE"/>
+    <uses-permission android:name="android.permission.ACCESS_FINE_LOCATION"/>
+    <uses-permission android:name="android.permission.BLUETOOTH"/>
+    <uses-permission android:name="android.permission.BLUETOOTH_ADMIN"/>
+    <uses-permission android:name="android.permission.CAMERA"/>
+    <uses-permission android:name="android.permission.CHANGE_WIFI_STATE"/>
+    <uses-permission android:name="android.permission.INTERNET" />
+    <uses-permission android:name="android.permission.WAKE_LOCK" />
+    <uses-permission android:name="android.permission.READ_SYNC_STATS" />
+    <uses-permission android:name="android.permission.WRITE_SYNC_SETTINGS" />
+    <uses-permission android:name="android.permission.SYSTEM_ALERT_WINDOW" />
+    <uses-permission android:name="android.permission.DUMP" /> <!-- must be granted via pm grant -->
+
+
+    <application android:label="@string/app_name">
+        <uses-library android:name="android.test.runner" />
+        <uses-library android:name="org.apache.http.legacy" android:required="false" />
+        <activity
+            android:name=".VideoPlayerActivity"
+            android:label="@string/app_name"
+            android:launchMode="singleTop" >
+            <intent-filter>
+                <action android:name="android.intent.action.MAIN" />
+                <category android:name="android.intent.category.LAUNCHER" />
+            </intent-filter>
+        </activity>
+    </application>
+
+    <instrumentation android:name="android.support.test.runner.AndroidJUnitRunner"
+                     android:targetPackage="com.android.server.cts.device.statsd"
+                     android:label="CTS tests of android.os.statsd stats collection">
+        <meta-data android:name="listener"
+                   android:value="com.android.cts.runner.CtsTestRunListener" />
+    </instrumentation>/>
+</manifest>
\ No newline at end of file
diff --git a/hostsidetests/statsd/apps/statsdapp/AndroidTest.xml b/hostsidetests/statsd/apps/statsdapp/AndroidTest.xml
new file mode 100644
index 0000000..402a73b
--- /dev/null
+++ b/hostsidetests/statsd/apps/statsdapp/AndroidTest.xml
@@ -0,0 +1,31 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2017 The Android Open Source Project
+
+     Licensed under the Apache License, Version 2.0 (the "License");
+     you may not use this file except in compliance with the License.
+     You may obtain a copy of the License at
+
+          http://www.apache.org/licenses/LICENSE-2.0
+
+     Unless required by applicable law or agreed to in writing, software
+     distributed under the License is distributed on an "AS IS" BASIS,
+     WITHOUT 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 Statsd 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="CtsStatsdAtomsApp.apk" />
+    </target_preparer>
+    <target_preparer class="com.android.tradefed.targetprep.RunCommandTargetPreparer">
+        <option name="run-command" value="pm grant com.android.server.cts.device.statsd android.permission.DUMP" />
+        <option name="teardown-command" value="pm revoke com.android.server.cts.device.statsd android.permission.DUMP"/>
+    </target_preparer>
+    <test class="com.android.tradefed.testtype.AndroidJUnitTest" >
+        <option name="package" value="com.android.server.cts.device.statsd" />
+        <option name="runtime-hint" value="1m" />
+    </test>
+</configuration>
\ No newline at end of file
diff --git a/hostsidetests/statsd/apps/statsdapp/res/layout/activity_main.xml b/hostsidetests/statsd/apps/statsdapp/res/layout/activity_main.xml
new file mode 100644
index 0000000..a029c80
--- /dev/null
+++ b/hostsidetests/statsd/apps/statsdapp/res/layout/activity_main.xml
@@ -0,0 +1,32 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2017 The Android Open Source Project
+
+     Licensed under the Apache License, Version 2.0 (the "License");
+     you may not use this file except in compliance with the License.
+     You may obtain a copy of the License at
+
+          http://www.apache.org/licenses/LICENSE-2.0
+
+     Unless required by applicable law or agreed to in writing, software
+     distributed under the License is distributed on an "AS IS" BASIS,
+     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+     See the License for the specific language governing permissions and
+     limitations under the License.
+-->
+
+<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
+    android:layout_width="fill_parent"
+    android:layout_height="fill_parent"
+    android:orientation="vertical">
+
+    <FrameLayout
+        android:id="@+id/video_frame"
+        android:layout_width="fill_parent"
+        android:layout_height="fill_parent">
+
+        <VideoView
+            android:id="@+id/video_player_view"
+            android:layout_width="fill_parent"
+            android:layout_height="fill_parent" />
+    </FrameLayout>
+</RelativeLayout>
\ No newline at end of file
diff --git a/hostsidetests/statsd/apps/statsdapp/res/raw/colors_video.mp4 b/hostsidetests/statsd/apps/statsdapp/res/raw/colors_video.mp4
new file mode 100644
index 0000000..0bec670
--- /dev/null
+++ b/hostsidetests/statsd/apps/statsdapp/res/raw/colors_video.mp4
Binary files differ
diff --git a/hostsidetests/statsd/apps/statsdapp/res/raw/good.mp3 b/hostsidetests/statsd/apps/statsdapp/res/raw/good.mp3
new file mode 100644
index 0000000..d20f772
--- /dev/null
+++ b/hostsidetests/statsd/apps/statsdapp/res/raw/good.mp3
Binary files differ
diff --git a/hostsidetests/statsd/apps/statsdapp/res/values/strings.xml b/hostsidetests/statsd/apps/statsdapp/res/values/strings.xml
new file mode 100644
index 0000000..e40d2ac
--- /dev/null
+++ b/hostsidetests/statsd/apps/statsdapp/res/values/strings.xml
@@ -0,0 +1,20 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- Copyright (C) 2017 The Android Open Source Project
+
+     Licensed under the Apache License, Version 2.0 (the "License");
+     you may not use this file except in compliance with the License.
+     You may obtain a copy of the License at
+
+          http://www.apache.org/licenses/LICENSE-2.0
+
+     Unless required by applicable law or agreed to in writing, software
+     distributed under the License is distributed on an "AS IS" BASIS,
+     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+     See the License for the specific language governing permissions and
+     limitations under the License.
+-->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+           xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="app_name">CTS Statsd Atoms App</string>
+</resources>
\ No newline at end of file
diff --git a/hostsidetests/statsd/apps/statsdapp/src/com/android/server/cts/device/statsd/AtomTests.java b/hostsidetests/statsd/apps/statsdapp/src/com/android/server/cts/device/statsd/AtomTests.java
new file mode 100644
index 0000000..a2f7bcf
--- /dev/null
+++ b/hostsidetests/statsd/apps/statsdapp/src/com/android/server/cts/device/statsd/AtomTests.java
@@ -0,0 +1,339 @@
+/*
+ * 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.server.cts.device.statsd;
+
+import android.app.AlarmManager;
+import android.app.PendingIntent;
+import android.app.Activity;
+import android.bluetooth.BluetoothAdapter;
+import android.bluetooth.le.BluetoothLeScanner;
+import android.bluetooth.le.ScanCallback;
+import android.bluetooth.le.ScanResult;
+import android.bluetooth.le.ScanSettings;
+import android.content.BroadcastReceiver;
+import android.content.Context;
+import android.content.Intent;
+import android.content.IntentFilter;
+import android.hardware.camera2.CameraDevice;
+import android.hardware.camera2.CameraManager;
+import android.hardware.camera2.CameraCharacteristics;
+import android.hardware.camera2.CameraManager;
+import android.location.Location;
+import android.location.LocationListener;
+import android.location.LocationManager;
+import android.media.MediaPlayer;
+import android.net.wifi.WifiManager;
+import android.os.AsyncTask;
+import android.os.Bundle;
+import android.os.Handler;
+import android.os.HandlerThread;
+import android.os.Looper;
+import android.os.SystemClock;
+import android.support.test.InstrumentationRegistry;
+import android.util.Log;
+
+import org.junit.Test;
+
+import java.util.List;
+import java.util.concurrent.CountDownLatch;
+import java.util.concurrent.TimeUnit;
+
+public class AtomTests{
+    private static final String TAG = AtomTests.class.getSimpleName();
+
+    @Test
+    public void testAudioState() {
+        Context context = InstrumentationRegistry.getContext();
+        MediaPlayer mediaPlayer = MediaPlayer.create(context, R.raw.good);
+        mediaPlayer.start();
+        sleep(2_000);
+        mediaPlayer.stop();
+    }
+
+    @Test
+    public void testBleScanOptimized() {
+        ScanSettings scanSettings = new ScanSettings.Builder()
+                .setScanMode(ScanSettings.SCAN_MODE_OPPORTUNISTIC).build();
+        performBleScan(scanSettings, false);
+    }
+
+    @Test
+    public void testBleScanUnoptimized() {
+        ScanSettings scanSettings = new ScanSettings.Builder()
+                .setScanMode(ScanSettings.SCAN_MODE_LOW_LATENCY).build();
+        performBleScan(scanSettings, false);
+    }
+
+    @Test
+    public void testBleScanResult() {
+        ScanSettings scanSettings = new ScanSettings.Builder()
+                .setScanMode(ScanSettings.SCAN_MODE_LOW_LATENCY).build();
+        // TODO: Add an extremely weak ScanFilter to allow background ble scan results.
+        performBleScan(scanSettings, true);
+    }
+
+    private static void performBleScan(ScanSettings scanSettings, boolean waitForResult) {
+        BluetoothAdapter bluetoothAdapter = BluetoothAdapter.getDefaultAdapter();
+        if (bluetoothAdapter == null) {
+            Log.e(TAG, "Device does not support Bluetooth");
+            return;
+        }
+        boolean bluetoothEnabledByTest = false;
+        if (!bluetoothAdapter.isEnabled()) {
+            if (!bluetoothAdapter.enable()) {
+                Log.e(TAG, "Bluetooth is not enabled");
+                return;
+            }
+            sleep(8_000);
+            bluetoothEnabledByTest = true;
+        }
+        BluetoothLeScanner bleScanner = bluetoothAdapter.getBluetoothLeScanner();
+        if (bleScanner == null) {
+            Log.e(TAG, "Cannot access BLE scanner");
+            return;
+        }
+
+        CountDownLatch resultsLatch = new CountDownLatch(1);
+        ScanCallback scanCallback = new ScanCallback() {
+            @Override
+            public void onScanResult(int callbackType, ScanResult result) {
+                Log.v(TAG, "called onScanResult");
+                resultsLatch.countDown();
+            }
+            @Override
+            public void onScanFailed(int errorCode) {
+                Log.v(TAG, "called onScanFailed");
+            }
+            @Override
+            public void onBatchScanResults(List<ScanResult> results) {
+                Log.v(TAG, "called onBatchScanResults");
+                resultsLatch.countDown();
+            }
+        };
+
+        bleScanner.startScan(null, scanSettings, scanCallback);
+        if (waitForResult) {
+            waitForReceiver(InstrumentationRegistry.getContext(), 59_000, resultsLatch, null);
+        } else {
+            sleep(2_000);
+        }
+        bleScanner.stopScan(scanCallback);
+
+        // Restore adapter state at end of test
+        if (bluetoothEnabledByTest) {
+            bluetoothAdapter.disable();
+        }
+    }
+
+    @Test
+    public void testCameraState() throws Exception {
+        Context context = InstrumentationRegistry.getContext();
+        CameraManager cam = context.getSystemService(CameraManager.class);
+        String[] cameraIds = cam.getCameraIdList();
+        if (cameraIds.length == 0) {
+            Log.e(TAG, "No camera found on device");
+            return;
+        }
+
+        CountDownLatch latch = new CountDownLatch(1);
+        final CameraDevice.StateCallback cb = new CameraDevice.StateCallback() {
+            @Override
+            public void onOpened(CameraDevice cd) {
+                Log.i(TAG, "CameraDevice " + cd.getId() + " opened");
+                sleep(2_000);
+                cd.close();
+            }
+            @Override
+            public void onClosed(CameraDevice cd) {
+                latch.countDown();
+                Log.i(TAG, "CameraDevice " + cd.getId() + " closed");
+            }
+            @Override
+            public void onDisconnected(CameraDevice cd) {
+                Log.w(TAG, "CameraDevice  " + cd.getId() + " disconnected");
+            }
+            @Override
+            public void onError(CameraDevice cd, int error) {
+                Log.e(TAG, "CameraDevice " + cd.getId() + "had error " + error);
+            }
+        };
+
+        HandlerThread handlerThread = new HandlerThread("br_handler_thread");
+        handlerThread.start();
+        Looper looper = handlerThread.getLooper();
+        Handler handler = new Handler(looper);
+
+        cam.openCamera(cameraIds[0], cb, handler);
+        waitForReceiver(context, 10_000, latch, null);
+    }
+
+    @Test
+    public void testFlashlight() throws Exception {
+        Context context = InstrumentationRegistry.getContext();
+        CameraManager cam = context.getSystemService(CameraManager.class);
+        String[] cameraIds = cam.getCameraIdList();
+        boolean foundFlash = false;
+        for (int i = 0; i < cameraIds.length; i++) {
+            String id = cameraIds[i];
+            if(cam.getCameraCharacteristics(id).get(CameraCharacteristics.FLASH_INFO_AVAILABLE)) {
+                cam.setTorchMode(id, true);
+                sleep(500);
+                cam.setTorchMode(id, false);
+                foundFlash = true;
+                break;
+            }
+        }
+        if(!foundFlash) {
+            Log.e(TAG, "No flashlight found on device");
+        }
+    }
+
+    @Test
+    public void testGpsScan() {
+        Context context = InstrumentationRegistry.getContext();
+        final LocationManager locManager = context.getSystemService(LocationManager.class);
+        if (!locManager.isProviderEnabled(LocationManager.GPS_PROVIDER)) {
+            Log.e(TAG, "GPS provider is not enabled");
+            return;
+        }
+        CountDownLatch latch = new CountDownLatch(1);
+
+        final LocationListener locListener = new LocationListener() {
+            public void onLocationChanged(Location location) {
+                Log.v(TAG, "onLocationChanged: location has been obtained");
+            }
+            public void onProviderDisabled(String provider) {
+                Log.w(TAG, "onProviderDisabled " + provider);
+            }
+            public void onProviderEnabled(String provider) {
+                Log.w(TAG, "onProviderEnabled " + provider);
+            }
+            public void onStatusChanged(String provider, int status, Bundle extras) {
+                Log.w(TAG, "onStatusChanged " + provider + " " + status);
+            }
+        };
+
+        new AsyncTask<Void, Void, Void>() {
+            @Override
+            protected Void doInBackground(Void... params) {
+                Looper.prepare();
+                locManager.requestLocationUpdates(LocationManager.GPS_PROVIDER, 990, 0,
+                        locListener);
+                sleep(1_000);
+                locManager.removeUpdates(locListener);
+                latch.countDown();
+                return null;
+            }
+        }.execute();
+
+        waitForReceiver(context, 59_000, latch, null);
+    }
+
+    @Test
+    public void testWakeupAlarm() {
+        Context context = InstrumentationRegistry.getContext();
+        String name = "android.cts.statsd.testWakeupAlarm";
+        CountDownLatch onReceiveLatch = new CountDownLatch(1);
+        BroadcastReceiver receiver =
+                registerReceiver(context, onReceiveLatch, new IntentFilter(name));
+        AlarmManager manager = (AlarmManager) (context.getSystemService(AlarmManager.class));
+        PendingIntent pintent = PendingIntent.getBroadcast(context, 0, new Intent(name), 0);
+        manager.setExact(AlarmManager.ELAPSED_REALTIME_WAKEUP,
+            SystemClock.elapsedRealtime() + 2_000, pintent);
+        waitForReceiver(context, 10_000, onReceiveLatch, receiver);
+    }
+
+    @Test
+    public void testWifiLock() {
+        Context context = InstrumentationRegistry.getContext();
+        WifiManager wm = context.getSystemService(WifiManager.class);
+        WifiManager.WifiLock lock = wm.createWifiLock("StatsdCTSWifiLock");
+        lock.acquire();
+        sleep(500);
+        lock.release();
+    }
+
+    @Test
+    /** Does two wifi scans. */
+    // TODO: Copied this from BatterystatsValidation but we probably don't need to wait for results.
+    public void testWifiScan() {
+        Context context = InstrumentationRegistry.getContext();
+        IntentFilter intentFilter = new IntentFilter(WifiManager.SCAN_RESULTS_AVAILABLE_ACTION);
+        // Sometimes a scan was already running (from a different uid), so the first scan doesn't
+        // start when requested. Therefore, additionally wait for whatever scan is currently running
+        // to finish, then request a scan again - at least one of these two scans should be
+        // attributed to this app.
+        for (int i = 0; i < 2; i++) {
+            CountDownLatch onReceiveLatch = new CountDownLatch(1);
+            BroadcastReceiver receiver = registerReceiver(context, onReceiveLatch, intentFilter);
+            context.getSystemService(WifiManager.class).startScan();
+            waitForReceiver(context, 60_000, onReceiveLatch, receiver);
+        }
+    }
+
+    // ------- Helper methods
+
+    /** Puts the current thread to sleep. */
+    private static void sleep(int millis) {
+        try {
+            Thread.sleep(millis);
+        } catch (InterruptedException e) {
+            Log.e(TAG, "Interrupted exception while sleeping", e);
+        }
+    }
+
+    /** Register receiver to determine when given action is complete. */
+    private static BroadcastReceiver registerReceiver(
+            Context ctx, CountDownLatch onReceiveLatch, IntentFilter intentFilter) {
+        BroadcastReceiver receiver = new BroadcastReceiver() {
+            @Override
+            public void onReceive(Context context, Intent intent) {
+                onReceiveLatch.countDown();
+            }
+        };
+        // Run Broadcast receiver in a different thread since the main thread will wait.
+        HandlerThread handlerThread = new HandlerThread("br_handler_thread");
+        handlerThread.start();
+        Looper looper = handlerThread.getLooper();
+        Handler handler = new Handler(looper);
+        ctx.registerReceiver(receiver, intentFilter, null, handler);
+        return receiver;
+    }
+
+    /**
+     * Uses the receiver to wait until the action is complete. ctx and receiver may be null if no
+     * receiver is needed to be unregistered.
+     */
+    private static void waitForReceiver(Context ctx,
+            int maxWaitTimeMs, CountDownLatch latch, BroadcastReceiver receiver) {
+        try {
+            boolean didFinish = latch.await(maxWaitTimeMs, TimeUnit.MILLISECONDS);
+            if (didFinish) {
+                Log.v(TAG, "Finished performing action");
+            } else {
+                // This is not necessarily a problem. If we just want to make sure a count was
+                // recorded for the request, it doesn't matter if the action actually finished.
+                Log.w(TAG, "Did not finish in specified time.");
+            }
+        } catch (InterruptedException e) {
+            Log.e(TAG, "Interrupted exception while awaiting action to finish", e);
+        }
+        if (ctx != null && receiver != null) {
+            ctx.unregisterReceiver(receiver);
+        }
+    }
+}
diff --git a/hostsidetests/statsd/apps/statsdapp/src/com/android/server/cts/device/statsd/VideoPlayerActivity.java b/hostsidetests/statsd/apps/statsdapp/src/com/android/server/cts/device/statsd/VideoPlayerActivity.java
new file mode 100644
index 0000000..b15ec35
--- /dev/null
+++ b/hostsidetests/statsd/apps/statsdapp/src/com/android/server/cts/device/statsd/VideoPlayerActivity.java
@@ -0,0 +1,36 @@
+/*
+ * 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.server.cts.device.statsd;
+
+import android.app.Activity;
+import android.os.Bundle;
+import android.widget.VideoView;
+
+public class VideoPlayerActivity extends Activity {
+    /** Called when the activity is first created. */
+    @Override
+    public void onCreate(Bundle savedInstanceState) {
+        super.onCreate(savedInstanceState);
+        setContentView(R.layout.activity_main);
+        VideoView videoView = (VideoView)findViewById(R.id.video_player_view);
+        videoView.setVideoPath("android.resource://" + getPackageName() + "/" + R.raw.colors_video);
+        videoView.start();
+    }
+}
+
+
+
diff --git a/hostsidetests/statsd/src/android/cts/statsd/AtomTestCase.java b/hostsidetests/statsd/src/android/cts/statsd/AtomTestCase.java
new file mode 100644
index 0000000..cbb63ac
--- /dev/null
+++ b/hostsidetests/statsd/src/android/cts/statsd/AtomTestCase.java
@@ -0,0 +1,443 @@
+/*
+ * 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.cts.statsd;
+
+
+import com.android.internal.os.StatsdConfigProto.AtomMatcher;
+import com.android.internal.os.StatsdConfigProto.Bucket;
+import com.android.internal.os.StatsdConfigProto.EventMetric;
+import com.android.internal.os.StatsdConfigProto.FieldFilter;
+import com.android.internal.os.StatsdConfigProto.FieldMatcher;
+import com.android.internal.os.StatsdConfigProto.FieldValueMatcher;
+import com.android.internal.os.StatsdConfigProto.GaugeMetric;
+import com.android.internal.os.StatsdConfigProto.Predicate;
+import com.android.internal.os.StatsdConfigProto.SimpleAtomMatcher;
+import com.android.internal.os.StatsdConfigProto.SimplePredicate;
+import com.android.internal.os.StatsdConfigProto.StatsdConfig;
+import com.android.internal.os.StatsdConfigProto.TimeUnit;
+import com.android.os.AtomsProto.Atom;
+import com.android.os.AtomsProto.ScreenStateChanged;
+import com.android.os.StatsLog.ConfigMetricsReport;
+import com.android.os.StatsLog.ConfigMetricsReportList;
+import com.android.os.StatsLog.EventMetricData;
+import com.android.os.StatsLog.GaugeMetricData;
+import com.android.os.StatsLog.StatsLogReport;
+import com.android.tradefed.log.LogUtil;
+import com.google.common.io.Files;
+import com.sun.istack.internal.Nullable;
+
+import java.io.File;
+import java.text.SimpleDateFormat;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Comparator;
+import java.util.Date;
+import java.util.List;
+import java.util.Set;
+import java.util.function.Function;
+
+/**
+ * Base class for testing Statsd atoms.
+ * Validates reporting of statsd logging based on different events
+ */
+public class AtomTestCase extends BaseTestCase {
+
+    private static final String UPDATE_CONFIG_CMD = "cmd stats config update";
+    private static final String DUMP_REPORT_CMD = "cmd stats dump-report";
+    private static final String REMOVE_CONFIG_CMD = "cmd stats config remove";
+    protected static final String CONFIG_UID = "1000";
+    protected static final long CONFIG_ID = "cts_config".hashCode();
+
+    protected static final int WAIT_TIME_SHORT = 500;
+    protected static final int WAIT_TIME_LONG = 2_000;
+
+    @Override
+    protected void setUp() throws Exception {
+        super.setUp();
+        // TODO: need to do these before running real test:
+        // 1. compile statsd and push to device
+        // 2. make sure StatsCompanionService and incidentd is running
+        // 3. start statsd
+        // These should go away once we have statsd properly set up.
+
+        // Uninstall to clear the history in case it's still on the device.
+        removeConfig(CONFIG_ID);
+    }
+
+    @Override
+    protected void tearDown() throws Exception {
+        removeConfig(CONFIG_ID);
+        super.tearDown();
+    }
+
+    /**
+     * Determines whether logcat indicates that incidentd fired since the given device date.
+     */
+    protected boolean didIncidentdFireSince(String date) throws Exception {
+        final String INCIDENTD_TAG = "incidentd";
+        final String INCIDENTD_STARTED_STRING = "reportIncident";
+        // 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", INCIDENTD_TAG, INCIDENTD_STARTED_STRING));
+        return log.contains(INCIDENTD_STARTED_STRING);
+    }
+
+    protected static StatsdConfig.Builder createConfigBuilder() {
+        return StatsdConfig.newBuilder().setId(CONFIG_ID);
+    }
+
+    protected void createAndUploadConfig(int atomTag) throws Exception {
+        StatsdConfig.Builder conf = createConfigBuilder();
+        addAtomEvent(conf, atomTag);
+        uploadConfig(conf);
+    }
+
+    protected void uploadConfig(StatsdConfig.Builder config) throws Exception {
+        uploadConfig(config.build());
+    }
+
+    protected void uploadConfig(StatsdConfig config) throws Exception {
+        File configFile = File.createTempFile("statsdconfig", ".config");
+        configFile.deleteOnExit();
+        Files.write(config.toByteArray(), configFile);
+        String remotePath = "/data/" + configFile.getName();
+        getDevice().pushFile(configFile, remotePath);
+        getDevice().executeShellCommand(
+                String.join(" ", "cat", remotePath, "|", UPDATE_CONFIG_CMD, CONFIG_UID,
+                        String.valueOf(CONFIG_ID)));
+        getDevice().executeShellCommand("rm " + remotePath);
+    }
+
+    protected void removeConfig(long configId) throws Exception {
+        getDevice().executeShellCommand(
+                String.join(" ", REMOVE_CONFIG_CMD, CONFIG_UID, String.valueOf(configId)));
+    }
+
+    protected List<EventMetricData> getEventMetricDataList() throws Exception {
+        ConfigMetricsReportList reportList = getReportList();
+        assertTrue(reportList.getReportsCount() == 1);
+        ConfigMetricsReport report = reportList.getReports(0);
+
+        List<EventMetricData> data = new ArrayList<>();
+        for (StatsLogReport metric : report.getMetricsList()) {
+            data.addAll(metric.getEventMetrics().getDataList());
+        }
+        data.sort(Comparator.comparing(EventMetricData::getTimestampNanos));
+
+        LogUtil.CLog.d("Get EventMetricDataList as following:\n");
+        for (EventMetricData d : data) {
+            LogUtil.CLog.d("Atom at " + d.getTimestampNanos() + ":\n" + d.getAtom().toString());
+        }
+        return data;
+    }
+
+    protected List<Atom> getGaugeMetricDataList() throws Exception {
+        ConfigMetricsReportList reportList = getReportList();
+        assertTrue(reportList.getReportsCount() == 1);
+        // only config
+        ConfigMetricsReport report = reportList.getReports(0);
+
+        List<Atom> data = new ArrayList<>();
+        for (GaugeMetricData gaugeMetricData : report.getMetrics(0).getGaugeMetrics().getDataList()) {
+            data.add(gaugeMetricData.getBucketInfo(0).getAtom());
+        }
+
+        LogUtil.CLog.d("Get GaugeMetricDataList as following:\n");
+        for (Atom d : data) {
+            LogUtil.CLog.d("Atom:\n" + d.toString());
+        }
+        return data;
+    }
+
+    protected ConfigMetricsReportList getReportList() throws Exception {
+        ConfigMetricsReportList reportList = getDump(ConfigMetricsReportList.parser(),
+                String.join(" ", DUMP_REPORT_CMD, CONFIG_UID, String.valueOf(CONFIG_ID), "--proto"));
+        return reportList;
+    }
+
+    /** Creates a FieldValueMatcher.Builder corresponding to the given key. */
+    protected static FieldValueMatcher.Builder createKvm(int key) {
+        return FieldValueMatcher.newBuilder().setField(key);
+    }
+
+    protected void addAtomEvent(StatsdConfig.Builder conf, int atomTag) throws Exception {
+        addAtomEvent(conf, atomTag, new ArrayList<FieldValueMatcher.Builder>());
+    }
+
+    /**
+     * Adds an event to the config for an atom that matches the given key.
+     * @param conf configuration
+     * @param atomTag atom tag (from atoms.proto)
+     * @param kvm FieldValueMatcher.Builder for the relevant key
+     */
+    protected void addAtomEvent(StatsdConfig.Builder conf, int atomTag, FieldValueMatcher.Builder kvm)
+            throws Exception {
+        addAtomEvent(conf, atomTag, Arrays.asList(kvm));
+    }
+
+    /**
+     * Adds an event to the config for an atom that matches the given keys.
+     * @param conf configuration
+     * @param atomId atom tag (from atoms.proto)
+     * @param kvms list of FieldValueMatcher.Builders to attach to the atom. May be null.
+     */
+    protected void addAtomEvent(StatsdConfig.Builder conf, int atomId,
+            List<FieldValueMatcher.Builder> kvms) throws Exception {
+
+        final String atomName = "Atom" + System.nanoTime();
+        final String eventName = "Event" + System.nanoTime();
+
+        SimpleAtomMatcher.Builder sam = SimpleAtomMatcher.newBuilder().setAtomId(atomId);
+        if (kvms != null) {
+            for (FieldValueMatcher.Builder kvm : kvms) {
+                sam.addFieldValueMatcher(kvm);
+            }
+        }
+        conf.addAtomMatcher(AtomMatcher.newBuilder()
+                .setId(atomName.hashCode())
+                .setSimpleAtomMatcher(sam));
+        conf.addEventMetric(EventMetric.newBuilder()
+                .setId(eventName.hashCode())
+                .setWhat(atomName.hashCode()));
+    }
+
+    /**
+     * Adds an atom to a gauge metric of a config
+     * @param conf configuration
+     * @param atomId atom id (from atoms.proto)
+     * @param dimension dimension is needed for most pulled atoms
+     */
+    protected void addGaugeAtom(StatsdConfig.Builder conf, int atomId, @Nullable FieldMatcher.Builder dimension) throws Exception {
+        final String atomName = "Atom" + System.nanoTime();
+        final String gaugeName = "Gauge" +  + System.nanoTime();
+        final String predicateName = "SCREEN_IS_ON";
+        SimpleAtomMatcher.Builder sam = SimpleAtomMatcher.newBuilder().setAtomId(atomId);
+        conf.addAtomMatcher(AtomMatcher.newBuilder()
+                .setId(atomName.hashCode())
+                .setSimpleAtomMatcher(sam));
+        // TODO: change this predicate to something simpler and easier
+        final String predicateTrueName = "SCREEN_TURNED_ON";
+        final String predicateFalseName = "SCREEN_TURNED_OFF";
+        conf.addAtomMatcher(AtomMatcher.newBuilder()
+                .setId(predicateTrueName.hashCode())
+                .setSimpleAtomMatcher(SimpleAtomMatcher.newBuilder()
+                        .setAtomId(Atom.SCREEN_STATE_CHANGED_FIELD_NUMBER)
+                        .addFieldValueMatcher(FieldValueMatcher.newBuilder()
+                                .setField(ScreenStateChanged.DISPLAY_STATE_FIELD_NUMBER)
+                                .setEqInt(ScreenStateChanged.State.STATE_ON_VALUE)
+                        )
+                )
+        )
+                // Used to trigger predicate
+                .addAtomMatcher(AtomMatcher.newBuilder()
+                        .setId(predicateFalseName.hashCode())
+                        .setSimpleAtomMatcher(SimpleAtomMatcher.newBuilder()
+                                .setAtomId(Atom.SCREEN_STATE_CHANGED_FIELD_NUMBER)
+                                .addFieldValueMatcher(FieldValueMatcher.newBuilder()
+                                        .setField(ScreenStateChanged.DISPLAY_STATE_FIELD_NUMBER)
+                                        .setEqInt(ScreenStateChanged.State.STATE_OFF_VALUE)
+                                )
+                        )
+                );
+        conf.addPredicate(Predicate.newBuilder()
+                .setId(predicateName.hashCode())
+                .setSimplePredicate(SimplePredicate.newBuilder()
+                        .setStart(predicateTrueName.hashCode())
+                        .setStop(predicateFalseName.hashCode())
+                        .setCountNesting(false)
+                )
+        );
+        GaugeMetric.Builder gaugeMetric = GaugeMetric.newBuilder()
+                .setId(gaugeName.hashCode())
+                .setWhat(atomName.hashCode())
+                .setGaugeFieldsFilter(FieldFilter.newBuilder().setIncludeAll(true).build())
+                .setBucket(TimeUnit.CTS)
+                .setCondition(predicateName.hashCode());
+        if (dimension != null) {
+            gaugeMetric.setDimensions(dimension.build());
+        }
+        conf.addGaugeMetric(gaugeMetric.build());
+    }
+
+    /**
+     * Asserts that each set of states in stateSets occurs at least once in data.
+     * Asserts that the states in data occur in the same order as the sets in stateSets.
+     * @param stateSets A list of set of states, where each set represents an equivalent state of
+     *                  the device for the purpose of CTS.
+     * @param data list of EventMetricData from statsd, produced by getReportMetricListData()
+     * @param getStateFromAtom expression that takes in an Atom and returns the state it contains
+     */
+    public void assertStatesOccurred(List<Set<Integer>> stateSets, List<EventMetricData> data,
+            int wait, Function<Atom, Integer> getStateFromAtom) {
+        // Sometimes, there are more events than there are states.
+        // Eg: When the screen turns off, it may go into OFF and then DOZE immediately.
+        assertTrue(data.size() >= stateSets.size());
+        int stateSetIndex = 0; // Tracks which state set we expect the data to be in.
+        for (int dataIndex = 0; dataIndex < data.size(); dataIndex++) {
+            Atom atom = data.get(dataIndex).getAtom();
+            int state = getStateFromAtom.apply(atom);
+            // If state is in the current state set, we do not assert anything.
+            // If it is not, we expect to have transitioned to the next state set.
+            if (!stateSets.get(stateSetIndex).contains(state)) {
+                stateSetIndex += 1;
+                LogUtil.CLog.i("Assert that the following atom at dataIndex=" + dataIndex + " is"
+                        + " in stateSetIndex " + stateSetIndex + ":\n"
+                        + data.get(dataIndex).getAtom().toString());
+                assertTrue(dataIndex != 0); // We shoud not be on the first data.
+                assertTrue(stateSetIndex < stateSets.size()); // Out of bounds check.
+                assertTrue(stateSets.get(stateSetIndex).contains(state));
+                long diffMs = (data.get(dataIndex).getTimestampNanos() -
+                        data.get(dataIndex - 1).getTimestampNanos()) / 1_000_000;
+                assertTrue(wait / 2 < diffMs);
+                assertTrue(wait * 5 > diffMs);
+            }
+        }
+        assertTrue(stateSetIndex == stateSets.size() - 1); // We saw each state set.
+    }
+
+    protected void turnScreenOn() throws Exception {
+        getDevice().executeShellCommand("input keyevent KEYCODE_WAKEUP");
+        getDevice().executeShellCommand("wm dismiss-keyguard");
+    }
+
+    protected void turnScreenOff() throws Exception {
+        getDevice().executeShellCommand("input keyevent KEYCODE_SLEEP");
+    }
+
+    protected void setChargingState(int state) throws Exception {
+        getDevice().executeShellCommand("cmd battery set status " + state);
+    }
+
+    protected void unplugDevice() throws Exception {
+        getDevice().executeShellCommand("cmd battery unplug");
+    }
+
+    protected void plugInAc() throws Exception {
+        getDevice().executeShellCommand("cmd battery set ac 1");
+    }
+
+    protected void plugInUsb() throws Exception {
+        getDevice().executeShellCommand("cmd battery set usb 1");
+    }
+
+    protected void plugInWireless() throws Exception {
+        getDevice().executeShellCommand("cmd battery set wireless 1");
+    }
+
+    protected void setBatteryLevel(int level) throws Exception {
+        getDevice().executeShellCommand("cmd battery set level " + level);
+    }
+
+    protected void resetBatteryStatus() throws Exception {
+        getDevice().executeShellCommand("cmd battery reset");
+    }
+
+    protected int getScreenBrightness() throws Exception {
+        return Integer.parseInt(
+                getDevice().executeShellCommand("settings get system screen_brightness").trim());
+    }
+
+    protected void setScreenBrightness(int brightness) throws Exception {
+        getDevice().executeShellCommand("settings put system screen_brightness " + brightness);
+    }
+
+    protected boolean isScreenBrightnessModeManual() throws Exception {
+        String mode = getDevice().executeShellCommand("settings get system screen_brightness_mode");
+        return Integer.parseInt(mode.trim()) == 0;
+    }
+
+    protected void setScreenBrightnessMode(boolean manual) throws Exception {
+        getDevice().executeShellCommand(
+                "settings put system screen_brightness_mode " + (manual? 0 : 1));
+    }
+
+    protected int getScreenTimeoutMs() throws Exception {
+        return Integer.parseInt(
+                getDevice().executeShellCommand("settings get system screen_off_timeout").trim());
+    }
+    protected void setScreenTimeoutMs(int timeout) throws Exception {
+        getDevice().executeShellCommand("settings put system screen_off_timeout " + timeout);
+    }
+
+    protected void enterDozeModeLight() throws Exception {
+        getDevice().executeShellCommand("dumpsys deviceidle force-idle light");
+    }
+
+    protected void enterDozeModeDeep() throws Exception {
+        getDevice().executeShellCommand("dumpsys deviceidle force-idle deep");
+    }
+    protected void leaveDozeMode() throws Exception {
+        getDevice().executeShellCommand("dumpsys deviceidle unforce");
+        getDevice().executeShellCommand("dumpsys deviceidle disable");
+        getDevice().executeShellCommand("dumpsys deviceidle enable");
+    }
+
+    protected void turnBatterySaverOn() throws Exception {
+        getDevice().executeShellCommand("cmd battery unplug");
+        getDevice().executeShellCommand("settings put global low_power 1");
+    }
+
+    protected void turnBatterySaverOff() throws Exception {
+        getDevice().executeShellCommand("settings put global low_power 0");
+        getDevice().executeShellCommand("cmd battery reset");
+    }
+
+    protected void rebootDevice() throws Exception {
+        getDevice().rebootUntilOnline();
+    }
+
+    /**
+     * Determines whether the two events are within the specified range of each other.
+     * @param d0 the event that should occur first
+     * @param d1 the event that should occur second
+     * @param minDiffMs d0 should precede d1 by at least this amount
+     * @param maxDiffMs d0 should precede d1 by at most this amount
+     * @return
+     */
+    public static boolean isTimeDiffBetween(EventMetricData d0, EventMetricData d1,
+            int minDiffMs, int maxDiffMs) {
+        long diffMs = (d1.getTimestampNanos() - d0.getTimestampNanos()) / 1_000_000;
+        return minDiffMs <= diffMs && diffMs <= maxDiffMs;
+    }
+
+    protected String getCurrentLogcatDate() throws Exception {
+        // TODO: Do something more robust than this for getting logcat markers.
+        long timestampMs = getDevice().getDeviceDate();
+        return new SimpleDateFormat("MM-dd HH:mm:ss.SSS")
+                .format(new Date(timestampMs));
+    }
+
+    protected String getLogcatSince(String date, String logcatParams) throws Exception {
+        return getDevice().executeShellCommand(String.format(
+                "logcat -v threadtime -t '%s' -d %s", date, logcatParams));
+    }
+
+    /**
+     * Determines if the device has the given feature.
+     * Prints a warning if its value differs from requiredAnswer.
+     */
+    protected boolean hasFeature(String featureName, boolean requiredAnswer) throws Exception {
+        final String features = getDevice().executeShellCommand("pm list features");
+        boolean hasIt = features.contains(featureName);
+        if (hasIt != requiredAnswer) {
+            LogUtil.CLog.w("Device does " + (requiredAnswer ? "not " : "") + "have feature "
+                    + featureName);
+        }
+        return hasIt;
+    }
+
+}
\ No newline at end of file
diff --git a/hostsidetests/statsd/src/android/cts/statsd/BaseTestCase.java b/hostsidetests/statsd/src/android/cts/statsd/BaseTestCase.java
new file mode 100644
index 0000000..31eec47
--- /dev/null
+++ b/hostsidetests/statsd/src/android/cts/statsd/BaseTestCase.java
@@ -0,0 +1,144 @@
+/*
+ * 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.cts.statsd;
+
+import com.android.compatibility.common.tradefed.build.CompatibilityBuildHelper;
+import com.android.ddmlib.testrunner.RemoteAndroidTestRunner;
+import com.android.ddmlib.testrunner.TestIdentifier;
+import com.android.ddmlib.testrunner.TestResult;
+import com.android.ddmlib.testrunner.TestResult.TestStatus;
+import com.android.ddmlib.testrunner.TestRunResult;
+import com.android.tradefed.build.IBuildInfo;
+import com.android.tradefed.device.CollectingByteOutputReceiver;
+import com.android.tradefed.device.DeviceNotAvailableException;
+import com.android.tradefed.log.LogUtil.CLog;
+import com.android.tradefed.result.CollectingTestListener;
+import com.android.tradefed.testtype.DeviceTestCase;
+import com.android.tradefed.testtype.IBuildReceiver;
+
+import com.google.protobuf.InvalidProtocolBufferException;
+import com.google.protobuf.MessageLite;
+import com.google.protobuf.Parser;
+
+import java.io.FileNotFoundException;
+import java.util.Map;
+
+import javax.annotation.Nonnull;
+import javax.annotation.Nullable;
+
+// Largely copied from incident's ProtoDumpTestCase
+public class BaseTestCase extends DeviceTestCase implements IBuildReceiver {
+
+    protected IBuildInfo mCtsBuild;
+
+    private static final String TEST_RUNNER = "android.support.test.runner.AndroidJUnitRunner";
+
+    @Override
+    protected void setUp() throws Exception {
+        super.setUp();
+        assertNotNull(mCtsBuild);
+    }
+
+    @Override
+    public void setBuild(IBuildInfo buildInfo) {
+        mCtsBuild = buildInfo;
+    }
+
+    /**
+     * Call onto the device with an adb shell command and get the results of
+     * that as a proto of the given type.
+     *
+     * @param parser A protobuf parser object. e.g. MyProto.parser()
+     * @param command The adb shell command to run. e.g. "dumpsys fingerprint --proto"
+     *
+     * @throws DeviceNotAvailableException If there was a problem communicating with
+     *      the test device.
+     * @throws InvalidProtocolBufferException If there was an error parsing
+     *      the proto. Note that a 0 length buffer is not necessarily an error.
+     */
+    public <T extends MessageLite> T getDump(Parser<T> parser, String command)
+            throws DeviceNotAvailableException, InvalidProtocolBufferException {
+        final CollectingByteOutputReceiver receiver = new CollectingByteOutputReceiver();
+        getDevice().executeShellCommand(command, receiver);
+        return parser.parseFrom(receiver.getOutput());
+    }
+
+    /**
+     * Install a device side test package.
+     *
+     * @param appFileName Apk file name, such as "CtsNetStatsApp.apk".
+     * @param grantPermissions whether to give runtime permissions.
+     */
+    protected void installPackage(String appFileName, boolean grantPermissions)
+            throws FileNotFoundException, DeviceNotAvailableException {
+        CLog.d("Installing app " + appFileName);
+        CompatibilityBuildHelper buildHelper = new CompatibilityBuildHelper(mCtsBuild);
+        final String result = getDevice().installPackage(
+                buildHelper.getTestFile(appFileName), true, grantPermissions);
+        assertNull("Failed to install " + appFileName + ": " + result, result);
+    }
+
+    /**
+     * Run a device side test.
+     *
+     * @param pkgName Test package name, such as "com.android.server.cts.netstats".
+     * @param testClassName Test class name; either a fully qualified name, or "." + a class name.
+     * @param testMethodName Test method name.
+     * @throws DeviceNotAvailableException
+     */
+    protected void runDeviceTests(@Nonnull String pkgName,
+            @Nullable String testClassName, @Nullable String testMethodName)
+            throws DeviceNotAvailableException {
+        if (testClassName != null && testClassName.startsWith(".")) {
+            testClassName = pkgName + testClassName;
+        }
+
+        RemoteAndroidTestRunner testRunner = new RemoteAndroidTestRunner(
+                pkgName, TEST_RUNNER, getDevice().getIDevice());
+        if (testClassName != null && testMethodName != null) {
+            testRunner.setMethodName(testClassName, testMethodName);
+        } else if (testClassName != null) {
+            testRunner.setClassName(testClassName);
+        }
+
+        CollectingTestListener listener = new CollectingTestListener();
+        assertTrue(getDevice().runInstrumentationTests(testRunner, listener));
+
+        final TestRunResult result = listener.getCurrentRunResults();
+        if (result.isRunFailure()) {
+            throw new AssertionError("Failed to successfully run device tests for "
+                    + result.getName() + ": " + result.getRunFailureMessage());
+        }
+        if (result.getNumTests() == 0) {
+            throw new AssertionError("No tests were run on the device");
+        }
+
+        if (result.hasFailedTests()) {
+            // build a meaningful error message
+            StringBuilder errorBuilder = new StringBuilder("On-device tests failed:\n");
+            for (Map.Entry<TestIdentifier, TestResult> resultEntry :
+                    result.getTestResults().entrySet()) {
+                if (!resultEntry.getValue().getStatus().equals(TestStatus.PASSED)) {
+                    errorBuilder.append(resultEntry.getKey().toString());
+                    errorBuilder.append(":\n");
+                    errorBuilder.append(resultEntry.getValue().getStackTrace());
+                }
+            }
+            throw new AssertionError(errorBuilder.toString());
+        }
+    }
+}
diff --git a/hostsidetests/statsd/src/android/cts/statsd/DeviceAtomTestCase.java b/hostsidetests/statsd/src/android/cts/statsd/DeviceAtomTestCase.java
new file mode 100644
index 0000000..7c49407
--- /dev/null
+++ b/hostsidetests/statsd/src/android/cts/statsd/DeviceAtomTestCase.java
@@ -0,0 +1,157 @@
+/*
+ * 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.cts.statsd;
+
+import com.android.internal.os.StatsdConfigProto.AtomMatcher;
+import com.android.internal.os.StatsdConfigProto.EventMetric;
+import com.android.internal.os.StatsdConfigProto.FieldValueMatcher;
+import com.android.internal.os.StatsdConfigProto.SimpleAtomMatcher;
+import com.android.internal.os.StatsdConfigProto.StatsdConfig;
+import com.android.os.StatsLog.ConfigMetricsReport;
+import com.android.os.StatsLog.ConfigMetricsReportList;
+import com.android.os.StatsLog.EventMetricData;
+import com.android.tradefed.log.LogUtil;
+
+import java.util.Arrays;
+import java.util.List;
+
+/**
+ * Base class for testing Statsd atoms that report a uid. Tests are performed via a device-side app.
+ */
+public class DeviceAtomTestCase extends AtomTestCase {
+
+    protected static final String DEVICE_SIDE_TEST_APK = "CtsStatsdAtomsApp.apk";
+    protected static final String DEVICE_SIDE_TEST_PACKAGE
+            = "com.android.server.cts.device.statsd";
+
+    protected static final String CONFIG_NAME = "cts_config";
+
+    @Override
+    protected void setUp() throws Exception {
+        super.setUp();
+        getDevice().uninstallPackage(DEVICE_SIDE_TEST_PACKAGE);
+        installTestApp();
+    }
+
+    @Override
+    protected void tearDown() throws Exception {
+        getDevice().uninstallPackage(DEVICE_SIDE_TEST_PACKAGE);
+        super.tearDown();
+    }
+
+    /**
+     * Performs a device-side test by calling a method on the app and returns its stats events.
+     * @param methodName the name of the method in the app's AtomTests to perform
+     * @param atom atom tag (from atoms.proto)
+     * @param key atom's field corresponding to state
+     * @param stateOn 'on' value
+     * @param stateOff 'off' value
+     * @param minTimeDiffMs max allowed time between start and stop
+     * @param maxTimeDiffMs min allowed time between start and stop
+     * @param demandExactlyTwo whether there must be precisely two events logged (1 start, 1 stop)
+     * @return list of events with the app's uid matching the configuration defined by the params.
+     */
+    protected List<EventMetricData> doDeviceMethodOnOff(
+            String methodName, int atom, int key, int stateOn, int stateOff,
+            int minTimeDiffMs, int maxTimeDiffMs, boolean demandExactlyTwo) throws Exception {
+        StatsdConfig.Builder conf = createConfigBuilder();
+        addAtomEvent(conf, atom, createKvm(key).setEqInt(stateOn));
+        addAtomEvent(conf, atom, createKvm(key).setEqInt(stateOff));
+        List<EventMetricData> data = doDeviceMethod(methodName, conf);
+
+        if (demandExactlyTwo) {
+            assertTrue(data.size() == 2);
+        } else {
+            assertTrue(data.size() >= 2);
+        }
+        assertTrue(isTimeDiffBetween(data.get(0), data.get(1), minTimeDiffMs, maxTimeDiffMs));
+        return data;
+    }
+
+    /**
+     *
+     * @param methodName the name of the method in the app's AtomTests to perform
+     * @param cfg statsd configuration
+     * @return list of events with the app's uid matching the configuration.
+     */
+    protected List<EventMetricData> doDeviceMethod(String methodName, StatsdConfig.Builder cfg)
+            throws Exception {
+        removeConfig(CONFIG_ID);
+        uploadConfig(cfg);
+        int appUid = getUid();
+        LogUtil.CLog.d("\nPerforming device-side test of " + methodName + " for uid " + appUid);
+        runDeviceTests(DEVICE_SIDE_TEST_PACKAGE, ".AtomTests", methodName);
+
+        return getEventMetricDataList();
+    }
+
+    /**
+     * Adds an event to the config for an atom that matches the given key AND has the app's uid.
+     * @param conf configuration
+     * @param atomTag atom tag (from atoms.proto)
+     * @param kvm FieldValueMatcher.Builder for the relevant key
+     */
+    @Override
+    protected void addAtomEvent(StatsdConfig.Builder conf, int atomTag, FieldValueMatcher.Builder kvm)
+            throws Exception {
+
+        final int UID_KEY = 1;
+        FieldValueMatcher.Builder kvmUid = createKvm(UID_KEY).setEqInt(getUid());
+        addAtomEvent(conf, atomTag, Arrays.asList(kvm, kvmUid));
+    }
+
+    /**
+     * Adds an event to the config for an atom that matches the app's uid.
+     * @param conf configuration
+     * @param atomTag atom tag (from atoms.proto)
+     */
+    @Override
+    protected void addAtomEvent(StatsdConfig.Builder conf, int atomTag) throws Exception {
+        final int UID_KEY = 1;
+        FieldValueMatcher.Builder kvmUid = createKvm(UID_KEY).setEqInt(getUid());
+        addAtomEvent(conf, atomTag, Arrays.asList(kvmUid));
+    }
+
+    /**
+     * Gets the uid of the test app.
+     */
+    protected int getUid() throws Exception {
+        String uidLine = getDevice().executeShellCommand("cmd package list packages -U "
+                + DEVICE_SIDE_TEST_PACKAGE);
+        String[] uidLineParts = uidLine.split(":");
+        // 3rd entry is package uid
+        assertTrue(uidLineParts.length > 2);
+        int uid = Integer.parseInt(uidLineParts[2].trim());
+        assertTrue(uid > 10000);
+        return uid;
+    }
+
+    /**
+     * Installs the test apk.
+     */
+    protected void installTestApp() throws Exception {
+        installPackage(DEVICE_SIDE_TEST_APK, true);
+        allowBackgroundServices();
+    }
+
+    /**
+     * Required to successfully start a background service from adb in O.
+     */
+    protected void allowBackgroundServices() throws Exception {
+        getDevice().executeShellCommand(String.format(
+                "cmd deviceidle tempwhitelist %s", DEVICE_SIDE_TEST_PACKAGE));
+    }
+}
diff --git a/hostsidetests/statsd/src/android/cts/statsd/HostAtomTests.java b/hostsidetests/statsd/src/android/cts/statsd/HostAtomTests.java
new file mode 100644
index 0000000..5ef6deb
--- /dev/null
+++ b/hostsidetests/statsd/src/android/cts/statsd/HostAtomTests.java
@@ -0,0 +1,756 @@
+/*
+ * 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.cts.statsd;
+
+import com.android.internal.os.StatsdConfigProto.Alert;
+import com.android.internal.os.StatsdConfigProto.IncidentdDetails;
+import com.android.internal.os.StatsdConfigProto.Bucket;
+import com.android.internal.os.StatsdConfigProto.CountMetric;
+import com.android.internal.os.StatsdConfigProto.DurationMetric;
+import com.android.internal.os.StatsdConfigProto.FieldFilter;
+import com.android.internal.os.StatsdConfigProto.FieldMatcher;
+import com.android.internal.os.StatsdConfigProto.GaugeMetric;
+import com.android.internal.os.StatsdConfigProto.StatsdConfig;
+import com.android.internal.os.StatsdConfigProto.Subscription;
+import com.android.internal.os.StatsdConfigProto.TimeUnit;
+import com.android.internal.os.StatsdConfigProto.ValueMetric;
+import com.android.os.AtomsProto.Atom;
+import com.android.os.AtomsProto.BatteryLevelChanged;
+import com.android.os.AtomsProto.BatterySaverModeStateChanged;
+import com.android.os.AtomsProto.ChargingStateChanged;
+import com.android.os.AtomsProto.CpuTimePerUid;
+import com.android.os.AtomsProto.CpuTimePerFreq;
+import com.android.os.AtomsProto.CpuTimePerUidFreq;
+import com.android.os.AtomsProto.DeviceIdleModeStateChanged;
+import com.android.os.AtomsProto.KernelWakelock;
+import com.android.os.AtomsProto.PlatformSleepState;
+import com.android.os.AtomsProto.PluggedStateChanged;
+import com.android.os.AtomsProto.ScreenBrightnessChanged;
+import com.android.os.AtomsProto.ScreenStateChanged;
+import com.android.os.AtomsProto.SleepStateVoter;
+import com.android.os.AtomsProto.SubsystemSleepState;
+import com.android.os.StatsLog.EventMetricData;
+
+import java.util.Arrays;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Set;
+
+/**
+ * Statsd atom tests that are done via adb (hostside).
+ */
+public class HostAtomTests extends AtomTestCase {
+
+    private static final String TAG = "Statsd.HostAtomTests";
+
+    private static final boolean TESTS_ENABLED = false;
+    // For tests that require incidentd. Keep as true until TESTS_ENABLED is permanently enabled.
+    private static final boolean INCIDENTD_TESTS_ENABLED = false;
+
+    private static final long TEST_CONFIG_ID = "cts_test_config".hashCode();
+
+    public void testScreenStateChangedAtom() throws Exception {
+        if (!TESTS_ENABLED) {return;}
+
+        // Setup, make sure the screen is off.
+        turnScreenOff();
+        Thread.sleep(WAIT_TIME_SHORT);
+
+        final int atomTag = Atom.SCREEN_STATE_CHANGED_FIELD_NUMBER;
+
+        Set<Integer> screenOnStates = new HashSet<>(
+                Arrays.asList(ScreenStateChanged.State.STATE_ON_VALUE,
+                        ScreenStateChanged.State.STATE_ON_SUSPEND_VALUE,
+                        ScreenStateChanged.State.STATE_VR_VALUE));
+        Set<Integer> screenOffStates = new HashSet<>(
+                Arrays.asList(ScreenStateChanged.State.STATE_OFF_VALUE,
+                        ScreenStateChanged.State.STATE_DOZE_VALUE,
+                        ScreenStateChanged.State.STATE_DOZE_SUSPEND_VALUE,
+                        ScreenStateChanged.State.STATE_UNKNOWN_VALUE));
+
+        // Add state sets to the list in order.
+        List<Set<Integer>> stateSet = Arrays.asList(screenOnStates, screenOffStates);
+
+        createAndUploadConfig(atomTag);
+        Thread.sleep(WAIT_TIME_SHORT);
+
+        // Trigger events in same order.
+        turnScreenOn();
+        Thread.sleep(WAIT_TIME_LONG);
+        turnScreenOff();
+        Thread.sleep(WAIT_TIME_LONG);
+
+        // Sorted list of events in order in which they occurred.
+        List<EventMetricData> data = getEventMetricDataList();
+
+        // Assert that the events happened in the expected order.
+        assertStatesOccurred(stateSet, data, WAIT_TIME_LONG,
+                atom -> atom.getScreenStateChanged().getDisplayState().getNumber());
+    }
+
+    public void testChargingStateChangedAtom() throws Exception {
+        if (!TESTS_ENABLED) {return;}
+
+        // Setup, set charging state to full.
+        setChargingState(5);
+        Thread.sleep(WAIT_TIME_SHORT);
+
+        final int atomTag = Atom.CHARGING_STATE_CHANGED_FIELD_NUMBER;
+
+        Set<Integer> batteryUnknownStates = new HashSet<>(
+                Arrays.asList(ChargingStateChanged.State.BATTERY_STATUS_UNKNOWN_VALUE));
+        Set<Integer> batteryChargingStates = new HashSet<>(
+                Arrays.asList(ChargingStateChanged.State.BATTERY_STATUS_CHARGING_VALUE));
+        Set<Integer> batteryDischargingStates = new HashSet<>(
+                Arrays.asList(ChargingStateChanged.State.BATTERY_STATUS_DISCHARGING_VALUE));
+        Set<Integer> batteryNotChargingStates = new HashSet<>(
+                Arrays.asList(ChargingStateChanged.State.BATTERY_STATUS_NOT_CHARGING_VALUE));
+        Set<Integer> batteryFullStates = new HashSet<>(
+                Arrays.asList(ChargingStateChanged.State.BATTERY_STATUS_FULL_VALUE));
+
+        // Add state sets to the list in order.
+        List<Set<Integer>> stateSet = Arrays.asList(batteryUnknownStates, batteryChargingStates,
+                batteryDischargingStates, batteryNotChargingStates, batteryFullStates);
+
+        createAndUploadConfig(atomTag);
+        Thread.sleep(WAIT_TIME_SHORT);
+
+        // Trigger events in same order.
+        setChargingState(1);
+        Thread.sleep(WAIT_TIME_SHORT);
+        setChargingState(2);
+        Thread.sleep(WAIT_TIME_SHORT);
+        setChargingState(3);
+        Thread.sleep(WAIT_TIME_SHORT);
+        setChargingState(4);
+        Thread.sleep(WAIT_TIME_SHORT);
+        setChargingState(5);
+        Thread.sleep(WAIT_TIME_SHORT);
+
+        // Sorted list of events in order in which they occurred.
+        List<EventMetricData> data = getEventMetricDataList();
+
+        // Unfreeze battery state after test
+        resetBatteryStatus();
+        Thread.sleep(WAIT_TIME_SHORT);
+
+        // Assert that the events happened in the expected order.
+        assertStatesOccurred(stateSet, data, WAIT_TIME_SHORT,
+                atom -> atom.getChargingStateChanged().getChargingState().getNumber());
+    }
+
+    public void testPluggedStateChangedAtom() throws Exception {
+        if (!TESTS_ENABLED) {return;}
+
+        // Setup, unplug device.
+        unplugDevice();
+        Thread.sleep(WAIT_TIME_SHORT);
+
+        final int atomTag = Atom.PLUGGED_STATE_CHANGED_FIELD_NUMBER;
+
+        Set<Integer> unpluggedStates = new HashSet<>(
+                Arrays.asList(PluggedStateChanged.State.BATTERY_PLUGGED_NONE_VALUE));
+        Set<Integer> acStates = new HashSet<>(
+                Arrays.asList(PluggedStateChanged.State.BATTERY_PLUGGED_AC_VALUE));
+        Set<Integer> usbStates = new HashSet<>(
+                Arrays.asList(PluggedStateChanged.State.BATTERY_PLUGGED_USB_VALUE));
+        Set<Integer> wirelessStates = new HashSet<>(
+                Arrays.asList(PluggedStateChanged.State.BATTERY_PLUGGED_WIRELESS_VALUE));
+
+        // Add state sets to the list in order.
+        List<Set<Integer>> stateSet = Arrays.asList(acStates, unpluggedStates, usbStates,
+                unpluggedStates, wirelessStates, unpluggedStates);
+
+        createAndUploadConfig(atomTag);
+        Thread.sleep(WAIT_TIME_SHORT);
+
+        // Trigger events in same order.
+        plugInAc();
+        Thread.sleep(WAIT_TIME_SHORT);
+        unplugDevice();
+        Thread.sleep(WAIT_TIME_SHORT);
+        plugInUsb();
+        Thread.sleep(WAIT_TIME_SHORT);
+        unplugDevice();
+        Thread.sleep(WAIT_TIME_SHORT);
+        plugInWireless();
+        Thread.sleep(WAIT_TIME_SHORT);
+        unplugDevice();
+        Thread.sleep(WAIT_TIME_SHORT);
+
+        // Sorted list of events in order in which they occurred.
+        List<EventMetricData> data = getEventMetricDataList();
+
+        // Unfreeze battery state after test
+        resetBatteryStatus();
+        Thread.sleep(WAIT_TIME_SHORT);
+
+        // Assert that the events happened in the expected order.
+        assertStatesOccurred(stateSet, data, WAIT_TIME_SHORT,
+                atom -> atom.getPluggedStateChanged().getPluggedState().getNumber());
+    }
+
+    public void testBatteryLevelChangedAtom() throws Exception {
+        if (!TESTS_ENABLED) {return;}
+
+        // Setup, set battery level to full.
+        setBatteryLevel(100);
+        Thread.sleep(WAIT_TIME_SHORT);
+
+        final int atomTag = Atom.BATTERY_LEVEL_CHANGED_FIELD_NUMBER;
+
+        Set<Integer> batteryDead = new HashSet<>(Arrays.asList(0));
+        Set<Integer> battery25p = new HashSet<>(Arrays.asList(25));
+        Set<Integer> battery50p = new HashSet<>(Arrays.asList(50));
+        Set<Integer> battery75p = new HashSet<>(Arrays.asList(75));
+        Set<Integer> batteryFull = new HashSet<>(Arrays.asList(100));
+
+        // Add state sets to the list in order.
+        List<Set<Integer>> stateSet = Arrays.asList(batteryDead, battery25p, battery50p,
+                battery75p, batteryFull);
+
+        createAndUploadConfig(atomTag);
+        Thread.sleep(WAIT_TIME_SHORT);
+
+        // Trigger events in same order.
+        setBatteryLevel(0);
+        Thread.sleep(WAIT_TIME_SHORT);
+        setBatteryLevel(25);
+        Thread.sleep(WAIT_TIME_SHORT);
+        setBatteryLevel(50);
+        Thread.sleep(WAIT_TIME_SHORT);
+        setBatteryLevel(75);
+        Thread.sleep(WAIT_TIME_SHORT);
+        setBatteryLevel(100);
+        Thread.sleep(WAIT_TIME_SHORT);
+
+        // Sorted list of events in order in which they occurred.
+        List<EventMetricData> data = getEventMetricDataList();
+
+        // Unfreeze battery state after test
+        resetBatteryStatus();
+        Thread.sleep(WAIT_TIME_SHORT);
+
+        // Assert that the events happened in the expected order.
+        assertStatesOccurred(stateSet, data, WAIT_TIME_SHORT,
+                atom -> atom.getBatteryLevelChanged().getBatteryLevel());
+    }
+
+    public void testScreenBrightnessChangedAtom() throws Exception {
+        if (!TESTS_ENABLED) {return;}
+
+        // Setup: record initial brightness state, set mode to manual and brightness to full.
+        int initialBrightness = getScreenBrightness();
+        boolean isInitialManual = isScreenBrightnessModeManual();
+        int initialTimeout = getScreenTimeoutMs();
+        setScreenTimeoutMs(600000);
+        turnScreenOn();
+        setScreenBrightnessMode(true);
+        setScreenBrightness(255);
+        Thread.sleep(WAIT_TIME_SHORT);
+
+        final int atomTag = Atom.SCREEN_BRIGHTNESS_CHANGED_FIELD_NUMBER;
+
+        Set<Integer> screenMin = new HashSet<>(Arrays.asList(25));
+        Set<Integer> screen100 = new HashSet<>(Arrays.asList(100));
+        Set<Integer> screen200 = new HashSet<>(Arrays.asList(200));
+        Set<Integer> screenMax = new HashSet<>(Arrays.asList(255));
+
+        // Add state sets to the list in order.
+        List<Set<Integer>> stateSet = Arrays.asList(screenMin, screen100, screen200, screenMax);
+
+        createAndUploadConfig(atomTag);
+        Thread.sleep(WAIT_TIME_SHORT);
+
+        // Trigger events in same order.
+        setScreenBrightness(25);
+        Thread.sleep(WAIT_TIME_SHORT);
+        setScreenBrightness(100);
+        Thread.sleep(WAIT_TIME_SHORT);
+        setScreenBrightness(200);
+        Thread.sleep(WAIT_TIME_SHORT);
+        setScreenBrightness(255);
+        Thread.sleep(WAIT_TIME_SHORT);
+
+
+        // Sorted list of events in order in which they occurred.
+        List<EventMetricData> data = getEventMetricDataList();
+
+        // Restore initial screen brightness
+        setScreenBrightness(initialBrightness);
+        setScreenBrightnessMode(isInitialManual);
+        setScreenTimeoutMs(initialTimeout);
+        turnScreenOff();
+        Thread.sleep(WAIT_TIME_SHORT);
+
+        // Assert that the events happened in the expected order.
+        assertStatesOccurred(stateSet, data, WAIT_TIME_SHORT,
+                atom -> atom.getScreenBrightnessChanged().getLevel());
+    }
+
+    public void testDeviceIdleModeStateChangedAtom() throws Exception {
+        if (!TESTS_ENABLED) {return;}
+
+        // Setup, leave doze mode.
+        leaveDozeMode();
+        Thread.sleep(WAIT_TIME_SHORT);
+
+        final int atomTag = Atom.DEVICE_IDLE_MODE_STATE_CHANGED_FIELD_NUMBER;
+
+        Set<Integer> dozeOff = new HashSet<>(
+                Arrays.asList(DeviceIdleModeStateChanged.State.DEVICE_IDLE_MODE_OFF_VALUE));
+        Set<Integer> dozeLight = new HashSet<>(
+                Arrays.asList(DeviceIdleModeStateChanged.State.DEVICE_IDLE_MODE_LIGHT_VALUE));
+        Set<Integer> dozeDeep = new HashSet<>(
+                Arrays.asList(DeviceIdleModeStateChanged.State.DEVICE_IDLE_MODE_DEEP_VALUE));
+
+        // Add state sets to the list in order.
+        List<Set<Integer>> stateSet = Arrays.asList(dozeLight, dozeDeep, dozeOff);
+
+        createAndUploadConfig(atomTag);
+        Thread.sleep(WAIT_TIME_SHORT);
+
+        // Trigger events in same order.
+        enterDozeModeLight();
+        Thread.sleep(WAIT_TIME_SHORT);
+        enterDozeModeDeep();
+        Thread.sleep(WAIT_TIME_SHORT);
+        leaveDozeMode();
+        Thread.sleep(WAIT_TIME_SHORT);
+
+        // Sorted list of events in order in which they occurred.
+        List<EventMetricData> data = getEventMetricDataList();;
+
+        // Assert that the events happened in the expected order.
+        assertStatesOccurred(stateSet, data, WAIT_TIME_SHORT,
+                atom -> atom.getDeviceIdleModeStateChanged().getState().getNumber());
+    }
+
+    public void testBatterySaverModeStateChangedAtom() throws Exception {
+        if (!TESTS_ENABLED) {return;}
+
+        // Setup, turn off battery saver.
+        turnBatterySaverOff();
+        Thread.sleep(WAIT_TIME_SHORT);
+
+        final int atomTag = Atom.BATTERY_SAVER_MODE_STATE_CHANGED_FIELD_NUMBER;
+
+        Set<Integer> batterySaverOn = new HashSet<>(
+                Arrays.asList(BatterySaverModeStateChanged.State.ON_VALUE));
+        Set<Integer> batterySaverOff = new HashSet<>(
+                Arrays.asList(BatterySaverModeStateChanged.State.OFF_VALUE));
+
+        // Add state sets to the list in order.
+        List<Set<Integer>> stateSet = Arrays.asList(batterySaverOn, batterySaverOff);
+
+        createAndUploadConfig(atomTag);
+        Thread.sleep(WAIT_TIME_SHORT);
+
+        // Trigger events in same order.
+        turnBatterySaverOn();
+        Thread.sleep(WAIT_TIME_SHORT);
+        turnBatterySaverOff();
+        Thread.sleep(WAIT_TIME_SHORT);
+
+        // Sorted list of events in order in which they occurred.
+        List<EventMetricData> data = getEventMetricDataList();
+
+        // Assert that the events happened in the expected order.
+        assertStatesOccurred(stateSet, data, WAIT_TIME_SHORT,
+                atom -> atom.getBatterySaverModeStateChanged().getState().getNumber());
+    }
+
+    // TODO: Anomaly detection will be moved to general statsd device-side tests.
+    // Tests that anomaly detection for count works.
+    // Also tests that anomaly detection works when spanning multiple buckets.
+    public void testCountAnomalyDetection() throws Exception {
+        if (!TESTS_ENABLED) return;
+        if (!INCIDENTD_TESTS_ENABLED) return;
+        // TODO: Don't use screen-state as the atom.
+        StatsdConfig config = getPulledAndAnomalyConfig()
+                .addCountMetric(CountMetric.newBuilder()
+                        .setId("METRIC".hashCode())
+                        .setWhat("SCREEN_TURNED_ON".hashCode())
+                        .setBucket(TimeUnit.CTS)
+                )
+                .addAlert(Alert.newBuilder()
+                        .setId("testCountAnomalyDetectionAlert".hashCode())
+                        .setMetricId("METRIC".hashCode())
+                        .setNumBuckets(4)
+                        .setRefractoryPeriodSecs(20)
+                        .setTriggerIfSumGt(2)
+                )
+                .addSubscription(Subscription.newBuilder()
+                        .setId("AlertSub".hashCode())
+                        .setRuleType(Subscription.RuleType.ALERT)
+                        .setRuleId("testCountAnomalyDetectionAlert".hashCode())
+                        .setIncidentdDetails(IncidentdDetails.newBuilder().addSection(-1))
+                )
+                .build();
+        uploadConfig(config);
+
+        String markDeviceDate = getCurrentLogcatDate();
+        turnScreenOn(); // count -> 1 (not an anomaly, since not "greater than 2")
+        Thread.sleep(1000);
+        turnScreenOff();
+        Thread.sleep(3000);
+        assertFalse(didIncidentdFireSince(markDeviceDate));
+
+        turnScreenOn(); // count ->2 (not an anomaly, since not "greater than 2")
+        Thread.sleep(1000);
+        turnScreenOff();
+        Thread.sleep(1000);
+        assertFalse(didIncidentdFireSince(markDeviceDate));
+
+        turnScreenOn(); // count ->3 (anomaly, since "greater than 2"!)
+        Thread.sleep(1000);
+        assertTrue(didIncidentdFireSince(markDeviceDate));
+
+        turnScreenOff();
+    }
+
+    // Tests that anomaly detection for duration works.
+    // Also tests that refractory periods in anomaly detection work.
+    public void testDurationAnomalyDetection() throws Exception {
+        if (!TESTS_ENABLED) return;
+        if (!INCIDENTD_TESTS_ENABLED) return;
+        // TODO: Do NOT use screenState for this, since screens auto-turn-off after a variable time.
+        StatsdConfig config = getPulledAndAnomalyConfig()
+                .addDurationMetric(DurationMetric.newBuilder()
+                        .setId("METRIC".hashCode())
+                        .setWhat("SCREEN_IS_ON".hashCode())
+                        .setAggregationType(DurationMetric.AggregationType.SUM)
+                        .setBucket(TimeUnit.CTS)
+                )
+                .addAlert(Alert.newBuilder()
+                        .setId("testDurationAnomalyDetectionAlert".hashCode())
+                        .setMetricId("METRIC".hashCode())
+                        .setNumBuckets(12)
+                        .setRefractoryPeriodSecs(20)
+                        .setTriggerIfSumGt(15_000_000_000L) // 15 seconds in nanoseconds
+                )
+                .addSubscription(Subscription.newBuilder()
+                        .setId("AlertSub".hashCode())
+                        .setRuleType(Subscription.RuleType.ALERT)
+                        .setRuleId("testDurationAnomalyDetectionAlert".hashCode())
+                        .setIncidentdDetails(IncidentdDetails.newBuilder().addSection(-1))
+                )
+                .build();
+        uploadConfig(config);
+
+        // Test that alarm doesn't fire early.
+        String markDeviceDate = getCurrentLogcatDate();
+        turnScreenOn();
+        Thread.sleep(6_000);
+        assertFalse(didIncidentdFireSince(markDeviceDate));
+
+        turnScreenOff();
+        Thread.sleep(1_000);
+        assertFalse(didIncidentdFireSince(markDeviceDate));
+
+        // Test that alarm does fire when it is supposed to.
+        turnScreenOn();
+        Thread.sleep(13_000);
+        assertTrue(didIncidentdFireSince(markDeviceDate));
+
+        // Now test that the refractory period is obeyed.
+        markDeviceDate = getCurrentLogcatDate();
+        turnScreenOff();
+        Thread.sleep(1_000);
+        turnScreenOn();
+        Thread.sleep(1_000);
+        assertFalse(didIncidentdFireSince(markDeviceDate));
+
+        // Test that detection works again after refractory period finishes.
+        turnScreenOff();
+        Thread.sleep(20_000);
+        turnScreenOn();
+        Thread.sleep(15_000);
+        assertTrue(didIncidentdFireSince(markDeviceDate));
+    }
+
+    // TODO: There is no value anomaly detection code yet! So this will fail.
+    // Tests that anomaly detection for value works.
+    public void testValueAnomalyDetection() throws Exception {
+        if (!TESTS_ENABLED) return;
+        if (!INCIDENTD_TESTS_ENABLED) return;
+        // TODO: Definitely don't use screen-state as the atom. This MUST be changed.
+        StatsdConfig config = getPulledAndAnomalyConfig()
+                .addValueMetric(ValueMetric.newBuilder()
+                        .setId("METRIC".hashCode())
+                        .setWhat("SCREEN_TURNED_ON".hashCode())
+                        .setValueField(FieldMatcher.newBuilder()
+                                .setField(Atom.SCREEN_STATE_CHANGED_FIELD_NUMBER)
+                                .addChild(FieldMatcher.newBuilder()
+                                        .setField(ScreenStateChanged.DISPLAY_STATE_FIELD_NUMBER)))
+                        .setBucket(TimeUnit.CTS)
+                )
+                .addAlert(Alert.newBuilder()
+                        .setId("testValueAnomalyDetectionAlert".hashCode())
+                        .setMetricId("METRIC".hashCode())
+                        .setNumBuckets(4)
+                        .setRefractoryPeriodSecs(20)
+                        .setTriggerIfSumGt(ScreenStateChanged.State.STATE_OFF.getNumber())
+                )
+                .addSubscription(Subscription.newBuilder()
+                        .setId("AlertSub".hashCode())
+                        .setRuleType(Subscription.RuleType.ALERT)
+                        .setRuleId("testValueAnomalyDetectionAlert".hashCode())
+                        .setIncidentdDetails(IncidentdDetails.newBuilder().addSection(-1))
+                )
+                .build();
+        uploadConfig(config);
+
+        turnScreenOff();
+        String markDeviceDate = getCurrentLogcatDate();
+        turnScreenOff(); // value = STATE_OFF = 1 (probably)
+        Thread.sleep(2000);
+        assertFalse(didIncidentdFireSince(markDeviceDate));
+        turnScreenOn(); // value = STATE_ON = 2 (probably)
+        Thread.sleep(2000);
+        assertTrue(didIncidentdFireSince(markDeviceDate));
+
+        turnScreenOff();
+    }
+
+    // Tests that anomaly detection for gauge works.
+    public void testGaugeAnomalyDetection() throws Exception {
+        if (!TESTS_ENABLED) return;
+        if (!INCIDENTD_TESTS_ENABLED) return;
+        // TODO: Definitely don't use screen-state as the atom. This MUST be changed.
+        StatsdConfig config = getPulledAndAnomalyConfig()
+                .addGaugeMetric(GaugeMetric.newBuilder()
+                        .setId("METRIC".hashCode())
+                        .setWhat("SCREEN_TURNED_ON".hashCode())
+                        .setGaugeFieldsFilter(
+                                FieldFilter.newBuilder()
+                                        .setFields(FieldMatcher.newBuilder()
+                                                .setField(Atom.SCREEN_STATE_CHANGED_FIELD_NUMBER)
+                                                .addChild(FieldMatcher.newBuilder()
+                                                        .setField(ScreenStateChanged.DISPLAY_STATE_FIELD_NUMBER))
+                                        ))
+                        .setBucket(TimeUnit.CTS)
+                )
+                .addAlert(Alert.newBuilder()
+                        .setId("testGaugeAnomalyDetectionAlert".hashCode())
+                        .setMetricId("METRIC".hashCode())
+                        .setNumBuckets(1)
+                        .setRefractoryPeriodSecs(20)
+                        .setTriggerIfSumGt(ScreenStateChanged.State.STATE_OFF.getNumber())
+                )
+                .addSubscription(Subscription.newBuilder()
+                        .setId("AlertSub".hashCode())
+                        .setRuleType(Subscription.RuleType.ALERT)
+                        .setRuleId("testGaugeAnomalyDetectionAlert".hashCode())
+                        .setIncidentdDetails(IncidentdDetails.newBuilder().addSection(-1))
+                )
+                .build();
+        uploadConfig(config);
+
+        turnScreenOff();
+        String markDeviceDate = getCurrentLogcatDate();
+        turnScreenOff(); // gauge = STATE_OFF = 1 (probably)
+        Thread.sleep(2000);
+        assertFalse(didIncidentdFireSince(markDeviceDate));
+        turnScreenOn(); // gauge = STATE_ON = 2 (probably)
+        Thread.sleep(2000);
+        assertTrue(didIncidentdFireSince(markDeviceDate));
+
+        turnScreenOff();
+    }
+
+    public void testKernelWakelock() throws Exception {
+        if (!TESTS_ENABLED) {return;}
+        StatsdConfig.Builder config = getPulledAndAnomalyConfig();
+        FieldMatcher.Builder dimension = FieldMatcher.newBuilder()
+                .setField(Atom.KERNEL_WAKELOCK_FIELD_NUMBER)
+                .addChild(FieldMatcher.newBuilder()
+                        .setField(KernelWakelock.NAME_FIELD_NUMBER));
+        addGaugeAtom(config, Atom.KERNEL_WAKELOCK_FIELD_NUMBER, dimension);
+
+        turnScreenOff();
+
+        uploadConfig(config);
+
+        Thread.sleep(WAIT_TIME_LONG);
+        turnScreenOn();
+        Thread.sleep(WAIT_TIME_LONG);
+
+        List<Atom> data = getGaugeMetricDataList();
+
+        Atom atom = data.get(0);
+        assertTrue(!atom.getKernelWakelock().getName().equals(""));
+        assertTrue(atom.getKernelWakelock().hasCount());
+        assertTrue(atom.getKernelWakelock().hasVersion());
+        assertTrue(atom.getKernelWakelock().getVersion() > 0);
+        assertTrue(atom.getKernelWakelock().hasTime());
+    }
+
+    public void testCpuTimePerFreq() throws Exception {
+        if (!TESTS_ENABLED) {return;}
+        StatsdConfig.Builder config = getPulledAndAnomalyConfig();
+        FieldMatcher.Builder dimension = FieldMatcher.newBuilder()
+                .setField(Atom.CPU_TIME_PER_FREQ_FIELD_NUMBER)
+                .addChild(FieldMatcher.newBuilder()
+                        .setField(CpuTimePerFreq.CLUSTER_FIELD_NUMBER));
+        addGaugeAtom(config, Atom.CPU_TIME_PER_FREQ_FIELD_NUMBER, dimension);
+
+        turnScreenOff();
+
+        uploadConfig(config);
+
+        Thread.sleep(2000);
+        turnScreenOn();
+        Thread.sleep(2000);
+
+        List<Atom> data = getGaugeMetricDataList();
+
+        Atom atom = data.get(0);
+        assertTrue(atom.getCpuTimePerFreq().getCluster() >= 0);
+        assertTrue(atom.getCpuTimePerFreq().getFreqIndex() >= 0);
+        assertTrue(atom.getCpuTimePerFreq().getTimeMs() > 0);
+    }
+
+    public void testCpuTimePerUidFreq() throws Exception {
+        if (!TESTS_ENABLED) {return;}
+        StatsdConfig.Builder config = getPulledAndAnomalyConfig();
+        FieldMatcher.Builder dimension = FieldMatcher.newBuilder()
+                .setField(Atom.CPU_TIME_PER_UID_FREQ_FIELD_NUMBER)
+                .addChild(FieldMatcher.newBuilder()
+                        .setField(CpuTimePerUidFreq.UID_FIELD_NUMBER));
+        addGaugeAtom(config, Atom.CPU_TIME_PER_UID_FREQ_FIELD_NUMBER, dimension);
+
+        turnScreenOff();
+
+        uploadConfig(config);
+
+        Thread.sleep(WAIT_TIME_LONG);
+        turnScreenOn();
+        Thread.sleep(WAIT_TIME_LONG);
+
+        List<Atom> data = getGaugeMetricDataList();
+
+        Atom atom = data.get(0);
+        assertTrue(atom.getCpuTimePerUidFreq().getUid() > 0);
+        assertTrue(atom.getCpuTimePerUidFreq().getFreqIdx() >= 0);
+        assertTrue(atom.getCpuTimePerUidFreq().getTimeMs() > 0);
+    }
+
+    public void testCpuTimePerUid() throws Exception {
+        if (!TESTS_ENABLED) {return;}
+        StatsdConfig.Builder config = getPulledAndAnomalyConfig();
+        FieldMatcher.Builder dimension = FieldMatcher.newBuilder()
+                .setField(Atom.CPU_TIME_PER_UID_FIELD_NUMBER)
+                .addChild(FieldMatcher.newBuilder()
+                        .setField(CpuTimePerUid.UID_FIELD_NUMBER));
+        addGaugeAtom(config, Atom.CPU_TIME_PER_UID_FIELD_NUMBER, dimension);
+
+        turnScreenOff();
+
+        uploadConfig(config);
+
+        Thread.sleep(WAIT_TIME_LONG);
+        turnScreenOn();
+        Thread.sleep(WAIT_TIME_LONG);
+
+        List<Atom> data = getGaugeMetricDataList();
+
+        Atom atom = data.get(0);
+        assertTrue(atom.getCpuTimePerUid().getUid() > 0);
+        assertTrue(atom.getCpuTimePerUid().getUserTimeMs() > 0);
+        assertTrue(atom.getCpuTimePerUid().getSysTimeMs() > 0);
+    }
+
+    public void testPlatformSleepState() throws Exception {
+        if (!TESTS_ENABLED) {return;}
+        StatsdConfig.Builder config = getPulledAndAnomalyConfig();
+        FieldMatcher.Builder dimension = FieldMatcher.newBuilder()
+                .setField(Atom.PLATFORM_SLEEP_STATE_FIELD_NUMBER)
+                .addChild(FieldMatcher.newBuilder()
+                        .setField(PlatformSleepState.NAME_FIELD_NUMBER));
+        addGaugeAtom(config, Atom.PLATFORM_SLEEP_STATE_FIELD_NUMBER, dimension);
+
+        turnScreenOff();
+
+        uploadConfig(config);
+
+        Thread.sleep(WAIT_TIME_LONG);
+        turnScreenOn();
+        Thread.sleep(WAIT_TIME_LONG);
+
+        List<Atom> data = getGaugeMetricDataList();
+
+        Atom atom = data.get(0);
+        assertTrue(!atom.getPlatformSleepState().getName().equals(""));
+    }
+
+    public void testSleepStateVoter() throws Exception {
+        if (!TESTS_ENABLED) {return;}
+        StatsdConfig.Builder config = getPulledAndAnomalyConfig();
+        FieldMatcher.Builder dimension = FieldMatcher.newBuilder()
+                .setField(Atom.SLEEP_STATE_VOTER_FIELD_NUMBER)
+                .addChild(FieldMatcher.newBuilder()
+                        .setField(SleepStateVoter.VOTER_NAME_FIELD_NUMBER));
+        addGaugeAtom(config, Atom.SLEEP_STATE_VOTER_FIELD_NUMBER, dimension);
+
+        turnScreenOff();
+
+        uploadConfig(config);
+
+        Thread.sleep(WAIT_TIME_LONG);
+        turnScreenOn();
+        Thread.sleep(WAIT_TIME_LONG);
+
+        List<Atom> data = getGaugeMetricDataList();
+
+        Atom atom = data.get(0);
+        assertTrue(!atom.getSleepStateVoter().getPlatformSleepStateName().equals(""));
+        assertTrue(!atom.getSleepStateVoter().getVoterName().equals(""));
+    }
+
+    public void testSubsystemSleepState() throws Exception {
+        if (!TESTS_ENABLED) {return;}
+        StatsdConfig.Builder config = getPulledAndAnomalyConfig();
+        FieldMatcher.Builder dimension = FieldMatcher.newBuilder()
+                .setField(Atom.SUBSYSTEM_SLEEP_STATE_FIELD_NUMBER)
+                .addChild(FieldMatcher.newBuilder()
+                        .setField(SubsystemSleepState.SUBSYSTEM_NAME_FIELD_NUMBER));
+        addGaugeAtom(config, Atom.SUBSYSTEM_SLEEP_STATE_FIELD_NUMBER, dimension);
+
+        turnScreenOff();
+
+        uploadConfig(config);
+
+        Thread.sleep(WAIT_TIME_LONG);
+        turnScreenOn();
+        Thread.sleep(WAIT_TIME_LONG);
+
+        List<Atom> data = getGaugeMetricDataList();
+
+        Atom atom = data.get(0);
+        assertTrue(!atom.getSubsystemSleepState().getSubsystemName().equals(""));
+        assertTrue(!atom.getSubsystemSleepState().getSubsystemSleepStateName().equals(""));
+    }
+
+    /**
+     * TODO: Anomaly detection will be moved to general statsd device-side tests.
+     * Pulled atoms also should have a better way of constructing the config.
+     * Remove this config when that happens.
+     */
+    protected StatsdConfig.Builder getPulledAndAnomalyConfig() {
+        return StatsdConfig.newBuilder().setId(TEST_CONFIG_ID);
+    }
+}
\ No newline at end of file
diff --git a/hostsidetests/statsd/src/android/cts/statsd/UidAtomTests.java b/hostsidetests/statsd/src/android/cts/statsd/UidAtomTests.java
new file mode 100644
index 0000000..b0e9e5c
--- /dev/null
+++ b/hostsidetests/statsd/src/android/cts/statsd/UidAtomTests.java
@@ -0,0 +1,325 @@
+/*
+ * 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.cts.statsd;
+
+import com.android.internal.os.StatsdConfigProto.StatsdConfig;
+import com.android.os.AtomsProto.Atom;
+import com.android.os.AtomsProto.BleScanResultReceived;
+import com.android.os.AtomsProto.BleScanStateChanged;
+import com.android.os.AtomsProto.BleUnoptimizedScanStateChanged;
+import com.android.os.AtomsProto.CameraStateChanged;
+import com.android.os.AtomsProto.FlashlightStateChanged;
+import com.android.os.AtomsProto.WifiRadioPowerStateChanged;
+import com.android.os.AtomsProto.MobileRadioPowerStateChanged;
+import com.android.os.AtomsProto.AudioStateChanged;
+import com.android.os.AtomsProto.MediaCodecActivityChanged;
+import com.android.os.AtomsProto.GpsScanStateChanged;
+import com.android.os.AtomsProto.WakeupAlarmOccurred;
+import com.android.os.AtomsProto.WifiLockStateChanged;
+import com.android.os.AtomsProto.WifiScanStateChanged;
+import com.android.os.StatsLog.EventMetricData;
+import com.android.tradefed.log.LogUtil;
+
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Set;
+
+/**
+ * Statsd atom tests that are done via app, for atoms that report a uid.
+ */
+public class UidAtomTests extends DeviceAtomTestCase {
+
+    private static final String TAG = "Statsd.UidAtomTests";
+
+    private static final boolean TESTS_ENABLED = false;
+
+    // These constants are those in PackageManager.
+    private static final String FEATURE_BLUETOOTH_LE = "android.hardware.bluetooth_le";
+    private static final String FEATURE_LOCATION_GPS = "android.hardware.location.gps";
+    private static final String FEATURE_WIFI = "android.hardware.wifi";
+    private static final String FEATURE_CAMERA_FLASH = "android.hardware.camera.flash";
+    private static final String FEATURE_CAMERA = "android.hardware.camera";
+    private static final String FEATURE_CAMERA_FRONT = "android.hardware.camera.front";
+    private static final String FEATURE_AUDIO_OUTPUT = "android.hardware.audio.output";
+
+    public void testBleScan() throws Exception {
+        if (!TESTS_ENABLED) return;
+        if (!hasFeature(FEATURE_BLUETOOTH_LE, true)) return;
+
+        final int atom = Atom.BLE_SCAN_STATE_CHANGED_FIELD_NUMBER;
+        final int key = BleScanStateChanged.STATE_FIELD_NUMBER;
+        final int stateOn = BleScanStateChanged.State.ON_VALUE;
+        final int stateOff = BleScanStateChanged.State.OFF_VALUE;
+        final int minTimeDiffMs = 1_500;
+        final int maxTimeDiffMs = 3_000;
+
+        List<EventMetricData> data = doDeviceMethodOnOff("testBleScanUnoptimized", atom, key,
+                stateOn, stateOff, minTimeDiffMs, maxTimeDiffMs, true);
+
+        BleScanStateChanged a0 = data.get(0).getAtom().getBleScanStateChanged();
+        BleScanStateChanged a1 = data.get(1).getAtom().getBleScanStateChanged();
+        assertTrue(a0.getState().getNumber() == stateOn);
+        assertTrue(a1.getState().getNumber() == stateOff);
+    }
+
+    public void testBleUnoptimizedScan() throws Exception {
+        if (!TESTS_ENABLED) return;
+        if (!hasFeature(FEATURE_BLUETOOTH_LE, true)) return;
+
+        final int atom = Atom.BLE_UNOPTIMIZED_SCAN_STATE_CHANGED_FIELD_NUMBER;
+        final int key = BleUnoptimizedScanStateChanged.STATE_FIELD_NUMBER;
+        final int stateOn = BleUnoptimizedScanStateChanged.State.ON_VALUE;
+        final int stateOff = BleUnoptimizedScanStateChanged.State.OFF_VALUE;
+        final int minTimeDiffMs = 1_500;
+        final int maxTimeDiffMs = 3_000;
+
+        List<EventMetricData> data = doDeviceMethodOnOff("testBleScanUnoptimized", atom, key,
+                stateOn, stateOff, minTimeDiffMs, maxTimeDiffMs, true);
+
+        BleUnoptimizedScanStateChanged a0 = data.get(0).getAtom().getBleUnoptimizedScanStateChanged();
+        BleUnoptimizedScanStateChanged a1 = data.get(1).getAtom().getBleUnoptimizedScanStateChanged();
+        assertTrue(a0.getState().getNumber() == stateOn);
+        assertTrue(a1.getState().getNumber() == stateOff);
+
+        // Now repeat the test for optimized scanning and make sure no atoms are reported.
+        StatsdConfig.Builder conf = createConfigBuilder();
+        addAtomEvent(conf, atom, createKvm(key).setEqInt(stateOn));
+        addAtomEvent(conf, atom, createKvm(key).setEqInt(stateOff));
+        data = doDeviceMethod("testBleScanOptimized", conf);
+        assertTrue(data.isEmpty());
+    }
+
+    public void testBleScanResult() throws Exception {
+        if (!TESTS_ENABLED) return;
+        if (!hasFeature(FEATURE_BLUETOOTH_LE, true)) return;
+        turnScreenOn(); // BLE results are not given unless screen is on. TODO: make more robust.
+
+        final int atom = Atom.BLE_SCAN_RESULT_RECEIVED_FIELD_NUMBER;
+        final int key = BleScanResultReceived.NUM_OF_RESULTS_FIELD_NUMBER;
+
+        StatsdConfig.Builder conf = createConfigBuilder();
+        addAtomEvent(conf, atom, createKvm(key).setGteInt(0));
+        List<EventMetricData> data = doDeviceMethod("testBleScanResult", conf);
+
+        assertTrue(data.size() >= 1);
+        BleScanResultReceived a0 = data.get(0).getAtom().getBleScanResultReceived();
+        assertTrue(a0.getNumOfResults() >= 1);
+
+        turnScreenOff();
+    }
+
+    public void testCameraState() throws Exception {
+        if (!TESTS_ENABLED) return;
+        if (!hasFeature(FEATURE_CAMERA, true) && !hasFeature(FEATURE_CAMERA_FRONT, true)) return;
+
+        final int atomTag = Atom.CAMERA_STATE_CHANGED_FIELD_NUMBER;
+        Set<Integer> cameraOn = new HashSet<>(Arrays.asList(CameraStateChanged.State.ON_VALUE));
+        Set<Integer> cameraOff = new HashSet<>(Arrays.asList(CameraStateChanged.State.OFF_VALUE));
+
+        // Add state sets to the list in order.
+        List<Set<Integer>> stateSet = Arrays.asList(cameraOn, cameraOff);
+
+        createAndUploadConfig(atomTag);
+        runDeviceTests(DEVICE_SIDE_TEST_PACKAGE, ".AtomTests", "testCameraState");
+
+        // Sorted list of events in order in which they occurred.
+        List<EventMetricData> data = getEventMetricDataList();
+
+        // Assert that the events happened in the expected order.
+        assertStatesOccurred(stateSet, data, WAIT_TIME_LONG,
+                atom -> atom.getCameraStateChanged().getState().getNumber());
+    }
+
+    public void testFlashlightState() throws Exception {
+        if (!TESTS_ENABLED)
+            return;
+        if (!hasFeature(FEATURE_CAMERA_FLASH, true))
+            return;
+
+        final int atomTag = Atom.FLASHLIGHT_STATE_CHANGED_FIELD_NUMBER;
+        final String name = "testFlashlight";
+
+        Set<Integer> flashlightOn = new HashSet<>(
+            Arrays.asList(FlashlightStateChanged.State.ON_VALUE));
+        Set<Integer> flashlightOff = new HashSet<>(
+            Arrays.asList(FlashlightStateChanged.State.OFF_VALUE));
+
+        // Add state sets to the list in order.
+        List<Set<Integer>> stateSet = Arrays.asList(flashlightOn, flashlightOff);
+
+        createAndUploadConfig(atomTag);
+        Thread.sleep(WAIT_TIME_SHORT);
+
+        runDeviceTests(DEVICE_SIDE_TEST_PACKAGE, ".AtomTests", name);
+
+        // Sorted list of events in order in which they occurred.
+        List<EventMetricData> data = getEventMetricDataList();
+
+        // Assert that the events happened in the expected order.
+        assertStatesOccurred(stateSet, data, WAIT_TIME_SHORT,
+                atom -> atom.getFlashlightStateChanged().getState().getNumber());
+    }
+
+    public void testGpsScan() throws Exception {
+        if (!TESTS_ENABLED) return;
+        if (!hasFeature(FEATURE_LOCATION_GPS, true)) return;
+        // Whitelist this app against background location request throttling
+        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 minTimeDiffMs = 500;
+        final int maxTimeDiffMs = 60_000;
+
+        List<EventMetricData> data = doDeviceMethodOnOff("testGpsScan", atom, key,
+                stateOn, stateOff, minTimeDiffMs, maxTimeDiffMs, 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);
+    }
+
+    public void testWakeupAlarm() throws Exception {
+        if (!TESTS_ENABLED) return;
+
+        final int atomTag = Atom.WAKEUP_ALARM_OCCURRED_FIELD_NUMBER;
+
+        StatsdConfig.Builder config = createConfigBuilder();
+        addAtomEvent(config, atomTag);
+
+        List<EventMetricData> data = doDeviceMethod("testWakeupAlarm", config);
+        assertTrue(data.size() >= 1);
+        for (int i = 0; i < data.size(); i++) {
+            String tag = data.get(i).getAtom().getWakeupAlarmOccurred().getTag();
+            assertTrue(tag.equals("*walarm*:android.cts.statsd.testWakeupAlarm"));
+        }
+    }
+
+    public void testWifiLock() throws Exception {
+        if (!TESTS_ENABLED) return;
+        if (!hasFeature(FEATURE_WIFI, true)) return;
+
+        final int atomTag = Atom.WIFI_LOCK_STATE_CHANGED_FIELD_NUMBER;
+        Set<Integer> lockOn = new HashSet<>(Arrays.asList(WifiLockStateChanged.State.ON_VALUE));
+        Set<Integer> lockOff = new HashSet<>(Arrays.asList(WifiLockStateChanged.State.OFF_VALUE));
+
+        // Add state sets to the list in order.
+        List<Set<Integer>> stateSet = Arrays.asList(lockOn, lockOff);
+
+        createAndUploadConfig(atomTag);
+        runDeviceTests(DEVICE_SIDE_TEST_PACKAGE, ".AtomTests", "testWifiLock");
+
+        // Sorted list of events in order in which they occurred.
+        List<EventMetricData> data = getEventMetricDataList();
+
+        // Assert that the events happened in the expected order.
+        assertStatesOccurred(stateSet, data, WAIT_TIME_SHORT,
+            atom -> atom.getWifiLockStateChanged().getState().getNumber());
+    }
+
+    public void testWifiScan() throws Exception {
+        if (!TESTS_ENABLED) return;
+        if (!hasFeature(FEATURE_WIFI, true)) return;
+
+        final int atom = Atom.WIFI_SCAN_STATE_CHANGED_FIELD_NUMBER;
+        final int key = WifiScanStateChanged.STATE_FIELD_NUMBER;
+        final int stateOn = WifiScanStateChanged.State.ON_VALUE;
+        final int stateOff = WifiScanStateChanged.State.OFF_VALUE;
+        final int minTimeDiffMs = 500;
+        final int maxTimeDiffMs = 60_000;
+        final boolean demandExactlyTwo = false; // Two scans are performed, so up to 4 atoms logged.
+
+        List<EventMetricData> data = doDeviceMethodOnOff("testWifiScan", atom, key,
+                stateOn, stateOff, minTimeDiffMs, maxTimeDiffMs, demandExactlyTwo);
+
+        assertTrue(data.size() >= 2);
+        assertTrue(data.size() <= 4);
+        WifiScanStateChanged a0 = data.get(0).getAtom().getWifiScanStateChanged();
+        WifiScanStateChanged a1 = data.get(1).getAtom().getWifiScanStateChanged();
+        assertTrue(a0.getState().getNumber() == stateOn);
+        assertTrue(a1.getState().getNumber() == stateOff);
+    }
+
+    public void testAudioState() throws Exception {
+        if (!TESTS_ENABLED) return;
+        if (!hasFeature(FEATURE_AUDIO_OUTPUT, true)) return;
+
+        final int atomTag = Atom.AUDIO_STATE_CHANGED_FIELD_NUMBER;
+        final String name = "testAudioState";
+
+        Set<Integer> onState = new HashSet<>(
+                Arrays.asList(AudioStateChanged.State.ON_VALUE));
+        Set<Integer> offState = new HashSet<>(
+                Arrays.asList(AudioStateChanged.State.OFF_VALUE));
+
+        // Add state sets to the list in order.
+        List<Set<Integer>> stateSet = Arrays.asList(onState, offState);
+
+        createAndUploadConfig(atomTag);
+        Thread.sleep(WAIT_TIME_SHORT);
+
+        runDeviceTests(DEVICE_SIDE_TEST_PACKAGE, ".AtomTests", name);
+
+        Thread.sleep(WAIT_TIME_SHORT);
+        // Sorted list of events in order in which they occurred.
+        List<EventMetricData> data = getEventMetricDataList();
+
+        // Sorted list of events in order in which they occurred.
+        // Assert that the events happened in the expected order.
+        assertStatesOccurred(stateSet, data, 200,
+                atom -> atom.getAudioStateChanged().getState().getNumber());
+    }
+
+    public void testMediaCodecActivity() throws Exception {
+        if (!TESTS_ENABLED) return;
+        final int atomTag = Atom.MEDIA_CODEC_ACTIVITY_CHANGED_FIELD_NUMBER;
+
+        Set<Integer> onState = new HashSet<>(
+                Arrays.asList(MediaCodecActivityChanged.State.ON_VALUE));
+        Set<Integer> offState = new HashSet<>(
+                Arrays.asList(MediaCodecActivityChanged.State.OFF_VALUE));
+
+        // Add state sets to the list in order.
+        List<Set<Integer>> stateSet = Arrays.asList(onState, offState);
+
+        createAndUploadConfig(atomTag);
+        Thread.sleep(WAIT_TIME_SHORT);
+        turnScreenOn();
+
+        getDevice().executeShellCommand(
+                "am start -n com.android.server.cts.device.statsd/.VideoPlayerActivity");
+
+        Thread.sleep(WAIT_TIME_LONG);
+        getDevice().executeShellCommand(
+                "am force-stop com.android.server.cts.device.statsd");
+
+        Thread.sleep(WAIT_TIME_SHORT);
+
+        // Sorted list of events in order in which they occurred.
+        List<EventMetricData> data = getEventMetricDataList();
+
+        // Assert that the events happened in the expected order.
+        assertStatesOccurred(stateSet, data, WAIT_TIME_LONG,
+                atom -> atom.getMediaCodecActivityChanged().getState().getNumber());
+    }
+}
diff --git a/hostsidetests/sustainedperf/AndroidTest.xml b/hostsidetests/sustainedperf/AndroidTest.xml
index e7cbee8..dd49674 100644
--- a/hostsidetests/sustainedperf/AndroidTest.xml
+++ b/hostsidetests/sustainedperf/AndroidTest.xml
@@ -14,6 +14,7 @@
      limitations under the License.
 -->
 <configuration description="Config for CTS Sustained Performance host test cases">
+    <option name="test-suite-tag" value="cts" />
     <option name="config-descriptor:metadata" key="component" value="systems" />
     <target_preparer class="com.android.tradefed.targetprep.suite.SuiteApkInstaller">
         <option name="cleanup-apks" value="true" />
diff --git a/hostsidetests/systemui/AndroidTest.xml b/hostsidetests/systemui/AndroidTest.xml
index 18e44ee..e1ae6e9 100644
--- a/hostsidetests/systemui/AndroidTest.xml
+++ b/hostsidetests/systemui/AndroidTest.xml
@@ -14,6 +14,7 @@
      limitations under the License.
 -->
 <configuration description="Config for CTS System UI host test cases">
+    <option name="test-suite-tag" value="cts" />
     <option name="config-descriptor:metadata" key="component" value="sysui" />
     <target_preparer class="com.android.tradefed.targetprep.suite.SuiteApkInstaller">
         <option name="cleanup-apks" value="true" />
diff --git a/hostsidetests/theme/AndroidTest.xml b/hostsidetests/theme/AndroidTest.xml
index fb5d6bb..beb9218 100644
--- a/hostsidetests/theme/AndroidTest.xml
+++ b/hostsidetests/theme/AndroidTest.xml
@@ -14,6 +14,7 @@
      limitations under the License.
 -->
 <configuration description="Config for CTS Theme host test cases">
+    <option name="test-suite-tag" value="cts" />
     <option name="config-descriptor:metadata" key="component" value="uitoolkit" />
     <target_preparer class="com.android.tradefed.targetprep.suite.SuiteApkInstaller">
         <option name="cleanup-apks" value="true" />
diff --git a/hostsidetests/theme/README b/hostsidetests/theme/README
index 4f93e5a..4bf32cd 100644
--- a/hostsidetests/theme/README
+++ b/hostsidetests/theme/README
@@ -67,6 +67,22 @@
 
      ./cts/hostsidetests/theme/generate_images.py
 
+There is an option to build locally an Android system image and use an emulator that is stored in
+Android source tree under "prebuilts/android-emulator/linux-x86_64/emulator". This option does not
+require a SDK and can be used to generate images with locally modified source code: for example
+right before making a test breaking change.
+
+  1. From the console, set up your build environment for sdk_phone_x86_64 and build Android and CTS:
+
+     lunch sdk_phone_x86_64-userdebug && make -j32 && make cts -j32
+
+  2. Use the reference image script to generate the reference images. The script
+     will automatically start the emulator in the required configurations and
+     install the resulting reference images in assets/<platform>/<dpi>.zip,
+     overwriting any existing images.
+
+     ./cts/hostsidetests/theme/generate_images.py local
+
 A complete collection of reference images for a given API revision must include
 a set for each possible DPI bucket (tvdpi, xxhdpi, etc.) that may be tested.
 
diff --git a/hostsidetests/theme/assets/P b/hostsidetests/theme/assets/P
deleted file mode 120000
index a5c750f..0000000
--- a/hostsidetests/theme/assets/P
+++ /dev/null
@@ -1 +0,0 @@
-27
\ No newline at end of file
diff --git a/hostsidetests/theme/assets/P/260dpi.zip b/hostsidetests/theme/assets/P/260dpi.zip
new file mode 100644
index 0000000..b34fde7
--- /dev/null
+++ b/hostsidetests/theme/assets/P/260dpi.zip
Binary files differ
diff --git a/hostsidetests/theme/assets/P/280dpi.zip b/hostsidetests/theme/assets/P/280dpi.zip
new file mode 100644
index 0000000..f4185a4
--- /dev/null
+++ b/hostsidetests/theme/assets/P/280dpi.zip
Binary files differ
diff --git a/hostsidetests/theme/assets/P/300dpi.zip b/hostsidetests/theme/assets/P/300dpi.zip
new file mode 100644
index 0000000..617aac3
--- /dev/null
+++ b/hostsidetests/theme/assets/P/300dpi.zip
Binary files differ
diff --git a/hostsidetests/theme/assets/P/340dpi.zip b/hostsidetests/theme/assets/P/340dpi.zip
new file mode 100644
index 0000000..98042c3
--- /dev/null
+++ b/hostsidetests/theme/assets/P/340dpi.zip
Binary files differ
diff --git a/hostsidetests/theme/assets/P/360dpi.zip b/hostsidetests/theme/assets/P/360dpi.zip
new file mode 100644
index 0000000..296dffb
--- /dev/null
+++ b/hostsidetests/theme/assets/P/360dpi.zip
Binary files differ
diff --git a/hostsidetests/theme/assets/P/400dpi.zip b/hostsidetests/theme/assets/P/400dpi.zip
new file mode 100644
index 0000000..b1f30ca
--- /dev/null
+++ b/hostsidetests/theme/assets/P/400dpi.zip
Binary files differ
diff --git a/hostsidetests/theme/assets/P/420dpi.zip b/hostsidetests/theme/assets/P/420dpi.zip
new file mode 100644
index 0000000..e159ab2
--- /dev/null
+++ b/hostsidetests/theme/assets/P/420dpi.zip
Binary files differ
diff --git a/hostsidetests/theme/assets/P/560dpi.zip b/hostsidetests/theme/assets/P/560dpi.zip
new file mode 100644
index 0000000..dff72b0
--- /dev/null
+++ b/hostsidetests/theme/assets/P/560dpi.zip
Binary files differ
diff --git a/hostsidetests/theme/assets/P/hdpi.zip b/hostsidetests/theme/assets/P/hdpi.zip
new file mode 100644
index 0000000..9fe11cc
--- /dev/null
+++ b/hostsidetests/theme/assets/P/hdpi.zip
Binary files differ
diff --git a/hostsidetests/theme/assets/P/ldpi.zip b/hostsidetests/theme/assets/P/ldpi.zip
new file mode 100644
index 0000000..8c5de21
--- /dev/null
+++ b/hostsidetests/theme/assets/P/ldpi.zip
Binary files differ
diff --git a/hostsidetests/theme/assets/P/mdpi.zip b/hostsidetests/theme/assets/P/mdpi.zip
new file mode 100644
index 0000000..ddd08b3
--- /dev/null
+++ b/hostsidetests/theme/assets/P/mdpi.zip
Binary files differ
diff --git a/hostsidetests/theme/assets/P/tvdpi.zip b/hostsidetests/theme/assets/P/tvdpi.zip
new file mode 100644
index 0000000..dc40713
--- /dev/null
+++ b/hostsidetests/theme/assets/P/tvdpi.zip
Binary files differ
diff --git a/hostsidetests/theme/assets/P/xhdpi.zip b/hostsidetests/theme/assets/P/xhdpi.zip
new file mode 100644
index 0000000..4c63343
--- /dev/null
+++ b/hostsidetests/theme/assets/P/xhdpi.zip
Binary files differ
diff --git a/hostsidetests/theme/assets/P/xxhdpi.zip b/hostsidetests/theme/assets/P/xxhdpi.zip
new file mode 100644
index 0000000..b4c6a78
--- /dev/null
+++ b/hostsidetests/theme/assets/P/xxhdpi.zip
Binary files differ
diff --git a/hostsidetests/theme/assets/P/xxxhdpi.zip b/hostsidetests/theme/assets/P/xxxhdpi.zip
new file mode 100644
index 0000000..891a0cd
--- /dev/null
+++ b/hostsidetests/theme/assets/P/xxxhdpi.zip
Binary files differ
diff --git a/hostsidetests/theme/avd.py b/hostsidetests/theme/avd.py
index 4c444a2..2a43129 100644
--- a/hostsidetests/theme/avd.py
+++ b/hostsidetests/theme/avd.py
@@ -46,8 +46,12 @@
         port_adb = find_free_port()
         port_tty = find_free_port()
         # -no-window might be useful here
-        emu_cmd = "%s -avd %s %s-ports %d,%d" \
-                  % (self._emu_path, self._name, self._opts, port_adb, port_tty)
+        if self._name == "local":
+            emu_cmd = "emulator %s-ports %d,%d -gpu on -wipe-data" \
+                      % (self._opts, port_adb, port_tty)
+        else:
+            emu_cmd = "%s -avd %s %s-ports %d,%d" \
+                      % (self._emu_path, self._name, self._opts, port_adb, port_tty)
         print(emu_cmd)
 
         emu_proc = subprocess.Popen(emu_cmd.split(" "), bufsize=-1, stdout=subprocess.PIPE,
diff --git a/hostsidetests/theme/generate_images.py b/hostsidetests/theme/generate_images.py
index 8b70f00..5dcc76f 100755
--- a/hostsidetests/theme/generate_images.py
+++ b/hostsidetests/theme/generate_images.py
@@ -50,7 +50,7 @@
 
 
 class ParallelExecutor(threading.Thread):
-    def __init__(self, tasks, q):
+    def __init__(self, tasks, setup, q):
         threading.Thread.__init__(self)
         self._q = q
         self._tasks = tasks
@@ -60,7 +60,7 @@
     def run(self):
         try:
             while True:
-                config = q.get(block=True, timeout=2)
+                config = self._q.get(block=True, timeout=2)
                 for t in self._tasks:
                     try:
                         if t(self._setup, config):
@@ -70,7 +70,7 @@
                     except:
                         print("Failed to execute thread:", sys.exc_info()[0])
                         traceback.print_exc()
-                q.task_done()
+                self._q.task_done()
         except KeyboardInterrupt:
             raise
         except Empty:
@@ -86,7 +86,7 @@
     result = 0
     threads = []
     for i in range(num_threads):
-        t = ParallelExecutor(tasks, q)
+        t = ParallelExecutor(tasks, setup, q)
         t.start()
         threads.append(t)
     for t in threads:
@@ -181,7 +181,10 @@
 
 
 def start_emulator(name, density):
-    emu_path = get_emulator_path()
+    if name == "local":
+        emu_path = ""
+    else:
+        emu_path = get_emulator_path()
 
     # Start emulator for 560dpi, normal screen size.
     test_avd = AVD(name, emu_path)
@@ -226,18 +229,21 @@
         tasks = [do_capture]
         setup = (theme_apk, out_path)
 
-        devices = enumerate_android_devices('emulator')
+        devices = enumerate_android_devices()
 
-        device_queue = Queue()
-        for device in devices:
-            device_queue.put(device)
+        if len(devices) > 0:
+            device_queue = Queue()
+            for device in devices:
+                device_queue.put(device)
 
-        result = execute_parallel(tasks, setup, device_queue, len(devices))
+            result = execute_parallel(tasks, setup, device_queue, len(devices))
 
-        if result > 0:
-            print('Generated reference images for %(count)d devices' % {"count": result})
+            if result > 0:
+                print('Generated reference images for %(count)d devices' % {"count": result})
+            else:
+                print('Failed to generate reference images')
         else:
-            print('Failed to generate reference images')
+            print('No devices found')
 
 
 if __name__ == '__main__':
diff --git a/hostsidetests/theme/src/android/theme/cts/ThemeHostTest.java b/hostsidetests/theme/src/android/theme/cts/ThemeHostTest.java
index 5c82e2a..017febc 100644
--- a/hostsidetests/theme/src/android/theme/cts/ThemeHostTest.java
+++ b/hostsidetests/theme/src/android/theme/cts/ThemeHostTest.java
@@ -93,7 +93,7 @@
         mDevice = getDevice();
         mHardwareType = mDevice.executeShellCommand(HARDWARE_TYPE_CMD).trim();
 
-        final String density = getDensityBucketForDevice(mDevice, mHardwareType);
+        final String density = getDensityBucketForDevice(mDevice);
         final String referenceZipAssetPath = String.format("/%s.zip", density);
         mReferences = extractReferenceImages(referenceZipAssetPath);
 
@@ -231,11 +231,7 @@
         return receiver.getOutput().contains("OK ");
     }
 
-    private static String getDensityBucketForDevice(ITestDevice device, String hardwareType) {
-        if (hardwareType.contains("android.hardware.type.television")) {
-            // references images for tv are under bucket "tvdpi".
-            return "tvdpi";
-        }
+    private static String getDensityBucketForDevice(ITestDevice device) {
         final int density;
         try {
             density = getDensityForDevice(device);
@@ -250,6 +246,9 @@
             case 160:
                 bucket = "mdpi";
                 break;
+            case 213:
+                bucket = "tvdpi";
+                break;
             case 240:
                 bucket = "hdpi";
                 break;
diff --git a/hostsidetests/trustedvoice/AndroidTest.xml b/hostsidetests/trustedvoice/AndroidTest.xml
index 3e23236..2767992 100644
--- a/hostsidetests/trustedvoice/AndroidTest.xml
+++ b/hostsidetests/trustedvoice/AndroidTest.xml
@@ -14,6 +14,7 @@
      limitations under the License.
 -->
 <configuration description="Config for CTS Trustedvoice host test cases">
+    <option name="test-suite-tag" value="cts" />
     <option name="config-descriptor:metadata" key="component" value="framework" />
     <target_preparer class="com.android.tradefed.targetprep.suite.SuiteApkInstaller">
         <option name="cleanup-apks" value="true" />
diff --git a/hostsidetests/tv/AndroidTest.xml b/hostsidetests/tv/AndroidTest.xml
index d9edad0..07c38de 100644
--- a/hostsidetests/tv/AndroidTest.xml
+++ b/hostsidetests/tv/AndroidTest.xml
@@ -14,6 +14,7 @@
      limitations under the License.
 -->
 <configuration description="Config for CTS tv host test cases">
+    <option name="test-suite-tag" value="cts" />
     <option name="config-descriptor:metadata" key="component" value="tv" />
     <test class="com.android.compatibility.common.tradefed.testtype.JarHostTest" >
         <option name="jar" value="CtsHostsideTvTests.jar" />
diff --git a/hostsidetests/tzdata/AndroidTest.xml b/hostsidetests/tzdata/AndroidTest.xml
index 1a2716e..39fc109 100644
--- a/hostsidetests/tzdata/AndroidTest.xml
+++ b/hostsidetests/tzdata/AndroidTest.xml
@@ -14,6 +14,7 @@
      limitations under the License.
 -->
 <configuration description="Config for CTS tzdatacheck host test cases">
+    <option name="test-suite-tag" value="cts" />
     <option name="config-descriptor:metadata" key="component" value="libcore" />
     <test class="com.android.compatibility.common.tradefed.testtype.JarHostTest" >
         <option name="jar" value="CtsHostTzDataTests.jar" />
diff --git a/hostsidetests/ui/AndroidTest.xml b/hostsidetests/ui/AndroidTest.xml
index 9aaf7a4..88c515a 100644
--- a/hostsidetests/ui/AndroidTest.xml
+++ b/hostsidetests/ui/AndroidTest.xml
@@ -14,6 +14,7 @@
      limitations under the License.
 -->
 <configuration description="Config for CTS UI host test cases">
+    <option name="test-suite-tag" value="cts" />
     <option name="config-descriptor:metadata" key="component" value="misc" />
     <test class="com.android.compatibility.common.tradefed.testtype.JarHostTest" >
         <option name="jar" value="CtsUiHostTestCases.jar" />
diff --git a/hostsidetests/ui/appA/Android.mk b/hostsidetests/ui/appA/Android.mk
index 7da5606..3abc7a0 100644
--- a/hostsidetests/ui/appA/Android.mk
+++ b/hostsidetests/ui/appA/Android.mk
@@ -20,7 +20,10 @@
 
 LOCAL_MODULE_PATH := $(TARGET_OUT_DATA_APPS)
 
-LOCAL_STATIC_JAVA_LIBRARIES := compatibility-device-util ctstestrunner
+LOCAL_STATIC_JAVA_LIBRARIES := \
+    compatibility-device-util \
+    ctstestrunner \
+
 
 LOCAL_SRC_FILES := $(call all-java-files-under, src)
 
diff --git a/hostsidetests/ui/appA/AndroidManifest.xml b/hostsidetests/ui/appA/AndroidManifest.xml
index f336abd..dd2a901 100644
--- a/hostsidetests/ui/appA/AndroidManifest.xml
+++ b/hostsidetests/ui/appA/AndroidManifest.xml
@@ -21,6 +21,8 @@
     <uses-permission android:name="android.permission.DISABLE_KEYGUARD" />
 
     <application>
+        <uses-library android:name="android.test.runner" />
+
         <activity
             android:name=".AppAActivity"
             android:configChanges="keyboard|keyboardHidden|orientation|screenSize|uiMode"
@@ -33,4 +35,4 @@
         </activity>
     </application>
 
-</manifest>
\ No newline at end of file
+</manifest>
diff --git a/hostsidetests/ui/appB/Android.mk b/hostsidetests/ui/appB/Android.mk
index 07e2858..4501cf3 100644
--- a/hostsidetests/ui/appB/Android.mk
+++ b/hostsidetests/ui/appB/Android.mk
@@ -20,7 +20,10 @@
 
 LOCAL_MODULE_PATH := $(TARGET_OUT_DATA_APPS)
 
-LOCAL_STATIC_JAVA_LIBRARIES := compatibility-device-util ctstestrunner
+LOCAL_STATIC_JAVA_LIBRARIES := \
+    compatibility-device-util \
+    ctstestrunner \
+
 
 LOCAL_SRC_FILES := $(call all-java-files-under, src)
 
diff --git a/hostsidetests/ui/appB/AndroidManifest.xml b/hostsidetests/ui/appB/AndroidManifest.xml
index aaf7a2c..9d99377 100644
--- a/hostsidetests/ui/appB/AndroidManifest.xml
+++ b/hostsidetests/ui/appB/AndroidManifest.xml
@@ -20,6 +20,8 @@
     <uses-permission android:name="android.permission.DISABLE_KEYGUARD" />
 
     <application>
+        <uses-library android:name="android.test.runner" />
+
         <activity
             android:name=".AppBActivity"
             android:configChanges="keyboard|keyboardHidden|orientation|screenSize|uiMode"
diff --git a/hostsidetests/ui/control/Android.mk b/hostsidetests/ui/control/Android.mk
index 688ace7..3b4f8da 100644
--- a/hostsidetests/ui/control/Android.mk
+++ b/hostsidetests/ui/control/Android.mk
@@ -22,6 +22,8 @@
 
 LOCAL_STATIC_JAVA_LIBRARIES := compatibility-device-util ctstestrunner
 
+LOCAL_JAVA_LIBRARIES := android.test.runner.stubs android.test.base.stubs
+
 LOCAL_SRC_FILES := $(call all-java-files-under, src)
 
 LOCAL_PACKAGE_NAME := CtsDeviceTaskSwitchingControl
diff --git a/hostsidetests/usage/AndroidTest.xml b/hostsidetests/usage/AndroidTest.xml
index 6dd8e6f..7078705 100644
--- a/hostsidetests/usage/AndroidTest.xml
+++ b/hostsidetests/usage/AndroidTest.xml
@@ -14,13 +14,15 @@
      limitations under the License.
 -->
 <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" />
     <target_preparer class="com.android.tradefed.targetprep.suite.SuiteApkInstaller">
         <option name="cleanup-apks" value="true" />
         <option name="test-file-name" value="CtsAppUsageTestApp.apk" />
+        <option name="test-file-name" value="CtsAppUsageTestAppToo.apk" />
     </target_preparer>
     <test class="com.android.compatibility.common.tradefed.testtype.JarHostTest" >
         <option name="jar" value="CtsAppUsageHostTestCases.jar" />
-        <option name="runtime-hint" value="8m" />
+        <option name="runtime-hint" value="2m" />
     </test>
 </configuration>
diff --git a/hostsidetests/usage/app/Android.mk b/hostsidetests/usage/app/Android.mk
index 59626d6..3d51230 100644
--- a/hostsidetests/usage/app/Android.mk
+++ b/hostsidetests/usage/app/Android.mk
@@ -29,3 +29,23 @@
 LOCAL_COMPATIBILITY_SUITE := cts vts general-tests
 
 include $(BUILD_CTS_SUPPORT_PACKAGE)
+
+# Build a second one similar to the first
+
+include $(CLEAR_VARS)
+
+# Don't include this package in any target.
+LOCAL_MODULE_TAGS := tests
+
+LOCAL_SRC_FILES := $(call all-java-files-under, src)
+
+LOCAL_SDK_VERSION := current
+
+LOCAL_PACKAGE_NAME := CtsAppUsageTestAppToo
+
+# tag this module as a cts test artifact
+LOCAL_COMPATIBILITY_SUITE := cts vts general-tests
+
+LOCAL_AAPT_FLAGS += --rename-manifest-package android.app.usage.apptoo
+
+include $(BUILD_CTS_SUPPORT_PACKAGE)
diff --git a/hostsidetests/usage/app/AndroidManifest.xml b/hostsidetests/usage/app/AndroidManifest.xml
index 7d5ce48..303c26f 100755
--- a/hostsidetests/usage/app/AndroidManifest.xml
+++ b/hostsidetests/usage/app/AndroidManifest.xml
@@ -19,7 +19,7 @@
     package="android.app.usage.app">
 
     <application>
-        <activity android:name=".TestActivity">
+        <activity android:name="android.app.usage.app.TestActivity">
             <intent-filter>
                 <action android:name="android.intent.action.MAIN" />
                 <category android:name="android.intent.category.DEFAULT" />
diff --git a/hostsidetests/usage/src/android/app/usage/cts/AppIdleHostTest.java b/hostsidetests/usage/src/android/app/usage/cts/AppIdleHostTest.java
index 3cd7bda..1243cdd 100644
--- a/hostsidetests/usage/src/android/app/usage/cts/AppIdleHostTest.java
+++ b/hostsidetests/usage/src/android/app/usage/cts/AppIdleHostTest.java
@@ -16,18 +16,31 @@
 
 package android.app.usage.cts;
 
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNotEquals;
+
 import com.android.tradefed.device.DeviceNotAvailableException;
 import com.android.tradefed.device.ITestDevice;
 import com.android.tradefed.testtype.DeviceTestCase;
 
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
+
 public class AppIdleHostTest extends DeviceTestCase {
     private static final String SETTINGS_APP_IDLE_CONSTANTS = "app_idle_constants";
 
     private static final String TEST_APP_PACKAGE = "android.app.usage.app";
     private static final String TEST_APP_CLASS = "TestActivity";
+    private static final String TEST_APP_PACKAGE2 = "android.app.usage.apptoo";
 
     private static final long ACTIVITY_LAUNCH_WAIT_MILLIS = 500;
 
+    private static final int SB_ACTIVE = 10;
+    private static final int SB_WORKING_SET = 20;
+    private static final int SB_FREQUENT = 30;
+    private static final int SB_RARE = 40;
+    private static final int SB_NEVER = 50;
+
     /**
      * A reference to the device under test.
      */
@@ -104,6 +117,55 @@
         }
     }
 
+    private void setAppStandbyBucket(String packageName, int bucket) throws Exception {
+        mDevice.executeShellCommand(
+                String.format("am set-standby-bucket %s %s", packageName, bucket));
+    }
+
+    private int getAppStandbyBucket(String packageName) throws Exception {
+        String bucketString = mDevice.executeShellCommand(
+                String.format("am get-standby-bucket %s", packageName));
+        try {
+            return Integer.parseInt(bucketString.trim());
+        } catch (NumberFormatException nfe) {
+        }
+        return -1;
+    }
+
+    public void testSetAppStandbyBucket() throws Exception {
+        // Set to ACTIVE
+        setAppStandbyBucket(TEST_APP_PACKAGE, SB_ACTIVE);
+        assertEquals(SB_ACTIVE, getAppStandbyBucket(TEST_APP_PACKAGE));
+        // set to WORKING_SET
+        setAppStandbyBucket(TEST_APP_PACKAGE, 20);
+        assertEquals(20, getAppStandbyBucket(TEST_APP_PACKAGE));
+    }
+
+    public void testSetAppStandbyBuckets() throws Exception {
+        // Set multiple packages states
+        String command = String.format("am set-standby-bucket %s %d %s %d",
+                TEST_APP_PACKAGE, SB_FREQUENT, TEST_APP_PACKAGE2, SB_WORKING_SET);
+        mDevice.executeShellCommand(command);
+        assertEquals(SB_FREQUENT, getAppStandbyBucket(TEST_APP_PACKAGE));
+        assertEquals(SB_WORKING_SET, getAppStandbyBucket(TEST_APP_PACKAGE2));
+    }
+
+    public void testCantSetOwnStandbyBucket() throws Exception {
+        setAppStandbyBucket("com.android.shell", 40);
+        assertNotEquals(40, getAppStandbyBucket("com.android.shell"));
+    }
+
+    public void testOutOfBoundsStandbyBucket() throws Exception {
+        setAppStandbyBucket(TEST_APP_PACKAGE, SB_ACTIVE);
+        assertEquals(SB_ACTIVE, getAppStandbyBucket(TEST_APP_PACKAGE));
+        // Try lower than min
+        setAppStandbyBucket(TEST_APP_PACKAGE, SB_ACTIVE - 1);
+        assertEquals(SB_ACTIVE, getAppStandbyBucket(TEST_APP_PACKAGE));
+        // Try higher than max
+        setAppStandbyBucket(TEST_APP_PACKAGE, 50 + 1);
+        assertEquals(SB_ACTIVE, getAppStandbyBucket(TEST_APP_PACKAGE));
+    }
+
     private static void sleepUninterrupted(long timeMillis) {
         boolean interrupted;
         do {
diff --git a/hostsidetests/usb/AndroidTest.xml b/hostsidetests/usb/AndroidTest.xml
index cfdcaea..bad4ff9 100644
--- a/hostsidetests/usb/AndroidTest.xml
+++ b/hostsidetests/usb/AndroidTest.xml
@@ -14,6 +14,7 @@
      limitations under the License.
 -->
 <configuration description="Config for CTS USB host test cases">
+    <option name="test-suite-tag" value="cts" />
     <option name="config-descriptor:metadata" key="component" value="misc" />
     <test class="com.android.compatibility.common.tradefed.testtype.JarHostTest" >
         <option name="jar" value="CtsUsbTests.jar" />
diff --git a/hostsidetests/usb/SerialTestApp/Android.mk b/hostsidetests/usb/SerialTestApp/Android.mk
index 2ddf30f..bbd55f6 100644
--- a/hostsidetests/usb/SerialTestApp/Android.mk
+++ b/hostsidetests/usb/SerialTestApp/Android.mk
@@ -20,7 +20,9 @@
 
 LOCAL_MODULE_PATH := $(TARGET_OUT_DATA_APPS)
 
-LOCAL_STATIC_JAVA_LIBRARIES := ctstestrunner legacy-android-test
+LOCAL_STATIC_JAVA_LIBRARIES := ctstestrunner
+
+LOCAL_JAVA_LIBRARIES := android.test.base.stubs
 
 LOCAL_SRC_FILES := $(call all-java-files-under, src)
 
diff --git a/hostsidetests/usb/SerialTestApp/AndroidManifest.xml b/hostsidetests/usb/SerialTestApp/AndroidManifest.xml
index a75dd75..7c627ee 100644
--- a/hostsidetests/usb/SerialTestApp/AndroidManifest.xml
+++ b/hostsidetests/usb/SerialTestApp/AndroidManifest.xml
@@ -17,6 +17,10 @@
 <manifest xmlns:android="http://schemas.android.com/apk/res/android"
         package="com.android.cts.usb.serialtest">
 
+    <uses-sdk android:minSdkVersion="27" android:targetSdkVersion="28" />
+
+    <uses-permission android:name="android.permission.READ_PHONE_STATE" />
+
     <application>
         <uses-library android:name="android.test.runner" />
     </application>
diff --git a/hostsidetests/usb/SerialTestApp/src/com/android/cts/usb/serialtest/UsbSerialTest.java b/hostsidetests/usb/SerialTestApp/src/com/android/cts/usb/serialtest/UsbSerialTest.java
index a2b0c72..f47c2d2 100644
--- a/hostsidetests/usb/SerialTestApp/src/com/android/cts/usb/serialtest/UsbSerialTest.java
+++ b/hostsidetests/usb/SerialTestApp/src/com/android/cts/usb/serialtest/UsbSerialTest.java
@@ -28,6 +28,6 @@
     private static final String TAG = "CtsUsbSerialTest";
 
     public void testSerial() throws Exception {
-        Log.e(TAG, Build.SERIAL);
+        Log.e(TAG, Build.getSerial());
     }
 }
diff --git a/hostsidetests/usb/src/com/android/cts/usb/TestUsbTest.java b/hostsidetests/usb/src/com/android/cts/usb/TestUsbTest.java
index 81cc265..47efe85 100644
--- a/hostsidetests/usb/src/com/android/cts/usb/TestUsbTest.java
+++ b/hostsidetests/usb/src/com/android/cts/usb/TestUsbTest.java
@@ -65,7 +65,7 @@
         CompatibilityBuildHelper buildHelper = new CompatibilityBuildHelper(mBuild);
         File app = buildHelper.getTestFile(APK_NAME);
         String[] options = {AbiUtils.createAbiFlag(mAbi.getName())};
-        mDevice.installPackage(app, false, options);
+        mDevice.installPackage(app, false, true, options);
     }
 
     @Override
diff --git a/hostsidetests/webkit/AndroidTest.xml b/hostsidetests/webkit/AndroidTest.xml
index 37d0467..414fad4 100644
--- a/hostsidetests/webkit/AndroidTest.xml
+++ b/hostsidetests/webkit/AndroidTest.xml
@@ -14,6 +14,7 @@
      limitations under the License.
 -->
 <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" />
     <target_preparer class="com.android.tradefed.targetprep.suite.SuiteApkInstaller">
         <option name="cleanup-apks" value="true" />
diff --git a/hostsidetests/webkit/app/Android.mk b/hostsidetests/webkit/app/Android.mk
index 60e9f3f..fd2ee9a 100644
--- a/hostsidetests/webkit/app/Android.mk
+++ b/hostsidetests/webkit/app/Android.mk
@@ -27,6 +27,8 @@
     ctstestserver \
     ctstestrunner
 
+LOCAL_JAVA_LIBRARIES := android.test.runner.stubs android.test.base.stubs
+
 # When built, explicitly put it in the data partition.
 #LOCAL_MODULE_PATH := $(TARGET_OUT_DATA_APPS)
 
diff --git a/hostsidetests/webkit/src/com/android/cts/webkit/WebViewHostSideStartupTest.java b/hostsidetests/webkit/src/com/android/cts/webkit/WebViewHostSideStartupTest.java
index 53f4a49..5953c5d 100644
--- a/hostsidetests/webkit/src/com/android/cts/webkit/WebViewHostSideStartupTest.java
+++ b/hostsidetests/webkit/src/com/android/cts/webkit/WebViewHostSideStartupTest.java
@@ -17,12 +17,13 @@
 
 import com.android.ddmlib.testrunner.RemoteAndroidTestRunner;
 import com.android.ddmlib.testrunner.TestResult.TestStatus;
+import com.android.ddmlib.testrunner.TestResult;
 import com.android.ddmlib.testrunner.TestRunResult;
 import com.android.tradefed.device.DeviceNotAvailableException;
 import com.android.tradefed.result.CollectingTestListener;
 import com.android.tradefed.testtype.DeviceTestCase;
 
-import java.util.Map;
+import java.util.Collection;
 
 public class WebViewHostSideStartupTest extends DeviceTestCase {
     private static final String RUNNER = "android.support.test.runner.AndroidJUnitRunner";
@@ -35,26 +36,35 @@
     }
 
     public void testCookieManager() throws DeviceNotAvailableException {
-        assertTrue(runDeviceTest(DEVICE_WEBVIEW_STARTUP_PKG, DEVICE_WEBVIEW_STARTUP_TEST_CLASS,
-                    "testCookieManagerBlockingUiThread"));
+        assertDeviceTestPasses("testCookieManagerBlockingUiThread");
     }
 
     public void testWebViewVersionApiOnUiThread() throws DeviceNotAvailableException {
-        assertTrue(runDeviceTest(DEVICE_WEBVIEW_STARTUP_PKG, DEVICE_WEBVIEW_STARTUP_TEST_CLASS,
-                    "testGetCurrentWebViewPackageOnUiThread"));
+        assertDeviceTestPasses("testGetCurrentWebViewPackageOnUiThread");
     }
 
     public void testWebViewVersionApi() throws DeviceNotAvailableException {
-        assertTrue(runDeviceTest(DEVICE_WEBVIEW_STARTUP_PKG, DEVICE_WEBVIEW_STARTUP_TEST_CLASS,
-                    "testGetCurrentWebViewPackage"));
+        assertDeviceTestPasses("testGetCurrentWebViewPackage");
     }
 
     public void testStrictMode() throws DeviceNotAvailableException {
-        assertTrue(runDeviceTest(DEVICE_WEBVIEW_STARTUP_PKG, DEVICE_WEBVIEW_STARTUP_TEST_CLASS,
-                    "testStrictModeNotViolatedOnStartup"));
+        assertDeviceTestPasses("testStrictModeNotViolatedOnStartup");
     }
 
-    private boolean runDeviceTest(String packageName, String testClassName,
+    private void assertDeviceTestPasses(String testMethodName) throws DeviceNotAvailableException {
+        TestRunResult testRunResult = runSingleDeviceTest(DEVICE_WEBVIEW_STARTUP_PKG,
+                                                 DEVICE_WEBVIEW_STARTUP_TEST_CLASS,
+                                                 testMethodName);
+        assertTrue(testRunResult.isRunComplete());
+
+        Collection<TestResult> testResults = testRunResult.getTestResults().values();
+        // We're only running a single test.
+        assertEquals(1, testResults.size());
+        TestResult testResult = testResults.toArray(new TestResult[1])[0];
+        assertTrue(testResult.getStackTrace(), TestStatus.PASSED.equals(testResult.getStatus()));
+    }
+
+    private TestRunResult runSingleDeviceTest(String packageName, String testClassName,
             String testMethodName) throws DeviceNotAvailableException {
         testClassName = packageName + "." + testClassName;
 
@@ -65,7 +75,6 @@
         CollectingTestListener listener = new CollectingTestListener();
         assertTrue(getDevice().runInstrumentationTests(testRunner, listener));
 
-        TestRunResult runResult = listener.getCurrentRunResults();
-        return !runResult.hasFailedTests() && runResult.getNumTestsInState(TestStatus.PASSED) > 0;
+        return listener.getCurrentRunResults();
     }
 }
diff --git a/libs/deviceutillegacy/Android.mk b/libs/deviceutillegacy/Android.mk
index f169ca8..aa8255e 100644
--- a/libs/deviceutillegacy/Android.mk
+++ b/libs/deviceutillegacy/Android.mk
@@ -18,8 +18,9 @@
 
 LOCAL_STATIC_JAVA_LIBRARIES := \
     compatibility-device-util \
-    junit \
-    legacy-android-test
+    junit
+
+LOCAL_JAVA_LIBRARIES := android.test.base
 
 LOCAL_SRC_FILES := \
     $(call all-java-files-under, src)
diff --git a/tests/AlarmManager/Android.mk b/tests/AlarmManager/Android.mk
new file mode 100755
index 0000000..a1676ab
--- /dev/null
+++ b/tests/AlarmManager/Android.mk
@@ -0,0 +1,38 @@
+# 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)
+
+# 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)
+
+LOCAL_STATIC_JAVA_LIBRARIES := ub-uiautomator android-support-test
+
+LOCAL_SRC_FILES := $(call all-java-files-under, src)
+LOCAL_SRC_FILES += $(call all-java-files-under, app/src)
+
+LOCAL_COMPATIBILITY_SUITE := cts vts general-tests
+
+LOCAL_PACKAGE_NAME := CtsAlarmManagerTestCases
+
+LOCAL_SDK_VERSION := current
+
+include $(BUILD_CTS_PACKAGE)
+
+include $(call all-makefiles-under, $(LOCAL_PATH))
diff --git a/tests/AlarmManager/AndroidManifest.xml b/tests/AlarmManager/AndroidManifest.xml
new file mode 100644
index 0000000..f557b51
--- /dev/null
+++ b/tests/AlarmManager/AndroidManifest.xml
@@ -0,0 +1,28 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2017 The Android Open Source Project
+
+     Licensed under the Apache License, Version 2.0 (the "License");
+     you may not use this file except in compliance with the License.
+     You may obtain a copy of the License at
+
+          http://www.apache.org/licenses/LICENSE-2.0
+
+     Unless required by applicable law or agreed to in writing, software
+     distributed under the License is distributed on an "AS IS" BASIS,
+     WITHOUT 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.alarmmanager.cts" >
+
+    <application android:label="Cts Alarm Manager Test">
+        <uses-library android:name="android.test.runner"/>
+    </application>
+
+    <instrumentation android:name="android.support.test.runner.AndroidJUnitRunner"
+                     android:functionalTest="true"
+                     android:targetPackage="android.alarmmanager.cts"
+                     android:label="Alarm Manager Tests"/>
+</manifest>
diff --git a/tests/AlarmManager/AndroidTest.xml b/tests/AlarmManager/AndroidTest.xml
new file mode 100644
index 0000000..a8c8955
--- /dev/null
+++ b/tests/AlarmManager/AndroidTest.xml
@@ -0,0 +1,32 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2017 The Android Open Source Project
+
+     Licensed under the Apache License, Version 2.0 (the "License");
+     you may not use this file except in compliance with the License.
+     You may obtain a copy of the License at
+
+          http://www.apache.org/licenses/LICENSE-2.0
+
+     Unless required by applicable law or agreed to in writing, software
+     distributed under the License is distributed on an "AS IS" BASIS,
+     WITHOUT 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 Alarm Manager test cases">
+    <option name="test-suite-tag" value="cts" />
+    <option name="config-descriptor:metadata" key="component" value="framework" />
+
+    <target_preparer class="com.android.tradefed.targetprep.suite.SuiteApkInstaller">
+        <option name="cleanup-apks" value="true" />
+        <option name="test-file-name" value="CtsAlarmManagerTestCases.apk" />
+        <option name="test-file-name" value="AlarmTestApp.apk" />
+    </target_preparer>
+
+    <test class="com.android.tradefed.testtype.AndroidJUnitTest" >
+        <option name="package" value="android.alarmmanager.cts" />
+        <option name="runtime-hint" value="1m" />
+    </test>
+
+</configuration>
diff --git a/tests/AlarmManager/app/Android.mk b/tests/AlarmManager/app/Android.mk
new file mode 100644
index 0000000..b2023e9
--- /dev/null
+++ b/tests/AlarmManager/app/Android.mk
@@ -0,0 +1,33 @@
+# Copyright (C) 2017 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#      http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+LOCAL_PATH:= $(call my-dir)
+
+include $(CLEAR_VARS)
+
+LOCAL_MODULE_TAGS := optional
+
+LOCAL_MODULE_PATH := $(TARGET_OUT_DATA_APPS)
+
+LOCAL_SDK_VERSION := current
+
+LOCAL_COMPATIBILITY_SUITE := cts vts general-tests
+
+LOCAL_SRC_FILES := $(call all-java-files-under, src)
+
+LOCAL_PACKAGE_NAME := AlarmTestApp
+
+LOCAL_DEX_PREOPT := false
+
+include $(BUILD_CTS_SUPPORT_PACKAGE)
diff --git a/tests/AlarmManager/app/AndroidManifest.xml b/tests/AlarmManager/app/AndroidManifest.xml
new file mode 100644
index 0000000..ceb8fb9
--- /dev/null
+++ b/tests/AlarmManager/app/AndroidManifest.xml
@@ -0,0 +1,26 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2017 The Android Open Source Project
+
+     Licensed under the Apache License, Version 2.0 (the "License");
+     you may not use this file except in compliance with the License.
+     You may obtain a copy of the License at
+
+          http://www.apache.org/licenses/LICENSE-2.0
+
+     Unless required by applicable law or agreed to in writing, software
+     distributed under the License is distributed on an "AS IS" BASIS,
+     WITHOUT 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.alarmmanager.alarmtestapp.cts">
+
+    <application>
+        <activity android:name=".TestAlarmActivity"
+                  android:exported="true" />
+        <receiver android:name=".TestAlarmReceiver" />
+    </application>
+
+</manifest>
\ No newline at end of file
diff --git a/tests/AlarmManager/app/src/android/alarmmanager/alarmtestapp/cts/TestAlarmActivity.java b/tests/AlarmManager/app/src/android/alarmmanager/alarmtestapp/cts/TestAlarmActivity.java
new file mode 100644
index 0000000..8f666dd8
--- /dev/null
+++ b/tests/AlarmManager/app/src/android/alarmmanager/alarmtestapp/cts/TestAlarmActivity.java
@@ -0,0 +1,90 @@
+/*
+ * 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.alarmmanager.alarmtestapp.cts;
+
+import android.app.Activity;
+import android.app.AlarmManager;
+import android.app.PendingIntent;
+import android.content.Intent;
+import android.os.Bundle;
+import android.util.Log;
+
+/**
+ * This Activity is to be used as part of
+ * {@link android.alarmmanager.cts.BackgroundRestrictedAlarmsTest}
+ */
+public class TestAlarmActivity extends Activity {
+    private static final String TAG = TestAlarmActivity.class.getSimpleName();
+    private static final String PACKAGE_NAME = "android.alarmmanager.alarmtestapp.cts";
+
+    public static final String ACTION_SET_ALARM = PACKAGE_NAME + ".action.SET_ALARM";
+    public static final String EXTRA_TRIGGER_TIME = PACKAGE_NAME + ".extra.TRIGGER_TIME";
+    public static final String EXTRA_REPEAT_INTERVAL = PACKAGE_NAME + ".extra.REPEAT_INTERVAL";
+    public static final String EXTRA_TYPE = PACKAGE_NAME + ".extra.TYPE";
+    public static final String ACTION_SET_ALARM_CLOCK = PACKAGE_NAME + ".action.SET_ALARM_CLOCK";
+    public static final String EXTRA_ALARM_CLOCK_INFO = PACKAGE_NAME + ".extra.ALARM_CLOCK_INFO";
+    public static final String ACTION_CANCEL_ALL_ALARMS = PACKAGE_NAME + ".action.CANCEL_ALARMS";
+
+    @Override
+    public void onCreate(Bundle savedInstanceState) {
+        super.onCreate(savedInstanceState);
+        final AlarmManager am = getSystemService(AlarmManager.class);
+        final Intent intent = getIntent();
+        final Intent receiverIntent = new Intent(this, TestAlarmReceiver.class);
+        receiverIntent.addFlags(Intent.FLAG_RECEIVER_FOREGROUND);
+        final PendingIntent alarmClockSender =
+                PendingIntent.getBroadcast(this, 0, receiverIntent, 0);
+        final PendingIntent alarmSender = PendingIntent.getBroadcast(this, 1, receiverIntent, 0);
+        switch (intent.getAction()) {
+            case ACTION_SET_ALARM_CLOCK:
+                if (!intent.hasExtra(EXTRA_ALARM_CLOCK_INFO)) {
+                    Log.e(TAG, "No alarm clock supplied");
+                    break;
+                }
+                final AlarmManager.AlarmClockInfo alarmClockInfo =
+                        intent.getParcelableExtra(EXTRA_ALARM_CLOCK_INFO);
+                Log.d(TAG, "Setting alarm clock " + alarmClockInfo);
+                am.setAlarmClock(alarmClockInfo, alarmClockSender);
+                break;
+            case ACTION_SET_ALARM:
+                if (!intent.hasExtra(EXTRA_TYPE) || !intent.hasExtra(EXTRA_TRIGGER_TIME)) {
+                    Log.e(TAG, "Alarm type or trigger time not supplied");
+                    break;
+                }
+                final int type = intent.getIntExtra(EXTRA_TYPE, 0);
+                final long triggerTime = intent.getLongExtra(EXTRA_TRIGGER_TIME, 0);
+                final long interval = intent.getLongExtra(EXTRA_REPEAT_INTERVAL, 0);
+                Log.d(TAG, "Setting alarm: type=" + type + ", triggerTime=" + triggerTime
+                        + ", interval=" + interval);
+                if (interval > 0) {
+                    am.setRepeating(type, triggerTime, interval, alarmSender);
+                } else {
+                    am.setExact(type, triggerTime, alarmSender);
+                }
+                break;
+            case ACTION_CANCEL_ALL_ALARMS:
+                Log.d(TAG, "Cancelling all alarms");
+                am.cancel(alarmClockSender);
+                am.cancel(alarmSender);
+                break;
+            default:
+                Log.e(TAG, "Unspecified action " + intent.getAction());
+                break;
+        }
+        finish();
+    }
+}
\ No newline at end of file
diff --git a/tests/AlarmManager/app/src/android/alarmmanager/alarmtestapp/cts/TestAlarmReceiver.java b/tests/AlarmManager/app/src/android/alarmmanager/alarmtestapp/cts/TestAlarmReceiver.java
new file mode 100644
index 0000000..a083e08
--- /dev/null
+++ b/tests/AlarmManager/app/src/android/alarmmanager/alarmtestapp/cts/TestAlarmReceiver.java
@@ -0,0 +1,39 @@
+/*
+ * 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.alarmmanager.alarmtestapp.cts;
+
+import android.content.BroadcastReceiver;
+import android.content.Context;
+import android.content.Intent;
+import android.util.Log;
+
+public class TestAlarmReceiver extends BroadcastReceiver{
+    private static final String TAG = TestAlarmReceiver.class.getSimpleName();
+    private static final String PACKAGE_NAME = "android.alarmmanager.alarmtestapp.cts";
+    public static final String ACTION_REPORT_ALARM_EXPIRED = PACKAGE_NAME + ".action.ALARM_EXPIRED";
+    public static final String EXTRA_ALARM_COUNT = PACKAGE_NAME + ".extra.ALARM_COUNT";
+
+    @Override
+    public void onReceive(Context context, Intent intent) {
+        final int count = intent.getIntExtra(Intent.EXTRA_ALARM_COUNT, 1);
+        Log.d(TAG, "Alarm expired " + count + " times");
+        final Intent reportAlarmIntent = new Intent(ACTION_REPORT_ALARM_EXPIRED);
+        reportAlarmIntent.putExtra(EXTRA_ALARM_COUNT, count);
+        reportAlarmIntent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY);
+        context.sendBroadcast(reportAlarmIntent);
+    }
+}
diff --git a/tests/AlarmManager/src/android/alarmmanager/cts/BackgroundRestrictedAlarmsTest.java b/tests/AlarmManager/src/android/alarmmanager/cts/BackgroundRestrictedAlarmsTest.java
new file mode 100644
index 0000000..2b37bfd
--- /dev/null
+++ b/tests/AlarmManager/src/android/alarmmanager/cts/BackgroundRestrictedAlarmsTest.java
@@ -0,0 +1,196 @@
+/*
+ * 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.alarmmanager.cts;
+
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertTrue;
+
+import android.alarmmanager.alarmtestapp.cts.TestAlarmActivity;
+import android.alarmmanager.alarmtestapp.cts.TestAlarmReceiver;
+import android.app.AlarmManager;
+import android.content.BroadcastReceiver;
+import android.content.ComponentName;
+import android.content.Context;
+import android.content.Intent;
+import android.content.IntentFilter;
+import android.os.SystemClock;
+import android.support.test.InstrumentationRegistry;
+import android.support.test.filters.LargeTest;
+import android.support.test.runner.AndroidJUnit4;
+import android.support.test.uiautomator.UiDevice;
+import android.util.Log;
+
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+import java.io.IOException;
+
+@LargeTest
+@RunWith(AndroidJUnit4.class)
+public class BackgroundRestrictedAlarmsTest {
+    private static final String TAG = BackgroundRestrictedAlarmsTest.class.getSimpleName();
+    private static final String TEST_APP_PACKAGE = "android.alarmmanager.alarmtestapp.cts";
+    private static final String TEST_APP_ACTIVITY = TEST_APP_PACKAGE + ".TestAlarmActivity";
+    private static final String APP_OP_RUN_ANY_IN_BACKGROUND = "RUN_ANY_IN_BACKGROUND";
+    private static final String APP_OP_MODE_ALLOWED = "allow";
+    private static final String APP_OP_MODE_IGNORED = "ignore";
+
+    private static final long DEFAULT_WAIT = 1_000;
+    private static final long POLL_INTERVAL = 1_000;
+    private static final long MIN_REPEATING_INTERVAL = 10_000;
+
+    private Object mLock = new Object();
+    private Context mContext;
+    private ComponentName mAlarmActivity;
+    private UiDevice mUiDevice;
+    private int mAlarmCount;
+
+    private final BroadcastReceiver mAlarmStateReceiver = new BroadcastReceiver() {
+        @Override
+        public void onReceive(Context context, Intent intent) {
+            Log.d(TAG, "Received action " + intent.getAction()
+                    + " elapsed: " + SystemClock.elapsedRealtime());
+            synchronized (mLock) {
+                mAlarmCount = intent.getIntExtra(TestAlarmReceiver.EXTRA_ALARM_COUNT, 1);
+            }
+        }
+    };
+
+    @Before
+    public void setUp() throws Exception {
+        mContext = InstrumentationRegistry.getTargetContext();
+        mUiDevice = UiDevice.getInstance(InstrumentationRegistry.getInstrumentation());
+        mAlarmActivity = new ComponentName(TEST_APP_PACKAGE, TEST_APP_ACTIVITY);
+        mAlarmCount = 0;
+        updateAlarmManagerConstants();
+        setAppOpsMode(APP_OP_MODE_IGNORED);
+        final IntentFilter intentFilter = new IntentFilter();
+        intentFilter.addAction(TestAlarmReceiver.ACTION_REPORT_ALARM_EXPIRED);
+        mContext.registerReceiver(mAlarmStateReceiver, intentFilter);
+    }
+
+    private void scheduleAlarm(int type, long triggerMillis, long interval) {
+        final Intent setAlarmIntent = new Intent(TestAlarmActivity.ACTION_SET_ALARM);
+        setAlarmIntent.setComponent(mAlarmActivity);
+        setAlarmIntent.putExtra(TestAlarmActivity.EXTRA_TYPE, type);
+        setAlarmIntent.putExtra(TestAlarmActivity.EXTRA_TRIGGER_TIME, triggerMillis);
+        setAlarmIntent.putExtra(TestAlarmActivity.EXTRA_REPEAT_INTERVAL, interval);
+        setAlarmIntent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
+        mContext.startActivity(setAlarmIntent);
+    }
+
+    private void scheduleAlarmClock(long triggerRTC) {
+        AlarmManager.AlarmClockInfo alarmInfo = new AlarmManager.AlarmClockInfo(triggerRTC, null);
+
+        final Intent setAlarmClockIntent = new Intent(TestAlarmActivity.ACTION_SET_ALARM_CLOCK);
+        setAlarmClockIntent.setComponent(mAlarmActivity);
+        setAlarmClockIntent.putExtra(TestAlarmActivity.EXTRA_ALARM_CLOCK_INFO, alarmInfo);
+        setAlarmClockIntent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
+        mContext.startActivity(setAlarmClockIntent);
+    }
+
+    private static int getMinExpectedExpirations(long now, long start, long interval) {
+        if (now - start <= 1000) {
+            return 0;
+        }
+        return 1 + (int)((now - start - 1000)/interval);
+    }
+
+    @Test
+    public void testRepeatingAlarmBlocked() throws Exception {
+        final long interval = MIN_REPEATING_INTERVAL;
+        final long triggerElapsed = SystemClock.elapsedRealtime() + interval;
+        scheduleAlarm(AlarmManager.ELAPSED_REALTIME_WAKEUP, triggerElapsed, interval);
+        Thread.sleep(DEFAULT_WAIT);
+        makeTestPackageIdle();
+        Thread.sleep(2 * interval);
+        assertFalse("Alarm got triggered even under restrictions", waitForAlarms(1, DEFAULT_WAIT));
+        Thread.sleep(interval);
+        setAppOpsMode(APP_OP_MODE_ALLOWED);
+        // The alarm is due to go off about 3 times by now. Adding some tolerance just in case
+        // an expiration is due right about now.
+        final int minCount = getMinExpectedExpirations(SystemClock.elapsedRealtime(),
+                triggerElapsed, interval);
+        assertTrue("Alarm should have expired at least " + minCount
+                + " times when restrictions were lifted", waitForAlarms(minCount, DEFAULT_WAIT));
+    }
+
+    @Test
+    public void testAlarmClockNotBlocked() throws Exception {
+        final long nowRTC = System.currentTimeMillis();
+        final long waitInterval = 10_000;
+        final long triggerRTC = nowRTC + waitInterval;
+        scheduleAlarmClock(triggerRTC);
+        Thread.sleep(waitInterval);
+        assertTrue("AlarmClock did not go off as scheduled when under restrictions",
+                waitForAlarms(1, DEFAULT_WAIT));
+    }
+
+    @After
+    public void tearDown() throws Exception {
+        deleteAlarmManagerConstants();
+        setAppOpsMode(APP_OP_MODE_ALLOWED);
+        // Cancel any leftover alarms
+        final Intent cancelAlarmsIntent = new Intent(TestAlarmActivity.ACTION_CANCEL_ALL_ALARMS);
+        cancelAlarmsIntent.setComponent(mAlarmActivity);
+        cancelAlarmsIntent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
+        mContext.startActivity(cancelAlarmsIntent);
+        mContext.unregisterReceiver(mAlarmStateReceiver);
+        // Broadcast unregister may race with the next register in setUp
+        Thread.sleep(DEFAULT_WAIT);
+    }
+
+    private void updateAlarmManagerConstants() throws IOException {
+        String cmd = "settings put global alarm_manager_constants min_interval="
+                + MIN_REPEATING_INTERVAL;
+        mUiDevice.executeShellCommand(cmd);
+    }
+
+    private void deleteAlarmManagerConstants() throws IOException {
+        mUiDevice.executeShellCommand("settings delete global alarm_manager_constants");
+    }
+
+    private void setAppOpsMode(String mode) throws IOException {
+        StringBuilder commandBuilder = new StringBuilder("appops set ")
+                .append(TEST_APP_PACKAGE)
+                .append(" ")
+                .append(APP_OP_RUN_ANY_IN_BACKGROUND)
+                .append(" ")
+                .append(mode);
+        mUiDevice.executeShellCommand(commandBuilder.toString());
+    }
+
+    private void makeTestPackageIdle() throws IOException {
+        StringBuilder commandBuilder = new StringBuilder("am make-uid-idle --user current ")
+                .append(TEST_APP_PACKAGE);
+        mUiDevice.executeShellCommand(commandBuilder.toString());
+    }
+
+    private boolean waitForAlarms(int expectedAlarms, long timeout) throws InterruptedException {
+        final long deadLine = SystemClock.uptimeMillis() + timeout;
+        int alarmCount;
+        do {
+            Thread.sleep(POLL_INTERVAL);
+            synchronized (mLock) {
+                alarmCount = mAlarmCount;
+            }
+        } while (alarmCount < expectedAlarms && SystemClock.uptimeMillis() < deadLine);
+        return alarmCount >= expectedAlarms;
+    }
+}
diff --git a/tests/JobScheduler/Android.mk b/tests/JobScheduler/Android.mk
index e58a494..fa6a5d1 100755
--- a/tests/JobScheduler/Android.mk
+++ b/tests/JobScheduler/Android.mk
@@ -22,9 +22,12 @@
 # When built, explicitly put it in the data partition.
 LOCAL_MODULE_PATH := $(TARGET_OUT_DATA_APPS)
 
-LOCAL_STATIC_JAVA_LIBRARIES := compatibility-device-util ctstestrunner ub-uiautomator
+LOCAL_STATIC_JAVA_LIBRARIES := compatibility-device-util ub-uiautomator android-support-test
+
+LOCAL_JAVA_LIBRARIES := android.test.base
 
 LOCAL_SRC_FILES := $(call all-java-files-under, src)
+LOCAL_SRC_FILES += $(call all-java-files-under, JobTestApp/src)
 
 # Tag this module as a cts test artifact
 LOCAL_COMPATIBILITY_SUITE := cts vts general-tests
@@ -32,7 +35,7 @@
 # Must match the package name in CtsTestCaseList.mk
 LOCAL_PACKAGE_NAME := CtsJobSchedulerTestCases
 
-LOCAL_SDK_VERSION := current
+#LOCAL_SDK_VERSION := current
 
 include $(BUILD_CTS_PACKAGE)
 
diff --git a/tests/JobScheduler/AndroidManifest.xml b/tests/JobScheduler/AndroidManifest.xml
index e433252..4c9625d 100755
--- a/tests/JobScheduler/AndroidManifest.xml
+++ b/tests/JobScheduler/AndroidManifest.xml
@@ -43,8 +43,5 @@
         android:name="android.support.test.runner.AndroidJUnitRunner"
         android:label="JobScheduler device-side tests"
         android:targetPackage="android.jobscheduler.cts" >
-        <meta-data
-            android:name="listener"
-            android:value="com.android.cts.runner.CtsTestRunListener" />
     </instrumentation>
 </manifest>
diff --git a/tests/JobScheduler/AndroidTest.xml b/tests/JobScheduler/AndroidTest.xml
index 855ad48..d7a5988 100644
--- a/tests/JobScheduler/AndroidTest.xml
+++ b/tests/JobScheduler/AndroidTest.xml
@@ -14,12 +14,14 @@
      limitations under the License.
 -->
 <configuration description="Config for CTS Job Scheduler test cases">
+    <option name="test-suite-tag" value="cts" />
     <option name="config-descriptor:metadata" key="component" value="framework" />
     <target_preparer class="com.android.tradefed.targetprep.suite.SuiteApkInstaller">
         <option name="cleanup-apks" value="true" />
         <option name="test-file-name" value="CtsJobSchedulerTestCases.apk" />
         <option name="test-file-name" value="CtsJobSchedulerJobPerm.apk" />
         <option name="test-file-name" value="CtsJobSchedulerSharedUid.apk" />
+        <option name="test-file-name" value="CtsJobTestApp.apk" />
     </target_preparer>
     <test class="com.android.tradefed.testtype.AndroidJUnitTest" >
         <option name="package" value="android.jobscheduler.cts" />
diff --git a/tests/JobScheduler/JobTestApp/Android.mk b/tests/JobScheduler/JobTestApp/Android.mk
new file mode 100644
index 0000000..f7b7a1d
--- /dev/null
+++ b/tests/JobScheduler/JobTestApp/Android.mk
@@ -0,0 +1,30 @@
+# Copyright (C) 2017 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#      http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+LOCAL_PATH:= $(call my-dir)
+
+include $(CLEAR_VARS)
+
+# Don't include this package in any target.
+LOCAL_MODULE_TAGS := tests
+
+LOCAL_SRC_FILES := \
+    $(call all-java-files-under, src) \
+
+# Tag this module as a cts test artifact
+LOCAL_COMPATIBILITY_SUITE := cts vts general-tests
+
+LOCAL_PACKAGE_NAME := CtsJobTestApp
+
+include $(BUILD_CTS_PACKAGE)
diff --git a/tests/JobScheduler/JobTestApp/AndroidManifest.xml b/tests/JobScheduler/JobTestApp/AndroidManifest.xml
new file mode 100644
index 0000000..866c5d4
--- /dev/null
+++ b/tests/JobScheduler/JobTestApp/AndroidManifest.xml
@@ -0,0 +1,31 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2017 The Android Open Source Project
+
+     Licensed under the Apache License, Version 2.0 (the "License");
+     you may not use this file except in compliance with the License.
+     You may obtain a copy of the License at
+
+          http://www.apache.org/licenses/LICENSE-2.0
+
+     Unless required by applicable law or agreed to in writing, software
+     distributed under the License is distributed on an "AS IS" BASIS,
+     WITHOUT 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.jobscheduler.cts.jobtestapp">
+
+    <!-- This application schedules jobs independently of the test instrumentation to make
+    it possible to test behaviour for different app states, whitelists and device idle modes -->
+    <application>
+        <service android:name=".TestJobService"
+                 android:permission="android.permission.BIND_JOB_SERVICE" />
+        <activity android:name=".TestActivity"
+                  android:exported="true" />
+        <receiver android:name=".TestJobSchedulerReceiver"
+                  android:exported="true" />
+    </application>
+
+</manifest>
\ No newline at end of file
diff --git a/tests/JobScheduler/JobTestApp/src/android/jobscheduler/cts/jobtestapp/TestActivity.java b/tests/JobScheduler/JobTestApp/src/android/jobscheduler/cts/jobtestapp/TestActivity.java
new file mode 100644
index 0000000..0368f05
--- /dev/null
+++ b/tests/JobScheduler/JobTestApp/src/android/jobscheduler/cts/jobtestapp/TestActivity.java
@@ -0,0 +1,68 @@
+/*
+ * 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.jobscheduler.cts.jobtestapp;
+
+import android.app.Activity;
+import android.content.BroadcastReceiver;
+import android.content.Context;
+import android.content.Intent;
+import android.content.IntentFilter;
+import android.os.Bundle;
+import android.os.Handler;
+import android.os.Message;
+import android.util.Log;
+
+/**
+ * Just a dummy activity to keep the test app process in the foreground state when desired.
+ */
+public class TestActivity extends Activity {
+    private static final String TAG = TestActivity.class.getSimpleName();
+    private static final String PACKAGE_NAME = "android.jobscheduler.cts.jobtestapp";
+    private static final long DEFAULT_WAIT_DURATION = 30_000;
+
+    static final int FINISH_ACTIVITY_MSG = 1;
+    public static final String ACTION_FINISH_ACTIVITY = PACKAGE_NAME + ".action.FINISH_ACTIVITY";
+
+    Handler mFinishHandler = new Handler() {
+        @Override
+        public void handleMessage(Message msg) {
+            switch (msg.what) {
+                case FINISH_ACTIVITY_MSG:
+                    Log.d(TAG, "Finishing test activity: " + TestActivity.class.getCanonicalName());
+                    unregisterReceiver(mFinishReceiver);
+                    finish();
+            }
+        }
+    };
+
+    final BroadcastReceiver mFinishReceiver = new BroadcastReceiver() {
+        @Override
+        public void onReceive(Context context, Intent intent) {
+            mFinishHandler.removeMessages(FINISH_ACTIVITY_MSG);
+            mFinishHandler.sendEmptyMessage(FINISH_ACTIVITY_MSG);
+        }
+    };
+
+    @Override
+    public void onCreate(Bundle savedInstance) {
+        Log.d(TAG, "Started test activity: " + TestActivity.class.getCanonicalName());
+        super.onCreate(savedInstance);
+        // automatically finish after 30 seconds.
+        mFinishHandler.sendEmptyMessageDelayed(FINISH_ACTIVITY_MSG, DEFAULT_WAIT_DURATION);
+        registerReceiver(mFinishReceiver, new IntentFilter(ACTION_FINISH_ACTIVITY));
+    }
+}
diff --git a/tests/JobScheduler/JobTestApp/src/android/jobscheduler/cts/jobtestapp/TestJobSchedulerReceiver.java b/tests/JobScheduler/JobTestApp/src/android/jobscheduler/cts/jobtestapp/TestJobSchedulerReceiver.java
new file mode 100644
index 0000000..97e8d74
--- /dev/null
+++ b/tests/JobScheduler/JobTestApp/src/android/jobscheduler/cts/jobtestapp/TestJobSchedulerReceiver.java
@@ -0,0 +1,68 @@
+/*
+ * 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.jobscheduler.cts.jobtestapp;
+
+import android.app.job.JobInfo;
+import android.app.job.JobScheduler;
+import android.content.BroadcastReceiver;
+import android.content.ComponentName;
+import android.content.Context;
+import android.content.Intent;
+import android.util.Log;
+
+/**
+ * Schedules jobs for this package but does not, by itself, occupy a foreground uid state
+ * while doing so.
+ */
+public class TestJobSchedulerReceiver extends BroadcastReceiver {
+    private static final String TAG = TestJobSchedulerReceiver.class.getSimpleName();
+    private static final String PACKAGE_NAME = "com.android.servicestests.apps.jobtestapp";
+
+    public static final String EXTRA_JOB_ID_KEY = PACKAGE_NAME + ".extra.JOB_ID";
+    public static final String EXTRA_ALLOW_IN_IDLE = PACKAGE_NAME + ".extra.ALLOW_IN_IDLE";
+    public static final String ACTION_SCHEDULE_JOB = PACKAGE_NAME + ".action.SCHEDULE_JOB";
+    public static final String ACTION_CANCEL_JOBS = PACKAGE_NAME + ".action.CANCEL_JOBS";
+    public static final int JOB_INITIAL_BACKOFF = 10_000;
+
+    @Override
+    public void onReceive(Context context, Intent intent) {
+        final ComponentName jobServiceComponent = new ComponentName(context, TestJobService.class);
+        final JobScheduler jobScheduler = context.getSystemService(JobScheduler.class);
+        switch (intent.getAction()) {
+            case ACTION_CANCEL_JOBS:
+                jobScheduler.cancelAll();
+                Log.d(TAG, "Cancelled all jobs for " + context.getPackageName());
+                break;
+            case ACTION_SCHEDULE_JOB:
+                final int jobId = intent.getIntExtra(EXTRA_JOB_ID_KEY, hashCode());
+                final boolean allowInIdle = intent.getBooleanExtra(EXTRA_ALLOW_IN_IDLE, false);
+                JobInfo.Builder jobBuilder = new JobInfo.Builder(jobId, jobServiceComponent)
+                        .setBackoffCriteria(JOB_INITIAL_BACKOFF, JobInfo.BACKOFF_POLICY_LINEAR)
+                        .setOverrideDeadline(0)
+                        .setImportantWhileForeground(allowInIdle);
+                final int result = jobScheduler.schedule(jobBuilder.build());
+                if (result != JobScheduler.RESULT_SUCCESS) {
+                    Log.e(TAG, "Could not schedule job " + jobId);
+                } else {
+                    Log.d(TAG, "Successfully scheduled job with id " + jobId);
+                }
+                break;
+            default:
+                Log.e(TAG, "Unknown action " + intent.getAction());
+        }
+    }
+}
\ No newline at end of file
diff --git a/tests/JobScheduler/JobTestApp/src/android/jobscheduler/cts/jobtestapp/TestJobService.java b/tests/JobScheduler/JobTestApp/src/android/jobscheduler/cts/jobtestapp/TestJobService.java
new file mode 100644
index 0000000..55031c3
--- /dev/null
+++ b/tests/JobScheduler/JobTestApp/src/android/jobscheduler/cts/jobtestapp/TestJobService.java
@@ -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
+ */
+
+package android.jobscheduler.cts.jobtestapp;
+
+import android.app.job.JobParameters;
+import android.app.job.JobService;
+import android.content.Intent;
+import android.util.Log;
+
+public class TestJobService extends JobService {
+    private static final String TAG = TestJobService.class.getSimpleName();
+    private static final String PACKAGE_NAME = "android.jobscheduler.cts.jobtestapp";
+    public static final String ACTION_JOB_STARTED = PACKAGE_NAME + ".action.JOB_STARTED";
+    public static final String ACTION_JOB_STOPPED = PACKAGE_NAME + ".action.JOB_STOPPED";
+    public static final String JOB_PARAMS_EXTRA_KEY = PACKAGE_NAME + ".extra.JOB_PARAMETERS";
+
+    @Override
+    public boolean onStartJob(JobParameters params) {
+        Log.i(TAG, "Test job executing: " + params.getJobId());
+        final Intent reportJobStartIntent = new Intent(ACTION_JOB_STARTED);
+        reportJobStartIntent.putExtra(JOB_PARAMS_EXTRA_KEY, params);
+        sendBroadcast(reportJobStartIntent);
+        return true;
+    }
+
+    @Override
+    public boolean onStopJob(JobParameters params) {
+        Log.i(TAG, "Test job stopped executing: " + params.getJobId());
+        final Intent reportJobStopIntent = new Intent(ACTION_JOB_STOPPED);
+        reportJobStopIntent.putExtra(JOB_PARAMS_EXTRA_KEY, params);
+        sendBroadcast(reportJobStopIntent);
+        return true;
+    }
+}
diff --git a/tests/JobScheduler/jobperm/AndroidManifest.xml b/tests/JobScheduler/jobperm/AndroidManifest.xml
index 14eb02b..493c3e8 100755
--- a/tests/JobScheduler/jobperm/AndroidManifest.xml
+++ b/tests/JobScheduler/jobperm/AndroidManifest.xml
@@ -27,6 +27,8 @@
     <uses-permission android:name="android.jobscheduler.cts.jobperm.perm" />
 
     <application>
+        <uses-library android:name="android.test.runner" />
+
         <!-- Need a way for another app to try to access the permission. So create a content
         provider which is enforced by the permission -->
         <provider android:name=".JobPermProvider"
diff --git a/tests/JobScheduler/shareduid/AndroidManifest.xml b/tests/JobScheduler/shareduid/AndroidManifest.xml
index 756a067..7b4bb56 100755
--- a/tests/JobScheduler/shareduid/AndroidManifest.xml
+++ b/tests/JobScheduler/shareduid/AndroidManifest.xml
@@ -19,5 +19,6 @@
           package="android.jobscheduler.cts.shareduid"
           android:sharedUserId="android.jobscheduler.cts.shared.uid">
     <application>
+        <uses-library android:name="android.test.runner" />
     </application>
 </manifest>
diff --git a/tests/JobScheduler/src/android/jobscheduler/MockJobService.java b/tests/JobScheduler/src/android/jobscheduler/MockJobService.java
index 94aee20..964853c 100644
--- a/tests/JobScheduler/src/android/jobscheduler/MockJobService.java
+++ b/tests/JobScheduler/src/android/jobscheduler/MockJobService.java
@@ -52,6 +52,8 @@
 
     ArrayList<JobWorkItem> mReceivedWork = new ArrayList<>();
 
+    ArrayList<JobWorkItem> mPendingCompletions = new ArrayList<>();
+
     private boolean mWaitingForStop;
 
     @Override
@@ -104,6 +106,8 @@
                 Log.i(TAG, "Received work #" + index + ": " + work.getIntent());
                 mReceivedWork.add(work);
 
+                int flags = 0;
+
                 if (index < expectedWork.length) {
                     TestWorkItem expected = expectedWork[index];
                     int grantFlags = work.getIntent().getFlags();
@@ -170,15 +174,33 @@
                         }
                     }
 
-                    if ((expected.flags & TestWorkItem.FLAG_WAIT_FOR_STOP) != 0) {
+                    flags = expected.flags;
+
+                    if ((flags & TestWorkItem.FLAG_WAIT_FOR_STOP) != 0) {
                         Log.i(TAG, "Now waiting to stop");
                         mWaitingForStop = true;
                         TestEnvironment.getTestEnvironment().notifyWaitingForStop();
                         return true;
                     }
+
+                    if ((flags & TestWorkItem.FLAG_COMPLETE_NEXT) != 0) {
+                        if (!processNextPendingCompletion()) {
+                            TestEnvironment.getTestEnvironment().notifyExecution(params,
+                                    0, 0, null,
+                                    "Expected to complete next pending work but there was none: "
+                                            + " @ #" + index);
+                            return false;
+                        }
+                    }
                 }
 
-                mParams.completeWork(work);
+                if ((flags & TestWorkItem.FLAG_DELAY_COMPLETE_PUSH_BACK) != 0) {
+                    mPendingCompletions.add(work);
+                } else if ((flags & TestWorkItem.FLAG_DELAY_COMPLETE_PUSH_TOP) != 0) {
+                    mPendingCompletions.add(0, work);
+                } else {
+                    mParams.completeWork(work);
+                }
 
                 if (index < expectedWork.length) {
                     TestWorkItem expected = expectedWork[index];
@@ -195,6 +217,20 @@
 
                 index++;
             }
+
+            if (processNextPendingCompletion()) {
+                // We had some pending completions, clean them all out...
+                while (processNextPendingCompletion()) {
+                }
+                // ...and we need to do a final dequeue to complete the job, which should not
+                // return any remaining work.
+                if ((work = params.dequeueWork()) != null) {
+                    TestEnvironment.getTestEnvironment().notifyExecution(params,
+                            0, 0, null,
+                            "Expected no remaining work after dequeue pending, but got: " + work);
+                }
+            }
+
             Log.i(TAG, "Done with all work at #" + index);
             // We don't notifyExecution here because we want to make sure the job properly
             // stops itself.
@@ -219,6 +255,16 @@
         }
     }
 
+    boolean processNextPendingCompletion() {
+        if (mPendingCompletions.size() <= 0) {
+            return false;
+        }
+
+        JobWorkItem next = mPendingCompletions.remove(0);
+        mParams.completeWork(next);
+        return true;
+    }
+
     @Override
     public boolean onStopJob(JobParameters params) {
         Log.i(TAG, "Received stop callback");
@@ -227,7 +273,24 @@
     }
 
     public static final class TestWorkItem {
+        /**
+         * Stop processing work for now, waiting for the service to be stopped.
+         */
         public static final int FLAG_WAIT_FOR_STOP = 1<<0;
+        /**
+         * Don't complete this work now, instead push it on the back of the stack of
+         * pending completions.
+         */
+        public static final int FLAG_DELAY_COMPLETE_PUSH_BACK = 1<<1;
+        /**
+         * Don't complete this work now, instead insert to the top of the stack of
+         * pending completions.
+         */
+        public static final int FLAG_DELAY_COMPLETE_PUSH_TOP = 1<<2;
+        /**
+         * Complete next pending completion on the stack before completing this one.
+         */
+        public static final int FLAG_COMPLETE_NEXT = 1<<3;
 
         public final Intent intent;
         public final JobInfo jobInfo;
@@ -247,6 +310,16 @@
             requireUrisNotGranted = null;
         }
 
+        public TestWorkItem(Intent _intent, int _flags) {
+            intent = _intent;
+            jobInfo = null;
+            flags = _flags;
+            deliveryCount = 1;
+            subitems = null;
+            requireUrisGranted = null;
+            requireUrisNotGranted = null;
+        }
+
         public TestWorkItem(Intent _intent, int _flags, int _deliveryCount) {
             intent = _intent;
             jobInfo = null;
diff --git a/tests/JobScheduler/src/android/jobscheduler/cts/BatteryConstraintTest.java b/tests/JobScheduler/src/android/jobscheduler/cts/BatteryConstraintTest.java
index 65cd02b..e28675d 100644
--- a/tests/JobScheduler/src/android/jobscheduler/cts/BatteryConstraintTest.java
+++ b/tests/JobScheduler/src/android/jobscheduler/cts/BatteryConstraintTest.java
@@ -28,6 +28,7 @@
 import android.net.ConnectivityManager;
 import android.net.NetworkInfo;
 import android.net.wifi.WifiManager;
+import android.os.BatteryManager;
 import android.os.SystemClock;
 import android.util.Log;
 
@@ -106,6 +107,10 @@
         boolean curNotLow = Boolean.parseBoolean(SystemUtil.runShellCommand(getInstrumentation(),
                 "cmd jobscheduler get-battery-not-low").trim());
         assertEquals(notLow, curNotLow);
+        IntentFilter filter = new IntentFilter(Intent.ACTION_BATTERY_CHANGED);
+        Intent batteryState = getContext().registerReceiver(null, filter);
+        assertEquals(notLow,
+                !batteryState.getBooleanExtra(BatteryManager.EXTRA_BATTERY_LOW, notLow));
     }
 
     String getJobState() throws Exception {
diff --git a/tests/JobScheduler/src/android/jobscheduler/cts/DeviceIdleJobsTest.java b/tests/JobScheduler/src/android/jobscheduler/cts/DeviceIdleJobsTest.java
new file mode 100644
index 0000000..0918a3b
--- /dev/null
+++ b/tests/JobScheduler/src/android/jobscheduler/cts/DeviceIdleJobsTest.java
@@ -0,0 +1,281 @@
+/*
+ * 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.jobscheduler.cts;
+
+import static android.jobscheduler.cts.jobtestapp.TestJobService.ACTION_JOB_STARTED;
+import static android.jobscheduler.cts.jobtestapp.TestJobService.ACTION_JOB_STOPPED;
+import static android.jobscheduler.cts.jobtestapp.TestJobService.JOB_PARAMS_EXTRA_KEY;
+import static android.os.PowerManager.ACTION_DEVICE_IDLE_MODE_CHANGED;
+import static android.os.PowerManager.ACTION_LIGHT_DEVICE_IDLE_MODE_CHANGED;
+
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertTrue;
+
+import android.app.job.JobParameters;
+import android.content.BroadcastReceiver;
+import android.content.ComponentName;
+import android.content.Context;
+import android.content.Intent;
+import android.content.IntentFilter;
+import android.jobscheduler.cts.jobtestapp.TestActivity;
+import android.jobscheduler.cts.jobtestapp.TestJobSchedulerReceiver;
+import android.os.PowerManager;
+import android.os.SystemClock;
+import android.support.test.InstrumentationRegistry;
+import android.support.test.filters.LargeTest;
+import android.support.test.runner.AndroidJUnit4;
+import android.support.test.uiautomator.UiDevice;
+import android.util.Log;
+
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+/**
+ * Tests that temp whitelisted apps can run jobs if all the other constraints are met
+ */
+@RunWith(AndroidJUnit4.class)
+@LargeTest
+public class DeviceIdleJobsTest {
+    private static final String TAG = DeviceIdleJobsTest.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";
+    private static final long BACKGROUND_JOBS_EXPECTED_DELAY = 3_000;
+    private static final long POLL_INTERVAL = 500;
+    private static final long DEFAULT_WAIT_TIMEOUT = 1000;
+
+    private Context mContext;
+    private UiDevice mUiDevice;
+    private PowerManager mPowerManager;
+    private long mTempWhitelistExpiryElapsed;
+    private int mTestJobId;
+    private int mTestPackageUid;
+    private boolean mDeviceInDoze;
+    /* accesses must be synchronized on itself */
+    private final TestJobStatus mTestJobStatus = new TestJobStatus();
+    private final BroadcastReceiver mReceiver = new BroadcastReceiver() {
+        @Override
+        public void onReceive(Context context, Intent intent) {
+            Log.d(TAG, "Received action " + intent.getAction());
+            switch (intent.getAction()) {
+                case ACTION_JOB_STARTED:
+                case ACTION_JOB_STOPPED:
+                    final JobParameters params = intent.getParcelableExtra(JOB_PARAMS_EXTRA_KEY);
+                    Log.d(TAG, "JobId: " + params.getJobId());
+                    synchronized (mTestJobStatus) {
+                        mTestJobStatus.running = ACTION_JOB_STARTED.equals(intent.getAction());
+                        mTestJobStatus.jobId = params.getJobId();
+                    }
+                    break;
+                case ACTION_DEVICE_IDLE_MODE_CHANGED:
+                case ACTION_LIGHT_DEVICE_IDLE_MODE_CHANGED:
+                    synchronized (DeviceIdleJobsTest.this) {
+                        mDeviceInDoze = mPowerManager.isDeviceIdleMode();
+                        Log.d(TAG, "mDeviceInDoze: " + mDeviceInDoze);
+                    }
+                    break;
+            }
+        }
+    };
+
+    @Before
+    public void setUp() throws Exception {
+        mContext = InstrumentationRegistry.getTargetContext();
+        mUiDevice = UiDevice.getInstance(InstrumentationRegistry.getInstrumentation());
+        mPowerManager = mContext.getSystemService(PowerManager.class);
+        mDeviceInDoze = mPowerManager.isDeviceIdleMode();
+        mTestPackageUid = mContext.getPackageManager().getPackageUid(TEST_APP_PACKAGE, 0);
+        mTestJobId = (int) (SystemClock.uptimeMillis() / 1000);
+        mTestJobStatus.reset();
+        mTempWhitelistExpiryElapsed = -1;
+        final IntentFilter intentFilter = new IntentFilter();
+        intentFilter.addAction(ACTION_JOB_STARTED);
+        intentFilter.addAction(ACTION_JOB_STOPPED);
+        intentFilter.addAction(ACTION_DEVICE_IDLE_MODE_CHANGED);
+        intentFilter.addAction(ACTION_LIGHT_DEVICE_IDLE_MODE_CHANGED);
+        mContext.registerReceiver(mReceiver, intentFilter);
+        assertFalse("Test package already in temp whitelist", isTestAppTempWhitelisted());
+        makeTestPackageIdle();
+        makeTestPackageStandbyActive();
+    }
+
+
+    @Test
+    public void testAllowWhileIdleJobInForeground() throws Exception {
+        toggleDeviceIdleState(true);
+        sendScheduleJobBroadcast(true);
+        assertFalse("Job started while in background", awaitJobStart(5_000));
+        startAndKeepTestActivity();
+        assertTrue("Job with allow_while_idle flag did not start when the app was in fg",
+                awaitJobStart(DEFAULT_WAIT_TIMEOUT));
+    }
+
+    @Test
+    public void testAllowWhileIdleJobInTempwhitelist() throws Exception {
+        toggleDeviceIdleState(true);
+        Thread.sleep(DEFAULT_WAIT_TIMEOUT);
+        sendScheduleJobBroadcast(true);
+        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));
+    }
+
+    @Test
+    public void testForegroundJobsStartImmediately() throws Exception {
+        sendScheduleJobBroadcast(false);
+        assertTrue("Job did not start after scheduling", awaitJobStart(DEFAULT_WAIT_TIMEOUT));
+        toggleDeviceIdleState(true);
+        assertTrue("Job did not stop on entering doze", awaitJobStop(DEFAULT_WAIT_TIMEOUT));
+        Thread.sleep(TestJobSchedulerReceiver.JOB_INITIAL_BACKOFF);
+        startAndKeepTestActivity();
+        toggleDeviceIdleState(false);
+        assertTrue("Job for foreground app did not start immediately when device exited doze",
+                awaitJobStart(DEFAULT_WAIT_TIMEOUT));
+    }
+
+    @Test
+    public void testBackgroundJobsDelayed() throws Exception {
+        sendScheduleJobBroadcast(false);
+        assertTrue("Job did not start after scheduling", awaitJobStart(DEFAULT_WAIT_TIMEOUT));
+        toggleDeviceIdleState(true);
+        assertTrue("Job did not stop on entering doze", awaitJobStop(DEFAULT_WAIT_TIMEOUT));
+        Thread.sleep(TestJobSchedulerReceiver.JOB_INITIAL_BACKOFF);
+        toggleDeviceIdleState(false);
+        assertFalse("Job for background app started immediately when device exited doze",
+                awaitJobStart(DEFAULT_WAIT_TIMEOUT));
+        Thread.sleep(BACKGROUND_JOBS_EXPECTED_DELAY - DEFAULT_WAIT_TIMEOUT);
+        assertTrue("Job for background app did not start after the expected delay of "
+                + BACKGROUND_JOBS_EXPECTED_DELAY + "ms", awaitJobStart(DEFAULT_WAIT_TIMEOUT));
+    }
+
+    @After
+    public void tearDown() throws Exception {
+        toggleDeviceIdleState(false);
+        final Intent cancelJobsIntent = new Intent(TestJobSchedulerReceiver.ACTION_CANCEL_JOBS);
+        cancelJobsIntent.setComponent(new ComponentName(TEST_APP_PACKAGE, TEST_APP_RECEIVER));
+        cancelJobsIntent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
+        mContext.sendBroadcast(cancelJobsIntent);
+        mContext.sendBroadcast(new Intent(TestActivity.ACTION_FINISH_ACTIVITY));
+        mContext.unregisterReceiver(mReceiver);
+        Thread.sleep(500); // To avoid any race between unregister and the next register in setUp
+        waitUntilTestAppNotInTempWhitelist();
+    }
+
+    private boolean isTestAppTempWhitelisted() throws Exception {
+        final String output = mUiDevice.executeShellCommand("cmd deviceidle tempwhitelist").trim();
+        for (String line : output.split("\n")) {
+            if (line.contains("UID="+mTestPackageUid)) {
+                return true;
+            }
+        }
+        return false;
+    }
+
+    private void startAndKeepTestActivity() {
+        final Intent testActivity = new Intent();
+        testActivity.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
+        testActivity.setComponent(new ComponentName(TEST_APP_PACKAGE, TEST_APP_ACTIVITY));
+        mContext.startActivity(testActivity);
+    }
+
+    private void sendScheduleJobBroadcast(boolean allowWhileIdle) throws Exception {
+        final Intent scheduleJobIntent = new Intent(TestJobSchedulerReceiver.ACTION_SCHEDULE_JOB);
+        scheduleJobIntent.putExtra(TestJobSchedulerReceiver.EXTRA_JOB_ID_KEY, mTestJobId);
+        scheduleJobIntent.putExtra(TestJobSchedulerReceiver.EXTRA_ALLOW_IN_IDLE, allowWhileIdle);
+        scheduleJobIntent.setComponent(new ComponentName(TEST_APP_PACKAGE, TEST_APP_RECEIVER));
+        mContext.sendBroadcast(scheduleJobIntent);
+    }
+
+    private void toggleDeviceIdleState(final boolean idle) throws Exception {
+        mUiDevice.executeShellCommand("cmd deviceidle " + (idle ? "force-idle" : "unforce"));
+        assertTrue("Could not change device idle state to " + idle,
+                waitUntilTrue(DEFAULT_WAIT_TIMEOUT, () -> {
+                    synchronized (DeviceIdleJobsTest.this) {
+                        return mDeviceInDoze == idle;
+                    }
+                }));
+    }
+
+    private void tempWhitelistTestApp(long duration) throws Exception {
+        mUiDevice.executeShellCommand("cmd deviceidle tempwhitelist -d " + duration
+                + " " + TEST_APP_PACKAGE);
+        mTempWhitelistExpiryElapsed = SystemClock.elapsedRealtime() + duration;
+    }
+
+    private void makeTestPackageIdle() throws Exception {
+        mUiDevice.executeShellCommand("am make-uid-idle --user current " + TEST_APP_PACKAGE);
+    }
+
+    private void makeTestPackageStandbyActive() throws Exception {
+        mUiDevice.executeShellCommand("am set-standby-bucket " + TEST_APP_PACKAGE + " active");
+    }
+
+    private boolean waitUntilTestAppNotInTempWhitelist() throws Exception {
+        long now;
+        boolean interrupted = false;
+        while ((now = SystemClock.elapsedRealtime()) < mTempWhitelistExpiryElapsed) {
+            try {
+                Thread.sleep(mTempWhitelistExpiryElapsed - now);
+            } catch (InterruptedException iexc) {
+                interrupted = true;
+            }
+        }
+        if (interrupted) {
+            Thread.currentThread().interrupt();
+        }
+        return waitUntilTrue(DEFAULT_WAIT_TIMEOUT, () -> !isTestAppTempWhitelisted());
+    }
+
+    private boolean awaitJobStart(long maxWait) throws Exception {
+        return waitUntilTrue(maxWait, () -> {
+            synchronized (mTestJobStatus) {
+                return (mTestJobStatus.jobId == mTestJobId) && mTestJobStatus.running;
+            }
+        });
+    }
+
+    private boolean awaitJobStop(long maxWait) throws Exception {
+        return waitUntilTrue(maxWait, () -> {
+            synchronized (mTestJobStatus) {
+                return (mTestJobStatus.jobId == mTestJobId) && !mTestJobStatus.running;
+            }
+        });
+    }
+
+    private boolean waitUntilTrue(long maxWait, Condition condition) throws Exception {
+        final long deadLine = SystemClock.uptimeMillis() + maxWait;
+        do {
+            Thread.sleep(POLL_INTERVAL);
+        } while (!condition.isTrue() && SystemClock.uptimeMillis() < deadLine);
+        return condition.isTrue();
+    }
+
+    private static final class TestJobStatus {
+        int jobId;
+        boolean running;
+        private void reset() {
+            running = false;
+        }
+    }
+
+    private interface Condition {
+        boolean isTrue() throws Exception;
+    }
+}
diff --git a/tests/JobScheduler/src/android/jobscheduler/cts/EnqueueJobWorkTest.java b/tests/JobScheduler/src/android/jobscheduler/cts/EnqueueJobWorkTest.java
index 6d2599f..604ccce 100644
--- a/tests/JobScheduler/src/android/jobscheduler/cts/EnqueueJobWorkTest.java
+++ b/tests/JobScheduler/src/android/jobscheduler/cts/EnqueueJobWorkTest.java
@@ -186,6 +186,87 @@
     }
 
     /**
+     * Test basic enqueueing batches of work that will be executed in parallel.
+     */
+    public void testEnqueueParallel2Work() throws Exception {
+        Intent work1 = new Intent("work1");
+        Intent work2 = new Intent("work2");
+        TestWorkItem[] work = new TestWorkItem[] {
+                new TestWorkItem(work1, TestWorkItem.FLAG_DELAY_COMPLETE_PUSH_BACK),
+                new TestWorkItem(work2, TestWorkItem.FLAG_DELAY_COMPLETE_PUSH_BACK) };
+        kTestEnvironment.setExpectedExecutions(1);
+        kTestEnvironment.setExpectedWork(work);
+        JobInfo ji = mBuilder.setOverrideDeadline(0).build();
+        mJobScheduler.enqueue(ji, new JobWorkItem(work1));
+        mJobScheduler.enqueue(ji, new JobWorkItem(work2));
+        kTestEnvironment.readyToWork();
+        assertTrue("Job with work enqueued did not fire.",
+                kTestEnvironment.awaitExecution());
+        compareWork(work, kTestEnvironment.getLastReceivedWork());
+    }
+
+    /**
+     * Test basic enqueueing batches of work that will be executed in parallel and completed
+     * in reverse order.
+     */
+    public void testEnqueueParallel2ReverseWork() throws Exception {
+        Intent work1 = new Intent("work1");
+        Intent work2 = new Intent("work2");
+        TestWorkItem[] work = new TestWorkItem[] {
+                new TestWorkItem(work1, TestWorkItem.FLAG_DELAY_COMPLETE_PUSH_BACK),
+                new TestWorkItem(work2, TestWorkItem.FLAG_DELAY_COMPLETE_PUSH_TOP) };
+        kTestEnvironment.setExpectedExecutions(1);
+        kTestEnvironment.setExpectedWork(work);
+        JobInfo ji = mBuilder.setOverrideDeadline(0).build();
+        mJobScheduler.enqueue(ji, new JobWorkItem(work1));
+        mJobScheduler.enqueue(ji, new JobWorkItem(work2));
+        kTestEnvironment.readyToWork();
+        assertTrue("Job with work enqueued did not fire.",
+                kTestEnvironment.awaitExecution());
+        compareWork(work, kTestEnvironment.getLastReceivedWork());
+    }
+
+    /**
+     * Test basic enqueueing batches of work that will be executed in parallel.
+     */
+    public void testEnqueueMultipleParallelWork() throws Exception {
+        Intent work1 = new Intent("work1");
+        Intent work2 = new Intent("work2");
+        Intent work3 = new Intent("work3");
+        Intent work4 = new Intent("work4");
+        Intent work5 = new Intent("work5");
+        Intent work6 = new Intent("work6");
+        Intent work7 = new Intent("work7");
+        Intent work8 = new Intent("work8");
+        TestWorkItem[] work = new TestWorkItem[] {
+                new TestWorkItem(work1, TestWorkItem.FLAG_DELAY_COMPLETE_PUSH_BACK),
+                new TestWorkItem(work2, TestWorkItem.FLAG_DELAY_COMPLETE_PUSH_TOP),
+                new TestWorkItem(work3, TestWorkItem.FLAG_DELAY_COMPLETE_PUSH_BACK
+                        | TestWorkItem.FLAG_COMPLETE_NEXT),
+                new TestWorkItem(work4, TestWorkItem.FLAG_COMPLETE_NEXT),
+                new TestWorkItem(work5, TestWorkItem.FLAG_DELAY_COMPLETE_PUSH_TOP),
+                new TestWorkItem(work6, TestWorkItem.FLAG_DELAY_COMPLETE_PUSH_TOP),
+                new TestWorkItem(work7, TestWorkItem.FLAG_DELAY_COMPLETE_PUSH_TOP
+                        | TestWorkItem.FLAG_COMPLETE_NEXT),
+                new TestWorkItem(work8) };
+        kTestEnvironment.setExpectedExecutions(1);
+        kTestEnvironment.setExpectedWork(work);
+        JobInfo ji = mBuilder.setOverrideDeadline(0).build();
+        mJobScheduler.enqueue(ji, new JobWorkItem(work1));
+        mJobScheduler.enqueue(ji, new JobWorkItem(work2));
+        mJobScheduler.enqueue(ji, new JobWorkItem(work3));
+        mJobScheduler.enqueue(ji, new JobWorkItem(work4));
+        mJobScheduler.enqueue(ji, new JobWorkItem(work5));
+        mJobScheduler.enqueue(ji, new JobWorkItem(work6));
+        mJobScheduler.enqueue(ji, new JobWorkItem(work7));
+        mJobScheduler.enqueue(ji, new JobWorkItem(work8));
+        kTestEnvironment.readyToWork();
+        assertTrue("Job with work enqueued did not fire.",
+                kTestEnvironment.awaitExecution());
+        compareWork(work, kTestEnvironment.getLastReceivedWork());
+    }
+
+    /**
      * Test job getting stopped while processing work and that work being redelivered.
      */
     public void testEnqueueMultipleRedeliver() throws Exception {
@@ -238,6 +319,61 @@
     }
 
     /**
+     * Test job getting stopped while processing work in parallel and that work being redelivered.
+     */
+    public void testEnqueueMultipleParallelRedeliver() throws Exception {
+        Intent work1 = new Intent("work1");
+        Intent work2 = new Intent("work2");
+        Intent work3 = new Intent("work3");
+        Intent work4 = new Intent("work4");
+        Intent work5 = new Intent("work5");
+        Intent work6 = new Intent("work6");
+        TestWorkItem[] initialWork = new TestWorkItem[] {
+                new TestWorkItem(work1),
+                new TestWorkItem(work2, TestWorkItem.FLAG_DELAY_COMPLETE_PUSH_BACK),
+                new TestWorkItem(work3, TestWorkItem.FLAG_DELAY_COMPLETE_PUSH_BACK),
+                new TestWorkItem(work4, TestWorkItem.FLAG_WAIT_FOR_STOP, 1) };
+        kTestEnvironment.setExpectedExecutions(1);
+        kTestEnvironment.setExpectedWaitForStop();
+        kTestEnvironment.setExpectedWork(initialWork);
+        JobInfo ji = mBuilder.setOverrideDeadline(0).build();
+        mJobScheduler.enqueue(ji, new JobWorkItem(work1));
+        mJobScheduler.enqueue(ji, new JobWorkItem(work2));
+        mJobScheduler.enqueue(ji, new JobWorkItem(work3));
+        mJobScheduler.enqueue(ji, new JobWorkItem(work4));
+        kTestEnvironment.readyToWork();
+
+        // Now wait for the job to get to the point where it is processing the last
+        // work and waiting for it to be stopped.
+        assertTrue("Job with work enqueued did not wait to stop.",
+                kTestEnvironment.awaitWaitingForStop());
+
+        // Cause the job to timeout (stop) immediately, and wait for its execution to finish.
+        SystemUtil.runShellCommand(getInstrumentation(), "cmd jobscheduler timeout "
+                + kJobServiceComponent.getPackageName() + " " + ENQUEUE_WORK_JOB_ID);
+        assertTrue("Job with work enqueued did not finish.",
+                kTestEnvironment.awaitExecution());
+        compareWork(initialWork, kTestEnvironment.getLastReceivedWork());
+
+        // Now we are going to add some more work, restart the job, and see if it correctly
+        // redelivers the last work and delivers the new work.
+        TestWorkItem[] finalWork = new TestWorkItem[] {
+                new TestWorkItem(work2, 0, 2), new TestWorkItem(work3, 0, 2),
+                new TestWorkItem(work4, 0, 2), new TestWorkItem(work5), new TestWorkItem(work6) };
+        kTestEnvironment.setExpectedExecutions(1);
+        kTestEnvironment.setExpectedWork(finalWork);
+        mJobScheduler.enqueue(ji, new JobWorkItem(work5));
+        mJobScheduler.enqueue(ji, new JobWorkItem(work6));
+        kTestEnvironment.readyToWork();
+        SystemUtil.runShellCommand(getInstrumentation(), "cmd jobscheduler run "
+                + kJobServiceComponent.getPackageName() + " " + ENQUEUE_WORK_JOB_ID);
+
+        assertTrue("Restarted with work enqueued did not execute.",
+                kTestEnvironment.awaitExecution());
+        compareWork(finalWork, kTestEnvironment.getLastReceivedWork());
+    }
+
+    /**
      * Test basic enqueueing batches of work.
      */
     public void testEnqueueMultipleUriGrantWork() throws Exception {
diff --git a/tests/ProcessTest/Android.mk b/tests/ProcessTest/Android.mk
index 2feff2e..6b15240 100644
--- a/tests/ProcessTest/Android.mk
+++ b/tests/ProcessTest/Android.mk
@@ -24,7 +24,9 @@
 
 LOCAL_SRC_FILES := $(call all-subdir-java-files)
 
-LOCAL_STATIC_JAVA_LIBRARIES := legacy-android-test junit
+LOCAL_STATIC_JAVA_LIBRARIES := junit
+
+LOCAL_JAVA_LIBRARIES := android.test.base.stubs
 
 LOCAL_PACKAGE_NAME := ProcessTests
 
diff --git a/tests/acceleration/Android.mk b/tests/acceleration/Android.mk
index 81869bf..cef4379 100644
--- a/tests/acceleration/Android.mk
+++ b/tests/acceleration/Android.mk
@@ -25,7 +25,9 @@
 LOCAL_PROGUARD_ENABLED := disabled
 
 LOCAL_STATIC_JAVA_LIBRARIES := \
-    ctstestrunner compatibility-device-util legacy-android-test
+    ctstestrunner compatibility-device-util
+
+LOCAL_JAVA_LIBRARIES := android.test.runner.stubs android.test.base.stubs
 
 LOCAL_SRC_FILES := $(call all-java-files-under, src)
 
diff --git a/tests/acceleration/AndroidTest.xml b/tests/acceleration/AndroidTest.xml
index 01ff4cc..b28f845 100644
--- a/tests/acceleration/AndroidTest.xml
+++ b/tests/acceleration/AndroidTest.xml
@@ -14,6 +14,7 @@
      limitations under the License.
 -->
 <configuration description="Config for CTS Acceleration test cases">
+    <option name="test-suite-tag" value="cts" />
     <option name="config-descriptor:metadata" key="component" value="location" />
     <target_preparer class="com.android.tradefed.targetprep.suite.SuiteApkInstaller">
         <option name="cleanup-apks" value="true" />
diff --git a/tests/accessibility/Android.mk b/tests/accessibility/Android.mk
index 4cd7e92..cf41720 100644
--- a/tests/accessibility/Android.mk
+++ b/tests/accessibility/Android.mk
@@ -26,6 +26,8 @@
 
 LOCAL_STATIC_JAVA_LIBRARIES := compatibility-device-util ctstestrunner
 
+LOCAL_JAVA_LIBRARIES := android.test.base.stubs
+
 # Tag this module as a cts test artifact
 LOCAL_COMPATIBILITY_SUITE := cts vts general-tests
 
diff --git a/tests/accessibility/src/android/view/accessibility/cts/AccessibilityEventTest.java b/tests/accessibility/src/android/view/accessibility/cts/AccessibilityEventTest.java
index aa9db4f..5348373 100644
--- a/tests/accessibility/src/android/view/accessibility/cts/AccessibilityEventTest.java
+++ b/tests/accessibility/src/android/view/accessibility/cts/AccessibilityEventTest.java
@@ -32,7 +32,7 @@
 public class AccessibilityEventTest extends TestCase {
 
     /** The number of properties of the {@link AccessibilityEvent} class. */
-    private static final int NON_STATIC_FIELD_COUNT = 29;
+    private static final int NON_STATIC_FIELD_COUNT = 32;
 
     /**
      * Test that no new fields have been added without updating the
@@ -206,6 +206,8 @@
         sentEvent.setMaxScrollY(1);
         sentEvent.setScrollX(1);
         sentEvent.setScrollY(1);
+        sentEvent.setScrollDeltaX(3);
+        sentEvent.setScrollDeltaY(3);
         sentEvent.setToIndex(1);
         sentEvent.setScrollable(true);
         sentEvent.setAction(AccessibilityNodeInfo.ACTION_ACCESSIBILITY_FOCUS);
@@ -261,6 +263,10 @@
                 receivedEvent.getScrollX());
         assertSame("scrollY has incorect value", expectedEvent.getScrollY(),
                 receivedEvent.getScrollY());
+        assertSame("scrollDeltaX has incorect value", expectedEvent.getScrollDeltaX(),
+                receivedEvent.getScrollDeltaX());
+        assertSame("scrollDeltaY has incorect value", expectedEvent.getScrollDeltaY(),
+                receivedEvent.getScrollDeltaY());
         assertSame("toIndex has incorect value", expectedEvent.getToIndex(),
                 receivedEvent.getToIndex());
         assertSame("scrollable has incorect value", expectedEvent.isScrollable(),
@@ -269,6 +275,8 @@
                 receivedEvent.getMovementGranularity());
         assertSame("action has incorect value", expectedEvent.getAction(),
                 receivedEvent.getAction());
+        assertSame("windowChangeTypes has incorect value", expectedEvent.getWindowChanges(),
+                receivedEvent.getWindowChanges());
 
         assertSame("parcelableData has incorect value",
                 ((Message) expectedEvent.getParcelableData()).what,
diff --git a/tests/accessibility/src/android/view/accessibility/cts/AccessibilityRecordTest.java b/tests/accessibility/src/android/view/accessibility/cts/AccessibilityRecordTest.java
index 7862cb4..5bf02c8 100644
--- a/tests/accessibility/src/android/view/accessibility/cts/AccessibilityRecordTest.java
+++ b/tests/accessibility/src/android/view/accessibility/cts/AccessibilityRecordTest.java
@@ -35,7 +35,7 @@
 public class AccessibilityRecordTest extends AndroidTestCase {
 
     /** The number of properties of the {@link AccessibilityEvent} class. */
-    private static final int NON_STATIC_FIELD_COUNT = 22;
+    private static final int NON_STATIC_FIELD_COUNT = 24;
 
     /**
      * Test that no new fields have been added without updating the
diff --git a/tests/accessibility/src/android/view/accessibility/cts/ServiceControlUtils.java b/tests/accessibility/src/android/view/accessibility/cts/ServiceControlUtils.java
index 9783cb8..01275f3 100644
--- a/tests/accessibility/src/android/view/accessibility/cts/ServiceControlUtils.java
+++ b/tests/accessibility/src/android/view/accessibility/cts/ServiceControlUtils.java
@@ -181,6 +181,9 @@
         final Object waitLockForA11yOff = new Object();
         AccessibilityManager manager = (AccessibilityManager) instrumentation
                 .getContext().getSystemService(Context.ACCESSIBILITY_SERVICE);
+        if (!manager.isEnabled()) {
+            return;
+        }
         manager.addAccessibilityStateChangeListener(
                 new AccessibilityManager.AccessibilityStateChangeListener() {
                     @Override
diff --git a/tests/accessibilityservice/Android.mk b/tests/accessibilityservice/Android.mk
index 9a3bbe2..79e47ec 100644
--- a/tests/accessibilityservice/Android.mk
+++ b/tests/accessibilityservice/Android.mk
@@ -20,8 +20,9 @@
 
 LOCAL_STATIC_JAVA_LIBRARIES := \
 	ctstestrunner \
-	mockito-target-minus-junit4 \
-	legacy-android-test
+	mockito-target-minus-junit4
+
+LOCAL_JAVA_LIBRARIES := android.test.runner.stubs android.test.base.stubs
 
 LOCAL_SRC_FILES := $(call all-java-files-under, src)
 
@@ -33,3 +34,5 @@
 LOCAL_SDK_VERSION := test_current
 
 include $(BUILD_CTS_PACKAGE)
+
+include $(call all-makefiles-under,$(LOCAL_PATH))
diff --git a/tests/accessibilityservice/AndroidManifest.xml b/tests/accessibilityservice/AndroidManifest.xml
index cf6b774..fe5e18a 100644
--- a/tests/accessibilityservice/AndroidManifest.xml
+++ b/tests/accessibilityservice/AndroidManifest.xml
@@ -28,27 +28,28 @@
 
         <activity
             android:label="@string/accessibility_end_to_end_test_activity"
-            android:name=".AccessibilityEndToEndActivity" />
+            android:name=".activities.AccessibilityEndToEndActivity" />
 
         <activity
             android:label="@string/accessibility_query_window_test_activity"
-            android:name=".AccessibilityWindowQueryActivity"
+            android:name=".activities.AccessibilityWindowQueryActivity"
             android:supportsPictureInPicture="true" />
 
         <activity
             android:label="@string/accessibility_view_tree_reporting_test_activity"
-            android:name=".AccessibilityViewTreeReportingActivity" />
+            android:name=".activities.AccessibilityViewTreeReportingActivity" />
 
         <activity
             android:label="@string/accessibility_focus_and_input_focus_sync_test_activity"
-            android:name=".AccessibilityFocusAndInputFocusSyncActivity" />
+            android:name=".activities.AccessibilityFocusAndInputFocusSyncActivity" />
 
         <activity
             android:label="@string/accessibility_text_traversal_test_activity"
-            android:name=".AccessibilityTextTraversalActivity"/>
+            android:name=".activities.AccessibilityTextTraversalActivity"/>
 
         <activity android:label="Activity for testing window accessibility reporting"
-             android:name=".AccessibilityWindowReportingActivity"/>
+             android:name=".activities.AccessibilityWindowReportingActivity"
+             android:supportsPictureInPicture="true"/>
 
         <activity
             android:label="Full screen activity for gesture dispatch testing"
diff --git a/tests/accessibilityservice/AndroidTest.xml b/tests/accessibilityservice/AndroidTest.xml
index 6edfe8d..0454a298 100644
--- a/tests/accessibilityservice/AndroidTest.xml
+++ b/tests/accessibilityservice/AndroidTest.xml
@@ -14,11 +14,16 @@
      limitations under the License.
 -->
 <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" />
     <target_preparer class="com.android.tradefed.targetprep.suite.SuiteApkInstaller">
         <option name="cleanup-apks" value="true" />
         <option name="test-file-name" value="CtsAccessibilityServiceTestCases.apk" />
     </target_preparer>
+    <target_preparer class="com.android.tradefed.targetprep.suite.SuiteApkInstaller">
+        <option name="cleanup-apks" value="true" />
+        <option name="test-file-name" value="CtsAccessibilityWidgetProvider.apk" />
+    </target_preparer>
     <test class="com.android.tradefed.testtype.AndroidJUnitTest" >
         <option name="package" value="android.accessibilityservice.cts" />
         <option name="runtime-hint" value="2m12s" />
diff --git a/tests/accessibilityservice/res/layout/accessibility_focus_and_input_focus_sync_test.xml b/tests/accessibilityservice/res/layout/accessibility_focus_and_input_focus_sync_test.xml
index cb3d153..6a7ccf5 100644
--- a/tests/accessibilityservice/res/layout/accessibility_focus_and_input_focus_sync_test.xml
+++ b/tests/accessibilityservice/res/layout/accessibility_focus_and_input_focus_sync_test.xml
@@ -77,6 +77,7 @@
               android:id="@+id/secondButton"
               android:layout_width="wrap_content"
               android:layout_height="wrap_content"
+              android:screenReaderFocusable="true"
               android:text="@string/secondButton" />
 
       </LinearLayout>
diff --git a/tests/accessibilityservice/res/xml/stub_gesture_dispatch_a11y_service.xml b/tests/accessibilityservice/res/xml/stub_gesture_dispatch_a11y_service.xml
index 912c9c4..f4f6089 100644
--- a/tests/accessibilityservice/res/xml/stub_gesture_dispatch_a11y_service.xml
+++ b/tests/accessibilityservice/res/xml/stub_gesture_dispatch_a11y_service.xml
@@ -16,9 +16,9 @@
 -->
 
 <accessibility-service xmlns:android="http://schemas.android.com/apk/res/android"
-                       android:description="@string/stub_gesture_dispatch_a11y_service_description"
-                       android:accessibilityEventTypes="typeAllMask"
-                       android:accessibilityFeedbackType="feedbackGeneric"
-                       android:accessibilityFlags="flagDefault"
-                       android:canPerformGestures="true"
-                       android:notificationTimeout="0" />
+        android:description="@string/stub_gesture_dispatch_a11y_service_description"
+        android:accessibilityEventTypes="typeAllMask"
+        android:accessibilityFeedbackType="feedbackGeneric"
+        android:accessibilityFlags="flagDefault"
+        android:canPerformGestures="true"
+        android:notificationTimeout="0" />
diff --git a/tests/accessibilityservice/res/xml/stub_magnification_a11y_service.xml b/tests/accessibilityservice/res/xml/stub_magnification_a11y_service.xml
index 110f741..2559392 100644
--- a/tests/accessibilityservice/res/xml/stub_magnification_a11y_service.xml
+++ b/tests/accessibilityservice/res/xml/stub_magnification_a11y_service.xml
@@ -15,10 +15,11 @@
 -->
 
 <accessibility-service xmlns:android="http://schemas.android.com/apk/res/android"
-    android:accessibilityEventTypes="typeAllMask"
-    android:accessibilityFeedbackType="feedbackGeneric"
-    android:canRetrieveWindowContent="true"
-    android:canRequestTouchExplorationMode="true"
-    android:canRequestEnhancedWebAccessibility="true"
-    android:canRequestFilterKeyEvents="true"
-    android:canControlMagnification="true" />
+        android:accessibilityEventTypes="typeAllMask"
+        android:accessibilityFeedbackType="feedbackGeneric"
+        android:canRetrieveWindowContent="true"
+        android:canRequestTouchExplorationMode="true"
+        android:canRequestEnhancedWebAccessibility="true"
+        android:canRequestFilterKeyEvents="true"
+        android:canControlMagnification="true"
+        android:canPerformGestures="true"/>
diff --git a/tests/accessibilityservice/src/android/accessibilityservice/cts/AccessibilityEndToEndActivity.java b/tests/accessibilityservice/src/android/accessibilityservice/cts/AccessibilityEndToEndActivity.java
deleted file mode 100644
index 9a26ac7..0000000
--- a/tests/accessibilityservice/src/android/accessibilityservice/cts/AccessibilityEndToEndActivity.java
+++ /dev/null
@@ -1,68 +0,0 @@
-/*
- * Copyright (C) 2010 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package android.accessibilityservice.cts;
-
-import android.accessibilityservice.cts.R;
-
-import android.os.Bundle;
-import android.view.View;
-import android.view.ViewGroup;
-import android.widget.BaseAdapter;
-import android.widget.ListAdapter;
-import android.widget.ListView;
-import android.widget.TextView;
-
-/**
- * This class is an {@link android.app.Activity} used to perform end-to-end
- * testing of the accessibility feature by interaction with the
- * UI widgets.
- */
-public class AccessibilityEndToEndActivity extends AccessibilityTestActivity {
-    @Override
-    protected void onCreate(Bundle savedInstanceState) {
-        super.onCreate(savedInstanceState);
-        setContentView(R.layout.end_to_end_test);
-
-        ListAdapter listAdapter = new BaseAdapter() {
-            public View getView(int position, View convertView, ViewGroup parent) {
-                TextView textView = (TextView) View
-                        .inflate(AccessibilityEndToEndActivity.this, R.layout.list_view_row, null);
-                textView.setText((String) getItem(position));
-                return textView;
-            }
-
-            public long getItemId(int position) {
-                return position;
-            }
-
-            public Object getItem(int position) {
-                if (position == 0) {
-                    return AccessibilityEndToEndActivity.this.getString(R.string.first_list_item);
-                } else {
-                    return AccessibilityEndToEndActivity.this.getString(R.string.second_list_item);
-                }
-            }
-
-            public int getCount() {
-                return 2;
-            }
-        };
-
-        ListView listView = (ListView) findViewById(R.id.listview);
-        listView.setAdapter(listAdapter);
-    }
-}
diff --git a/tests/accessibilityservice/src/android/accessibilityservice/cts/AccessibilityEndToEndTest.java b/tests/accessibilityservice/src/android/accessibilityservice/cts/AccessibilityEndToEndTest.java
index b14b76a..ad1d712 100644
--- a/tests/accessibilityservice/src/android/accessibilityservice/cts/AccessibilityEndToEndTest.java
+++ b/tests/accessibilityservice/src/android/accessibilityservice/cts/AccessibilityEndToEndTest.java
@@ -16,6 +16,7 @@
 
 package android.accessibilityservice.cts;
 
+import android.accessibilityservice.cts.activities.AccessibilityEndToEndActivity;
 import android.app.Activity;
 import android.app.AlertDialog;
 import android.app.Notification;
@@ -24,22 +25,28 @@
 import android.app.PendingIntent;
 import android.app.Service;
 import android.app.UiAutomation;
+import android.appwidget.AppWidgetHost;
+import android.appwidget.AppWidgetManager;
+import android.appwidget.AppWidgetProviderInfo;
+import android.content.ComponentName;
+import android.content.Context;
 import android.content.Intent;
 import android.content.pm.PackageManager;
 import android.content.res.Configuration;
+import android.os.Process;
 import android.test.suitebuilder.annotation.MediumTest;
 import android.text.TextUtils;
 import android.util.Log;
 import android.view.accessibility.AccessibilityEvent;
 import android.view.accessibility.AccessibilityManager;
+import android.view.accessibility.AccessibilityNodeInfo;
 import android.widget.Button;
 import android.widget.EditText;
 import android.widget.ListView;
 
-import android.accessibilityservice.cts.R;
-
 import java.util.Iterator;
 import java.util.List;
+import java.util.concurrent.TimeoutException;
 
 /**
  * This class performs end-to-end testing of the accessibility feature by
@@ -51,6 +58,14 @@
 
     private static final String LOG_TAG = "AccessibilityEndToEndTest";
 
+    private static final String GRANT_BIND_APP_WIDGET_PERMISSION_COMMAND =
+            "appwidget grantbind --package android.accessibilityservice.cts --user 0";
+
+    private static final String REVOKE_BIND_APP_WIDGET_PERMISSION_COMMAND =
+            "appwidget revokebind --package android.accessibilityservice.cts --user 0";
+
+    private static final String APP_WIDGET_PROVIDER_PACKAGE = "foo.bar.baz";
+
     /**
      * Creates a new instance for testing {@link AccessibilityEndToEndActivity}.
      */
@@ -430,6 +445,158 @@
         }
     }
 
+    @MediumTest
+    public void testPackageNameCannotBeFaked() throws Exception {
+        getActivity().runOnUiThread(() -> {
+            // Set the activity to report fake package for events and nodes
+            getActivity().setReportedPackageName("foo.bar.baz");
+
+            // Make sure node package cannot be faked
+            AccessibilityNodeInfo root = getInstrumentation().getUiAutomation()
+                    .getRootInActiveWindow();
+            assertPackageName(root, getActivity().getPackageName());
+        });
+
+        // Make sure event package cannot be faked
+        try {
+            getInstrumentation().getUiAutomation().executeAndWaitForEvent(() ->
+                getInstrumentation().runOnMainSync(() ->
+                    getActivity().findViewById(R.id.button).requestFocus())
+                , (AccessibilityEvent event) ->
+                    event.getEventType() == AccessibilityEvent.TYPE_VIEW_FOCUSED
+                            && event.getPackageName().equals(getActivity().getPackageName())
+                , TIMEOUT_ASYNC_PROCESSING);
+        } catch (TimeoutException e) {
+            fail("Events from fake package should be fixed to use the correct package");
+        }
+    }
+
+    @MediumTest
+    public void testPackageNameCannotBeFakedAppWidget() throws Exception {
+        if (!hasAppWidgets()) {
+            return;
+        }
+
+        getInstrumentation().runOnMainSync(() -> {
+            // Set the activity to report fake package for events and nodes
+            getActivity().setReportedPackageName(APP_WIDGET_PROVIDER_PACKAGE);
+
+            // Make sure we cannot report nodes as if from the widget package
+            AccessibilityNodeInfo root = getInstrumentation().getUiAutomation()
+                    .getRootInActiveWindow();
+            assertPackageName(root, getActivity().getPackageName());
+        });
+
+        // Make sure we cannot send events as if from the widget package
+        try {
+            getInstrumentation().getUiAutomation().executeAndWaitForEvent(() ->
+                getInstrumentation().runOnMainSync(() ->
+                    getActivity().findViewById(R.id.button).requestFocus())
+                , (AccessibilityEvent event) ->
+                    event.getEventType() == AccessibilityEvent.TYPE_VIEW_FOCUSED
+                            && event.getPackageName().equals(getActivity().getPackageName())
+                , TIMEOUT_ASYNC_PROCESSING);
+        } catch (TimeoutException e) {
+            fail("Should not be able to send events from a widget package if no widget hosted");
+        }
+
+        // Create a host and start listening.
+        final AppWidgetHost host = new AppWidgetHost(getInstrumentation().getTargetContext(), 0);
+        host.deleteHost();
+        host.startListening();
+
+        // Well, app do not have this permission unless explicitly granted
+        // by the user. Now we will pretend for the user and grant it.
+        grantBindAppWidgetPermission();
+
+        // Allocate an app widget id to bind.
+        final int appWidgetId = host.allocateAppWidgetId();
+        try {
+            // Grab a provider we defined to be bound.
+            final AppWidgetProviderInfo provider = getAppWidgetProviderInfo();
+
+            // Bind the widget.
+            final boolean widgetBound = getAppWidgetManager().bindAppWidgetIdIfAllowed(
+                    appWidgetId, provider.getProfile(), provider.provider, null);
+            assertTrue(widgetBound);
+
+            // Make sure the app can use the package of a widget it hosts
+            getInstrumentation().runOnMainSync(() -> {
+                // Make sure we can report nodes as if from the widget package
+                AccessibilityNodeInfo root = getInstrumentation().getUiAutomation()
+                        .getRootInActiveWindow();
+                assertPackageName(root, APP_WIDGET_PROVIDER_PACKAGE);
+            });
+
+            // Make sure we can send events as if from the widget package
+            try {
+                getInstrumentation().getUiAutomation().executeAndWaitForEvent(() ->
+                    getInstrumentation().runOnMainSync(() ->
+                        getActivity().findViewById(R.id.button).performClick())
+                    , (AccessibilityEvent event) ->
+                            event.getEventType() == AccessibilityEvent.TYPE_VIEW_CLICKED
+                                    && event.getPackageName().equals(APP_WIDGET_PROVIDER_PACKAGE)
+                    , TIMEOUT_ASYNC_PROCESSING);
+            } catch (TimeoutException e) {
+                fail("Should be able to send events from a widget package if widget hosted");
+            }
+        } finally {
+            // Clean up.
+            host.deleteAppWidgetId(appWidgetId);
+            host.deleteHost();
+            revokeBindAppWidgetPermission();
+        }
+    }
+
+    private static void assertPackageName(AccessibilityNodeInfo node, String packageName) {
+        if (node == null) {
+            return;
+        }
+        assertEquals(packageName, node.getPackageName());
+        final int childCount = node.getChildCount();
+        for (int i = 0; i < childCount; i++) {
+            AccessibilityNodeInfo child = node.getChild(i);
+            if (child != null) {
+                assertPackageName(child, packageName);
+            }
+        }
+    }
+
+    private AppWidgetProviderInfo getAppWidgetProviderInfo() {
+        final ComponentName componentName = new ComponentName(
+                "foo.bar.baz", "foo.bar.baz.MyAppWidgetProvider");
+        final List<AppWidgetProviderInfo> providers = getAppWidgetManager().getInstalledProviders();
+        final int providerCount = providers.size();
+        for (int i = 0; i < providerCount; i++) {
+            final AppWidgetProviderInfo provider = providers.get(i);
+            if (componentName.equals(provider.provider)
+                    && Process.myUserHandle().equals(provider.getProfile())) {
+                return provider;
+            }
+        }
+        return null;
+    }
+
+    private void grantBindAppWidgetPermission() throws Exception {
+        ShellCommandBuilder.execShellCommand(getInstrumentation().getUiAutomation(),
+                GRANT_BIND_APP_WIDGET_PERMISSION_COMMAND);
+    }
+
+    private void revokeBindAppWidgetPermission() throws Exception {
+        ShellCommandBuilder.execShellCommand(getInstrumentation().getUiAutomation(),
+                REVOKE_BIND_APP_WIDGET_PERMISSION_COMMAND);
+    }
+
+    private AppWidgetManager getAppWidgetManager() {
+        return (AppWidgetManager) getInstrumentation().getTargetContext()
+                .getSystemService(Context.APPWIDGET_SERVICE);
+    }
+
+    private boolean hasAppWidgets() {
+        return getInstrumentation().getTargetContext().getPackageManager()
+                .hasSystemFeature(PackageManager.FEATURE_APP_WIDGETS);
+    }
+
     /**
      * Compares all properties of the <code>first</code> and the
      * <code>second</code>.
diff --git a/tests/accessibilityservice/src/android/accessibilityservice/cts/AccessibilityFocusAndInputFocusSyncActivity.java b/tests/accessibilityservice/src/android/accessibilityservice/cts/AccessibilityFocusAndInputFocusSyncActivity.java
deleted file mode 100644
index 62831a4..0000000
--- a/tests/accessibilityservice/src/android/accessibilityservice/cts/AccessibilityFocusAndInputFocusSyncActivity.java
+++ /dev/null
@@ -1,35 +0,0 @@
-/**
- * Copyright (C) 2012 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except
- * in compliance with the License. You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software distributed under the
- * License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either
- * express or implied. See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package android.accessibilityservice.cts;
-
-import android.os.Bundle;
-
-import android.accessibilityservice.cts.R;
-
-/**
- * Activity for testing the accessibility focus APIs exposed to
- * accessibility services. These APIs allow moving accessibility
- * focus in the view tree from an AccessiiblityService. Specifically,
- * this activity is for verifying the the sync between accessibility
- * and input focus.
- */
-public class AccessibilityFocusAndInputFocusSyncActivity extends AccessibilityTestActivity {
-
-    @Override
-    public void onCreate(Bundle savedInstanceState) {
-        super.onCreate(savedInstanceState);
-        setContentView(R.layout.accessibility_focus_and_input_focus_sync_test);
-    }
-}
diff --git a/tests/accessibilityservice/src/android/accessibilityservice/cts/AccessibilityFocusAndInputFocusSyncTest.java b/tests/accessibilityservice/src/android/accessibilityservice/cts/AccessibilityFocusAndInputFocusSyncTest.java
index 08e231f..aad045f 100644
--- a/tests/accessibilityservice/src/android/accessibilityservice/cts/AccessibilityFocusAndInputFocusSyncTest.java
+++ b/tests/accessibilityservice/src/android/accessibilityservice/cts/AccessibilityFocusAndInputFocusSyncTest.java
@@ -17,8 +17,11 @@
 import static android.view.accessibility.AccessibilityNodeInfo.ACTION_ACCESSIBILITY_FOCUS;
 import static android.view.accessibility.AccessibilityNodeInfo.ACTION_CLEAR_ACCESSIBILITY_FOCUS;
 
+import android.accessibilityservice.cts.activities.AccessibilityFocusAndInputFocusSyncActivity;
+import android.app.Instrumentation;
 import android.app.UiAutomation;
 import android.test.suitebuilder.annotation.MediumTest;
+import android.view.View;
 import android.view.accessibility.AccessibilityEvent;
 import android.view.accessibility.AccessibilityNodeInfo;
 
@@ -26,6 +29,7 @@
 
 import java.util.LinkedList;
 import java.util.Queue;
+import java.util.concurrent.atomic.AtomicBoolean;
 
 /**
  * Test cases for testing the accessibility focus APIs exposed to accessibility
@@ -228,4 +232,29 @@
             }
         }
     }
+
+    public void testScreenReaderFocusableAttribute_reportedToAccessibility() {
+        final Instrumentation instrumentation = getInstrumentation();
+        final UiAutomation uiAutomation = instrumentation.getUiAutomation();
+        final AccessibilityNodeInfo secondButton = uiAutomation.getRootInActiveWindow()
+                .findAccessibilityNodeInfosByText(getString(R.string.secondButton)).get(0);
+        assertTrue("Screen reader focusability not propagated from xml to accessibility",
+                secondButton.isScreenReaderFocusable());
+
+        // Verify the setter and getter work
+        final AtomicBoolean isScreenReaderFocusableAtomic = new AtomicBoolean(false);
+        instrumentation.runOnMainSync(() -> {
+            View secondButtonView = getActivity().findViewById(R.id.secondButton);
+            secondButtonView.setScreenReaderFocusable(false);
+            isScreenReaderFocusableAtomic.set(secondButtonView.isScreenReaderFocusable());
+        });
+
+        assertFalse("isScreenReaderFocusable did not change after value set",
+                isScreenReaderFocusableAtomic.get());
+
+        secondButton.refresh();
+        assertFalse(
+                "Screen reader focusability not propagated to accessibility after calling setter",
+                secondButton.isScreenReaderFocusable());
+    }
 }
diff --git a/tests/accessibilityservice/src/android/accessibilityservice/cts/AccessibilityGestureDispatchTest.java b/tests/accessibilityservice/src/android/accessibilityservice/cts/AccessibilityGestureDispatchTest.java
index df8c6f3..cc68b72 100644
--- a/tests/accessibilityservice/src/android/accessibilityservice/cts/AccessibilityGestureDispatchTest.java
+++ b/tests/accessibilityservice/src/android/accessibilityservice/cts/AccessibilityGestureDispatchTest.java
@@ -14,6 +14,17 @@
 
 package android.accessibilityservice.cts;
 
+import static android.accessibilityservice.cts.utils.AsyncUtils.await;
+import static android.accessibilityservice.cts.utils.AsyncUtils.awaitCancellation;
+import static android.accessibilityservice.cts.utils.GestureUtils.add;
+import static android.accessibilityservice.cts.utils.GestureUtils.ceil;
+import static android.accessibilityservice.cts.utils.GestureUtils.click;
+import static android.accessibilityservice.cts.utils.GestureUtils.diff;
+import static android.accessibilityservice.cts.utils.GestureUtils.dispatchGesture;
+import static android.accessibilityservice.cts.utils.GestureUtils.longClick;
+import static android.accessibilityservice.cts.utils.GestureUtils.path;
+import static android.accessibilityservice.cts.utils.GestureUtils.times;
+
 import static org.hamcrest.CoreMatchers.allOf;
 import static org.hamcrest.CoreMatchers.any;
 import static org.hamcrest.CoreMatchers.both;
@@ -21,24 +32,26 @@
 import static org.hamcrest.CoreMatchers.hasItem;
 import static org.hamcrest.MatcherAssert.assertThat;
 
+import static java.util.concurrent.TimeUnit.MILLISECONDS;
+
 import android.accessibilityservice.AccessibilityService;
 import android.accessibilityservice.GestureDescription;
 import android.accessibilityservice.GestureDescription.StrokeDescription;
+import android.accessibilityservice.cts.activities.AccessibilityTestActivity;
 import android.content.Context;
 import android.content.pm.PackageManager;
 import android.graphics.Matrix;
 import android.graphics.Path;
-import android.graphics.Point;
 import android.graphics.PointF;
 import android.os.Bundle;
 import android.os.SystemClock;
 import android.test.ActivityInstrumentationTestCase2;
-import android.util.DisplayMetrics;
 import android.view.MotionEvent;
 import android.view.View;
 import android.view.ViewConfiguration;
 import android.view.WindowManager;
 import android.widget.TextView;
+
 import org.hamcrest.Description;
 import org.hamcrest.Matcher;
 import org.hamcrest.TypeSafeMatcher;
@@ -72,7 +85,6 @@
     final List<MotionEvent> mMotionEvents = new ArrayList<>();
     StubGestureAccessibilityService mService;
     MyTouchListener mMyTouchListener = new MyTouchListener();
-    MyGestureCallback mCallback;
     TextView mFullScreenTextView;
     int[] mViewLocation = new int[2];
     boolean mGotUpEvent;
@@ -107,7 +119,6 @@
         mService = StubGestureAccessibilityService.enableSelf(getInstrumentation());
 
         mMotionEvents.clear();
-        mCallback = new MyGestureCallback();
         mGotUpEvent = false;
     }
 
@@ -126,10 +137,8 @@
             return;
         }
 
-        Point clickPoint = new Point(10, 20);
-        GestureDescription click = createClickInViewBounds(clickPoint);
-        mService.runOnServiceSync(() -> mService.doDispatchGesture(click, mCallback, null));
-        mCallback.assertGestureCompletes(GESTURE_COMPLETION_TIMEOUT);
+        PointF clickPoint = new PointF(10, 20);
+        dispatch(clickWithinView(clickPoint), GESTURE_COMPLETION_TIMEOUT);
         waitForMotionEvents(any(MotionEvent.class), 2);
 
         assertEquals(2, mMotionEvents.size());
@@ -161,10 +170,8 @@
             return;
         }
 
-        Point clickPoint = new Point(10, 20);
-        GestureDescription longClick = createLongClickInViewBounds(clickPoint);
-        mService.runOnServiceSync(() -> mService.doDispatchGesture(longClick, mCallback, null));
-        mCallback.assertGestureCompletes(
+        PointF clickPoint = new PointF(10, 20);
+        dispatch(longClickWithinView(clickPoint),
                 ViewConfiguration.getLongPressTimeout() + GESTURE_COMPLETION_TIMEOUT);
 
         waitForMotionEvents(any(MotionEvent.class), 2);
@@ -183,13 +190,12 @@
             return;
         }
 
-        Point startPoint = new Point(10, 20);
-        Point endPoint = new Point(20, 40);
+        PointF startPoint = new PointF(10, 20);
+        PointF endPoint = new PointF(20, 40);
         int gestureTime = 500;
 
-        GestureDescription swipe = createSwipeInViewBounds(startPoint, endPoint, gestureTime);
-        mService.runOnServiceSync(() -> mService.doDispatchGesture(swipe, mCallback, null));
-        mCallback.assertGestureCompletes(gestureTime + GESTURE_COMPLETION_TIMEOUT);
+        dispatch(swipeWithinView(startPoint, endPoint, gestureTime),
+                gestureTime + GESTURE_COMPLETION_TIMEOUT);
         waitForMotionEvents(IS_ACTION_UP, 1);
 
         int numEvents = mMotionEvents.size();
@@ -206,30 +212,31 @@
             assertTrue(moveEvent.getEventTime() >= lastEventTime);
             float fractionOfSwipe =
                     ((float) (moveEvent.getEventTime() - downEvent.getEventTime())) / gestureTime;
-            float fractionX = ((float) (endPoint.x - startPoint.x)) * fractionOfSwipe + 0.5f;
-            float fractionY = ((float) (endPoint.y - startPoint.y)) * fractionOfSwipe + 0.5f;
-            Point intermediatePoint = new Point(startPoint);
-            intermediatePoint.offset((int) fractionX, (int) fractionY);
+            PointF intermediatePoint = add(startPoint,
+                    ceil(times(fractionOfSwipe, diff(endPoint, startPoint))));
             assertThat(moveEvent, both(IS_ACTION_MOVE).and(isAtPoint(intermediatePoint)));
             lastEventTime = moveEvent.getEventTime();
         }
     }
 
+    public void dispatch(GestureDescription gesture, int timeoutMs) {
+        await(dispatchGesture(mService, gesture), timeoutMs, MILLISECONDS);
+    }
+
     public void testSlowSwipe_shouldNotContainMovesForTinyMovement() throws InterruptedException {
         if (!mHasTouchScreen) {
             return;
         }
 
-        Point startPoint = new Point(10, 20);
-        Point intermediatePoint1 = new Point(10, 21);
-        Point intermediatePoint2 = new Point(11, 21);
-        Point intermediatePoint3 = new Point(11, 22);
-        Point endPoint = new Point(11, 22);
+        PointF startPoint = new PointF(10, 20);
+        PointF intermediatePoint1 = new PointF(10, 21);
+        PointF intermediatePoint2 = new PointF(11, 21);
+        PointF intermediatePoint3 = new PointF(11, 22);
+        PointF endPoint = new PointF(11, 22);
         int gestureTime = 1000;
 
-        GestureDescription swipe = createSwipeInViewBounds(startPoint, endPoint, gestureTime);
-        mService.runOnServiceSync(() -> mService.doDispatchGesture(swipe, mCallback, null));
-        mCallback.assertGestureCompletes(gestureTime + GESTURE_COMPLETION_TIMEOUT);
+        dispatch(swipeWithinView(startPoint, endPoint, gestureTime),
+                gestureTime + GESTURE_COMPLETION_TIMEOUT);
         waitForMotionEvents(IS_ACTION_UP, 1);
 
         assertEquals(5, mMotionEvents.size());
@@ -245,16 +252,14 @@
             return;
         }
 
-        Point centerPoint = new Point(50, 60);
+        PointF centerPoint = new PointF(50, 60);
         int startSpacing = 100;
         int endSpacing = 50;
         int gestureTime = 500;
         float pinchTolerance = 2.0f;
 
-        GestureDescription pinch = createPinchInViewBounds(centerPoint, startSpacing,
-                endSpacing, 45.0F, gestureTime);
-        mService.runOnServiceSync(() -> mService.doDispatchGesture(pinch, mCallback, null));
-        mCallback.assertGestureCompletes(gestureTime + GESTURE_COMPLETION_TIMEOUT);
+        dispatch(pinchWithinView(centerPoint, startSpacing, endSpacing, 45.0F, gestureTime),
+                gestureTime + GESTURE_COMPLETION_TIMEOUT);
         waitForMotionEvents(IS_ACTION_UP, 1);
         int numEvents = mMotionEvents.size();
 
@@ -313,13 +318,10 @@
             magRegionCenterPoint.set(magnificationController.getCenterX(),
                     magnificationController.getCenterY());
         });
-        final PointF magRegionOffsetPoint = new PointF();
-        magRegionOffsetPoint.set(magRegionCenterPoint);
-        magRegionOffsetPoint.offset(CLICK_OFFSET_X, CLICK_OFFSET_Y);
+        final PointF magRegionOffsetPoint
+                = add(magRegionCenterPoint, CLICK_OFFSET_X, CLICK_OFFSET_Y);
 
-        final PointF magRegionOffsetClickPoint = new PointF();
-        magRegionOffsetClickPoint.set(magRegionCenterPoint);
-        magRegionOffsetClickPoint.offset(
+        final PointF magRegionOffsetClickPoint = add(magRegionCenterPoint,
                 CLICK_OFFSET_X * MAGNIFICATION_FACTOR, CLICK_OFFSET_Y * MAGNIFICATION_FACTOR);
 
         try {
@@ -331,16 +333,16 @@
             assertTrue("Failed to set scale", setScale.get());
 
             // Click in the center of the magnification region
-            GestureDescription magRegionCenterClick = createClick(magRegionCenterPoint);
-            mService.runOnServiceSync(() -> mService.doDispatchGesture(
-                    magRegionCenterClick, mCallback, null));
-            mCallback.assertGestureCompletes(GESTURE_COMPLETION_TIMEOUT);
+            dispatch(new GestureDescription.Builder()
+                    .addStroke(click(magRegionCenterPoint))
+                    .build(),
+                    GESTURE_COMPLETION_TIMEOUT);
 
             // Click at a slightly offset point
-            GestureDescription magRegionOffsetClick = createClick(magRegionOffsetClickPoint);
-            mService.runOnServiceSync(() -> mService.doDispatchGesture(
-                    magRegionOffsetClick, mCallback, null));
-            mCallback.assertGestureCompletes(GESTURE_COMPLETION_TIMEOUT);
+            dispatch(new GestureDescription.Builder()
+                    .addStroke(click(magRegionOffsetClickPoint))
+                    .build(),
+                    GESTURE_COMPLETION_TIMEOUT);
             waitForMotionEvents(any(MotionEvent.class), 4);
         } finally {
             // Reset magnification
@@ -376,30 +378,25 @@
             return;
         }
 
-        Point start = new Point(10, 20);
-        Point mid1 = new Point(20, 20);
-        Point mid2 = new Point(20, 25);
-        Point end = new Point(20, 30);
+        PointF start = new PointF(10, 20);
+        PointF mid1 = new PointF(20, 20);
+        PointF mid2 = new PointF(20, 25);
+        PointF end = new PointF(20, 30);
         int gestureTime = 500;
 
         StrokeDescription s1 = new StrokeDescription(
-                linePathInViewBounds(start, mid1), 0, gestureTime, true);
+                lineWithinView(start, mid1), 0, gestureTime, true);
         StrokeDescription s2 = s1.continueStroke(
-                linePathInViewBounds(mid1, mid2), 0, gestureTime, true);
+                lineWithinView(mid1, mid2), 0, gestureTime, true);
         StrokeDescription s3 = s2.continueStroke(
-                linePathInViewBounds(mid2, end), 0, gestureTime, false);
+                lineWithinView(mid2, end), 0, gestureTime, false);
+
         GestureDescription gesture1 = new GestureDescription.Builder().addStroke(s1).build();
         GestureDescription gesture2 = new GestureDescription.Builder().addStroke(s2).build();
         GestureDescription gesture3 = new GestureDescription.Builder().addStroke(s3).build();
-
-        mService.runOnServiceSync(() -> mService.doDispatchGesture(gesture1, mCallback, null));
-        mCallback.assertGestureCompletes(gestureTime + GESTURE_COMPLETION_TIMEOUT);
-        mCallback.reset();
-        mService.runOnServiceSync(() -> mService.doDispatchGesture(gesture2, mCallback, null));
-        mCallback.assertGestureCompletes(gestureTime + GESTURE_COMPLETION_TIMEOUT);
-        mCallback.reset();
-        mService.runOnServiceSync(() -> mService.doDispatchGesture(gesture3, mCallback, null));
-        mCallback.assertGestureCompletes(gestureTime + GESTURE_COMPLETION_TIMEOUT);
+        dispatch(gesture1, gestureTime + GESTURE_COMPLETION_TIMEOUT);
+        dispatch(gesture2, gestureTime + GESTURE_COMPLETION_TIMEOUT);
+        dispatch(gesture3, gestureTime + GESTURE_COMPLETION_TIMEOUT);
         waitForMotionEvents(IS_ACTION_UP, 1);
 
         assertThat(mMotionEvents.get(0), allOf(IS_ACTION_DOWN, isAtPoint(start)));
@@ -415,25 +412,24 @@
             return;
         }
 
-        Point startPoint = new Point(10, 20);
-        Point midPoint = new Point(20, 20);
-        Point endPoint = new Point(20, 30);
+        PointF startPoint = new PointF(10, 20);
+        PointF midPoint = new PointF(20, 20);
+        PointF endPoint = new PointF(20, 30);
         int gestureTime = 500;
 
-        StrokeDescription stroke1 = new StrokeDescription(
-                linePathInViewBounds(startPoint, midPoint), 0, gestureTime, true);
-        GestureDescription gesture1 = new GestureDescription.Builder().addStroke(stroke1).build();
-        mService.runOnServiceSync(() -> mService.doDispatchGesture(gesture1, mCallback, null));
-        mCallback.assertGestureCompletes(gestureTime + GESTURE_COMPLETION_TIMEOUT);
+        StrokeDescription stroke1 =
+                new StrokeDescription(lineWithinView(startPoint, midPoint), 0, gestureTime, true);
+        dispatch(new GestureDescription.Builder().addStroke(stroke1).build(),
+                gestureTime + GESTURE_COMPLETION_TIMEOUT);
         waitForMotionEvents(both(IS_ACTION_MOVE).and(isAtPoint(midPoint)), 1);
 
-        StrokeDescription stroke2 = stroke1.continueStroke(
-                linePathInViewBounds(endPoint, midPoint), 0, gestureTime, false);
-        GestureDescription gesture2 = new GestureDescription.Builder().addStroke(stroke2).build();
-        mCallback.reset();
+        StrokeDescription stroke2 =
+                stroke1.continueStroke(lineWithinView(endPoint, midPoint), 0, gestureTime, false);
         mMotionEvents.clear();
-        mService.runOnServiceSync(() -> mService.doDispatchGesture(gesture2, mCallback, null));
-        mCallback.assertGestureCancels(gestureTime + GESTURE_COMPLETION_TIMEOUT);
+        awaitCancellation(
+                dispatchGesture(mService,
+                        new GestureDescription.Builder().addStroke(stroke2).build()),
+                gestureTime + GESTURE_COMPLETION_TIMEOUT, MILLISECONDS);
 
         waitForMotionEvents(IS_ACTION_CANCEL, 1);
         assertEquals(1, mMotionEvents.size());
@@ -444,23 +440,20 @@
             return;
         }
 
-        Point startPoint = new Point(10, 20);
-        Point midPoint = new Point(20, 20);
-        Point endPoint = new Point(20, 30);
+        PointF startPoint = new PointF(10, 20);
+        PointF midPoint = new PointF(20, 20);
+        PointF endPoint = new PointF(20, 30);
         int gestureTime = 500;
 
-        StrokeDescription stroke1 = new StrokeDescription(
-                linePathInViewBounds(startPoint, midPoint), 0, gestureTime, true);
-        GestureDescription gesture1 = new GestureDescription.Builder().addStroke(stroke1).build();
-        mService.runOnServiceSync(() -> mService.doDispatchGesture(gesture1, mCallback, null));
-        mCallback.assertGestureCompletes(gestureTime + GESTURE_COMPLETION_TIMEOUT);
+        StrokeDescription stroke1 =
+                new StrokeDescription(lineWithinView(startPoint, midPoint), 0, gestureTime, true);
+        dispatch(new GestureDescription.Builder().addStroke(stroke1).build(),
+                gestureTime + GESTURE_COMPLETION_TIMEOUT);
 
-        StrokeDescription stroke2 = new StrokeDescription(
-                linePathInViewBounds(midPoint, endPoint), 0, gestureTime, false);
-        GestureDescription gesture2 = new GestureDescription.Builder().addStroke(stroke2).build();
-        mCallback.reset();
-        mService.runOnServiceSync(() -> mService.doDispatchGesture(gesture2, mCallback, null));
-        mCallback.assertGestureCompletes(gestureTime + GESTURE_COMPLETION_TIMEOUT);
+        StrokeDescription stroke2 =
+                new StrokeDescription(lineWithinView(midPoint, endPoint), 0, gestureTime, false);
+        dispatch(new GestureDescription.Builder().addStroke(stroke2).build(),
+                gestureTime + GESTURE_COMPLETION_TIMEOUT);
 
         waitForMotionEvents(IS_ACTION_UP, 1);
 
@@ -479,20 +472,20 @@
             return;
         }
 
-        Point startPoint = new Point(10, 20);
-        Point midPoint = new Point(20, 20);
-        Point endPoint = new Point(20, 30);
+        PointF startPoint = new PointF(10, 20);
+        PointF midPoint = new PointF(20, 20);
+        PointF endPoint = new PointF(20, 30);
         int gestureTime = 500;
 
-        StrokeDescription stroke1 = new StrokeDescription(
-                linePathInViewBounds(startPoint, midPoint), 0, gestureTime, true);
+        StrokeDescription stroke1 =
+                new StrokeDescription(lineWithinView(startPoint, midPoint), 0, gestureTime, true);
 
-        StrokeDescription stroke2 = stroke1.continueStroke(
-                linePathInViewBounds(midPoint, endPoint), 0, gestureTime, false);
-        GestureDescription gesture = new GestureDescription.Builder().addStroke(stroke2).build();
-        mCallback.reset();
-        mService.runOnServiceSync(() -> mService.doDispatchGesture(gesture, mCallback, null));
-        mCallback.assertGestureCancels(gestureTime + GESTURE_COMPLETION_TIMEOUT);
+        StrokeDescription stroke2 =
+                stroke1.continueStroke(lineWithinView(midPoint, endPoint), 0, gestureTime, false);
+        awaitCancellation(
+                dispatchGesture(mService,
+                        new GestureDescription.Builder().addStroke(stroke2).build()),
+                gestureTime + GESTURE_COMPLETION_TIMEOUT, MILLISECONDS);
     }
 
     public static class GestureDispatchActivity extends AccessibilityTestActivity {
@@ -507,52 +500,6 @@
         }
     }
 
-    public static class MyGestureCallback extends AccessibilityService.GestureResultCallback {
-        private boolean mCompleted;
-        private boolean mCancelled;
-
-        @Override
-        public synchronized void onCompleted(GestureDescription gestureDescription) {
-            mCompleted = true;
-            notifyAll();
-        }
-
-        @Override
-        public synchronized void onCancelled(GestureDescription gestureDescription) {
-            mCancelled = true;
-            notifyAll();
-        }
-
-        public synchronized void assertGestureCompletes(long timeout) {
-            if (mCompleted) {
-                return;
-            }
-            try {
-                wait(timeout);
-            } catch (InterruptedException e) {
-                throw new RuntimeException(e);
-            }
-            assertTrue("Gesture did not complete. Canceled = " + mCancelled, mCompleted);
-        }
-
-        public synchronized void assertGestureCancels(long timeout) {
-            if (mCancelled) {
-                return;
-            }
-            try {
-                wait(timeout);
-            } catch (InterruptedException e) {
-                throw new RuntimeException(e);
-            }
-            assertTrue("Gesture did not cancel. Completed = " + mCompleted, mCancelled);
-        }
-
-        public synchronized void reset() {
-            mCancelled = false;
-            mCompleted = false;
-        }
-    }
-
     private void waitForMotionEvents(Matcher<MotionEvent> matcher, int numEventsExpected)
             throws InterruptedException {
         synchronized (mMotionEvents) {
@@ -597,53 +544,38 @@
         }
     }
 
-    private GestureDescription createClickInViewBounds(Point clickPoint) {
-        Point offsetClick = new Point(clickPoint);
-        offsetClick.offset(mViewLocation[0], mViewLocation[1]);
-        return createClick(offsetClick);
-    }
-
-    private GestureDescription createClick(Point clickPoint) {
-        return createClick(new PointF(clickPoint.x, clickPoint.y));
-    }
-
-    private GestureDescription createClick(PointF clickPoint) {
-        Path clickPath = new Path();
-        clickPath.moveTo(clickPoint.x, clickPoint.y);
-        StrokeDescription clickStroke =
-                new StrokeDescription(clickPath, 0, ViewConfiguration.getTapTimeout());
-        GestureDescription.Builder clickBuilder = new GestureDescription.Builder();
-        clickBuilder.addStroke(clickStroke);
-        return clickBuilder.build();
-    }
-
-    private GestureDescription createLongClickInViewBounds(Point clickPoint) {
-        Point offsetPoint = new Point(clickPoint);
-        offsetPoint.offset(mViewLocation[0], mViewLocation[1]);
-        Path clickPath = new Path();
-        clickPath.moveTo(offsetPoint.x, offsetPoint.y);
-        int longPressTime = ViewConfiguration.getLongPressTimeout();
-
-        StrokeDescription longClickStroke =
-                new StrokeDescription(clickPath, 0, longPressTime + (longPressTime / 2));
-        GestureDescription.Builder longClickBuilder = new GestureDescription.Builder();
-        longClickBuilder.addStroke(longClickStroke);
-        return longClickBuilder.build();
-    }
-
-    private GestureDescription createSwipeInViewBounds(Point start, Point end, long duration) {
-        return new GestureDescription.Builder().addStroke(
-                new StrokeDescription(linePathInViewBounds(start, end), 0, duration, false))
+    private GestureDescription clickWithinView(PointF clickPoint) {
+        return new GestureDescription.Builder()
+                .addStroke(click(withinView(clickPoint)))
                 .build();
     }
 
-    private GestureDescription createPinchInViewBounds(Point centerPoint, int startSpacing,
+    private GestureDescription longClickWithinView(PointF clickPoint) {
+        return new GestureDescription.Builder()
+                .addStroke(longClick(withinView(clickPoint)))
+                .build();
+    }
+
+    private PointF withinView(PointF clickPoint) {
+        return add(clickPoint, mViewLocation[0], mViewLocation[1]);
+    }
+
+    private GestureDescription swipeWithinView(PointF start, PointF end, long duration) {
+        return new GestureDescription.Builder()
+                .addStroke(new StrokeDescription(lineWithinView(start, end), 0, duration))
+                .build();
+    }
+
+    private Path lineWithinView(PointF startPoint, PointF endPoint) {
+        return path(withinView(startPoint), withinView(endPoint));
+    }
+
+    private GestureDescription pinchWithinView(PointF centerPoint, int startSpacing,
             int endSpacing, float orientation, long duration) {
         if ((startSpacing < 0) || (endSpacing < 0)) {
             throw new IllegalArgumentException("Pinch spacing cannot be negative");
         }
-        Point offsetCenter = new Point(centerPoint);
-        offsetCenter.offset(mViewLocation[0], mViewLocation[1]);
+        PointF offsetCenter = withinView(centerPoint);
         float[] startPoint1 = new float[2];
         float[] endPoint1 = new float[2];
         float[] startPoint2 = new float[2];
@@ -675,19 +607,10 @@
         path2.moveTo(startPoint2[0], startPoint2[1]);
         path2.lineTo(endPoint2[0], endPoint2[1]);
 
-        StrokeDescription path1Stroke = new StrokeDescription(path1, 0, duration);
-        StrokeDescription path2Stroke = new StrokeDescription(path2, 0, duration);
-        GestureDescription.Builder swipeBuilder = new GestureDescription.Builder();
-        swipeBuilder.addStroke(path1Stroke);
-        swipeBuilder.addStroke(path2Stroke);
-        return swipeBuilder.build();
-    }
-
-    Path linePathInViewBounds(Point startPoint, Point endPoint) {
-        Path path = new Path();
-        path.moveTo(startPoint.x + mViewLocation[0], startPoint.y + mViewLocation[1]);
-        path.lineTo(endPoint.x + mViewLocation[0], endPoint.y + mViewLocation[1]);
-        return path;
+        return new GestureDescription.Builder()
+                .addStroke(new StrokeDescription(path1, 0, duration))
+                .addStroke(new StrokeDescription(path2, 0, duration))
+                .build();
     }
 
     private static class MotionEventActionMatcher extends TypeSafeMatcher<MotionEvent> {
@@ -705,13 +628,13 @@
 
         @Override
         public void describeTo(Description description) {
-            description.appendText("Matching to action " + mAction);
+            description.appendText("Matching to action " + MotionEvent.actionToString(mAction));
         }
     }
 
 
-    Matcher<MotionEvent> isAtPoint(final Point point) {
-        return isAtPoint(new PointF(point.x, point.y), 0.01f);
+    Matcher<MotionEvent> isAtPoint(final PointF point) {
+        return isAtPoint(point, 0.01f);
     }
 
     Matcher<MotionEvent> isAtPoint(final PointF point, final float tol) {
diff --git a/tests/accessibilityservice/src/android/accessibilityservice/cts/AccessibilitySoftKeyboardModesTest.java b/tests/accessibilityservice/src/android/accessibilityservice/cts/AccessibilitySoftKeyboardModesTest.java
index 49c209f..f873849 100644
--- a/tests/accessibilityservice/src/android/accessibilityservice/cts/AccessibilitySoftKeyboardModesTest.java
+++ b/tests/accessibilityservice/src/android/accessibilityservice/cts/AccessibilitySoftKeyboardModesTest.java
@@ -16,7 +16,9 @@
 
 import android.accessibilityservice.AccessibilityServiceInfo;
 import android.accessibilityservice.AccessibilityService.SoftKeyboardController;
+import android.accessibilityservice.cts.activities.AccessibilityTestActivity;
 import android.app.Activity;
+import android.app.Instrumentation;
 import android.app.UiAutomation;
 import android.os.Bundle;
 import android.os.Handler;
@@ -36,6 +38,7 @@
 import java.util.concurrent.BlockingQueue;
 import java.util.concurrent.TimeUnit;
 import java.util.concurrent.TimeoutException;
+import java.util.concurrent.atomic.AtomicBoolean;
 
 /**
  * Test cases for testing the accessibility APIs for interacting with the soft keyboard show mode.
@@ -70,6 +73,8 @@
     private InstrumentedAccessibilityService mService;
     private SoftKeyboardController mKeyboardController;
     private UiAutomation mUiAutomation;
+    private Activity mActivity;
+    private View mKeyboardTargetView;
 
     private Object mLock = new Object();
 
@@ -83,7 +88,7 @@
 
         // If we don't call getActivity(), we get an empty list when requesting the number of
         // windows on screen.
-        getActivity();
+        mActivity = getActivity();
 
         mService = InstrumentedAccessibilityService.enableService(
                 getInstrumentation(), InstrumentedAccessibilityService.class);
@@ -93,6 +98,8 @@
         AccessibilityServiceInfo info = mUiAutomation.getServiceInfo();
         info.flags |= AccessibilityServiceInfo.FLAG_RETRIEVE_INTERACTIVE_WINDOWS;
         mUiAutomation.setServiceInfo(info);
+        getInstrumentation().runOnMainSync(
+                () -> mKeyboardTargetView = mActivity.findViewById(R.id.edit_text));
     }
 
     @Override
@@ -100,8 +107,11 @@
         mKeyboardController.setShowMode(SHOW_MODE_AUTO);
         mService.runOnServiceSync(() -> mService.disableSelf());
         Activity activity = getActivity();
-        activity.getSystemService(InputMethodManager.class)
-                .hideSoftInputFromWindow(activity.getCurrentFocus().getWindowToken(), 0);
+        View currentFocus = activity.getCurrentFocus();
+        if (currentFocus != null) {
+            activity.getSystemService(InputMethodManager.class)
+                    .hideSoftInputFromWindow(currentFocus.getWindowToken(), 0);
+        }
     }
 
     public void testApiReturnValues_shouldChangeValueOnRequestAndSendCallback() throws Exception {
@@ -228,20 +238,24 @@
      */
     private boolean tryShowSoftInput() throws Exception {
         final BlockingQueue<Integer> queue = new ArrayBlockingQueue<>(1);
+        final AtomicBoolean showSoftInputResult = new AtomicBoolean(false);
+        final Activity activity = getActivity();
+        final ResultReceiver resultReceiver =
+                new ResultReceiver(new Handler(activity.getMainLooper())) {
+                    @Override
+                    protected void onReceiveResult(int resultCode, Bundle resultData) {
+                        queue.add(resultCode);
+                    }
+                };
 
-        getInstrumentation().runOnMainSync(() -> {
-            Activity activity = getActivity();
-            ResultReceiver resultReceiver =
-                    new ResultReceiver(new Handler(activity.getMainLooper())) {
-                            @Override
-                            protected void onReceiveResult(int resultCode, Bundle resultData) {
-                                queue.add(resultCode);
-                            }
-                    };
-            View editText = activity.findViewById(R.id.edit_text);
-            activity.getSystemService(InputMethodManager.class)
-                    .showSoftInput(editText, InputMethodManager.SHOW_FORCED, resultReceiver);
-        });
+        final Instrumentation instrumentation = getInstrumentation();
+        instrumentation.runOnMainSync(() -> mKeyboardTargetView.requestFocus());
+        instrumentation.waitForIdleSync();
+        final InputMethodManager imm = mActivity.getSystemService(InputMethodManager.class);
+        instrumentation.runOnMainSync(() -> showSoftInputResult.set(imm.showSoftInput(
+                mKeyboardTargetView, InputMethodManager.SHOW_FORCED, resultReceiver)));
+
+        assertTrue("InputMethodManager refused to show a soft input", showSoftInputResult.get());
 
         Integer result;
         try {
diff --git a/tests/accessibilityservice/src/android/accessibilityservice/cts/AccessibilityTestActivity.java b/tests/accessibilityservice/src/android/accessibilityservice/cts/AccessibilityTestActivity.java
deleted file mode 100644
index 42a4375..0000000
--- a/tests/accessibilityservice/src/android/accessibilityservice/cts/AccessibilityTestActivity.java
+++ /dev/null
@@ -1,31 +0,0 @@
-/**
- * Copyright (C) 2014 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except
- * in compliance with the License. You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software distributed under the
- * License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either
- * express or implied. See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package android.accessibilityservice.cts;
-
-import android.app.Activity;
-import android.os.Bundle;
-import android.view.WindowManager;
-
-public abstract class AccessibilityTestActivity extends Activity {
-
-    @Override
-    protected void onCreate(Bundle savedInstanceState) {
-        super.onCreate(savedInstanceState);
-
-        getWindow().addFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON
-                | WindowManager.LayoutParams.FLAG_TURN_SCREEN_ON
-                | WindowManager.LayoutParams.FLAG_DISMISS_KEYGUARD);
-    }
-}
diff --git a/tests/accessibilityservice/src/android/accessibilityservice/cts/AccessibilityTextActionTest.java b/tests/accessibilityservice/src/android/accessibilityservice/cts/AccessibilityTextActionTest.java
index abc759c..38bb738 100644
--- a/tests/accessibilityservice/src/android/accessibilityservice/cts/AccessibilityTextActionTest.java
+++ b/tests/accessibilityservice/src/android/accessibilityservice/cts/AccessibilityTextActionTest.java
@@ -14,10 +14,10 @@
 
 package android.accessibilityservice.cts;
 
+import android.accessibilityservice.cts.activities.AccessibilityTextTraversalActivity;
 import android.app.UiAutomation;
 import android.graphics.RectF;
 import android.os.Bundle;
-import android.os.Debug;
 import android.os.Message;
 import android.os.Parcelable;
 import android.text.SpannableString;
@@ -52,7 +52,6 @@
 import static org.mockito.Mockito.timeout;
 import static org.mockito.Mockito.times;
 import static org.mockito.Mockito.verify;
-import static org.mockito.Mockito.verifyNoMoreInteractions;
 
 /**
  * Test cases for actions taken on text views.
diff --git a/tests/accessibilityservice/src/android/accessibilityservice/cts/AccessibilityTextTraversalActivity.java b/tests/accessibilityservice/src/android/accessibilityservice/cts/AccessibilityTextTraversalActivity.java
deleted file mode 100644
index 2b93a08..0000000
--- a/tests/accessibilityservice/src/android/accessibilityservice/cts/AccessibilityTextTraversalActivity.java
+++ /dev/null
@@ -1,32 +0,0 @@
-/**
- * Copyright (C) 2012 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except
- * in compliance with the License. You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software distributed under the
- * License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either
- * express or implied. See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package android.accessibilityservice.cts;
-
-import android.os.Bundle;
-
-import android.accessibilityservice.cts.R;
-
-/**
- * Activity for testing the accessibility APIs for traversing the
- * text content of a View at several granularities.
- */
-public class AccessibilityTextTraversalActivity extends AccessibilityTestActivity {
-
-    @Override
-    public void onCreate(Bundle savedInstanceState) {
-        super.onCreate(savedInstanceState);
-        setContentView(R.layout.accessibility_text_traversal_test);
-    }
-}
diff --git a/tests/accessibilityservice/src/android/accessibilityservice/cts/AccessibilityTextTraversalTest.java b/tests/accessibilityservice/src/android/accessibilityservice/cts/AccessibilityTextTraversalTest.java
index 6bc2969..646d9c3 100644
--- a/tests/accessibilityservice/src/android/accessibilityservice/cts/AccessibilityTextTraversalTest.java
+++ b/tests/accessibilityservice/src/android/accessibilityservice/cts/AccessibilityTextTraversalTest.java
@@ -14,6 +14,7 @@
 
 package android.accessibilityservice.cts;
 
+import android.accessibilityservice.cts.activities.AccessibilityTextTraversalActivity;
 import android.app.UiAutomation;
 import android.content.pm.PackageManager;
 import android.os.Bundle;
diff --git a/tests/accessibilityservice/src/android/accessibilityservice/cts/AccessibilityViewTreeReportingActivity.java b/tests/accessibilityservice/src/android/accessibilityservice/cts/AccessibilityViewTreeReportingActivity.java
deleted file mode 100644
index c28e7e8..0000000
--- a/tests/accessibilityservice/src/android/accessibilityservice/cts/AccessibilityViewTreeReportingActivity.java
+++ /dev/null
@@ -1,35 +0,0 @@
-/**
- * Copyright (C) 2012 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except
- * in compliance with the License. You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software distributed under the
- * License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either
- * express or implied. See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package android.accessibilityservice.cts;
-
-import android.os.Bundle;
-
-import android.accessibilityservice.cts.R;
-
-/**
- * Activity for testing the accessibility focus APIs exposed to
- * accessibility services. These APIs allow moving accessibility
- * focus in the view tree from an AccessiiblityService. Specifically,
- * this activity is for verifying the hierarchical movement of the
- * accessibility focus.
- */
-public class AccessibilityViewTreeReportingActivity extends AccessibilityTestActivity {
-
-    @Override
-    public void onCreate(Bundle savedInstanceState) {
-        super.onCreate(savedInstanceState);
-        setContentView(R.layout.accessibility_view_tree_reporting_test);
-    }
-}
diff --git a/tests/accessibilityservice/src/android/accessibilityservice/cts/AccessibilityViewTreeReportingTest.java b/tests/accessibilityservice/src/android/accessibilityservice/cts/AccessibilityViewTreeReportingTest.java
index 305050b..d8094e0 100644
--- a/tests/accessibilityservice/src/android/accessibilityservice/cts/AccessibilityViewTreeReportingTest.java
+++ b/tests/accessibilityservice/src/android/accessibilityservice/cts/AccessibilityViewTreeReportingTest.java
@@ -14,10 +14,22 @@
 
 package android.accessibilityservice.cts;
 
+import static android.accessibilityservice.cts.utils.ActivityLaunchUtils.launchActivityAndWaitForItToBeOnscreen;
+
+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 android.accessibilityservice.AccessibilityServiceInfo;
+import android.accessibilityservice.cts.activities.AccessibilityViewTreeReportingActivity;
+import android.app.Instrumentation;
 import android.app.UiAutomation;
 import android.content.Context;
-import android.test.suitebuilder.annotation.MediumTest;
+import android.support.test.rule.ActivityTestRule;
+import android.support.test.runner.AndroidJUnit4;
+import android.support.test.InstrumentationRegistry;
 import android.text.TextUtils;
 import android.view.View;
 import android.view.ViewGroup;
@@ -28,100 +40,124 @@
 import android.widget.Button;
 import android.widget.LinearLayout;
 
+import org.junit.AfterClass;
+import org.junit.Before;
+import org.junit.BeforeClass;
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
 /**
  * Test cases for testing the accessibility focus APIs exposed to accessibility
  * services. This test checks how the view hierarchy is reported to accessibility
  * services.
  */
-public class AccessibilityViewTreeReportingTest
-        extends AccessibilityActivityTestCase<AccessibilityViewTreeReportingActivity>{
+@RunWith(AndroidJUnit4.class)
+public class AccessibilityViewTreeReportingTest {
+    private static final int TIMEOUT_ASYNC_PROCESSING = 5000;
 
-    public AccessibilityViewTreeReportingTest() {
-        super(AccessibilityViewTreeReportingActivity.class);
+    private static Instrumentation sInstrumentation;
+    private static UiAutomation sUiAutomation;
+
+    private AccessibilityViewTreeReportingActivity mActivity;
+
+    @Rule
+    public ActivityTestRule<AccessibilityViewTreeReportingActivity> mActivityRule =
+            new ActivityTestRule<>(AccessibilityViewTreeReportingActivity.class, false, false);
+
+    @BeforeClass
+    public static void oneTimeSetup() throws Exception {
+        sInstrumentation = InstrumentationRegistry.getInstrumentation();
+        sUiAutomation = sInstrumentation.getUiAutomation();
+        AccessibilityServiceInfo info = sUiAutomation.getServiceInfo();
+        info.flags |= AccessibilityServiceInfo.FLAG_RETRIEVE_INTERACTIVE_WINDOWS;
+        sUiAutomation.setServiceInfo(info);
     }
 
-    @MediumTest
+    @AfterClass
+    public static void finalTearDown() throws Exception {
+        sUiAutomation.destroy();
+    }
+
+    @Before
+    public void setUp() throws Exception {
+        mActivity = (AccessibilityViewTreeReportingActivity) launchActivityAndWaitForItToBeOnscreen(
+                sInstrumentation, sUiAutomation, mActivityRule);
+        setGetNonImportantViews(false);
+    }
+
+
+    @Test
     public void testDescendantsOfNotImportantViewReportedInOrder1() throws Exception {
-        UiAutomation uiAutomation = getUiAutomation(false);
-        AccessibilityNodeInfo firstFrameLayout =
-                getNodeByText(uiAutomation, R.string.firstFrameLayout);
+        AccessibilityNodeInfo firstFrameLayout = getNodeByText(R.string.firstFrameLayout);
         assertNotNull(firstFrameLayout);
         assertSame(3, firstFrameLayout.getChildCount());
 
         // Check if the first child is the right one.
-        AccessibilityNodeInfo firstTextView = getNodeByText(uiAutomation, R.string.firstTextView);
+        AccessibilityNodeInfo firstTextView = getNodeByText(R.string.firstTextView);
         assertEquals(firstTextView, firstFrameLayout.getChild(0));
 
         // Check if the second child is the right one.
-        AccessibilityNodeInfo firstEditText = getNodeByText(uiAutomation, R.string.firstEditText);
+        AccessibilityNodeInfo firstEditText = getNodeByText(R.string.firstEditText);
         assertEquals(firstEditText, firstFrameLayout.getChild(1));
 
         // Check if the third child is the right one.
-        AccessibilityNodeInfo firstButton = getNodeByText(uiAutomation, R.string.firstButton);
+        AccessibilityNodeInfo firstButton = getNodeByText(R.string.firstButton);
         assertEquals(firstButton, firstFrameLayout.getChild(2));
     }
 
-    @MediumTest
+    @Test
     public void testDescendantsOfNotImportantViewReportedInOrder2() throws Exception {
-        UiAutomation uiAutomation = getUiAutomation(false);
-        AccessibilityNodeInfo secondFrameLayout =
-                getNodeByText(uiAutomation, R.string.secondFrameLayout);
+        AccessibilityNodeInfo secondFrameLayout = getNodeByText(R.string.secondFrameLayout);
         assertNotNull(secondFrameLayout);
         assertSame(3, secondFrameLayout.getChildCount());
 
         // Check if the first child is the right one.
-        AccessibilityNodeInfo secondTextView = getNodeByText(uiAutomation, R.string.secondTextView);
+        AccessibilityNodeInfo secondTextView = getNodeByText(R.string.secondTextView);
         assertEquals(secondTextView, secondFrameLayout.getChild(0));
 
         // Check if the second child is the right one.
-        AccessibilityNodeInfo secondEditText = getNodeByText(uiAutomation, R.string.secondEditText);
+        AccessibilityNodeInfo secondEditText = getNodeByText(R.string.secondEditText);
         assertEquals(secondEditText, secondFrameLayout.getChild(1));
 
         // Check if the third child is the right one.
-        AccessibilityNodeInfo secondButton = getNodeByText(uiAutomation, R.string.secondButton);
+        AccessibilityNodeInfo secondButton = getNodeByText(R.string.secondButton);
         assertEquals(secondButton, secondFrameLayout.getChild(2));
     }
 
-    @MediumTest
+    @Test
     public void testDescendantsOfNotImportantViewReportedInOrder3() throws Exception {
-        UiAutomation uiAutomation = getUiAutomation(false);
         AccessibilityNodeInfo rootLinearLayout =
-                getNodeByText(uiAutomation, R.string.rootLinearLayout);
+                getNodeByText(R.string.rootLinearLayout);
         assertNotNull(rootLinearLayout);
         assertSame(4, rootLinearLayout.getChildCount());
 
         // Check if the first child is the right one.
         AccessibilityNodeInfo firstFrameLayout =
-                getNodeByText(uiAutomation, R.string.firstFrameLayout);
+                getNodeByText(R.string.firstFrameLayout);
         assertEquals(firstFrameLayout, rootLinearLayout.getChild(0));
 
         // Check if the second child is the right one.
-        AccessibilityNodeInfo secondTextView = getNodeByText(uiAutomation, R.string.secondTextView);
+        AccessibilityNodeInfo secondTextView = getNodeByText(R.string.secondTextView);
         assertEquals(secondTextView, rootLinearLayout.getChild(1));
 
         // Check if the third child is the right one.
-        AccessibilityNodeInfo secondEditText = getNodeByText(uiAutomation, R.string.secondEditText);
+        AccessibilityNodeInfo secondEditText = getNodeByText(R.string.secondEditText);
         assertEquals(secondEditText, rootLinearLayout.getChild(2));
 
         // Check if the fourth child is the right one.
-        AccessibilityNodeInfo secondButton = getNodeByText(uiAutomation, R.string.secondButton);
+        AccessibilityNodeInfo secondButton = getNodeByText(R.string.secondButton);
         assertEquals(secondButton, rootLinearLayout.getChild(3));
     }
 
-    @MediumTest
+    @Test
     public void testDrawingOrderInImportantParentFollowsXmlOrder() throws Exception {
-        getInstrumentation().runOnMainSync(new Runnable() {
-            @Override
-            public void run() {
-                getActivity().findViewById(R.id.firstLinearLayout)
-                        .setImportantForAccessibility(View.IMPORTANT_FOR_ACCESSIBILITY_YES);
-            }
-        });
+        sInstrumentation.runOnMainSync(() -> mActivity.findViewById(R.id.firstLinearLayout)
+                .setImportantForAccessibility(View.IMPORTANT_FOR_ACCESSIBILITY_YES));
 
-        UiAutomation uiAutomation = getUiAutomation(false);
-        AccessibilityNodeInfo firstTextView = getNodeByText(uiAutomation, R.string.firstTextView);
-        AccessibilityNodeInfo firstEditText = getNodeByText(uiAutomation, R.string.firstEditText);
-        AccessibilityNodeInfo firstButton = getNodeByText(uiAutomation, R.string.firstButton);
+        AccessibilityNodeInfo firstTextView = getNodeByText(R.string.firstTextView);
+        AccessibilityNodeInfo firstEditText = getNodeByText(R.string.firstEditText);
+        AccessibilityNodeInfo firstButton = getNodeByText(R.string.firstButton);
 
         // Drawing order is: firstTextView, firstEditText, firstButton
         assertTrue(firstTextView.getDrawingOrder() < firstEditText.getDrawingOrder());
@@ -133,202 +169,160 @@
         assertTrue(copyOfFirstEditText.getDrawingOrder() < firstButton.getDrawingOrder());
     }
 
-    @MediumTest
+    @Test
     public void testDrawingOrderGettingAllViewsFollowsXmlOrder() throws Exception {
-        UiAutomation uiAutomation = getUiAutomation(true);
-        AccessibilityNodeInfo firstTextView = getNodeByText(uiAutomation, R.string.firstTextView);
-        AccessibilityNodeInfo firstEditText = getNodeByText(uiAutomation, R.string.firstEditText);
-        AccessibilityNodeInfo firstButton = getNodeByText(uiAutomation, R.string.firstButton);
+        setGetNonImportantViews(true);
+        AccessibilityNodeInfo firstTextView = getNodeByText(R.string.firstTextView);
+        AccessibilityNodeInfo firstEditText = getNodeByText(R.string.firstEditText);
+        AccessibilityNodeInfo firstButton = getNodeByText(R.string.firstButton);
 
         // Drawing order is: firstTextView, firstEditText, firstButton
         assertTrue(firstTextView.getDrawingOrder() < firstEditText.getDrawingOrder());
         assertTrue(firstEditText.getDrawingOrder() < firstButton.getDrawingOrder());
     }
 
-    @MediumTest
+    @Test
     public void testDrawingOrderWithZCoordsDrawsHighestZLast() throws Exception {
-        getInstrumentation().runOnMainSync(new Runnable() {
-           @Override
-           public void run() {
-               AccessibilityViewTreeReportingActivity activity = getActivity();
-               activity.findViewById(R.id.firstTextView).setZ(50);
-               activity.findViewById(R.id.firstEditText).setZ(100);
-           }
+        setGetNonImportantViews(true);
+        sInstrumentation.runOnMainSync(() -> {
+            mActivity.findViewById(R.id.firstTextView).setZ(50);
+            mActivity.findViewById(R.id.firstEditText).setZ(100);
         });
 
-        UiAutomation uiAutomation = getUiAutomation(true);
-        AccessibilityNodeInfo firstTextView = getNodeByText(uiAutomation, R.string.firstTextView);
-        AccessibilityNodeInfo firstEditText = getNodeByText(uiAutomation, R.string.firstEditText);
-        AccessibilityNodeInfo firstButton = getNodeByText(uiAutomation, R.string.firstButton);
+        AccessibilityNodeInfo firstTextView = getNodeByText(R.string.firstTextView);
+        AccessibilityNodeInfo firstEditText = getNodeByText(R.string.firstEditText);
+        AccessibilityNodeInfo firstButton = getNodeByText(R.string.firstButton);
 
         // Drawing order is firstButton (no z), firstTextView (z=50), firstEditText (z=100)
         assertTrue(firstButton.getDrawingOrder() < firstTextView.getDrawingOrder());
         assertTrue(firstTextView.getDrawingOrder() < firstEditText.getDrawingOrder());
     }
 
-    @MediumTest
+    @Test
     public void testDrawingOrderWithCustomDrawingOrder() throws Exception {
-        getInstrumentation().runOnMainSync(new Runnable() {
-            @Override
-            public void run() {
-                // Reorganize the hiearchy to replace firstLinearLayout with one that allows us to
-                // control the draw order
-                AccessibilityViewTreeReportingActivity activity = getActivity();
-                LinearLayout rootLinearLayout =
-                        (LinearLayout) activity.findViewById(R.id.rootLinearLayout);
-                LinearLayout firstLinearLayout =
-                        (LinearLayout) activity.findViewById(R.id.firstLinearLayout);
-                View firstTextView = activity.findViewById(R.id.firstTextView);
-                View firstEditText = activity.findViewById(R.id.firstEditText);
-                View firstButton = activity.findViewById(R.id.firstButton);
-                firstLinearLayout.removeAllViews();
-                LinearLayoutWithDrawingOrder layoutWithDrawingOrder =
-                        new LinearLayoutWithDrawingOrder(activity);
-                rootLinearLayout.addView(layoutWithDrawingOrder);
-                layoutWithDrawingOrder.addView(firstTextView);
-                layoutWithDrawingOrder.addView(firstEditText);
-                layoutWithDrawingOrder.addView(firstButton);
-                layoutWithDrawingOrder.childDrawingOrder = new int[] {2, 0, 1};
-            }
+        setGetNonImportantViews(true);
+        sInstrumentation.runOnMainSync(() -> {
+            // Reorganize the hiearchy to replace firstLinearLayout with one that allows us to
+            // control the draw order
+            LinearLayout rootLinearLayout =
+                    (LinearLayout) mActivity.findViewById(R.id.rootLinearLayout);
+            LinearLayout firstLinearLayout =
+                    (LinearLayout) mActivity.findViewById(R.id.firstLinearLayout);
+            View firstTextView = mActivity.findViewById(R.id.firstTextView);
+            View firstEditText = mActivity.findViewById(R.id.firstEditText);
+            View firstButton = mActivity.findViewById(R.id.firstButton);
+            firstLinearLayout.removeAllViews();
+            LinearLayoutWithDrawingOrder layoutWithDrawingOrder =
+                    new LinearLayoutWithDrawingOrder(mActivity);
+            rootLinearLayout.addView(layoutWithDrawingOrder);
+            layoutWithDrawingOrder.addView(firstTextView);
+            layoutWithDrawingOrder.addView(firstEditText);
+            layoutWithDrawingOrder.addView(firstButton);
+            layoutWithDrawingOrder.childDrawingOrder = new int[] {2, 0, 1};
         });
 
-        UiAutomation uiAutomation = getUiAutomation(true);
-        AccessibilityNodeInfo firstTextView = getNodeByText(uiAutomation, R.string.firstTextView);
-        AccessibilityNodeInfo firstEditText = getNodeByText(uiAutomation, R.string.firstEditText);
-        AccessibilityNodeInfo firstButton = getNodeByText(uiAutomation, R.string.firstButton);
+        AccessibilityNodeInfo firstTextView = getNodeByText(R.string.firstTextView);
+        AccessibilityNodeInfo firstEditText = getNodeByText(R.string.firstEditText);
+        AccessibilityNodeInfo firstButton = getNodeByText(R.string.firstButton);
 
         // Drawing order is firstEditText, firstButton, firstTextView
         assertTrue(firstEditText.getDrawingOrder() < firstButton.getDrawingOrder());
         assertTrue(firstButton.getDrawingOrder() < firstTextView.getDrawingOrder());
     }
 
-    @MediumTest
+    @Test
     public void testDrawingOrderWithNotImportantSiblingConsidersItsChildren() throws Exception {
-        getInstrumentation().runOnMainSync(new Runnable() {
-            @Override
-            public void run() {
-                // Make the first frame layout a higher Z so it's drawn last
-                getActivity().findViewById(R.id.firstFrameLayout).setZ(100);
-            }
-        });
-        UiAutomation uiAutomation = getUiAutomation(false);
-        AccessibilityNodeInfo secondTextView = getNodeByText(uiAutomation, R.string.secondTextView);
-        AccessibilityNodeInfo secondEditText = getNodeByText(uiAutomation, R.string.secondEditText);
-        AccessibilityNodeInfo secondButton = getNodeByText(uiAutomation, R.string.secondButton);
-        AccessibilityNodeInfo firstFrameLayout =
-                getNodeByText(uiAutomation, R.string.firstFrameLayout);
+        // Make the first frame layout a higher Z so it's drawn last
+        sInstrumentation.runOnMainSync(
+                () -> mActivity.findViewById(R.id.firstFrameLayout).setZ(100));
+        AccessibilityNodeInfo secondTextView = getNodeByText(R.string.secondTextView);
+        AccessibilityNodeInfo secondEditText = getNodeByText(R.string.secondEditText);
+        AccessibilityNodeInfo secondButton = getNodeByText(R.string.secondButton);
+        AccessibilityNodeInfo firstFrameLayout = getNodeByText( R.string.firstFrameLayout);
         assertTrue(secondTextView.getDrawingOrder() < firstFrameLayout.getDrawingOrder());
         assertTrue(secondEditText.getDrawingOrder() < firstFrameLayout.getDrawingOrder());
         assertTrue(secondButton.getDrawingOrder() < firstFrameLayout.getDrawingOrder());
     }
 
-    @MediumTest
+    @Test
     public void testDrawingOrderWithNotImportantParentConsidersParentSibling() throws Exception {
-        UiAutomation uiAutomation = getUiAutomation(false);
-        AccessibilityNodeInfo firstFrameLayout =
-                getNodeByText(uiAutomation, R.string.firstFrameLayout);
-        AccessibilityNodeInfo secondTextView = getNodeByText(uiAutomation, R.string.secondTextView);
-        AccessibilityNodeInfo secondEditText = getNodeByText(uiAutomation, R.string.secondEditText);
-        AccessibilityNodeInfo secondButton = getNodeByText(uiAutomation, R.string.secondButton);
+        AccessibilityNodeInfo firstFrameLayout = getNodeByText(R.string.firstFrameLayout);
+        AccessibilityNodeInfo secondTextView = getNodeByText(R.string.secondTextView);
+        AccessibilityNodeInfo secondEditText = getNodeByText(R.string.secondEditText);
+        AccessibilityNodeInfo secondButton = getNodeByText(R.string.secondButton);
 
         assertTrue(secondTextView.getDrawingOrder() > firstFrameLayout.getDrawingOrder());
         assertTrue(secondEditText.getDrawingOrder() > firstFrameLayout.getDrawingOrder());
         assertTrue(secondButton.getDrawingOrder() > firstFrameLayout.getDrawingOrder());
     }
 
-    @MediumTest
+    @Test
     public void testDrawingOrderRootNodeHasIndex0() throws Exception {
-        assertEquals(0, getUiAutomation(false).getRootInActiveWindow().getDrawingOrder());
+        assertEquals(0, sUiAutomation.getRootInActiveWindow().getDrawingOrder());
     }
 
-    @MediumTest
+    @Test
     public void testAccessibilityImportanceReportingForImportantView() throws Exception {
-        getInstrumentation().runOnMainSync(new Runnable() {
-            @Override
-            public void run() {
-                // Manually control importance for firstButton
-                AccessibilityViewTreeReportingActivity activity = getActivity();
-                View firstButton = activity.findViewById(R.id.firstButton);
-                firstButton.setImportantForAccessibility(View.IMPORTANT_FOR_ACCESSIBILITY_YES);
-            }
+        setGetNonImportantViews(true);
+        sInstrumentation.runOnMainSync(() -> {
+            // Manually control importance for firstButton
+            View firstButton = mActivity.findViewById(R.id.firstButton);
+            firstButton.setImportantForAccessibility(View.IMPORTANT_FOR_ACCESSIBILITY_YES);
         });
 
-        UiAutomation uiAutomation = getUiAutomation(true);
-        AccessibilityNodeInfo firstButtonNode = getNodeByText(uiAutomation, R.string.firstButton);
+        AccessibilityNodeInfo firstButtonNode = getNodeByText(R.string.firstButton);
         assertTrue(firstButtonNode.isImportantForAccessibility());
     }
 
-    @MediumTest
+    @Test
     public void testAccessibilityImportanceReportingForUnimportantView() throws Exception {
-        getInstrumentation().runOnMainSync(new Runnable() {
-            @Override
-            public void run() {
-                // Manually control importance for firstButton
-                AccessibilityViewTreeReportingActivity activity = getActivity();
-                View firstButton = activity.findViewById(R.id.firstButton);
-                firstButton.setImportantForAccessibility(View.IMPORTANT_FOR_ACCESSIBILITY_NO);
-            }
+        setGetNonImportantViews(true);
+        sInstrumentation.runOnMainSync(() -> {
+            View firstButton = mActivity.findViewById(R.id.firstButton);
+            firstButton.setImportantForAccessibility(View.IMPORTANT_FOR_ACCESSIBILITY_NO);
         });
 
-        UiAutomation uiAutomation = getUiAutomation(true);
-        AccessibilityNodeInfo firstButtonNode = getNodeByText(uiAutomation, R.string.firstButton);
+        AccessibilityNodeInfo firstButtonNode = getNodeByText(R.string.firstButton);
         assertFalse(firstButtonNode.isImportantForAccessibility());
     }
 
-    @MediumTest
+    @Test
     public void testAddViewToLayout_receiveSubtreeEvent() throws Throwable {
         final LinearLayout layout =
-                (LinearLayout) getActivity().findViewById(R.id.secondLinearLayout);
-        final Button newButton = new Button(getActivity());
+                (LinearLayout) mActivity.findViewById(R.id.secondLinearLayout);
+        final Button newButton = new Button(mActivity);
         newButton.setText("New Button");
         newButton.setWidth(ViewGroup.LayoutParams.WRAP_CONTENT);
         newButton.setHeight(ViewGroup.LayoutParams.WRAP_CONTENT);
         AccessibilityEvent awaitedEvent =
-                getInstrumentation().getUiAutomation().executeAndWaitForEvent(
-                        new Runnable() {
-                            @Override
-                            public void run() {
-                                // trigger the event
-                                getActivity().runOnUiThread(new Runnable() {
-                                    @Override
-                                    public void run() {
-                                        layout.addView(newButton);
-                                    }
-                                });
-                            }},
-                        new UiAutomation.AccessibilityEventFilter() {
-                            // check the received event
-                            @Override
-                            public boolean accept(AccessibilityEvent event) {
-                                boolean isContentChanged = event.getEventType()
-                                        == AccessibilityEvent.TYPE_WINDOW_CONTENT_CHANGED;
-                                int isSubTree = (event.getContentChangeTypes()
-                                        & AccessibilityEvent.CONTENT_CHANGE_TYPE_SUBTREE);
-                                boolean isFromThisPackage = TextUtils.equals(event.getPackageName(),
-                                        getActivity().getPackageName());
-                                return isContentChanged && (isSubTree != 0) && isFromThisPackage;
-                            }
-                        },
-                        TIMEOUT_ASYNC_PROCESSING);
+                sUiAutomation.executeAndWaitForEvent(
+                        () -> mActivity.runOnUiThread(() -> layout.addView(newButton)),
+                        (event) -> {
+                            boolean isContentChanged = event.getEventType()
+                                    == AccessibilityEvent.TYPE_WINDOW_CONTENT_CHANGED;
+                            int isSubTree = (event.getContentChangeTypes()
+                                    & AccessibilityEvent.CONTENT_CHANGE_TYPE_SUBTREE);
+                            boolean isFromThisPackage = TextUtils.equals(event.getPackageName(),
+                                    mActivity.getPackageName());
+                            return isContentChanged && (isSubTree != 0) && isFromThisPackage;
+                        }, TIMEOUT_ASYNC_PROCESSING);
         // The event should come from a view that's important for accessibility, even though the
         // layout we added it to isn't important. Otherwise services may not find out about the
         // new button.
         assertTrue(awaitedEvent.getSource().isImportantForAccessibility());
     }
 
-    private UiAutomation getUiAutomation(boolean getNonImportantViews) {
-        UiAutomation uiAutomation = getInstrumentation().getUiAutomation();
-        AccessibilityServiceInfo serviceInfo = uiAutomation.getServiceInfo();
+    private void setGetNonImportantViews(boolean getNonImportantViews) {
+        AccessibilityServiceInfo serviceInfo = sUiAutomation.getServiceInfo();
         serviceInfo.flags &= ~AccessibilityServiceInfo.FLAG_INCLUDE_NOT_IMPORTANT_VIEWS;
         serviceInfo.flags |= getNonImportantViews ?
                 AccessibilityServiceInfo.FLAG_INCLUDE_NOT_IMPORTANT_VIEWS : 0;
-        uiAutomation.setServiceInfo(serviceInfo);
-        return uiAutomation;
+        sUiAutomation.setServiceInfo(serviceInfo);
     }
 
-    private AccessibilityNodeInfo getNodeByText(UiAutomation uiAutomation, int stringId) {
-        return uiAutomation.getRootInActiveWindow()
-                .findAccessibilityNodeInfosByText(getString(stringId)).get(0);
+    private AccessibilityNodeInfo getNodeByText(int stringId) {
+        return sUiAutomation.getRootInActiveWindow().findAccessibilityNodeInfosByText(
+                sInstrumentation.getContext().getString(stringId)).get(0);
     }
 
     class LinearLayoutWithDrawingOrder extends LinearLayout {
diff --git a/tests/accessibilityservice/src/android/accessibilityservice/cts/AccessibilityVolumeTest.java b/tests/accessibilityservice/src/android/accessibilityservice/cts/AccessibilityVolumeTest.java
index b67fc28..8b1154e 100644
--- a/tests/accessibilityservice/src/android/accessibilityservice/cts/AccessibilityVolumeTest.java
+++ b/tests/accessibilityservice/src/android/accessibilityservice/cts/AccessibilityVolumeTest.java
@@ -63,16 +63,22 @@
         if (mSingleVolume) {
             return;
         }
-        int startingVolume = mAudioManager.getStreamVolume(AudioManager.STREAM_ACCESSIBILITY);
-        int otherVolume = (startingVolume == 0) ? 1 : startingVolume - 1;
-        InstrumentedAccessibilityService service = InstrumentedAccessibilityService.enableService(
-                mInstrumentation, InstrumentedAccessibilityService.class);
-
-        service.runOnServiceSync(() ->
-                mAudioManager.setStreamVolume(AudioManager.STREAM_ACCESSIBILITY, otherVolume, 0));
-        assertEquals("Accessibility service should be able to change accessibility volume",
-                otherVolume, mAudioManager.getStreamVolume(AudioManager.STREAM_ACCESSIBILITY));
-        service.runOnServiceSync(() -> mAudioManager.setStreamVolume(
-                AudioManager.STREAM_ACCESSIBILITY, startingVolume, 0));
+        final int startingVolume = mAudioManager.getStreamVolume(AudioManager.STREAM_ACCESSIBILITY);
+        final int otherVolume = (startingVolume == 0) ? 1 : startingVolume - 1;
+        final InstrumentedAccessibilityService service = InstrumentedAccessibilityService
+                .enableService(mInstrumentation, InstrumentedAccessibilityService.class);
+        try {
+            service.runOnServiceSync(() ->
+                    mAudioManager.setStreamVolume(AudioManager.STREAM_ACCESSIBILITY, otherVolume,
+                            0));
+            assertEquals("Accessibility service should be able to change accessibility volume",
+                    otherVolume, mAudioManager.getStreamVolume(AudioManager.STREAM_ACCESSIBILITY));
+            service.runOnServiceSync(() -> mAudioManager.setStreamVolume(
+                    AudioManager.STREAM_ACCESSIBILITY, startingVolume, 0));
+        } finally {
+            if (service != null) {
+                service.runOnServiceSync(() -> service.disableSelf());
+            }
+        }
     }
 }
diff --git a/tests/accessibilityservice/src/android/accessibilityservice/cts/AccessibilityWindowQueryActivity.java b/tests/accessibilityservice/src/android/accessibilityservice/cts/AccessibilityWindowQueryActivity.java
deleted file mode 100644
index 9070a81..0000000
--- a/tests/accessibilityservice/src/android/accessibilityservice/cts/AccessibilityWindowQueryActivity.java
+++ /dev/null
@@ -1,64 +0,0 @@
-/**
- * Copyright (C) 2011 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except
- * in compliance with the License. You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software distributed under the
- * License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either
- * express or implied. See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package android.accessibilityservice.cts;
-
-import android.os.Bundle;
-import android.view.View;
-
-import android.view.accessibility.AccessibilityNodeInfo;
-import android.view.accessibility.AccessibilityNodeInfo.AccessibilityAction;
-import android.accessibilityservice.cts.R;
-
-/**
- * Activity for testing the accessibility APIs for querying of
- * the screen content. These APIs allow exploring the screen and
- * requesting an action to be performed on a given view from an
- * AccessibilityService.
- */
-public class AccessibilityWindowQueryActivity extends AccessibilityTestActivity {
-
-    @Override
-    public void onCreate(Bundle savedInstanceState) {
-        super.onCreate(savedInstanceState);
-        setContentView(R.layout.query_window_test);
-
-        findViewById(R.id.button5).setOnClickListener(new View.OnClickListener() {
-            public void onClick(View v) {
-                /* do nothing */
-            }
-        });
-        findViewById(R.id.button5).setOnLongClickListener(new View.OnLongClickListener() {
-            public boolean onLongClick(View v) {
-                return true;
-            }
-        });
-
-        findViewById(R.id.button5).setAccessibilityDelegate(new View.AccessibilityDelegate() {
-            @Override
-            public void onInitializeAccessibilityNodeInfo(View host, AccessibilityNodeInfo info) {
-                super.onInitializeAccessibilityNodeInfo(host, info);
-                info.addAction(new AccessibilityAction(R.id.foo_custom_action, "Foo"));
-            }
-
-            @Override
-            public boolean performAccessibilityAction(View host, int action, Bundle args) {
-                if (action == R.id.foo_custom_action) {
-                    return true;
-                }
-                return super.performAccessibilityAction(host, action, args);
-            }
-        });
-    }
-}
diff --git a/tests/accessibilityservice/src/android/accessibilityservice/cts/AccessibilityWindowQueryTest.java b/tests/accessibilityservice/src/android/accessibilityservice/cts/AccessibilityWindowQueryTest.java
index 31292c3..0859e8a 100755
--- a/tests/accessibilityservice/src/android/accessibilityservice/cts/AccessibilityWindowQueryTest.java
+++ b/tests/accessibilityservice/src/android/accessibilityservice/cts/AccessibilityWindowQueryTest.java
@@ -16,7 +16,17 @@
 
 package android.accessibilityservice.cts;
 
+import static android.accessibilityservice.cts.utils.AccessibilityEventFilterUtils.filterForEventType;
+import static android.accessibilityservice.cts.utils.AccessibilityEventFilterUtils.filterWindowsChangedWithChangeTypes;
+import static android.accessibilityservice.cts.utils.DisplayUtils.getStatusBarHeight;
 import static android.content.pm.PackageManager.FEATURE_PICTURE_IN_PICTURE;
+import static android.view.accessibility.AccessibilityEvent.TYPE_VIEW_ACCESSIBILITY_FOCUS_CLEARED;
+import static android.view.accessibility.AccessibilityEvent.TYPE_VIEW_CLICKED;
+import static android.view.accessibility.AccessibilityEvent.TYPE_VIEW_FOCUSED;
+import static android.view.accessibility.AccessibilityEvent.TYPE_VIEW_LONG_CLICKED;
+import static android.view.accessibility.AccessibilityEvent.TYPE_WINDOWS_CHANGED;
+import static android.view.accessibility.AccessibilityEvent.WINDOWS_CHANGE_ACCESSIBILITY_FOCUSED;
+import static android.view.accessibility.AccessibilityEvent.WINDOWS_CHANGE_ADDED;
 import static android.view.accessibility.AccessibilityNodeInfo.ACTION_CLEAR_FOCUS;
 import static android.view.accessibility.AccessibilityNodeInfo.ACTION_CLEAR_SELECTION;
 import static android.view.accessibility.AccessibilityNodeInfo.ACTION_CLICK;
@@ -26,14 +36,15 @@
 
 import android.accessibilityservice.AccessibilityService;
 import android.accessibilityservice.AccessibilityServiceInfo;
+import android.accessibilityservice.cts.activities.AccessibilityWindowQueryActivity;
 import android.app.UiAutomation;
+import android.app.UiAutomation.AccessibilityEventFilter;
 import android.content.pm.PackageManager;
 import android.content.res.Resources;
 import android.graphics.Rect;
 import android.test.suitebuilder.annotation.MediumTest;
 import android.view.Gravity;
 import android.view.View;
-import android.view.Window;
 import android.view.WindowManager;
 import android.view.accessibility.AccessibilityEvent;
 import android.view.accessibility.AccessibilityNodeInfo;
@@ -59,23 +70,16 @@
     private static String CONTENT_VIEW_RES_NAME =
             "android.accessibilityservice.cts:id/added_content";
     private static final long TIMEOUT_WINDOW_STATE_IDLE = 500;
-    private final UiAutomation.AccessibilityEventFilter mWindowsChangedFilter =
-            new UiAutomation.AccessibilityEventFilter() {
-                @Override
-                public boolean accept(AccessibilityEvent event) {
-                    return (event.getEventType() == AccessibilityEvent.TYPE_WINDOWS_CHANGED);
-                }
-            };
-    private final UiAutomation.AccessibilityEventFilter mDividerPresentFilter =
-            new UiAutomation.AccessibilityEventFilter() {
+    private final AccessibilityEventFilter mDividerPresentFilter =
+            new AccessibilityEventFilter() {
                 @Override
                 public boolean accept(AccessibilityEvent event) {
                     return (event.getEventType() == AccessibilityEvent.TYPE_WINDOWS_CHANGED &&
                             isDividerWindowPresent(getInstrumentation().getUiAutomation())    );
                 }
             };
-    private final UiAutomation.AccessibilityEventFilter mDividerAbsentFilter =
-            new UiAutomation.AccessibilityEventFilter() {
+    private final AccessibilityEventFilter mDividerAbsentFilter =
+            new AccessibilityEventFilter() {
                 @Override
                 public boolean accept(AccessibilityEvent event) {
                     return (event.getEventType() == AccessibilityEvent.TYPE_WINDOWS_CHANGED &&
@@ -91,13 +95,8 @@
     public void testFindByText() throws Throwable {
         // First, make the root view of the activity an accessibility node. This allows us to
         // later exclude views that are part of the activity's DecorView.
-        runTestOnUiThread(new Runnable() {
-            @Override
-            public void run() {
-                getActivity().findViewById(R.id.added_content)
-                    .setImportantForAccessibility(View.IMPORTANT_FOR_ACCESSIBILITY_YES);
-            }
-        });
+        runTestOnUiThread(() -> getActivity().findViewById(R.id.added_content)
+                    .setImportantForAccessibility(View.IMPORTANT_FOR_ACCESSIBILITY_YES));
 
         // Start looking from the added content instead of from the root accessibility node so
         // that nodes that we don't expect (i.e. window control buttons) are not included in the
@@ -142,30 +141,12 @@
                 .findAccessibilityNodeInfosByViewId(
                         "android.accessibilityservice.cts:id/button1").get(0);
 
-        // Argh...
-        final List<AccessibilityEvent> events = new ArrayList<AccessibilityEvent>();
-
-        // Click the button.
-        uiAutomation.executeAndWaitForEvent(new Runnable() {
-            @Override
-            public void run() {
-                button1.performAction(AccessibilityNodeInfo.ACTION_CLICK);
-            }
-        },
-        new UiAutomation.AccessibilityEventFilter() {
-            @Override
-            public boolean accept(AccessibilityEvent event) {
-                if (event.getEventType() == AccessibilityEvent.TYPE_VIEW_CLICKED) {
-                    events.add(event);
-                    return true;
-                }
-                return false;
-            }
-        },
-        TIMEOUT_ASYNC_PROCESSING);
+        // Click the button to generate an event
+        AccessibilityEvent event = uiAutomation.executeAndWaitForEvent(
+                () -> button1.performAction(ACTION_CLICK),
+                filterForEventType(TYPE_VIEW_CLICKED), TIMEOUT_ASYNC_PROCESSING);
 
         // Make sure the source window cannot be accessed.
-        AccessibilityEvent event = events.get(0);
         assertNull(event.getSource().getWindow());
     }
 
@@ -214,30 +195,12 @@
                     .findAccessibilityNodeInfosByViewId(
                             "android.accessibilityservice.cts:id/button1").get(0);
 
-            // Argh...
-            final List<AccessibilityEvent> events = new ArrayList<AccessibilityEvent>();
-
             // Click the button.
-            uiAutomation.executeAndWaitForEvent(new Runnable() {
-                @Override
-                public void run() {
-                    button1.performAction(AccessibilityNodeInfo.ACTION_CLICK);
-                }
-            },
-            new UiAutomation.AccessibilityEventFilter() {
-                @Override
-                public boolean accept(AccessibilityEvent event) {
-                    if (event.getEventType() == AccessibilityEvent.TYPE_VIEW_CLICKED) {
-                        events.add(event);
-                        return true;
-                    }
-                    return false;
-                }
-            },
-            TIMEOUT_ASYNC_PROCESSING);
+            AccessibilityEvent event = uiAutomation.executeAndWaitForEvent(
+                    () -> button1.performAction(ACTION_CLICK),
+                    filterForEventType(TYPE_VIEW_CLICKED), TIMEOUT_ASYNC_PROCESSING);
 
             // Get the source window.
-            AccessibilityEvent event = events.get(0);
             AccessibilityWindowInfo window = event.getSource().getWindow();
 
             // Verify the application window.
@@ -269,30 +232,12 @@
                     .findAccessibilityNodeInfosByViewId(
                             "android.accessibilityservice.cts:id/button1").get(0);
 
-            // Argh...
-            final List<AccessibilityEvent> events = new ArrayList<AccessibilityEvent>();
-
             // Click the button.
-            uiAutomation.executeAndWaitForEvent(new Runnable() {
-                @Override
-                public void run() {
-                    button1.performAction(AccessibilityNodeInfo.ACTION_CLICK);
-                }
-            },
-            new UiAutomation.AccessibilityEventFilter() {
-                @Override
-                public boolean accept(AccessibilityEvent event) {
-                    if (event.getEventType() == AccessibilityEvent.TYPE_VIEW_CLICKED) {
-                        events.add(event);
-                        return true;
-                    }
-                    return false;
-                }
-            },
-            TIMEOUT_ASYNC_PROCESSING);
+            AccessibilityEvent event = uiAutomation.executeAndWaitForEvent(
+                    () -> button1.performAction(ACTION_CLICK),
+                    filterForEventType(TYPE_VIEW_CLICKED), TIMEOUT_ASYNC_PROCESSING);
 
             // Get the source window.
-            AccessibilityEvent event = events.get(0);
             AccessibilityWindowInfo window = event.getSource().getWindow();
 
             // Find a another button from the event's window.
@@ -301,19 +246,8 @@
                             "android.accessibilityservice.cts:id/button2").get(0);
 
             // Click the second button.
-            uiAutomation.executeAndWaitForEvent(new Runnable() {
-                @Override
-                public void run() {
-                    button2.performAction(AccessibilityNodeInfo.ACTION_CLICK);
-                }
-            },
-            new UiAutomation.AccessibilityEventFilter() {
-                @Override
-                public boolean accept(AccessibilityEvent event) {
-                    return event.getEventType() == AccessibilityEvent.TYPE_VIEW_CLICKED;
-                }
-            },
-            TIMEOUT_ASYNC_PROCESSING);
+            uiAutomation.executeAndWaitForEvent(() -> button2.performAction(ACTION_CLICK),
+                    filterForEventType(TYPE_VIEW_CLICKED), TIMEOUT_ASYNC_PROCESSING);
         } finally {
             clearAccessInteractiveWindowsFlag();
         }
@@ -324,22 +258,34 @@
         setAccessInteractiveWindowsFlag();
         try {
             // Add two more windows.
-            addTwoAppPanelWindows();
+            final View views[];
+            views = addTwoAppPanelWindows();
 
-            // Put accessibility focus in the first app window.
-            ensureAppWindowFocusedOrFail(0);
-            // Make sure there only one accessibility focus.
-            assertSingleAccessibilityFocus();
+            try {
+                // Put accessibility focus in the first app window.
+                ensureAppWindowFocusedOrFail(0);
+                // Make sure there only one accessibility focus.
+                assertSingleAccessibilityFocus();
 
-            // Put accessibility focus in the second app window.
-            ensureAppWindowFocusedOrFail(1);
-            // Make sure there only one accessibility focus.
-            assertSingleAccessibilityFocus();
+                // Put accessibility focus in the second app window.
+                ensureAppWindowFocusedOrFail(1);
+                // Make sure there only one accessibility focus.
+                assertSingleAccessibilityFocus();
 
-            // Put accessibility focus in the third app window.
-            ensureAppWindowFocusedOrFail(2);
-            // Make sure there only one accessibility focus.
-            assertSingleAccessibilityFocus();
+                // Put accessibility focus in the third app window.
+                ensureAppWindowFocusedOrFail(2);
+                // Make sure there only one accessibility focus.
+                assertSingleAccessibilityFocus();
+            } finally {
+                // Clean up panel windows
+                getInstrumentation().runOnMainSync(() -> {
+                    WindowManager wm =
+                            getInstrumentation().getContext().getSystemService(WindowManager.class);
+                    for (View view : views) {
+                        wm.removeView(view);
+                    }
+                });
+            }
         } finally {
             ensureAccessibilityFocusCleared();
             clearAccessInteractiveWindowsFlag();
@@ -347,24 +293,7 @@
     }
 
     @MediumTest
-    public void testPerformActionFocus() throws Exception {
-        // find a view and make sure it is not focused
-        AccessibilityNodeInfo button = getInstrumentation().getUiAutomation()
-                .getRootInActiveWindow().findAccessibilityNodeInfosByText(
-                        getString(R.string.button5)).get(0);
-        assertFalse(button.isFocused());
-
-        // focus the view
-        assertTrue(button.performAction(ACTION_FOCUS));
-
-        // find the view again and make sure it is focused
-        button = getInstrumentation().getUiAutomation().getRootInActiveWindow()
-                .findAccessibilityNodeInfosByText(getString(R.string.button5)).get(0);
-        assertTrue(button.isFocused());
-    }
-
-    @MediumTest
-    public void testPerformActionClearFocus() throws Exception {
+    public void testPerformActionSetAndClearFocus() throws Exception {
         // find a view and make sure it is not focused
         AccessibilityNodeInfo button = getInstrumentation().getUiAutomation()
                 .getRootInActiveWindow().findAccessibilityNodeInfosByText(
@@ -439,20 +368,10 @@
                         getString(R.string.button5)).get(0);
         assertFalse(button.isSelected());
 
-        // Make an action and wait for an event.
-        AccessibilityEvent expected = getInstrumentation().getUiAutomation()
-                .executeAndWaitForEvent(new Runnable() {
-            @Override
-            public void run() {
-                button.performAction(ACTION_CLICK);
-            }
-        }, new UiAutomation.AccessibilityEventFilter() {
-            @Override
-            public boolean accept(AccessibilityEvent event) {
-                return (event.getEventType() == AccessibilityEvent.TYPE_VIEW_CLICKED);
-            }
-        },
-        TIMEOUT_ASYNC_PROCESSING);
+        // Perform an action and wait for an event
+        AccessibilityEvent expected = getInstrumentation().getUiAutomation().executeAndWaitForEvent(
+                () -> button.performAction(ACTION_CLICK),
+                filterForEventType(TYPE_VIEW_CLICKED), TIMEOUT_ASYNC_PROCESSING);
 
         // Make sure the expected event was received.
         assertNotNull(expected);
@@ -466,20 +385,10 @@
                         getString(R.string.button5)).get(0);
         assertFalse(button.isSelected());
 
-        // Make an action and wait for an event.
-        AccessibilityEvent expected = getInstrumentation().getUiAutomation()
-                .executeAndWaitForEvent(new Runnable() {
-            @Override
-            public void run() {
-                button.performAction(ACTION_LONG_CLICK);
-            }
-        }, new UiAutomation.AccessibilityEventFilter() {
-            @Override
-            public boolean accept(AccessibilityEvent event) {
-                return (event.getEventType() == AccessibilityEvent.TYPE_VIEW_LONG_CLICKED);
-            }
-        },
-        TIMEOUT_ASYNC_PROCESSING);
+        // Perform an action and wait for an event.
+        AccessibilityEvent expected = getInstrumentation().getUiAutomation().executeAndWaitForEvent(
+                () -> button.performAction(ACTION_LONG_CLICK),
+                filterForEventType(TYPE_VIEW_LONG_CLICKED), TIMEOUT_ASYNC_PROCESSING);
 
         // Make sure the expected event was received.
         assertNotNull(expected);
@@ -517,20 +426,8 @@
 
         // focus and wait for the event
         AccessibilityEvent awaitedEvent = getInstrumentation().getUiAutomation()
-            .executeAndWaitForEvent(
-                new Runnable() {
-            @Override
-            public void run() {
-                assertTrue(button.performAction(ACTION_FOCUS));
-            }
-        },
-                new UiAutomation.AccessibilityEventFilter() {
-            @Override
-            public boolean accept(AccessibilityEvent event) {
-                return (event.getEventType() == AccessibilityEvent.TYPE_VIEW_FOCUSED);
-            }
-        },
-        TIMEOUT_ASYNC_PROCESSING);
+                .executeAndWaitForEvent(() -> button.performAction(ACTION_FOCUS),
+                        filterForEventType(TYPE_VIEW_FOCUSED), TIMEOUT_ASYNC_PROCESSING);
 
         assertNotNull(awaitedEvent);
 
@@ -627,13 +524,8 @@
         setAccessInteractiveWindowsFlag();
         final UiAutomation uiAutomation = getInstrumentation().getUiAutomation();
         assertFalse(isDividerWindowPresent(uiAutomation));
-        Runnable toggleSplitScreenRunnable = new Runnable() {
-            @Override
-            public void run() {
-                assertTrue(uiAutomation.performGlobalAction(
+        Runnable toggleSplitScreenRunnable = () -> assertTrue(uiAutomation.performGlobalAction(
                         AccessibilityService.GLOBAL_ACTION_TOGGLE_SPLIT_SCREEN));
-            }
-        };
 
         uiAutomation.executeAndWaitForEvent(toggleSplitScreenRunnable, mDividerPresentFilter,
                 TIMEOUT_ASYNC_PROCESSING);
@@ -652,7 +544,7 @@
             getInstrumentation().runOnMainSync(() -> {
                 getActivity().enterPictureInPictureMode();
             });
-        }, mWindowsChangedFilter, TIMEOUT_ASYNC_PROCESSING);
+        }, filterForEventType(TYPE_WINDOWS_CHANGED), TIMEOUT_ASYNC_PROCESSING);
         waitForIdle();
 
         // We should be able to find a picture-in-picture window now
@@ -673,8 +565,6 @@
         final int windowCount = windows.size();
         for (int i = 0; i < windowCount; i++) {
             AccessibilityWindowInfo window = windows.get(i);
-            Rect bounds = new Rect();
-            window.getBoundsInScreen(bounds);
             if (window.getType() == AccessibilityWindowInfo.TYPE_SPLIT_SCREEN_DIVIDER) {
                 return true;
             }
@@ -704,8 +594,11 @@
                     throw new AssertionError("Duplicate accessibility focus");
                 }
             } else {
-                assertNull(window.getRoot().findFocus(
-                        AccessibilityNodeInfo.FOCUS_ACCESSIBILITY));
+                AccessibilityNodeInfo root = window.getRoot();
+                if (root != null) {
+                    assertNull(root.findFocus(
+                            AccessibilityNodeInfo.FOCUS_ACCESSIBILITY));
+                }
             }
         }
     }
@@ -713,7 +606,7 @@
     private void ensureAppWindowFocusedOrFail(int appWindowIndex) throws TimeoutException {
         UiAutomation uiAutomation = getInstrumentation().getUiAutomation();
         List<AccessibilityWindowInfo> windows = uiAutomation.getWindows();
-        AccessibilityWindowInfo focusTareget = null;
+        AccessibilityWindowInfo focusTarget = null;
 
         int visitedAppWindows = -1;
         final int windowCount = windows.size();
@@ -722,118 +615,81 @@
             if (window.getType() == AccessibilityWindowInfo.TYPE_APPLICATION) {
                 visitedAppWindows++;
                 if (appWindowIndex <= visitedAppWindows) {
-                    focusTareget = window;
+                    focusTarget = window;
                     break;
                 }
             }
         }
 
-        if (focusTareget == null) {
+        if (focusTarget == null) {
             throw new IllegalStateException("Couldn't find app window: " + appWindowIndex);
         }
 
-        if (focusTareget.isAccessibilityFocused()) {
+        if (focusTarget.isAccessibilityFocused()) {
             return;
         }
 
-        final AccessibilityWindowInfo finalFocusTarget = focusTareget;
-        uiAutomation.executeAndWaitForEvent(new Runnable() {
-            @Override
-            public void run() {
-                assertTrue(finalFocusTarget.getRoot().performAction(
-                        AccessibilityNodeInfo.ACTION_ACCESSIBILITY_FOCUS));
-            }
-        }, new UiAutomation.AccessibilityEventFilter() {
-            @Override
-            public boolean accept(AccessibilityEvent event) {
-                return event.getEventType() == AccessibilityEvent.TYPE_WINDOWS_CHANGED;
-            }
-        }, TIMEOUT_ASYNC_PROCESSING);
+        final AccessibilityWindowInfo finalFocusTarget = focusTarget;
+        uiAutomation.executeAndWaitForEvent(() -> assertTrue(finalFocusTarget.getRoot()
+                .performAction(AccessibilityNodeInfo.ACTION_ACCESSIBILITY_FOCUS)),
+                filterWindowsChangedWithChangeTypes(WINDOWS_CHANGE_ACCESSIBILITY_FOCUSED),
+                TIMEOUT_ASYNC_PROCESSING);
 
         windows = uiAutomation.getWindows();
         for (int i = 0; i < windowCount; i++) {
             AccessibilityWindowInfo window = windows.get(i);
-            if (window.getId() == focusTareget.getId()) {
+            if (window.getId() == focusTarget.getId()) {
                 assertTrue(window.isAccessibilityFocused());
                 break;
             }
         }
     }
 
-    private void addTwoAppPanelWindows() throws TimeoutException {
+    private View[] addTwoAppPanelWindows() throws TimeoutException {
         final UiAutomation uiAutomation = getInstrumentation().getUiAutomation();
 
         uiAutomation.waitForIdle(TIMEOUT_WINDOW_STATE_IDLE, TIMEOUT_ASYNC_PROCESSING);
 
+        final View views[] = new View[2];
         // Add the first window.
-        uiAutomation.executeAndWaitForEvent(new Runnable() {
-            @Override
-            public void run() {
-                getInstrumentation().runOnMainSync(new Runnable() {
-                    @Override
-                    public void run() {
-                        WindowManager.LayoutParams params = new WindowManager.LayoutParams();
-                        params.gravity = Gravity.TOP;
-                        params.y = getStatusBarHeight();
-                        params.width = WindowManager.LayoutParams.MATCH_PARENT;
-                        params.height = WindowManager.LayoutParams.WRAP_CONTENT;
-                        params.flags = WindowManager.LayoutParams.FLAG_LAYOUT_IN_SCREEN
-                                | WindowManager.LayoutParams.FLAG_LAYOUT_INSET_DECOR
-                                | WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE
-                                | WindowManager.LayoutParams.FLAG_NOT_TOUCH_MODAL;
-                        params.type = WindowManager.LayoutParams.TYPE_APPLICATION_PANEL;
-                        params.token = getActivity().getWindow().getDecorView().getWindowToken();
+        uiAutomation.executeAndWaitForEvent(() -> getInstrumentation().runOnMainSync(() -> {
+            final WindowManager.LayoutParams params = new WindowManager.LayoutParams();
+            params.gravity = Gravity.TOP;
+            params.y = getStatusBarHeight(getActivity());
+            params.width = WindowManager.LayoutParams.MATCH_PARENT;
+            params.height = WindowManager.LayoutParams.WRAP_CONTENT;
+            params.flags = WindowManager.LayoutParams.FLAG_LAYOUT_IN_SCREEN
+                    | WindowManager.LayoutParams.FLAG_LAYOUT_INSET_DECOR
+                    | WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE
+                    | WindowManager.LayoutParams.FLAG_NOT_TOUCH_MODAL;
+            params.type = WindowManager.LayoutParams.TYPE_APPLICATION_PANEL;
+            params.token = getActivity().getWindow().getDecorView().getWindowToken();
 
-                        Button button = new Button(getActivity());
-                        button.setText(R.string.button1);
-                        getActivity().getWindowManager().addView(button, params);
-                    }
-                });
-            }
-        }, new UiAutomation.AccessibilityEventFilter() {
-            @Override
-            public boolean accept(AccessibilityEvent event) {
-                return event.getEventType() == AccessibilityEvent.TYPE_WINDOWS_CHANGED;
-            }
-        }, TIMEOUT_ASYNC_PROCESSING);
+            final Button button = new Button(getActivity());
+            button.setText(R.string.button1);
+            views[0] = button;
+            getActivity().getWindowManager().addView(button, params);
+        }), filterWindowsChangedWithChangeTypes(WINDOWS_CHANGE_ADDED), TIMEOUT_ASYNC_PROCESSING);
 
         // Add the second window.
-        uiAutomation.executeAndWaitForEvent(new Runnable() {
-            @Override
-            public void run() {
-                getInstrumentation().runOnMainSync(new Runnable() {
-                    @Override
-                    public void run() {
-                        WindowManager.LayoutParams params = new WindowManager.LayoutParams();
-                        params.gravity = Gravity.BOTTOM;
-                        params.width = WindowManager.LayoutParams.MATCH_PARENT;
-                        params.height = WindowManager.LayoutParams.WRAP_CONTENT;
-                        params.flags = WindowManager.LayoutParams.FLAG_LAYOUT_IN_SCREEN
-                                | WindowManager.LayoutParams.FLAG_LAYOUT_INSET_DECOR
-                                | WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE
-                                | WindowManager.LayoutParams.FLAG_NOT_TOUCH_MODAL;
-                        params.type = WindowManager.LayoutParams.TYPE_APPLICATION_PANEL;
-                        params.token = getActivity().getWindow().getDecorView().getWindowToken();
+        uiAutomation.executeAndWaitForEvent(() -> getInstrumentation().runOnMainSync(() -> {
+            final WindowManager.LayoutParams params = new WindowManager.LayoutParams();
+            params.gravity = Gravity.BOTTOM;
+            params.width = WindowManager.LayoutParams.MATCH_PARENT;
+            params.height = WindowManager.LayoutParams.WRAP_CONTENT;
+            params.flags = WindowManager.LayoutParams.FLAG_LAYOUT_IN_SCREEN
+                    | WindowManager.LayoutParams.FLAG_LAYOUT_INSET_DECOR
+                    | WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE
+                    | WindowManager.LayoutParams.FLAG_NOT_TOUCH_MODAL;
+            params.type = WindowManager.LayoutParams.TYPE_APPLICATION_PANEL;
+            params.token = getActivity().getWindow().getDecorView().getWindowToken();
 
-                        Button button = new Button(getActivity());
-                        button.setText(R.string.button2);
-                        getActivity().getWindowManager().addView(button, params);
-                    }
-                });
-            }
-        }, new UiAutomation.AccessibilityEventFilter() {
-            @Override
-            public boolean accept(AccessibilityEvent event) {
-                return event.getEventType() == AccessibilityEvent.TYPE_WINDOWS_CHANGED;
-            }
-        }, TIMEOUT_ASYNC_PROCESSING);
-    }
-
-    private int getStatusBarHeight() {
-        final Rect rect = new Rect();
-        Window window = getActivity().getWindow();
-        window.getDecorView().getWindowVisibleDisplayFrame(rect);
-        return rect.top;
+            final Button button = new Button(getActivity());
+            button.setText(R.string.button2);
+            views[1] = button;
+            getActivity().getWindowManager().addView(button, params);
+        }), filterWindowsChangedWithChangeTypes(WINDOWS_CHANGE_ADDED), TIMEOUT_ASYNC_PROCESSING);
+        return views;
     }
 
     private void setAccessInteractiveWindowsFlag () {
@@ -853,26 +709,20 @@
     private void ensureAccessibilityFocusCleared() {
         try {
             final UiAutomation uiAutomation = getInstrumentation().getUiAutomation();
-            uiAutomation.executeAndWaitForEvent(new Runnable() {
-                @Override
-                public void run() {
-                    List<AccessibilityWindowInfo> windows = uiAutomation.getWindows();
-                    final int windowCount = windows.size();
-                    for (int i = 0; i < windowCount; i++) {
-                        AccessibilityWindowInfo window = windows.get(i);
-                        if (window.isAccessibilityFocused()) {
-                            window.getRoot().performAction(
+            uiAutomation.executeAndWaitForEvent(() -> {
+                List<AccessibilityWindowInfo> windows = uiAutomation.getWindows();
+                final int windowCount = windows.size();
+                for (int i = 0; i < windowCount; i++) {
+                    AccessibilityWindowInfo window = windows.get(i);
+                    if (window.isAccessibilityFocused()) {
+                        AccessibilityNodeInfo root = window.getRoot();
+                        if (root != null) {
+                            root.performAction(
                                     AccessibilityNodeInfo.ACTION_CLEAR_ACCESSIBILITY_FOCUS);
                         }
                     }
                 }
-            }, new UiAutomation.AccessibilityEventFilter() {
-                @Override
-                public boolean accept(AccessibilityEvent event) {
-                    return event.getEventType() ==
-                            AccessibilityEvent.TYPE_VIEW_ACCESSIBILITY_FOCUS_CLEARED;
-                }
-            }, TIMEOUT_ASYNC_PROCESSING);
+            }, filterForEventType(TYPE_VIEW_ACCESSIBILITY_FOCUS_CLEARED), TIMEOUT_ASYNC_PROCESSING);
         } catch (TimeoutException te) {
             /* ignore */
         }
diff --git a/tests/accessibilityservice/src/android/accessibilityservice/cts/AccessibilityWindowReportingActivity.java b/tests/accessibilityservice/src/android/accessibilityservice/cts/AccessibilityWindowReportingActivity.java
deleted file mode 100644
index c638951..0000000
--- a/tests/accessibilityservice/src/android/accessibilityservice/cts/AccessibilityWindowReportingActivity.java
+++ /dev/null
@@ -1,29 +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.accessibilityservice.cts;
-
-import android.os.Bundle;
-
-/**
- * Activity used by ActivityWindowReportingTest
- */
-public class AccessibilityWindowReportingActivity extends AccessibilityTestActivity {
-    @Override
-    public void onCreate(Bundle savedInstanceState) {
-        super.onCreate(savedInstanceState);
-        setContentView(R.layout.accessibility_window_reporting_test);
-        setTitle("AccessibilityWindowReportingActivity");
-    }
-}
diff --git a/tests/accessibilityservice/src/android/accessibilityservice/cts/AccessibilityWindowReportingTest.java b/tests/accessibilityservice/src/android/accessibilityservice/cts/AccessibilityWindowReportingTest.java
index c9eef93..2977843 100644
--- a/tests/accessibilityservice/src/android/accessibilityservice/cts/AccessibilityWindowReportingTest.java
+++ b/tests/accessibilityservice/src/android/accessibilityservice/cts/AccessibilityWindowReportingTest.java
@@ -16,80 +16,253 @@
 
 package android.accessibilityservice.cts;
 
+import static android.accessibilityservice.cts.utils.AccessibilityEventFilterUtils.filterWindowsChangedWithChangeTypes;
+import static android.accessibilityservice.cts.utils.ActivityLaunchUtils.findWindowByTitle;
+import static android.accessibilityservice.cts.utils.ActivityLaunchUtils.getActivityTitle;
+import static android.accessibilityservice.cts.utils.ActivityLaunchUtils.launchActivityAndWaitForItToBeOnscreen;
+import static android.accessibilityservice.cts.utils.DisplayUtils.getStatusBarHeight;
+import static android.content.pm.PackageManager.FEATURE_PICTURE_IN_PICTURE;
+import static android.view.accessibility.AccessibilityEvent.TYPE_WINDOWS_CHANGED;
+import static android.view.accessibility.AccessibilityEvent.WINDOWS_CHANGE_ACCESSIBILITY_FOCUSED;
+import static android.view.accessibility.AccessibilityEvent.WINDOWS_CHANGE_ACTIVE;
+import static android.view.accessibility.AccessibilityEvent.WINDOWS_CHANGE_ADDED;
+import static android.view.accessibility.AccessibilityEvent.WINDOWS_CHANGE_BOUNDS;
+import static android.view.accessibility.AccessibilityEvent.WINDOWS_CHANGE_CHILDREN;
+import static android.view.accessibility.AccessibilityEvent.WINDOWS_CHANGE_FOCUSED;
+import static android.view.accessibility.AccessibilityEvent.WINDOWS_CHANGE_PIP;
+import static android.view.accessibility.AccessibilityEvent.WINDOWS_CHANGE_REMOVED;
+import static android.view.accessibility.AccessibilityEvent.WINDOWS_CHANGE_TITLE;
+
+import static junit.framework.TestCase.assertTrue;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertNotNull;
+
 import android.accessibilityservice.AccessibilityServiceInfo;
+import android.accessibilityservice.cts.activities.AccessibilityWindowReportingActivity;
+import android.app.Activity;
+import android.app.Instrumentation;
 import android.app.UiAutomation;
-import android.text.TextUtils;
+import android.os.Debug;
+import android.support.test.rule.ActivityTestRule;
+import android.support.test.runner.AndroidJUnit4;
+import android.support.test.InstrumentationRegistry;
+import android.util.Log;
+import android.view.Gravity;
+import android.view.View;
+import android.view.WindowManager;
 import android.view.accessibility.AccessibilityEvent;
 import android.view.accessibility.AccessibilityNodeInfo;
 import android.view.accessibility.AccessibilityWindowInfo;
 import android.widget.ArrayAdapter;
 import android.widget.AutoCompleteTextView;
+import android.widget.Button;
+
+import org.junit.AfterClass;
+import org.junit.Before;
+import org.junit.BeforeClass;
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.runner.RunWith;
 
 import java.util.List;
 import java.util.concurrent.TimeoutException;
 
 /**
- * Tests that AccessibilityWindowInfos are properly populated
+ * Tests that window changes produce the correct events and that AccessibilityWindowInfos are
+ * properly populated
  */
-public class AccessibilityWindowReportingTest
-        extends AccessibilityActivityTestCase<AccessibilityWindowReportingActivity> {
-    UiAutomation mUiAutomation;
+@RunWith(AndroidJUnit4.class)
+public class AccessibilityWindowReportingTest {
+    private static final int TIMEOUT_ASYNC_PROCESSING = 5000;
+    private static final CharSequence TOP_WINDOW_TITLE =
+            "android.accessibilityservice.cts.AccessibilityWindowReportingTest.TOP_WINDOW_TITLE";
 
-    public AccessibilityWindowReportingTest() {
-        super(AccessibilityWindowReportingActivity.class);
-    }
+    private static Instrumentation sInstrumentation;
+    private static UiAutomation sUiAutomation;
+    private Activity mActivity;
+    private CharSequence mActivityTitle;
 
-    public void setUp() throws Exception {
-        super.setUp();
-        mUiAutomation = getInstrumentation().getUiAutomation();
-        AccessibilityServiceInfo info = mUiAutomation.getServiceInfo();
+    @Rule
+    public ActivityTestRule<AccessibilityWindowReportingActivity> mActivityRule =
+            new ActivityTestRule<>(AccessibilityWindowReportingActivity.class, false, false);
+
+    @BeforeClass
+    public static void oneTimeSetup() throws Exception {
+        sInstrumentation = InstrumentationRegistry.getInstrumentation();
+        sUiAutomation = sInstrumentation.getUiAutomation();
+        AccessibilityServiceInfo info = sUiAutomation.getServiceInfo();
         info.flags |= AccessibilityServiceInfo.FLAG_RETRIEVE_INTERACTIVE_WINDOWS;
-        mUiAutomation.setServiceInfo(info);
+        sUiAutomation.setServiceInfo(info);
     }
 
-    public void tearDown() throws Exception {
-        mUiAutomation.destroy();
-        super.tearDown();
+    @AfterClass
+    public static void finalTearDown() throws Exception {
+        sUiAutomation.destroy();
     }
 
-    public void testWindowTitle_getTitleReturnsTitle() {
-        AccessibilityWindowInfo window = findWindowByTitle(getActivity().getTitle());
-        assertNotNull("Window title not reported to accessibility", window);
-        window.recycle();
+    @Before
+    public void setUp() throws Exception {
+        mActivity = launchActivityAndWaitForItToBeOnscreen(
+                sInstrumentation, sUiAutomation, mActivityRule);
+        mActivityTitle = getActivityTitle(sInstrumentation, mActivity);
     }
 
+    @Test
     public void testUpdatedWindowTitle_generatesEventAndIsReturnedByGetTitle() {
         final String updatedTitle = "Updated Title";
         try {
-            mUiAutomation.executeAndWaitForEvent(new Runnable() {
-                @Override
-                public void run() {
-                    getInstrumentation().runOnMainSync(new Runnable() {
-                        @Override
-                        public void run() {
-                            getActivity().setTitle(updatedTitle);
-                        }
-                    });
-                }
-            }, new UiAutomation.AccessibilityEventFilter() {
-                @Override
-                public boolean accept(AccessibilityEvent event) {
-                    return (event.getEventType() == AccessibilityEvent.TYPE_WINDOWS_CHANGED);
-                }
-            }, TIMEOUT_ASYNC_PROCESSING);
+            sUiAutomation.executeAndWaitForEvent(() -> sInstrumentation.runOnMainSync(
+                    () -> mActivity.setTitle(updatedTitle)),
+                    filterWindowsChangedWithChangeTypes(WINDOWS_CHANGE_TITLE),
+                    TIMEOUT_ASYNC_PROCESSING);
         } catch (TimeoutException exception) {
             throw new RuntimeException(
                     "Failed to get windows changed event for title update", exception);
         }
-        AccessibilityWindowInfo window = findWindowByTitle(updatedTitle);
+        final AccessibilityWindowInfo window = findWindowByTitle(sUiAutomation, updatedTitle);
         assertNotNull("Updated window title not reported to accessibility", window);
         window.recycle();
     }
 
+    @Test
+    public void testWindowAddedMovedAndRemoved_generatesEventsForAllThree() throws Exception {
+        final WindowManager.LayoutParams paramsForTop = layoutParmsForWindowOnTop();
+        final WindowManager.LayoutParams paramsForBottom = layoutParmsForWindowOnBottom();
+        final Button button = new Button(mActivity);
+        button.setText(R.string.button1);
+        sUiAutomation.executeAndWaitForEvent(() -> sInstrumentation.runOnMainSync(
+                () -> mActivity.getWindowManager().addView(button, paramsForTop)),
+                filterWindowsChangedWithChangeTypes(WINDOWS_CHANGE_ADDED),
+                TIMEOUT_ASYNC_PROCESSING);
+
+        // Move window from top to bottom
+        sUiAutomation.executeAndWaitForEvent(() -> sInstrumentation.runOnMainSync(
+                () -> mActivity.getWindowManager().updateViewLayout(button, paramsForBottom)),
+                filterWindowsChangedWithChangeTypes(WINDOWS_CHANGE_BOUNDS),
+                TIMEOUT_ASYNC_PROCESSING);
+        // Remove the view
+        sUiAutomation.executeAndWaitForEvent(() -> sInstrumentation.runOnMainSync(
+                () -> mActivity.getWindowManager().removeView(button)),
+                filterWindowsChangedWithChangeTypes(WINDOWS_CHANGE_REMOVED),
+                TIMEOUT_ASYNC_PROCESSING);
+    }
+
+    @Test
+    public void putWindowInPictureInPicture_generatesEventAndReportsProperty() throws Exception {
+        if (!sInstrumentation.getContext().getPackageManager()
+                .hasSystemFeature(FEATURE_PICTURE_IN_PICTURE)) {
+            return;
+        }
+        sUiAutomation.executeAndWaitForEvent(
+                () -> sInstrumentation.runOnMainSync(() -> mActivity.enterPictureInPictureMode()),
+                (event) -> {
+                    if (event.getEventType() != TYPE_WINDOWS_CHANGED) return false;
+                    // Look for a picture-in-picture window
+                    final List<AccessibilityWindowInfo> windows = sUiAutomation.getWindows();
+                    final int windowCount = windows.size();
+                    for (int i = 0; i < windowCount; i++) {
+                        if (windows.get(i).isInPictureInPictureMode()) {
+                            return true;
+                        }
+                    }
+                    return false;
+                }, TIMEOUT_ASYNC_PROCESSING);
+
+        // There should be exactly one picture-in-picture window now
+        int numPictureInPictureWindows = 0;
+        final List<AccessibilityWindowInfo> windows = sUiAutomation.getWindows();
+        final int windowCount = windows.size();
+        for (int i = 0; i < windowCount; i++) {
+            final AccessibilityWindowInfo window = windows.get(i);
+            if (window.isInPictureInPictureMode()) {
+                numPictureInPictureWindows++;
+            }
+        }
+        assertTrue(numPictureInPictureWindows >= 1);
+    }
+
+    @Test
+    public void moveFocusToAnotherWindow_generatesEventsAndMovesActiveAndFocus() throws Exception {
+        final View topWindowView = showTopWindowAndWaitForItToShowUp();
+        final AccessibilityWindowInfo topWindow =
+                findWindowByTitle(sUiAutomation, TOP_WINDOW_TITLE);
+
+        AccessibilityWindowInfo activityWindow = findWindowByTitle(sUiAutomation, mActivityTitle);
+        final AccessibilityNodeInfo buttonNode =
+                topWindow.getRoot().findAccessibilityNodeInfosByText(
+                        sInstrumentation.getContext().getString(R.string.button1)).get(0);
+
+        // Make sure activityWindow is not focused
+        if (activityWindow.isFocused()) {
+            sUiAutomation.executeAndWaitForEvent(
+                    () -> buttonNode.performAction(AccessibilityNodeInfo.ACTION_FOCUS),
+                    filterWindowsChangedWithChangeTypes(WINDOWS_CHANGE_FOCUSED),
+                    TIMEOUT_ASYNC_PROCESSING);
+        }
+
+        // Windows may have changed - refresh
+        activityWindow = findWindowByTitle(sUiAutomation, mActivityTitle);
+        assertFalse(activityWindow.isActive());
+        assertFalse(activityWindow.isFocused());
+
+        // Find a focusable view in the main activity menu
+        final AccessibilityNodeInfo autoCompleteTextInfo = activityWindow.getRoot()
+                .findAccessibilityNodeInfosByViewId(
+                        "android.accessibilityservice.cts:id/autoCompleteLayout")
+                .get(0);
+
+        // Remove the top window and focus on the main activity
+        sUiAutomation.executeAndWaitForEvent(
+                () -> {
+                    sInstrumentation.runOnMainSync(
+                            () -> mActivity.getWindowManager().removeView(topWindowView));
+                    buttonNode.performAction(AccessibilityNodeInfo.ACTION_FOCUS);
+                },
+                filterWindowsChangedWithChangeTypes(WINDOWS_CHANGE_FOCUSED | WINDOWS_CHANGE_ACTIVE),
+                TIMEOUT_ASYNC_PROCESSING);
+    }
+
+    @Test
+    public void testChangeAccessibilityFocusWindow_getEvent() throws Exception {
+        final AccessibilityServiceInfo info = sUiAutomation.getServiceInfo();
+        info.flags |= AccessibilityServiceInfo.FLAG_REQUEST_TOUCH_EXPLORATION_MODE;
+        sUiAutomation.setServiceInfo(info);
+
+        try {
+            showTopWindowAndWaitForItToShowUp();
+
+            final AccessibilityWindowInfo activityWindow =
+                    findWindowByTitle(sUiAutomation, mActivityTitle);
+            final AccessibilityWindowInfo topWindow =
+                    findWindowByTitle(sUiAutomation, TOP_WINDOW_TITLE);
+            final AccessibilityNodeInfo win2Node =
+                    topWindow.getRoot().findAccessibilityNodeInfosByText(
+                            sInstrumentation.getContext().getString(R.string.button1)).get(0);
+            final AccessibilityNodeInfo win1Node = activityWindow.getRoot()
+                    .findAccessibilityNodeInfosByViewId(
+                            "android.accessibilityservice.cts:id/autoCompleteLayout")
+                    .get(0);
+
+            sUiAutomation.executeAndWaitForEvent(
+                    () -> {
+                        win2Node.performAction(AccessibilityNodeInfo.ACTION_ACCESSIBILITY_FOCUS);
+                        win1Node.performAction(AccessibilityNodeInfo.ACTION_ACCESSIBILITY_FOCUS);
+                    },
+                    filterWindowsChangedWithChangeTypes(WINDOWS_CHANGE_ACCESSIBILITY_FOCUSED),
+                    TIMEOUT_ASYNC_PROCESSING);
+        } finally {
+            info.flags &= ~AccessibilityServiceInfo.FLAG_REQUEST_TOUCH_EXPLORATION_MODE;
+            sUiAutomation.setServiceInfo(info);
+        }
+    }
+
+    @Test
     public void testGetAnchorForDropDownForAutoCompleteTextView_returnsTextViewNode() {
         final AutoCompleteTextView autoCompleteTextView =
-                (AutoCompleteTextView) getActivity().findViewById(R.id.autoCompleteLayout);
-        AccessibilityNodeInfo autoCompleteTextInfo = mUiAutomation.getRootInActiveWindow()
+                (AutoCompleteTextView) mActivity.findViewById(R.id.autoCompleteLayout);
+        final AccessibilityNodeInfo autoCompleteTextInfo = sUiAutomation.getRootInActiveWindow()
                 .findAccessibilityNodeInfosByViewId(
                         "android.accessibilityservice.cts:id/autoCompleteLayout")
                 .get(0);
@@ -98,25 +271,15 @@
         final String[] COUNTRIES = new String[] {"Belgium", "France", "Italy", "Germany", "Spain"};
 
         try {
-            mUiAutomation.executeAndWaitForEvent(new Runnable() {
-                @Override
-                public void run() {
-                    getInstrumentation().runOnMainSync(new Runnable() {
-                        @Override
-                        public void run() {
-                            ArrayAdapter<String> adapter = new ArrayAdapter<String>(getActivity(),
-                                    android.R.layout.simple_dropdown_item_1line, COUNTRIES);
-                            autoCompleteTextView.setAdapter(adapter);
-                            autoCompleteTextView.showDropDown();
-                        }
-                    });
-                }
-            }, new UiAutomation.AccessibilityEventFilter() {
-                @Override
-                public boolean accept(AccessibilityEvent event) {
-                    return event.getEventType() == AccessibilityEvent.TYPE_WINDOWS_CHANGED;
-                }
-            }, TIMEOUT_ASYNC_PROCESSING);
+            sUiAutomation.executeAndWaitForEvent(() -> sInstrumentation.runOnMainSync(
+                    () -> {
+                        final ArrayAdapter<String> adapter = new ArrayAdapter<>(
+                                mActivity, android.R.layout.simple_dropdown_item_1line, COUNTRIES);
+                        autoCompleteTextView.setAdapter(adapter);
+                        autoCompleteTextView.showDropDown();
+                    }),
+                    filterWindowsChangedWithChangeTypes(WINDOWS_CHANGE_CHILDREN),
+                    TIMEOUT_ASYNC_PROCESSING);
         } catch (TimeoutException exception) {
             throw new RuntimeException(
                     "Failed to get window changed event when showing dropdown", exception);
@@ -124,9 +287,9 @@
 
         // Find the pop-up window
         boolean foundPopup = false;
-        List<AccessibilityWindowInfo> windows = mUiAutomation.getWindows();
+        final List<AccessibilityWindowInfo> windows = sUiAutomation.getWindows();
         for (int i = 0; i < windows.size(); i++) {
-            AccessibilityWindowInfo window = windows.get(i);
+            final AccessibilityWindowInfo window = windows.get(i);
             if (window.getAnchor() == null) {
                 continue;
             }
@@ -137,17 +300,48 @@
         assertTrue("Failed to find accessibility window for auto-complete pop-up", foundPopup);
     }
 
-    private AccessibilityWindowInfo findWindowByTitle(CharSequence title) {
-        List<AccessibilityWindowInfo> windows = mUiAutomation.getWindows();
-        AccessibilityWindowInfo returnValue = null;
-        for (int i = 0; i < windows.size(); i++) {
-            AccessibilityWindowInfo window = windows.get(i);
-            if (TextUtils.equals(title, window.getTitle())) {
-                returnValue = window;
-            } else {
-                window.recycle();
-            }
-        }
-        return returnValue;
+    private View showTopWindowAndWaitForItToShowUp() throws TimeoutException {
+        final WindowManager.LayoutParams paramsForTop = layoutParmsForWindowOnTop();
+        final Button button = new Button(mActivity);
+        button.setText(R.string.button1);
+        sUiAutomation.executeAndWaitForEvent(() -> sInstrumentation.runOnMainSync(
+                () -> mActivity.getWindowManager().addView(button, paramsForTop)),
+                (event) -> {
+                    return (event.getEventType() == TYPE_WINDOWS_CHANGED)
+                            && (findWindowByTitle(sUiAutomation, mActivityTitle) != null)
+                            && (findWindowByTitle(sUiAutomation, TOP_WINDOW_TITLE) != null);
+                },
+                TIMEOUT_ASYNC_PROCESSING);
+        return button;
     }
-}
+
+    private WindowManager.LayoutParams layoutParmsForWindowOnTop() {
+        final WindowManager.LayoutParams params = layoutParmsForTestWindow();
+        params.gravity = Gravity.TOP;
+        params.setTitle(TOP_WINDOW_TITLE);
+        sInstrumentation.runOnMainSync(() -> {
+            params.y = getStatusBarHeight(mActivity);
+        });
+        return params;
+    }
+
+    private WindowManager.LayoutParams layoutParmsForWindowOnBottom() {
+        final WindowManager.LayoutParams params = layoutParmsForTestWindow();
+        params.gravity = Gravity.BOTTOM;
+        return params;
+    }
+
+    private WindowManager.LayoutParams layoutParmsForTestWindow() {
+        final WindowManager.LayoutParams params = new WindowManager.LayoutParams();
+        params.width = WindowManager.LayoutParams.MATCH_PARENT;
+        params.height = WindowManager.LayoutParams.WRAP_CONTENT;
+        params.flags = WindowManager.LayoutParams.FLAG_LAYOUT_IN_SCREEN
+                | WindowManager.LayoutParams.FLAG_NOT_TOUCH_MODAL
+                | WindowManager.LayoutParams.FLAG_LAYOUT_INSET_DECOR;
+        params.type = WindowManager.LayoutParams.TYPE_APPLICATION_PANEL;
+        sInstrumentation.runOnMainSync(() -> {
+            params.token = mActivity.getWindow().getDecorView().getWindowToken();
+        });
+        return params;
+    }
+}
\ No newline at end of file
diff --git a/tests/accessibilityservice/src/android/accessibilityservice/cts/InstrumentedAccessibilityService.java b/tests/accessibilityservice/src/android/accessibilityservice/cts/InstrumentedAccessibilityService.java
index 2b6d2dd..d6dc7fa 100644
--- a/tests/accessibilityservice/src/android/accessibilityservice/cts/InstrumentedAccessibilityService.java
+++ b/tests/accessibilityservice/src/android/accessibilityservice/cts/InstrumentedAccessibilityService.java
@@ -22,11 +22,15 @@
 import static junit.framework.Assert.assertTrue;
 
 public class InstrumentedAccessibilityService extends AccessibilityService {
+
+    private static final boolean DEBUG = false;
+
     // Match com.android.server.accessibility.AccessibilityManagerService#COMPONENT_NAME_SEPARATOR
     private static final String COMPONENT_NAME_SEPARATOR = ":";
 
-    private static final int TIMEOUT_SERVICE_ENABLE = 10000;
-    private static final int TIMEOUT_SERVICE_PERFORM_SYNC = 5000;
+    // Timeout disabled in #DEBUG mode to prevent breakpoint-related failures
+    private static final int TIMEOUT_SERVICE_ENABLE = DEBUG ? Integer.MAX_VALUE : 10000;
+    private static final int TIMEOUT_SERVICE_PERFORM_SYNC = DEBUG ? Integer.MAX_VALUE : 5000;
 
     private static final HashMap<Class, WeakReference<InstrumentedAccessibilityService>>
             sInstances = new HashMap<>();
diff --git a/tests/accessibilityservice/src/android/accessibilityservice/cts/MagnificationGestureHandlerTest.java b/tests/accessibilityservice/src/android/accessibilityservice/cts/MagnificationGestureHandlerTest.java
new file mode 100644
index 0000000..5cc46ea1
--- /dev/null
+++ b/tests/accessibilityservice/src/android/accessibilityservice/cts/MagnificationGestureHandlerTest.java
@@ -0,0 +1,325 @@
+/*
+ * 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.accessibilityservice.cts;
+
+import static android.accessibilityservice.cts.utils.AsyncUtils.await;
+import static android.accessibilityservice.cts.utils.AsyncUtils.waitOn;
+import static android.accessibilityservice.cts.utils.GestureUtils.add;
+import static android.accessibilityservice.cts.utils.GestureUtils.click;
+import static android.accessibilityservice.cts.utils.GestureUtils.dispatchGesture;
+import static android.accessibilityservice.cts.utils.GestureUtils.distance;
+import static android.accessibilityservice.cts.utils.GestureUtils.drag;
+import static android.accessibilityservice.cts.utils.GestureUtils.endTimeOf;
+import static android.accessibilityservice.cts.utils.GestureUtils.lastPointOf;
+import static android.accessibilityservice.cts.utils.GestureUtils.longClick;
+import static android.accessibilityservice.cts.utils.GestureUtils.pointerDown;
+import static android.accessibilityservice.cts.utils.GestureUtils.pointerUp;
+import static android.accessibilityservice.cts.utils.GestureUtils.startingAt;
+import static android.accessibilityservice.cts.utils.GestureUtils.swipe;
+import static android.view.MotionEvent.ACTION_DOWN;
+import static android.view.MotionEvent.ACTION_MOVE;
+import static android.view.MotionEvent.ACTION_UP;
+
+import static org.hamcrest.CoreMatchers.is;
+import static org.hamcrest.Matchers.empty;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertThat;
+import static org.junit.Assert.assertTrue;
+import static org.junit.Assert.fail;
+
+import static java.util.concurrent.TimeUnit.SECONDS;
+
+import android.accessibilityservice.GestureDescription;
+import android.accessibilityservice.GestureDescription.StrokeDescription;
+import android.accessibilityservice.cts.AccessibilityGestureDispatchTest.GestureDispatchActivity;
+import android.accessibilityservice.cts.utils.EventCapturingTouchListener;
+import android.app.Instrumentation;
+import android.content.pm.PackageManager;
+import android.graphics.PointF;
+import android.os.SystemClock;
+import android.provider.Settings;
+import android.support.test.rule.ActivityTestRule;
+import android.support.test.runner.AndroidJUnit4;
+import android.support.test.InstrumentationRegistry;
+import android.view.MotionEvent;
+import android.widget.TextView;
+
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Collection;
+
+/**
+ * Class for testing magnification.
+ */
+@RunWith(AndroidJUnit4.class)
+public class MagnificationGestureHandlerTest {
+
+    private static final double MIN_SCALE = 1.2;
+
+    private InstrumentedAccessibilityService mService;
+    private Instrumentation mInstrumentation;
+    private EventCapturingTouchListener mTouchListener = new EventCapturingTouchListener();
+    float mCurrentScale = 1f;
+    PointF mCurrentZoomCenter = null;
+    PointF mTapLocation;
+    PointF mTapLocation2;
+    private boolean mHasTouchscreen;
+    private boolean mOriginalIsMagnificationEnabled;
+
+    private final Object mZoomLock = new Object();
+
+    @Rule
+    public ActivityTestRule<GestureDispatchActivity> mActivityRule =
+            new ActivityTestRule<>(GestureDispatchActivity.class);
+
+    @Before
+    public void setUp() throws Exception {
+        mInstrumentation = InstrumentationRegistry.getInstrumentation();
+
+        PackageManager pm = mInstrumentation.getContext().getPackageManager();
+        mHasTouchscreen = pm.hasSystemFeature(PackageManager.FEATURE_TOUCHSCREEN)
+                || pm.hasSystemFeature(PackageManager.FEATURE_FAKETOUCH);
+        if (!mHasTouchscreen) return;
+
+        mOriginalIsMagnificationEnabled =
+                Settings.Secure.getInt(mInstrumentation.getContext().getContentResolver(),
+                        Settings.Secure.ACCESSIBILITY_DISPLAY_MAGNIFICATION_ENABLED, 0) == 1;
+        setMagnificationEnabled(true);
+
+        mService = StubMagnificationAccessibilityService.enableSelf(mInstrumentation);
+        mService.getMagnificationController().addListener(
+                (controller, region, scale, centerX, centerY) -> {
+                    mCurrentScale = scale;
+                    mCurrentZoomCenter = isZoomed() ? new PointF(centerX, centerY) : null;
+
+                    synchronized (mZoomLock) {
+                        mZoomLock.notifyAll();
+                    }
+                });
+
+        TextView view = mActivityRule.getActivity().findViewById(R.id.full_screen_text_view);
+        mInstrumentation.runOnMainSync(() -> {
+            view.setOnTouchListener(mTouchListener);
+            int[] xy = new int[2];
+            view.getLocationOnScreen(xy);
+            mTapLocation = new PointF(xy[0] + view.getWidth() / 2, xy[1] + view.getHeight() / 2);
+            mTapLocation2 = add(mTapLocation, 31, 29);
+        });
+    }
+
+    @After
+    public void tearDown() throws Exception {
+        if (!mHasTouchscreen) return;
+
+        setMagnificationEnabled(mOriginalIsMagnificationEnabled);
+
+        if (mService != null) {
+            mService.runOnServiceSync(() -> mService.disableSelfAndRemove());
+            mService = null;
+        }
+    }
+
+    @Test
+    public void testZoomOnOff() {
+        if (!mHasTouchscreen) return;
+
+        assertFalse(isZoomed());
+
+        assertGesturesPropagateToView();
+        assertFalse(isZoomed());
+
+        setZoomByTripleTapping(true);
+
+        assertGesturesPropagateToView();
+        assertTrue(isZoomed());
+
+        setZoomByTripleTapping(false);
+    }
+
+    @Test
+    public void testViewportDragging() {
+        if (!mHasTouchscreen) return;
+
+        assertFalse(isZoomed());
+        tripleTapAndDragViewport();
+        waitOn(mZoomLock, () -> !isZoomed());
+
+        setZoomByTripleTapping(true);
+        tripleTapAndDragViewport();
+        assertTrue(isZoomed());
+
+        setZoomByTripleTapping(false);
+    }
+
+    @Test
+    public void testPanning() {
+        if (!mHasTouchscreen) return;
+        assertFalse(isZoomed());
+
+        float pan = Math.min(mTapLocation.x, mTapLocation2.x) / 2;
+
+        setZoomByTripleTapping(true);
+        PointF oldCenter = mCurrentZoomCenter;
+
+        dispatch(
+                swipe(mTapLocation, add(mTapLocation, -pan, 0)),
+                swipe(mTapLocation2, add(mTapLocation2, -pan, 0)));
+
+        waitOn(mZoomLock,
+                () -> (mCurrentZoomCenter.x - oldCenter.x >= pan / mCurrentScale * 0.9));
+
+        setZoomByTripleTapping(false);
+    }
+
+    private void setZoomByTripleTapping(boolean desiredZoomState) {
+        if (isZoomed() == desiredZoomState) return;
+        dispatch(tripleTap());
+        waitOn(mZoomLock, () -> isZoomed() == desiredZoomState);
+        assertNoTouchInputPropagated();
+    }
+
+    private void tripleTapAndDragViewport() {
+        StrokeDescription down = tripleTapAndHold();
+
+        float pan = mTapLocation.x / 2;
+        PointF oldCenter = mCurrentZoomCenter;
+
+        StrokeDescription drag = drag(down, add(lastPointOf(down), pan, 0f));
+        dispatch(drag);
+        waitOn(mZoomLock, () -> distance(mCurrentZoomCenter, oldCenter) >= pan / 5);
+        assertTrue(isZoomed());
+        assertNoTouchInputPropagated();
+
+        dispatch(pointerUp(drag));
+        assertNoTouchInputPropagated();
+    }
+
+    private StrokeDescription tripleTapAndHold() {
+        StrokeDescription tap1 = click(mTapLocation);
+        StrokeDescription tap2 = startingAt(endTimeOf(tap1) + 20, click(mTapLocation2));
+        StrokeDescription down = startingAt(endTimeOf(tap2) + 20, pointerDown(mTapLocation));
+        dispatch(tap1, tap2, down);
+        waitOn(mZoomLock, () -> isZoomed());
+        return down;
+    }
+
+    private void assertGesturesPropagateToView() {
+        dispatch(click(mTapLocation));
+        assertPropagated(ACTION_DOWN, ACTION_UP);
+
+        dispatch(longClick(mTapLocation));
+        assertPropagated(ACTION_DOWN, ACTION_UP);
+
+        dispatch(doubleTap());
+        assertPropagated(ACTION_DOWN, ACTION_UP, ACTION_DOWN, ACTION_UP);
+
+        dispatch(swipe(
+                mTapLocation,
+                add(mTapLocation, 31, 29)));
+        assertPropagated(ACTION_DOWN, ACTION_MOVE, ACTION_UP);
+    }
+
+    private void assertNoTouchInputPropagated() {
+        assertThat(prettyPrintable(mTouchListener.events), is(empty()));
+    }
+
+    private void setMagnificationEnabled(boolean enabled) {
+        Settings.Secure.putInt(mInstrumentation.getContext().getContentResolver(),
+                Settings.Secure.ACCESSIBILITY_DISPLAY_MAGNIFICATION_ENABLED, enabled ? 1 : 0);
+    }
+
+    private boolean isZoomed() {
+        return mCurrentScale >= MIN_SCALE;
+    }
+
+    private void assertPropagated(int... eventTypes) {
+        MotionEvent ev;
+        try {
+            while (true) {
+                if (eventTypes.length == 0) return;
+                int expectedEventType = eventTypes[0];
+                long startedPollingAt = SystemClock.uptimeMillis();
+                ev = mTouchListener.events.poll(5, SECONDS);
+                assertNotNull("Expected "
+                        + MotionEvent.actionToString(expectedEventType)
+                        + " but none present after "
+                        + (SystemClock.uptimeMillis() - startedPollingAt) + "ms",
+                        ev);
+                int action = ev.getActionMasked();
+                if (action == expectedEventType) {
+                    eventTypes = Arrays.copyOfRange(eventTypes, 1, eventTypes.length);
+                } else {
+                    if (action != ACTION_MOVE) fail("Unexpected event: " + ev);
+                }
+            }
+        } catch (InterruptedException e) {
+            throw new RuntimeException(e);
+        }
+    }
+
+    private GestureDescription doubleTap() {
+        return multiTap(2);
+    }
+
+    private GestureDescription tripleTap() {
+        return multiTap(3);
+    }
+
+    private GestureDescription multiTap(int taps) {
+        GestureDescription.Builder builder = new GestureDescription.Builder();
+        long time = 0;
+        for (int i = 0; i < taps; i++) {
+            StrokeDescription stroke = click(mTapLocation);
+            builder.addStroke(startingAt(time, stroke));
+            time += stroke.getDuration() + 20;
+        }
+        return builder.build();
+    }
+
+    public void dispatch(StrokeDescription firstStroke, StrokeDescription... rest) {
+        GestureDescription.Builder builder =
+                new GestureDescription.Builder().addStroke(firstStroke);
+        for (StrokeDescription stroke : rest) {
+            builder.addStroke(stroke);
+        }
+        dispatch(builder.build());
+    }
+
+    public void dispatch(GestureDescription gesture) {
+        await(dispatchGesture(mService, gesture));
+    }
+
+    private static <T> Collection<T> prettyPrintable(Collection<T> c) {
+        return new ArrayList<T>(c) {
+
+            @Override
+            public String toString() {
+                return stream()
+                        .map(t -> "\n" + t)
+                        .reduce(String::concat)
+                        .orElse("");
+            }
+        };
+    }
+}
diff --git a/tests/accessibilityservice/src/android/accessibilityservice/cts/ShellCommandBuilder.java b/tests/accessibilityservice/src/android/accessibilityservice/cts/ShellCommandBuilder.java
index 823b8d6..6bad735 100644
--- a/tests/accessibilityservice/src/android/accessibilityservice/cts/ShellCommandBuilder.java
+++ b/tests/accessibilityservice/src/android/accessibilityservice/cts/ShellCommandBuilder.java
@@ -69,7 +69,7 @@
         return this;
     }
 
-    private static void execShellCommand(UiAutomation automation, String command) {
+    public static void execShellCommand(UiAutomation automation, String command) {
         try (ParcelFileDescriptor fd = automation.executeShellCommand(command)) {
             try (InputStream inputStream = new FileInputStream(fd.getFileDescriptor())) {
                 try (BufferedReader reader = new BufferedReader(
diff --git a/tests/accessibilityservice/src/android/accessibilityservice/cts/activities/AccessibilityEndToEndActivity.java b/tests/accessibilityservice/src/android/accessibilityservice/cts/activities/AccessibilityEndToEndActivity.java
new file mode 100644
index 0000000..cd9055d
--- /dev/null
+++ b/tests/accessibilityservice/src/android/accessibilityservice/cts/activities/AccessibilityEndToEndActivity.java
@@ -0,0 +1,115 @@
+/*
+ * Copyright (C) 2010 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.accessibilityservice.cts.activities;
+
+import android.accessibilityservice.cts.R;
+
+import android.os.Bundle;
+import android.view.View;
+import android.view.ViewGroup;
+import android.view.accessibility.AccessibilityEvent;
+import android.view.accessibility.AccessibilityNodeInfo;
+import android.widget.BaseAdapter;
+import android.widget.ListAdapter;
+import android.widget.ListView;
+import android.widget.TextView;
+
+/**
+ * This class is an {@link android.app.Activity} used to perform end-to-end
+ * testing of the accessibility feature by interaction with the
+ * UI widgets.
+ */
+public class AccessibilityEndToEndActivity extends AccessibilityTestActivity {
+    private PackageNameInjector mPackageNameInjector;
+
+    @Override
+    protected void onCreate(Bundle savedInstanceState) {
+        super.onCreate(savedInstanceState);
+        setContentView(R.layout.end_to_end_test);
+
+        ListAdapter listAdapter = new BaseAdapter() {
+            public View getView(int position, View convertView, ViewGroup parent) {
+                TextView textView = (TextView) View
+                        .inflate(AccessibilityEndToEndActivity.this, R.layout.list_view_row, null);
+                textView.setText((String) getItem(position));
+                return textView;
+            }
+
+            public long getItemId(int position) {
+                return position;
+            }
+
+            public Object getItem(int position) {
+                if (position == 0) {
+                    return AccessibilityEndToEndActivity.this.getString(R.string.first_list_item);
+                } else {
+                    return AccessibilityEndToEndActivity.this.getString(R.string.second_list_item);
+                }
+            }
+
+            public int getCount() {
+                return 2;
+            }
+        };
+
+        ListView listView = (ListView) findViewById(R.id.listview);
+        listView.setAdapter(listAdapter);
+    }
+
+    public void setReportedPackageName(String packageName) {
+        if (packageName != null) {
+            mPackageNameInjector = new PackageNameInjector(packageName);
+        } else {
+            mPackageNameInjector = null;
+        }
+        setPackageNameInjector(getWindow().getDecorView(), mPackageNameInjector);
+    }
+
+    private static void setPackageNameInjector(View node, PackageNameInjector injector) {
+        if (node == null) {
+            return;
+        }
+        node.setAccessibilityDelegate(injector);
+        if (node instanceof ViewGroup) {
+            final ViewGroup viewGroup = (ViewGroup) node;
+            final int childCount = viewGroup.getChildCount();
+            for (int i = 0; i < childCount; i++) {
+                setPackageNameInjector(viewGroup.getChildAt(i), injector);
+            }
+        }
+    }
+
+    private static class PackageNameInjector extends View.AccessibilityDelegate {
+        private final String mPackageName;
+
+        PackageNameInjector(String packageName) {
+            mPackageName = packageName;
+        }
+
+        @Override
+        public void onInitializeAccessibilityEvent(View host, AccessibilityEvent event) {
+            super.onInitializeAccessibilityEvent(host, event);
+            event.setPackageName(mPackageName);
+        }
+
+        @Override
+        public void onInitializeAccessibilityNodeInfo(View host, AccessibilityNodeInfo info) {
+            super.onInitializeAccessibilityNodeInfo(host, info);
+            info.setPackageName(mPackageName);
+        }
+    }
+}
diff --git a/tests/accessibilityservice/src/android/accessibilityservice/cts/activities/AccessibilityFocusAndInputFocusSyncActivity.java b/tests/accessibilityservice/src/android/accessibilityservice/cts/activities/AccessibilityFocusAndInputFocusSyncActivity.java
new file mode 100644
index 0000000..921f01b
--- /dev/null
+++ b/tests/accessibilityservice/src/android/accessibilityservice/cts/activities/AccessibilityFocusAndInputFocusSyncActivity.java
@@ -0,0 +1,35 @@
+/**
+ * Copyright (C) 2012 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except
+ * in compliance with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software distributed under the
+ * License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either
+ * express or implied. See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.accessibilityservice.cts.activities;
+
+import android.os.Bundle;
+
+import android.accessibilityservice.cts.R;
+
+/**
+ * Activity for testing the accessibility focus APIs exposed to
+ * accessibility services. These APIs allow moving accessibility
+ * focus in the view tree from an AccessiiblityService. Specifically,
+ * this activity is for verifying the the sync between accessibility
+ * and input focus.
+ */
+public class AccessibilityFocusAndInputFocusSyncActivity extends AccessibilityTestActivity {
+
+    @Override
+    public void onCreate(Bundle savedInstanceState) {
+        super.onCreate(savedInstanceState);
+        setContentView(R.layout.accessibility_focus_and_input_focus_sync_test);
+    }
+}
diff --git a/tests/accessibilityservice/src/android/accessibilityservice/cts/activities/AccessibilityTestActivity.java b/tests/accessibilityservice/src/android/accessibilityservice/cts/activities/AccessibilityTestActivity.java
new file mode 100644
index 0000000..49be337
--- /dev/null
+++ b/tests/accessibilityservice/src/android/accessibilityservice/cts/activities/AccessibilityTestActivity.java
@@ -0,0 +1,31 @@
+/**
+ * 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.accessibilityservice.cts.activities;
+
+import android.app.Activity;
+import android.os.Bundle;
+import android.view.WindowManager;
+
+public abstract class AccessibilityTestActivity extends Activity {
+
+    @Override
+    protected void onCreate(Bundle savedInstanceState) {
+        super.onCreate(savedInstanceState);
+
+        getWindow().addFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON
+                | WindowManager.LayoutParams.FLAG_TURN_SCREEN_ON
+                | WindowManager.LayoutParams.FLAG_DISMISS_KEYGUARD);
+    }
+}
diff --git a/tests/accessibilityservice/src/android/accessibilityservice/cts/activities/AccessibilityTextTraversalActivity.java b/tests/accessibilityservice/src/android/accessibilityservice/cts/activities/AccessibilityTextTraversalActivity.java
new file mode 100644
index 0000000..2cd28c5
--- /dev/null
+++ b/tests/accessibilityservice/src/android/accessibilityservice/cts/activities/AccessibilityTextTraversalActivity.java
@@ -0,0 +1,32 @@
+/**
+ * Copyright (C) 2012 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except
+ * in compliance with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software distributed under the
+ * License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either
+ * express or implied. See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.accessibilityservice.cts.activities;
+
+import android.os.Bundle;
+
+import android.accessibilityservice.cts.R;
+
+/**
+ * Activity for testing the accessibility APIs for traversing the
+ * text content of a View at several granularities.
+ */
+public class AccessibilityTextTraversalActivity extends AccessibilityTestActivity {
+
+    @Override
+    public void onCreate(Bundle savedInstanceState) {
+        super.onCreate(savedInstanceState);
+        setContentView(R.layout.accessibility_text_traversal_test);
+    }
+}
diff --git a/tests/accessibilityservice/src/android/accessibilityservice/cts/activities/AccessibilityViewTreeReportingActivity.java b/tests/accessibilityservice/src/android/accessibilityservice/cts/activities/AccessibilityViewTreeReportingActivity.java
new file mode 100644
index 0000000..28fada1
--- /dev/null
+++ b/tests/accessibilityservice/src/android/accessibilityservice/cts/activities/AccessibilityViewTreeReportingActivity.java
@@ -0,0 +1,35 @@
+/**
+ * Copyright (C) 2012 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except
+ * in compliance with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software distributed under the
+ * License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either
+ * express or implied. See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.accessibilityservice.cts.activities;
+
+import android.os.Bundle;
+
+import android.accessibilityservice.cts.R;
+
+/**
+ * Activity for testing the accessibility focus APIs exposed to
+ * accessibility services. These APIs allow moving accessibility
+ * focus in the view tree from an AccessiiblityService. Specifically,
+ * this activity is for verifying the hierarchical movement of the
+ * accessibility focus.
+ */
+public class AccessibilityViewTreeReportingActivity extends AccessibilityTestActivity {
+
+    @Override
+    public void onCreate(Bundle savedInstanceState) {
+        super.onCreate(savedInstanceState);
+        setContentView(R.layout.accessibility_view_tree_reporting_test);
+    }
+}
diff --git a/tests/accessibilityservice/src/android/accessibilityservice/cts/activities/AccessibilityWindowQueryActivity.java b/tests/accessibilityservice/src/android/accessibilityservice/cts/activities/AccessibilityWindowQueryActivity.java
new file mode 100644
index 0000000..4424f58
--- /dev/null
+++ b/tests/accessibilityservice/src/android/accessibilityservice/cts/activities/AccessibilityWindowQueryActivity.java
@@ -0,0 +1,64 @@
+/**
+ * Copyright (C) 2011 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except
+ * in compliance with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software distributed under the
+ * License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either
+ * express or implied. See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.accessibilityservice.cts.activities;
+
+import android.os.Bundle;
+import android.view.View;
+
+import android.view.accessibility.AccessibilityNodeInfo;
+import android.view.accessibility.AccessibilityNodeInfo.AccessibilityAction;
+import android.accessibilityservice.cts.R;
+
+/**
+ * Activity for testing the accessibility APIs for querying of
+ * the screen content. These APIs allow exploring the screen and
+ * requesting an action to be performed on a given view from an
+ * AccessibilityService.
+ */
+public class AccessibilityWindowQueryActivity extends AccessibilityTestActivity {
+
+    @Override
+    public void onCreate(Bundle savedInstanceState) {
+        super.onCreate(savedInstanceState);
+        setContentView(R.layout.query_window_test);
+
+        findViewById(R.id.button5).setOnClickListener(new View.OnClickListener() {
+            public void onClick(View v) {
+                /* do nothing */
+            }
+        });
+        findViewById(R.id.button5).setOnLongClickListener(new View.OnLongClickListener() {
+            public boolean onLongClick(View v) {
+                return true;
+            }
+        });
+
+        findViewById(R.id.button5).setAccessibilityDelegate(new View.AccessibilityDelegate() {
+            @Override
+            public void onInitializeAccessibilityNodeInfo(View host, AccessibilityNodeInfo info) {
+                super.onInitializeAccessibilityNodeInfo(host, info);
+                info.addAction(new AccessibilityAction(R.id.foo_custom_action, "Foo"));
+            }
+
+            @Override
+            public boolean performAccessibilityAction(View host, int action, Bundle args) {
+                if (action == R.id.foo_custom_action) {
+                    return true;
+                }
+                return super.performAccessibilityAction(host, action, args);
+            }
+        });
+    }
+}
diff --git a/tests/accessibilityservice/src/android/accessibilityservice/cts/activities/AccessibilityWindowReportingActivity.java b/tests/accessibilityservice/src/android/accessibilityservice/cts/activities/AccessibilityWindowReportingActivity.java
new file mode 100644
index 0000000..dfbbfa3
--- /dev/null
+++ b/tests/accessibilityservice/src/android/accessibilityservice/cts/activities/AccessibilityWindowReportingActivity.java
@@ -0,0 +1,30 @@
+/**
+ * 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.accessibilityservice.cts.activities;
+
+import android.os.Bundle;
+
+/**
+ * Activity used by ActivityWindowReportingTest
+ */
+public class AccessibilityWindowReportingActivity extends AccessibilityTestActivity {
+    @Override
+    public void onCreate(Bundle savedInstanceState) {
+        super.onCreate(savedInstanceState);
+        setContentView(
+                android.accessibilityservice.cts.R.layout.accessibility_window_reporting_test);
+        setTitle("AccessibilityWindowReportingActivity");
+    }
+}
diff --git a/tests/accessibilityservice/src/android/accessibilityservice/cts/utils/AccessibilityEventFilterUtils.java b/tests/accessibilityservice/src/android/accessibilityservice/cts/utils/AccessibilityEventFilterUtils.java
new file mode 100644
index 0000000..846acd9
--- /dev/null
+++ b/tests/accessibilityservice/src/android/accessibilityservice/cts/utils/AccessibilityEventFilterUtils.java
@@ -0,0 +1,95 @@
+/**
+ * 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.accessibilityservice.cts.utils;
+
+import static org.hamcrest.CoreMatchers.both;
+
+import android.app.UiAutomation.AccessibilityEventFilter;
+import android.view.accessibility.AccessibilityEvent;
+
+import org.hamcrest.Description;
+import org.hamcrest.Matcher;
+import org.hamcrest.Matchers;
+import org.hamcrest.TypeSafeMatcher;
+
+/**
+ * Utility class for creating AccessibilityEventFilters
+ */
+public class AccessibilityEventFilterUtils {
+    public static AccessibilityEventFilter filterForEventType(int eventType) {
+        return (new AccessibilityEventTypeMatcher(eventType))::matches;
+    }
+
+    public static AccessibilityEventFilter filterWindowsChangedWithChangeTypes(int changes) {
+        return (both(new AccessibilityEventTypeMatcher(AccessibilityEvent.TYPE_WINDOWS_CHANGED))
+                        .and(new WindowChangesMatcher(changes)))::matches;
+    }
+    public static class AccessibilityEventTypeMatcher extends TypeSafeMatcher<AccessibilityEvent> {
+        private int mType;
+
+        public AccessibilityEventTypeMatcher(int type) {
+            super();
+            mType = type;
+        }
+
+        @Override
+        protected boolean matchesSafely(AccessibilityEvent event) {
+            return event.getEventType() == mType;
+        }
+
+        @Override
+        public void describeTo(Description description) {
+            description.appendText("Matching to type " + mType);
+        }
+    }
+
+    public static class WindowChangesMatcher extends TypeSafeMatcher<AccessibilityEvent> {
+        private int mWindowChanges;
+
+        public WindowChangesMatcher(int windowChanges) {
+            super();
+            mWindowChanges = windowChanges;
+        }
+
+        @Override
+        protected boolean matchesSafely(AccessibilityEvent event) {
+            return (event.getWindowChanges() & mWindowChanges) == mWindowChanges;
+        }
+
+        @Override
+        public void describeTo(Description description) {
+            description.appendText("With window change type " + mWindowChanges);
+        }
+    }
+
+    public static class ContentChangesMatcher extends TypeSafeMatcher<AccessibilityEvent> {
+        private int mContentChanges;
+
+        public ContentChangesMatcher(int contentChanges) {
+            super();
+            mContentChanges = contentChanges;
+        }
+
+        @Override
+        protected boolean matchesSafely(AccessibilityEvent event) {
+            return (event.getContentChangeTypes() & mContentChanges) == mContentChanges;
+        }
+
+        @Override
+        public void describeTo(Description description) {
+            description.appendText("With window change type " + mContentChanges);
+        }
+    }
+}
diff --git a/tests/accessibilityservice/src/android/accessibilityservice/cts/utils/ActivityLaunchUtils.java b/tests/accessibilityservice/src/android/accessibilityservice/cts/utils/ActivityLaunchUtils.java
new file mode 100644
index 0000000..312bdf1
--- /dev/null
+++ b/tests/accessibilityservice/src/android/accessibilityservice/cts/utils/ActivityLaunchUtils.java
@@ -0,0 +1,95 @@
+/**
+ * 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.accessibilityservice.cts.utils;
+
+import static android.accessibilityservice.cts.AccessibilityActivityTestCase
+        .TIMEOUT_ASYNC_PROCESSING;
+
+import static org.junit.Assert.assertNotNull;
+
+import android.app.Activity;
+import android.app.Instrumentation;
+import android.app.UiAutomation;
+import android.graphics.Rect;
+import android.support.test.rule.ActivityTestRule;
+import android.text.TextUtils;
+import android.view.accessibility.AccessibilityEvent;
+import android.view.accessibility.AccessibilityWindowInfo;
+
+import java.util.List;
+
+/**
+ * Utilities useful when launching an activity to make sure it's all the way on the screen
+ * before we start testing it.
+ */
+public class ActivityLaunchUtils {
+    // Using a static variable so it can be used in lambdas. Not preserving state in it.
+    private static Activity mTempActivity;
+
+    public static Activity launchActivityAndWaitForItToBeOnscreen(Instrumentation instrumentation,
+            UiAutomation uiAutomation, ActivityTestRule<? extends Activity> rule) throws Exception {
+        final int[] location = new int[2];
+        final StringBuilder activityPackage = new StringBuilder();
+        final Rect bounds = new Rect();
+        final StringBuilder activityTitle = new StringBuilder();
+        final AccessibilityEvent awaitedEvent = uiAutomation.executeAndWaitForEvent(
+                () -> {
+                    mTempActivity = rule.launchActivity(null);
+                    final StringBuilder builder = new StringBuilder();
+                    instrumentation.runOnMainSync(() -> {
+                        mTempActivity.getWindow().getDecorView().getLocationOnScreen(location);
+                        activityPackage.append(mTempActivity.getPackageName());
+                    });
+                    instrumentation.waitForIdleSync();
+                    activityTitle.append(getActivityTitle(instrumentation, mTempActivity));
+                },
+                (event) -> {
+                    final AccessibilityWindowInfo window =
+                            findWindowByTitle(uiAutomation, activityTitle);
+                    if (window == null) return false;
+                    window.getBoundsInScreen(bounds);
+                    mTempActivity.getWindow().getDecorView().getLocationOnScreen(location);
+                    if (bounds.isEmpty()) {
+                        return false;
+                    }
+                    return (!bounds.isEmpty())
+                            && (bounds.left == location[0]) && (bounds.top == location[1]);
+                }, TIMEOUT_ASYNC_PROCESSING);
+        assertNotNull(awaitedEvent);
+        return mTempActivity;
+    }
+
+    public static CharSequence getActivityTitle(
+            Instrumentation instrumentation, Activity activity) {
+        final StringBuilder titleBuilder = new StringBuilder();
+        instrumentation.runOnMainSync(() -> titleBuilder.append(activity.getTitle()));
+        return titleBuilder;
+    }
+
+    public static AccessibilityWindowInfo findWindowByTitle(
+            UiAutomation uiAutomation, CharSequence title) {
+        final List<AccessibilityWindowInfo> windows = uiAutomation.getWindows();
+        AccessibilityWindowInfo returnValue = null;
+        for (int i = 0; i < windows.size(); i++) {
+            final AccessibilityWindowInfo window = windows.get(i);
+            if (TextUtils.equals(title, window.getTitle())) {
+                returnValue = window;
+            } else {
+                window.recycle();
+            }
+        }
+        return returnValue;
+    }
+}
diff --git a/tests/accessibilityservice/src/android/accessibilityservice/cts/utils/AsyncUtils.java b/tests/accessibilityservice/src/android/accessibilityservice/cts/utils/AsyncUtils.java
new file mode 100644
index 0000000..12bb737
--- /dev/null
+++ b/tests/accessibilityservice/src/android/accessibilityservice/cts/utils/AsyncUtils.java
@@ -0,0 +1,96 @@
+/*
+ * 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.accessibilityservice.cts.utils;
+
+import static android.accessibilityservice.cts.utils.CtsTestUtils.assertThrows;
+
+import static java.util.concurrent.TimeUnit.MILLISECONDS;
+
+import android.os.SystemClock;
+
+import java.util.concurrent.CancellationException;
+import java.util.concurrent.Future;
+import java.util.concurrent.TimeUnit;
+import java.util.function.BooleanSupplier;
+
+public class AsyncUtils {
+    public static final long DEFAULT_TIMEOUT_MS = 5000;
+
+    private AsyncUtils() {}
+
+    public static <T> T await(Future<T> f) {
+        return await(f, DEFAULT_TIMEOUT_MS, MILLISECONDS);
+    }
+
+    public static <T> T await(Future<T> f, long time, TimeUnit timeUnit) {
+        try {
+            return f.get(time, timeUnit);
+        } catch (Exception e) {
+            throw new RuntimeException(e);
+        }
+    }
+
+    public static Throwable awaitFailure(Future<?> f) {
+        return awaitFailure(f, DEFAULT_TIMEOUT_MS, MILLISECONDS);
+    }
+
+    public static Throwable awaitFailure(Future<?> f, long time, TimeUnit timeUnit) {
+        return assertThrows(() -> await(f, time, timeUnit));
+    }
+
+    public static <T> CancellationException awaitCancellation(Future<T> f) {
+       return awaitCancellation(f, DEFAULT_TIMEOUT_MS, MILLISECONDS);
+    }
+
+    public static <T> CancellationException awaitCancellation(
+            Future<T> f, long time, TimeUnit timeUnit) {
+        Throwable ex = awaitFailure(f, time, timeUnit);
+        Throwable current = ex;
+        while (current != null) {
+            if (current instanceof CancellationException) {
+                return (CancellationException) current;
+            }
+            current = current.getCause();
+        }
+        throw new AssertionError("Expected cancellation", ex);
+    }
+
+    public static void waitOn(Object notifyLock, BooleanSupplier condition) {
+        waitOn(notifyLock, condition, DEFAULT_TIMEOUT_MS);
+    }
+
+    public static void waitOn(Object notifyLock, BooleanSupplier condition, long timeoutMs) {
+        if (condition.getAsBoolean()) return;
+
+        synchronized (notifyLock) {
+            try {
+                long timeSlept = 0;
+                while (!condition.getAsBoolean() && timeSlept < timeoutMs) {
+                    long sleepStart = SystemClock.uptimeMillis();
+                    notifyLock.wait(timeoutMs - timeSlept);
+                    timeSlept += SystemClock.uptimeMillis() - sleepStart;
+                }
+                if (!condition.getAsBoolean()) {
+                    throw new AssertionError("Timed out after " + timeSlept
+                            + "ms waiting for condition");
+                }
+            } catch (InterruptedException e) {
+                throw new RuntimeException(e);
+            }
+        }
+    }
+}
diff --git a/tests/accessibilityservice/src/android/accessibilityservice/cts/utils/CtsTestUtils.java b/tests/accessibilityservice/src/android/accessibilityservice/cts/utils/CtsTestUtils.java
new file mode 100644
index 0000000..545483b
--- /dev/null
+++ b/tests/accessibilityservice/src/android/accessibilityservice/cts/utils/CtsTestUtils.java
@@ -0,0 +1,38 @@
+/*
+ * 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.accessibilityservice.cts.utils;
+
+
+public class CtsTestUtils {
+    private CtsTestUtils() {}
+
+    public static Throwable assertThrows(Runnable action) {
+        return assertThrows(Throwable.class, action);
+    }
+
+    public static <E extends Throwable> E assertThrows(Class<E> exceptionClass, Runnable action) {
+        try {
+            action.run();
+            throw new AssertionError("Expected an exception");
+        } catch (Throwable e) {
+            if (exceptionClass.isInstance(e)) {
+                return (E) e;
+            }
+            throw new RuntimeException(e);
+        }
+    }
+}
diff --git a/tests/accessibilityservice/src/android/accessibilityservice/cts/utils/DisplayUtils.java b/tests/accessibilityservice/src/android/accessibilityservice/cts/utils/DisplayUtils.java
new file mode 100644
index 0000000..a65d859
--- /dev/null
+++ b/tests/accessibilityservice/src/android/accessibilityservice/cts/utils/DisplayUtils.java
@@ -0,0 +1,31 @@
+/**
+ * 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.accessibilityservice.cts.utils;
+
+import android.app.Activity;
+import android.graphics.Rect;
+import android.view.Window;
+
+/**
+ * Utilities needed when interacting with the display
+ */
+public class DisplayUtils {
+    public static int getStatusBarHeight(Activity activity) {
+        final Rect rect = new Rect();
+        Window window = activity.getWindow();
+        window.getDecorView().getWindowVisibleDisplayFrame(rect);
+        return rect.top;
+    }
+}
diff --git a/tests/accessibilityservice/src/android/accessibilityservice/cts/utils/EventCapturingTouchListener.java b/tests/accessibilityservice/src/android/accessibilityservice/cts/utils/EventCapturingTouchListener.java
new file mode 100644
index 0000000..d7ae4592
--- /dev/null
+++ b/tests/accessibilityservice/src/android/accessibilityservice/cts/utils/EventCapturingTouchListener.java
@@ -0,0 +1,37 @@
+/*
+ * 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.accessibilityservice.cts.utils;
+
+
+import static org.junit.Assert.assertTrue;
+
+import android.view.MotionEvent;
+import android.view.View;
+
+import java.util.concurrent.BlockingQueue;
+import java.util.concurrent.LinkedBlockingQueue;
+
+public class EventCapturingTouchListener implements View.OnTouchListener {
+
+    public final BlockingQueue<MotionEvent> events = new LinkedBlockingQueue<>();
+
+    @Override
+    public boolean onTouch(View view, MotionEvent motionEvent) {
+        assertTrue(events.offer(MotionEvent.obtain(motionEvent)));
+        return true;
+    }
+}
diff --git a/tests/accessibilityservice/src/android/accessibilityservice/cts/utils/GestureUtils.java b/tests/accessibilityservice/src/android/accessibilityservice/cts/utils/GestureUtils.java
new file mode 100644
index 0000000..076a6da
--- /dev/null
+++ b/tests/accessibilityservice/src/android/accessibilityservice/cts/utils/GestureUtils.java
@@ -0,0 +1,144 @@
+/*
+ * 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.accessibilityservice.cts.utils;
+
+import android.accessibilityservice.AccessibilityService.GestureResultCallback;
+import android.accessibilityservice.GestureDescription;
+import android.accessibilityservice.GestureDescription.StrokeDescription;
+import android.accessibilityservice.cts.InstrumentedAccessibilityService;
+import android.graphics.Path;
+import android.graphics.PointF;
+import android.view.ViewConfiguration;
+
+import java.util.concurrent.CompletableFuture;
+
+public class GestureUtils {
+
+    private GestureUtils() {}
+
+    public static CompletableFuture<Void> dispatchGesture(
+            InstrumentedAccessibilityService service,
+            GestureDescription gesture) {
+        CompletableFuture<Void> result = new CompletableFuture<>();
+        GestureResultCallback callback = new GestureResultCallback() {
+            @Override
+            public void onCompleted(GestureDescription gestureDescription) {
+                result.complete(null);
+            }
+
+            @Override
+            public void onCancelled(GestureDescription gestureDescription) {
+                result.cancel(false);
+            }
+        };
+        service.runOnServiceSync(() -> {
+            if (!service.dispatchGesture(gesture, callback, null)) {
+                result.completeExceptionally(new IllegalStateException());
+            }
+        });
+        return result;
+    }
+
+    public static StrokeDescription pointerDown(PointF point) {
+        return new StrokeDescription(path(point), 0, ViewConfiguration.getTapTimeout(), true);
+    }
+
+    public static StrokeDescription pointerUp(StrokeDescription lastStroke) {
+        return lastStroke.continueStroke(path(lastPointOf(lastStroke)),
+                endTimeOf(lastStroke), ViewConfiguration.getTapTimeout(), false);
+    }
+
+    public static PointF lastPointOf(StrokeDescription stroke) {
+        float[] p = stroke.getPath().approximate(0.3f);
+        return new PointF(p[p.length - 2], p[p.length - 1]);
+    }
+
+    public static StrokeDescription click(PointF point) {
+        return new StrokeDescription(path(point), 0, ViewConfiguration.getTapTimeout());
+    }
+
+    public static StrokeDescription longClick(PointF point) {
+        return new StrokeDescription(path(point), 0,
+                ViewConfiguration.getLongPressTimeout() * 3 / 2);
+    }
+
+    public static StrokeDescription swipe(PointF from, PointF to) {
+        return swipe(from, to, ViewConfiguration.getTapTimeout());
+    }
+
+    public static StrokeDescription swipe(PointF from, PointF to, long duration) {
+        return new StrokeDescription(path(from, to), 0, duration);
+    }
+
+    public static StrokeDescription drag(StrokeDescription from, PointF to) {
+        return from.continueStroke(
+                path(lastPointOf(from), to),
+                endTimeOf(from), ViewConfiguration.getTapTimeout(), true);
+    }
+
+    public static Path path(PointF first, PointF... rest) {
+        Path path = new Path();
+        path.moveTo(first.x, first.y);
+        for (PointF point : rest) {
+            path.lineTo(point.x, point.y);
+        }
+        return path;
+    }
+
+    public static StrokeDescription startingAt(long timeMs, StrokeDescription prototype) {
+        return new StrokeDescription(
+                prototype.getPath(), timeMs, prototype.getDuration(), prototype.willContinue());
+    }
+
+    public static long endTimeOf(StrokeDescription stroke) {
+        return stroke.getStartTime() + stroke.getDuration();
+    }
+
+    public static float distance(PointF a, PointF b) {
+        if (a == null) throw new NullPointerException();
+        if (b == null) throw new NullPointerException();
+        return (float) Math.hypot(a.x - b.x, a.y - b.y);
+    }
+
+    public static PointF add(PointF a, float x, float y) {
+        return new PointF(a.x + x, a.y + y);
+    }
+
+    public static PointF add(PointF a, PointF b) {
+        return add(a, b.x, b.y);
+    }
+
+    public static PointF diff(PointF a, PointF b) {
+        return add(a, -b.x, -b.y);
+    }
+
+    public static PointF negate(PointF p) {
+        return times(-1, p);
+    }
+
+    public static PointF times(float mult, PointF p) {
+        return new PointF(p.x * mult, p.y * mult);
+    }
+
+    public static float length(PointF p) {
+        return (float) Math.hypot(p.x, p.y);
+    }
+
+    public static PointF ceil(PointF p) {
+        return new PointF((float) Math.ceil(p.x), (float) Math.ceil(p.y));
+    }
+}
diff --git a/tests/accessibilityservice/test-apps/Android.mk b/tests/accessibilityservice/test-apps/Android.mk
new file mode 100644
index 0000000..c9afcf1
--- /dev/null
+++ b/tests/accessibilityservice/test-apps/Android.mk
@@ -0,0 +1,23 @@
+# 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)
+
+# tag this module as a cts test artifact
+LOCAL_COMPATIBILITY_SUITE := cts vts general-tests
+
+# Build the test APKs using their own makefiles
+include $(call all-makefiles-under,$(LOCAL_PATH))
diff --git a/tests/accessibilityservice/test-apps/WidgetProvider/Android.mk b/tests/accessibilityservice/test-apps/WidgetProvider/Android.mk
new file mode 100644
index 0000000..a0c1d97
--- /dev/null
+++ b/tests/accessibilityservice/test-apps/WidgetProvider/Android.mk
@@ -0,0 +1,30 @@
+# Copyright (C) 2017 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#      http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+LOCAL_PATH:= $(call my-dir)
+
+include $(CLEAR_VARS)
+
+LOCAL_MODULE_TAGS := optional
+
+LOCAL_SRC_FILES := $(call all-java-files-under, src)
+
+# Tag this module as a cts test artifact
+LOCAL_COMPATIBILITY_SUITE := cts vts general-tests
+
+LOCAL_PACKAGE_NAME := CtsAccessibilityWidgetProvider
+
+LOCAL_SDK_VERSION := test_current
+
+include $(BUILD_CTS_SUPPORT_PACKAGE)
diff --git a/tests/accessibilityservice/test-apps/WidgetProvider/AndroidManifest.xml b/tests/accessibilityservice/test-apps/WidgetProvider/AndroidManifest.xml
new file mode 100644
index 0000000..4a06f03
--- /dev/null
+++ b/tests/accessibilityservice/test-apps/WidgetProvider/AndroidManifest.xml
@@ -0,0 +1,31 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT 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="foo.bar.baz">
+
+    <application>
+        <receiver android:name="foo.bar.baz.MyAppWidgetProvider" >
+            <intent-filter>
+                <action android:name="android.appwidget.action.APPWIDGET_UPDATE" />
+            </intent-filter>
+            <meta-data android:name="android.appwidget.provider"
+                       android:resource="@xml/appwidget_info" />
+        </receiver>
+    </application>
+
+</manifest>
diff --git a/tests/accessibilityservice/test-apps/WidgetProvider/res/drawable/android_icon.png b/tests/accessibilityservice/test-apps/WidgetProvider/res/drawable/android_icon.png
new file mode 100644
index 0000000..4ba97a5
--- /dev/null
+++ b/tests/accessibilityservice/test-apps/WidgetProvider/res/drawable/android_icon.png
Binary files differ
diff --git a/tests/accessibilityservice/test-apps/WidgetProvider/res/layout/initial_layout.xml b/tests/accessibilityservice/test-apps/WidgetProvider/res/layout/initial_layout.xml
new file mode 100644
index 0000000..bebec9f
--- /dev/null
+++ b/tests/accessibilityservice/test-apps/WidgetProvider/res/layout/initial_layout.xml
@@ -0,0 +1,21 @@
+<?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.
+-->
+
+<Button xmlns:android="http://schemas.android.com/apk/res/android"
+    android:layout_width="wrap_content"
+    android:layout_height="wrap_content">
+</Button>
diff --git a/tests/accessibilityservice/test-apps/WidgetProvider/res/values/constants.xml b/tests/accessibilityservice/test-apps/WidgetProvider/res/values/constants.xml
new file mode 100644
index 0000000..b29e8da
--- /dev/null
+++ b/tests/accessibilityservice/test-apps/WidgetProvider/res/values/constants.xml
@@ -0,0 +1,27 @@
+<?xml version="1.0" encoding="utf-8"?>
+
+<!--
+     Copyright (C) 2017 The Android Open Source Project
+
+     Licensed under the Apache License, Version 2.0 (the "License");
+     you may not use this file except in compliance with the License.
+     You may obtain a copy of the License at
+
+          http://www.apache.org/licenses/LICENSE-2.0
+
+     Unless required by applicable law or agreed to in writing, software
+     distributed under the License is distributed on an "AS IS" BASIS,
+     WITHOUT 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>
+    <!-- App widget provider -->
+    <dimen name="min_appwidget_size">40dp</dimen>
+    <dimen name="min_resize_appwidget_size">60dp</dimen>
+    <integer name="update_period_millis">86400000</integer>
+    <integer name="resize_mode">3</integer>
+    <integer name="widget_category">3</integer>
+    <item type="id" name="auto_advance_view_id"/>
+</resources>
diff --git a/tests/accessibilityservice/test-apps/WidgetProvider/res/xml/appwidget_info.xml b/tests/accessibilityservice/test-apps/WidgetProvider/res/xml/appwidget_info.xml
new file mode 100644
index 0000000..283f543
--- /dev/null
+++ b/tests/accessibilityservice/test-apps/WidgetProvider/res/xml/appwidget_info.xml
@@ -0,0 +1,29 @@
+<?xml version="1.0" encoding="utf-8"?>
+
+<!--
+     Copyright (C) 2017 The Android Open Source Project
+
+     Licensed under the Apache License, Version 2.0 (the "License");
+     you may not use this file except in compliance with the License.
+     You may obtain a copy of the License at
+
+          http://www.apache.org/licenses/LICENSE-2.0
+
+     Unless required by applicable law or agreed to in writing, software
+     distributed under the License is distributed on an "AS IS" BASIS,
+     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+     See the License for the specific language governing permissions and
+     limitations under the License.
+-->
+<appwidget-provider xmlns:android="http://schemas.android.com/apk/res/android"
+    android:minWidth="@dimen/min_appwidget_size"
+    android:minHeight="@dimen/min_appwidget_size"
+    android:minResizeWidth="@dimen/min_resize_appwidget_size"
+    android:minResizeHeight="@dimen/min_resize_appwidget_size"
+    android:updatePeriodMillis="@integer/update_period_millis"
+    android:resizeMode="horizontal|vertical"
+    android:widgetCategory="home_screen"
+    android:initialLayout="@layout/initial_layout"
+    android:previewImage="@drawable/android_icon"
+    android:autoAdvanceViewId="@id/auto_advance_view_id">
+</appwidget-provider>
diff --git a/tests/accessibilityservice/test-apps/WidgetProvider/src/foo/bar/baz/MyAppWidgetProvider.java b/tests/accessibilityservice/test-apps/WidgetProvider/src/foo/bar/baz/MyAppWidgetProvider.java
new file mode 100644
index 0000000..e30b554
--- /dev/null
+++ b/tests/accessibilityservice/test-apps/WidgetProvider/src/foo/bar/baz/MyAppWidgetProvider.java
@@ -0,0 +1,22 @@
+/*
+ * 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 foo.bar.baz;
+
+import android.appwidget.AppWidgetProvider;
+
+public class MyAppWidgetProvider extends AppWidgetProvider {}
diff --git a/tests/admin/Android.mk b/tests/admin/Android.mk
index 24fdda3..bd5346d 100644
--- a/tests/admin/Android.mk
+++ b/tests/admin/Android.mk
@@ -23,6 +23,8 @@
 LOCAL_STATIC_JAVA_LIBRARIES := \
     ctstestrunner mockito-target-minus-junit4
 
+LOCAL_JAVA_LIBRARIES := android.test.runner.stubs android.test.base.stubs
+
 LOCAL_SRC_FILES := $(call all-java-files-under, src)
 
 LOCAL_PACKAGE_NAME := CtsAdminTestCases
diff --git a/tests/admin/AndroidTest.xml b/tests/admin/AndroidTest.xml
index a0233d7..02c5e3f 100644
--- a/tests/admin/AndroidTest.xml
+++ b/tests/admin/AndroidTest.xml
@@ -14,6 +14,7 @@
      limitations under the License.
 -->
 <configuration description="Config for the CTS device admin tests">
+    <option name="test-suite-tag" value="cts" />
     <option name="config-descriptor:metadata" key="component" value="framework" />
     <target_preparer class="com.android.tradefed.targetprep.suite.SuiteApkInstaller">
         <option name="cleanup-apks" value="true" />
diff --git a/tests/admin/app/Android.mk b/tests/admin/app/Android.mk
index 22b5c83..947fb7d 100644
--- a/tests/admin/app/Android.mk
+++ b/tests/admin/app/Android.mk
@@ -22,7 +22,7 @@
 
 LOCAL_JAVA_LIBRARIES := guava
 
-LOCAL_STATIC_JAVA_LIBRARIES := android-support-test legacy-android-test
+LOCAL_STATIC_JAVA_LIBRARIES := android-support-test
 
 LOCAL_SRC_FILES := $(call all-java-files-under, src)
 
diff --git a/tests/admin/src/android/admin/cts/DevicePolicyManagerTest.java b/tests/admin/src/android/admin/cts/DevicePolicyManagerTest.java
index 4b9e5fa..938939f 100644
--- a/tests/admin/src/android/admin/cts/DevicePolicyManagerTest.java
+++ b/tests/admin/src/android/admin/cts/DevicePolicyManagerTest.java
@@ -746,8 +746,7 @@
     }
 
     private void assertProfileOwnerMessage(String message) {
-        assertTrue("message is: "+ message,
-                message.contains("does not own the profile"));
+        assertTrue("message is: "+ message, message.contains("does not own the profile"));
     }
 
     public void testSetDelegatedCertInstaller_failIfNotProfileOwner() {
@@ -901,4 +900,43 @@
             assertProfileOwnerMessage(e.getMessage());
         }
     }
+
+    public void testIsUsingUnifiedPassword_failIfNotProfileOwner() {
+        if (!mDeviceAdmin) {
+            Log.w(TAG, "Skipping testIsUsingUnifiedPassword_failIfNotProfileOwner");
+            return;
+        }
+        try {
+            mDevicePolicyManager.isUsingUnifiedPassword(mComponent);
+            fail("did not throw expected SecurityException");
+        } catch (SecurityException e) {
+            assertProfileOwnerMessage(e.getMessage());
+        }
+    }
+
+    public void testSetPasswordBlacklist_failIfNotDeviceOrProfileOwner() {
+        if (!mDeviceAdmin) {
+            Log.w(TAG, "Skipping testSetPasswordBlacklist_failIfNotDeviceOrProfileOwner");
+            return;
+        }
+        try {
+            mDevicePolicyManager.setPasswordBlacklist(mComponent, null, null);
+            fail("did not throw expected SecurityException");
+        } catch (SecurityException e) {
+            assertProfileOwnerMessage(e.getMessage());
+        }
+    }
+
+    public void testGetPasswordBlacklistName_failIfNotDeviceOrProfileOwner() {
+        if (!mDeviceAdmin) {
+            Log.w(TAG, "Skipping testGetPasswordBlacklistName_failIfNotDeviceOrProfileOwner");
+            return;
+        }
+        try {
+            mDevicePolicyManager.getPasswordBlacklistName(mComponent);
+            fail("did not throw expected SecurityException");
+        } catch (SecurityException e) {
+            assertProfileOwnerMessage(e.getMessage());
+        }
+    }
 }
diff --git a/tests/app/Android.mk b/tests/app/Android.mk
index 6bd42ef..66d5de0 100644
--- a/tests/app/Android.mk
+++ b/tests/app/Android.mk
@@ -21,7 +21,13 @@
 # and when built explicitly put it in the data partition
 LOCAL_MODULE_PATH := $(TARGET_OUT_DATA_APPS)
 
-LOCAL_JAVA_LIBRARIES := android.test.runner telephony-common voip-common org.apache.http.legacy
+LOCAL_JAVA_LIBRARIES := \
+    android.test.runner \
+    telephony-common \
+    voip-common \
+    org.apache.http.legacy \
+    android.test.base.stubs \
+
 
 LOCAL_STATIC_JAVA_LIBRARIES := \
     compatibility-device-util \
@@ -29,12 +35,11 @@
     ctstestserver \
     mockito-target-minus-junit4 \
     android-support-test \
-    platform-test-annotations
+    platform-test-annotations \
+    cts-amwm-util
 
 LOCAL_SRC_FILES := \
-    $(call all-java-files-under, src) \
-    $(call all-java-files-under, app2/src) \
-    $(call all-java-files-under, appSdk25/src) \
+    $(call all-java-files-under, src)
 
 # Tag this module as a cts test artifact
 LOCAL_COMPATIBILITY_SUITE := cts vts general-tests
diff --git a/tests/app/AndroidTest.xml b/tests/app/AndroidTest.xml
index eb515b2..7aa3e20 100644
--- a/tests/app/AndroidTest.xml
+++ b/tests/app/AndroidTest.xml
@@ -14,6 +14,7 @@
      limitations under the License.
 -->
 <configuration description="Config for CTS App test cases">
+    <option name="test-suite-tag" value="cts" />
     <option name="config-descriptor:metadata" key="component" value="framework" />
     <target_preparer class="com.android.compatibility.common.tradefed.targetprep.LocationCheck" />
     <target_preparer class="com.android.tradefed.targetprep.suite.SuiteApkInstaller">
@@ -22,7 +23,8 @@
         <option name="test-file-name" value="CtsAppTestStubs.apk" />
         <option name="test-file-name" value="CtsAppTestStubsDifferentUid.apk" />
         <option name="test-file-name" value="CtsAppTestCases.apk" />
-        <option name="test-file-name" value="CtsAppTestSdk25.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="package" value="android.app.cts" />
diff --git a/tests/app/CantSaveState1/Android.mk b/tests/app/CantSaveState1/Android.mk
new file mode 100644
index 0000000..ad7e880
--- /dev/null
+++ b/tests/app/CantSaveState1/Android.mk
@@ -0,0 +1,32 @@
+# 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)
+
+# 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_SRC_FILES := $(call all-subdir-java-files)
+
+# Tag this module as a cts test artifact
+LOCAL_COMPATIBILITY_SUITE := cts vts general-tests
+
+LOCAL_PACKAGE_NAME := CtsCantSaveState1
+
+include $(BUILD_CTS_SUPPORT_PACKAGE)
diff --git a/tests/app/CantSaveState1/AndroidManifest.xml b/tests/app/CantSaveState1/AndroidManifest.xml
new file mode 100644
index 0000000..fadcaeb
--- /dev/null
+++ b/tests/app/CantSaveState1/AndroidManifest.xml
@@ -0,0 +1,27 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2017 The Android Open Source Project
+
+     Licensed under the Apache License, Version 2.0 (the "License");
+     you may not use this file except in compliance with the License.
+     You may obtain a copy of the License at
+
+          http://www.apache.org/licenses/LICENSE-2.0
+
+     Unless required by applicable law or agreed to in writing, software
+     distributed under the License is distributed on an "AS IS" BASIS,
+     WITHOUT 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.test.cantsavestate1">
+    <application android:label="Can't Save 1" android:cantSaveState="true">
+        <activity android:name="CantSave1Activity">
+            <intent-filter>
+                <action android:name="android.intent.action.MAIN" />
+                <category android:name="android.intent.category.DEFAULT" />
+                <category android:name="android.intent.category.LAUNCHER" />
+            </intent-filter>
+        </activity>
+    </application>
+</manifest>
diff --git a/tests/app/CantSaveState1/res/layout/cant_save_1_activity.xml b/tests/app/CantSaveState1/res/layout/cant_save_1_activity.xml
new file mode 100644
index 0000000..c5bf657
--- /dev/null
+++ b/tests/app/CantSaveState1/res/layout/cant_save_1_activity.xml
@@ -0,0 +1,31 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2017 The Android Open Source Project
+
+     Licensed under the Apache License, Version 2.0 (the "License");
+     you may not use this file except in compliance with the License.
+     You may obtain a copy of the License at
+
+          http://www.apache.org/licenses/LICENSE-2.0
+
+     Unless required by applicable law or agreed to in writing, software
+     distributed under the License is distributed on an "AS IS" BASIS,
+     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+     See the License for the specific language governing permissions and
+     limitations under the License.
+-->
+
+<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
+    android:layout_width="match_parent"
+    android:layout_height="match_parent"
+    android:orientation="vertical"
+>
+
+    <TextView
+        android:layout_width="match_parent"
+        android:layout_height="wrap_content"
+        android:layout_marginTop="25dp"
+        android:textAppearance="?android:attr/textAppearanceLarge"
+        android:text="This app #1 can't save its state"
+    />
+
+</LinearLayout>
diff --git a/tests/app/CantSaveState1/src/com/android/test/cantsavestate2/CantSave1Activity.java b/tests/app/CantSaveState1/src/com/android/test/cantsavestate2/CantSave1Activity.java
new file mode 100644
index 0000000..fb678cb
--- /dev/null
+++ b/tests/app/CantSaveState1/src/com/android/test/cantsavestate2/CantSave1Activity.java
@@ -0,0 +1,29 @@
+/*
+ * 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.test.cantsavestate1;
+
+import android.app.Activity;
+import android.os.Bundle;
+
+public class CantSave1Activity extends Activity {
+    @Override
+    public void onCreate(Bundle savedInstanceState) {
+        super.onCreate(savedInstanceState);
+        setContentView(R.layout.cant_save_1_activity);
+        getWindow().getDecorView().requestFocus();
+    }
+}
diff --git a/tests/app/CantSaveState2/Android.mk b/tests/app/CantSaveState2/Android.mk
new file mode 100644
index 0000000..05da3f6
--- /dev/null
+++ b/tests/app/CantSaveState2/Android.mk
@@ -0,0 +1,32 @@
+# 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)
+
+# 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_SRC_FILES := $(call all-subdir-java-files)
+
+# Tag this module as a cts test artifact
+LOCAL_COMPATIBILITY_SUITE := cts vts general-tests
+
+LOCAL_PACKAGE_NAME := CtsCantSaveState2
+
+include $(BUILD_CTS_SUPPORT_PACKAGE)
diff --git a/tests/app/CantSaveState2/AndroidManifest.xml b/tests/app/CantSaveState2/AndroidManifest.xml
new file mode 100644
index 0000000..8f4f01d
--- /dev/null
+++ b/tests/app/CantSaveState2/AndroidManifest.xml
@@ -0,0 +1,27 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2017 The Android Open Source Project
+
+     Licensed under the Apache License, Version 2.0 (the "License");
+     you may not use this file except in compliance with the License.
+     You may obtain a copy of the License at
+
+          http://www.apache.org/licenses/LICENSE-2.0
+
+     Unless required by applicable law or agreed to in writing, software
+     distributed under the License is distributed on an "AS IS" BASIS,
+     WITHOUT 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.test.cantsavestate2">
+    <application android:label="Can't Save 2" android:cantSaveState="true">
+        <activity android:name="CantSave2Activity">
+            <intent-filter>
+                <action android:name="android.intent.action.MAIN" />
+                <category android:name="android.intent.category.DEFAULT" />
+                <category android:name="android.intent.category.LAUNCHER" />
+            </intent-filter>
+        </activity>
+    </application>
+</manifest>
diff --git a/tests/app/CantSaveState2/res/layout/cant_save_2_activity.xml b/tests/app/CantSaveState2/res/layout/cant_save_2_activity.xml
new file mode 100644
index 0000000..c5b8e3d
--- /dev/null
+++ b/tests/app/CantSaveState2/res/layout/cant_save_2_activity.xml
@@ -0,0 +1,31 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2017 The Android Open Source Project
+
+     Licensed under the Apache License, Version 2.0 (the "License");
+     you may not use this file except in compliance with the License.
+     You may obtain a copy of the License at
+
+          http://www.apache.org/licenses/LICENSE-2.0
+
+     Unless required by applicable law or agreed to in writing, software
+     distributed under the License is distributed on an "AS IS" BASIS,
+     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+     See the License for the specific language governing permissions and
+     limitations under the License.
+-->
+
+<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
+    android:layout_width="match_parent"
+    android:layout_height="match_parent"
+    android:orientation="vertical"
+>
+
+    <TextView
+        android:layout_width="match_parent"
+        android:layout_height="wrap_content"
+        android:layout_marginTop="25dp"
+        android:textAppearance="?android:attr/textAppearanceLarge"
+        android:text="This app #2 can't save its state"
+    />
+
+</LinearLayout>
diff --git a/tests/app/CantSaveState2/src/com/android/test/cantsavestate2/CantSave2Activity.java b/tests/app/CantSaveState2/src/com/android/test/cantsavestate2/CantSave2Activity.java
new file mode 100644
index 0000000..420ac93
--- /dev/null
+++ b/tests/app/CantSaveState2/src/com/android/test/cantsavestate2/CantSave2Activity.java
@@ -0,0 +1,29 @@
+/*
+ * 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.test.cantsavestate2;
+
+import android.app.Activity;
+import android.os.Bundle;
+
+public class CantSave2Activity extends Activity {
+    @Override
+    public void onCreate(Bundle savedInstanceState) {
+        super.onCreate(savedInstanceState);
+        setContentView(R.layout.cant_save_2_activity);
+        getWindow().getDecorView().requestFocus();
+    }
+}
diff --git a/tests/app/app/Android.mk b/tests/app/app/Android.mk
index 191f4cc..d982246 100644
--- a/tests/app/app/Android.mk
+++ b/tests/app/app/Android.mk
@@ -23,15 +23,20 @@
 
 LOCAL_PROGUARD_ENABLED := disabled
 
-LOCAL_JAVA_LIBRARIES := android.test.runner telephony-common voip-common org.apache.http.legacy
+LOCAL_JAVA_LIBRARIES := \
+    android.test.runner \
+    telephony-common \
+    voip-common \
+    org.apache.http.legacy \
+    android.test.base \
+
 
 LOCAL_STATIC_JAVA_LIBRARIES := \
     compatibility-device-util \
     ctstestrunner \
     ctstestserver \
     mockito-target-minus-junit4 \
-    android-support-v4 \
-    legacy-android-test
+    android-support-v4
 
 LOCAL_SRC_FILES := $(call all-java-files-under, src) \
               src/android/app/stubs/ISecondary.aidl
diff --git a/tests/app/app/AndroidManifest.xml b/tests/app/app/AndroidManifest.xml
index 7db0717..6093384 100644
--- a/tests/app/app/AndroidManifest.xml
+++ b/tests/app/app/AndroidManifest.xml
@@ -104,6 +104,8 @@
 
         <service android:name="android.app.stubs.MockService" />
 
+        <service android:name="android.app.stubs.NullService" />
+
         <activity android:name="android.app.stubs.SearchManagerStubActivity"
                 android:label="SearchManagerStubActivity">
             <intent-filter>
@@ -326,45 +328,6 @@
         <activity android:name="android.app.stubs.ToolbarActivity"
                   android:theme="@android:style/Theme.Material.Light.NoActionBar" />
 
-        <activity android:name="android.app.stubs.MaxAspectRatioActivity"
-                  android:label="MaxAspectRatioActivity"
-                  android:resizeableActivity="false"
-                  android:maxAspectRatio="1.0">
-            <intent-filter>
-                <action android:name="android.intent.action.MAIN" />
-                <category android:name="android.intent.category.FRAMEWORK_INSTRUMENTATION_TEST" />
-            </intent-filter>
-        </activity>
-
-        <activity android:name="android.app.stubs.MetaDataMaxAspectRatioActivity"
-                  android:label="MetaDataMaxAspectRatioActivity"
-                  android:resizeableActivity="false">
-            <meta-data android:name="android.max_aspect" android:value="1.0" />
-            <intent-filter>
-                <action android:name="android.intent.action.MAIN" />
-                <category android:name="android.intent.category.FRAMEWORK_INSTRUMENTATION_TEST" />
-            </intent-filter>
-        </activity>
-
-        <activity android:name="android.app.stubs.MaxAspectRatioResizeableActivity"
-                  android:label="MaxAspectRatioResizeableActivity"
-                  android:resizeableActivity="true"
-                  android:maxAspectRatio="1.0">
-            <intent-filter>
-                <action android:name="android.intent.action.MAIN" />
-                <category android:name="android.intent.category.FRAMEWORK_INSTRUMENTATION_TEST" />
-            </intent-filter>
-        </activity>
-
-        <activity android:name="android.app.stubs.MaxAspectRatioUnsetActivity"
-                  android:label="MaxAspectRatioUnsetActivity"
-                  android:resizeableActivity="false">
-            <intent-filter>
-                <action android:name="android.intent.action.MAIN" />
-                <category android:name="android.intent.category.FRAMEWORK_INSTRUMENTATION_TEST" />
-            </intent-filter>
-        </activity>
-
         <service
             android:name="android.app.stubs.LiveWallpaper"
             android:icon="@drawable/robot"
diff --git a/tests/app/app/src/android/app/stubs/MaxAspectRatioActivity.java b/tests/app/app/src/android/app/stubs/MaxAspectRatioActivity.java
deleted file mode 100644
index 4a04861..0000000
--- a/tests/app/app/src/android/app/stubs/MaxAspectRatioActivity.java
+++ /dev/null
@@ -1,23 +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.app.stubs;
-
-import android.app.Activity;
-
-public class MaxAspectRatioActivity extends Activity {
-
-}
diff --git a/tests/app/app/src/android/app/stubs/MaxAspectRatioResizeableActivity.java b/tests/app/app/src/android/app/stubs/MaxAspectRatioResizeableActivity.java
deleted file mode 100644
index da1c605..0000000
--- a/tests/app/app/src/android/app/stubs/MaxAspectRatioResizeableActivity.java
+++ /dev/null
@@ -1,23 +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.app.stubs;
-
-import android.app.Activity;
-
-public class MaxAspectRatioResizeableActivity extends Activity {
-
-}
diff --git a/tests/app/app/src/android/app/stubs/MaxAspectRatioUnsetActivity.java b/tests/app/app/src/android/app/stubs/MaxAspectRatioUnsetActivity.java
deleted file mode 100644
index 06e4994..0000000
--- a/tests/app/app/src/android/app/stubs/MaxAspectRatioUnsetActivity.java
+++ /dev/null
@@ -1,23 +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.app.stubs;
-
-import android.app.Activity;
-
-public class MaxAspectRatioUnsetActivity extends Activity {
-
-}
diff --git a/tests/app/app/src/android/app/stubs/MetaDataMaxAspectRatioActivity.java b/tests/app/app/src/android/app/stubs/MetaDataMaxAspectRatioActivity.java
deleted file mode 100644
index 2e989de..0000000
--- a/tests/app/app/src/android/app/stubs/MetaDataMaxAspectRatioActivity.java
+++ /dev/null
@@ -1,23 +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.app.stubs;
-
-import android.app.Activity;
-
-public class MetaDataMaxAspectRatioActivity extends Activity {
-
-}
diff --git a/tests/app/app/src/android/app/stubs/NullService.java b/tests/app/app/src/android/app/stubs/NullService.java
new file mode 100644
index 0000000..6a1c618
--- /dev/null
+++ b/tests/app/app/src/android/app/stubs/NullService.java
@@ -0,0 +1,36 @@
+/*
+ * 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.app.stubs;
+
+import android.app.Service;
+import android.content.Intent;
+import android.os.IBinder;
+import android.util.Log;
+
+/**
+ * A Service that always returns null from onBind(Intent).
+ */
+public class NullService extends Service {
+    private static final String TAG = "NullService";
+
+    @Override
+    public IBinder onBind(Intent intent) {
+        Log.i(TAG, "onBind() returning null");
+        return null;
+    }
+
+}
diff --git a/tests/app/app2/Android.mk b/tests/app/app2/Android.mk
index 2689c30..304f79e 100644
--- a/tests/app/app2/Android.mk
+++ b/tests/app/app2/Android.mk
@@ -22,8 +22,7 @@
     compatibility-device-util \
 
 LOCAL_SRC_FILES := \
-    ../app/src/android/app/stubs/LocalService.java \
-    $(call all-java-files-under, src) \
+    ../app/src/android/app/stubs/LocalService.java
 
 LOCAL_SDK_VERSION := current
 
diff --git a/tests/app/app2/AndroidManifest.xml b/tests/app/app2/AndroidManifest.xml
index 8c30996..0926251 100644
--- a/tests/app/app2/AndroidManifest.xml
+++ b/tests/app/app2/AndroidManifest.xml
@@ -19,6 +19,8 @@
     <uses-permission android:name="android.permission.SYSTEM_ALERT_WINDOW" />
 
     <application>
+        <uses-library android:name="android.test.runner" />
+
         <service android:name="android.app.stubs.LocalService"
                  android:exported="true"/>
         <service android:name=".AlertWindowService"
diff --git a/tests/app/app2/src/com/android/app2/AlertWindowService.java b/tests/app/app2/src/com/android/app2/AlertWindowService.java
deleted file mode 100644
index a514e8a..0000000
--- a/tests/app/app2/src/com/android/app2/AlertWindowService.java
+++ /dev/null
@@ -1,145 +0,0 @@
-/*
- * Copyright (C) 2017 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License
- */
-
-package com.android.app2;
-
-import android.app.Service;
-import android.content.Intent;
-import android.graphics.Point;
-import android.os.Handler;
-import android.os.IBinder;
-import android.os.Message;
-import android.os.Messenger;
-import android.os.RemoteException;
-import android.util.Log;
-import android.view.View;
-import android.view.WindowManager;
-import android.widget.TextView;
-
-import java.util.LinkedList;
-
-import static android.graphics.Color.BLUE;
-import static android.view.Gravity.LEFT;
-import static android.view.Gravity.TOP;
-import static android.view.WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE;
-import static android.view.WindowManager.LayoutParams.FLAG_NOT_TOUCHABLE;
-import static android.view.WindowManager.LayoutParams.FLAG_WATCH_OUTSIDE_TOUCH;
-import static android.view.WindowManager.LayoutParams.TYPE_APPLICATION_OVERLAY;
-
-/** Service for creating and managing alert windows. */
-public class AlertWindowService extends Service {
-
-    private static final String TAG = "AlertWindowService";
-    private static final boolean DEBUG = false;
-
-    public static final int MSG_ADD_ALERT_WINDOW = 1;
-    public static final int MSG_REMOVE_ALERT_WINDOW = 2;
-    public static final int MSG_REMOVE_ALL_ALERT_WINDOWS = 3;
-
-    public static String NOTIFICATION_MESSENGER_EXTRA =
-            "com.android.app2.AlertWindowService.NOTIFICATION_MESSENGER_EXTRA";
-    public static final int MSG_ON_ALERT_WINDOW_ADDED = 4;
-    public static final int MSG_ON_ALERT_WINDOW_REMOVED = 5;
-
-    private LinkedList<View> mAlertWindows = new LinkedList<>();
-
-    private Messenger mOutgoingMessenger = null;
-    private final Messenger mIncomingMessenger = new Messenger(new IncomingHandler());
-
-    private class IncomingHandler extends Handler {
-        @Override
-        public void handleMessage(Message msg) {
-            switch (msg.what) {
-                case MSG_ADD_ALERT_WINDOW:
-                    addAlertWindow();
-                    break;
-                case MSG_REMOVE_ALERT_WINDOW:
-                    removeAlertWindow();
-                    break;
-                case MSG_REMOVE_ALL_ALERT_WINDOWS:
-                    removeAllAlertWindows();
-                    break;
-                default:
-                    super.handleMessage(msg);
-            }
-        }
-    }
-
-    private void addAlertWindow() {
-        final Point size = new Point();
-        final WindowManager wm = getSystemService(WindowManager.class);
-        wm.getDefaultDisplay().getSize(size);
-
-        final WindowManager.LayoutParams params = new WindowManager.LayoutParams(
-                TYPE_APPLICATION_OVERLAY,
-                FLAG_NOT_FOCUSABLE | FLAG_WATCH_OUTSIDE_TOUCH | FLAG_NOT_TOUCHABLE);
-        params.width = size.x / 3;
-        params.height = size.y / 3;
-        params.gravity = TOP | LEFT;
-
-        final TextView view = new TextView(this);
-        view.setText("AlertWindowService" + mAlertWindows.size());
-        view.setBackgroundColor(BLUE);
-        wm.addView(view, params);
-        mAlertWindows.add(view);
-
-        if (DEBUG) Log.e(TAG, "addAlertWindow " + mAlertWindows.size());
-        if (mOutgoingMessenger != null) {
-            try {
-                mOutgoingMessenger.send(Message.obtain(null, MSG_ON_ALERT_WINDOW_ADDED));
-            } catch (RemoteException e) {
-
-            }
-        }
-    }
-
-    private void removeAlertWindow() {
-        if (mAlertWindows.size() == 0) {
-            return;
-        }
-        final WindowManager wm = getSystemService(WindowManager.class);
-        wm.removeView(mAlertWindows.pop());
-
-        if (DEBUG) Log.e(TAG, "removeAlertWindow " + mAlertWindows.size());
-        if (mOutgoingMessenger != null) {
-            try {
-                mOutgoingMessenger.send(Message.obtain(null, MSG_ON_ALERT_WINDOW_REMOVED));
-            } catch (RemoteException e) {
-
-            }
-        }
-    }
-
-    private void removeAllAlertWindows() {
-        while (mAlertWindows.size() > 0) {
-            removeAlertWindow();
-        }
-    }
-
-    @Override
-    public IBinder onBind(Intent intent) {
-        if (DEBUG) Log.e(TAG, "onBind");
-        mOutgoingMessenger = intent.getParcelableExtra(NOTIFICATION_MESSENGER_EXTRA);
-        return mIncomingMessenger.getBinder();
-    }
-
-    @Override
-    public boolean onUnbind(Intent intent) {
-        if (DEBUG) Log.e(TAG, "onUnbind");
-        removeAllAlertWindows();
-        return super.onUnbind(intent);
-    }
-}
diff --git a/tests/app/appSdk25/Android.mk b/tests/app/appSdk25/Android.mk
deleted file mode 100644
index 36c6d07..0000000
--- a/tests/app/appSdk25/Android.mk
+++ /dev/null
@@ -1,35 +0,0 @@
-# Copyright (C) 2017 The Android Open Source Project
-#
-# Licensed under the Apache License, Version 2.0 (the "License");
-# you may not use this file except in compliance with the License.
-# You may obtain a copy of the License at
-#
-#      http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing, software
-# distributed under the License is distributed on an "AS IS" BASIS,
-# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-# See the License for the specific language governing permissions and
-# limitations under the License.
-
-LOCAL_PATH:= $(call my-dir)
-
-include $(CLEAR_VARS)
-
-# Don't include this package in any target.
-LOCAL_MODULE_TAGS := tests
-
-LOCAL_STATIC_JAVA_LIBRARIES := \
-    compatibility-device-util \
-
-LOCAL_SRC_FILES := \
-    $(call all-java-files-under, src) \
-
-LOCAL_SDK_VERSION := 25
-
-# Tag this module as a cts test artifact
-LOCAL_COMPATIBILITY_SUITE := cts vts general-tests
-
-LOCAL_PACKAGE_NAME := CtsAppTestSdk25
-
-include $(BUILD_CTS_SUPPORT_PACKAGE)
diff --git a/tests/app/appSdk25/AndroidManifest.xml b/tests/app/appSdk25/AndroidManifest.xml
deleted file mode 100755
index f564f51..0000000
--- a/tests/app/appSdk25/AndroidManifest.xml
+++ /dev/null
@@ -1,33 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
-  ~ Copyright (C) 2017 The Android Open Source Project
-  ~
-  ~ Licensed under the Apache License, Version 2.0 (the "License");
-  ~ you may not use this file except in compliance with the License.
-  ~ You may obtain a copy of the License at
-  ~
-  ~      http://www.apache.org/licenses/LICENSE-2.0
-  ~
-  ~ Unless required by applicable law or agreed to in writing, software
-  ~ distributed under the License is distributed on an "AS IS" BASIS,
-  ~ WITHOUT 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.appSdk25">
-
-    <application android:label="CtsAppTestSdk25">
-        <activity android:name=".Sdk25MaxAspectRatioActivity"
-                  android:label="Sdk25MaxAspectRatioActivity"
-                  android:resizeableActivity="false"
-                  android:exported="true">
-            <intent-filter>
-                <action android:name="android.intent.action.MAIN" />
-                <category android:name="android.intent.category.FRAMEWORK_INSTRUMENTATION_TEST" />
-            </intent-filter>
-        </activity>
-    </application>
-</manifest>
-
diff --git a/tests/app/appSdk25/src/com/android/appSdk25/Sdk25MaxAspectRatioActivity.java b/tests/app/appSdk25/src/com/android/appSdk25/Sdk25MaxAspectRatioActivity.java
deleted file mode 100644
index 7fa25e0..0000000
--- a/tests/app/appSdk25/src/com/android/appSdk25/Sdk25MaxAspectRatioActivity.java
+++ /dev/null
@@ -1,23 +0,0 @@
-/*
- * Copyright (C) 2017 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License
- */
-
-package com.android.appSdk25;
-
-import android.app.Activity;
-
-public class Sdk25MaxAspectRatioActivity extends Activity {
-
-}
diff --git a/tests/app/src/android/app/cts/ActivityKeyboardShortcutsTest.java b/tests/app/src/android/app/cts/ActivityKeyboardShortcutsTest.java
index 601dafd..4192883 100644
--- a/tests/app/src/android/app/cts/ActivityKeyboardShortcutsTest.java
+++ b/tests/app/src/android/app/cts/ActivityKeyboardShortcutsTest.java
@@ -56,15 +56,15 @@
             return;
         }
         // Open activity's options menu
-        mActivity.openOptionsMenu();
+        getInstrumentation().runOnMainSync(() -> mActivity.openOptionsMenu());
         mActivity.waitForMenuToBeOpen();
 
         // Request keyboard shortcuts
-        mActivity.requestShowKeyboardShortcuts();
+        getInstrumentation().runOnMainSync(() -> mActivity.requestShowKeyboardShortcuts());
         mActivity.waitForKeyboardShortcutsToBeRequested();
 
         // Close the shortcuts helper
-        mActivity.dismissKeyboardShortcutsHelper();
+        getInstrumentation().runOnMainSync(() -> mActivity.dismissKeyboardShortcutsHelper());
 
         // THEN the activity's onProvideKeyboardShortcuts should have been
         // triggered to get app specific shortcuts
diff --git a/tests/app/src/android/app/cts/ActivityManagerProcessStateTest.java b/tests/app/src/android/app/cts/ActivityManagerProcessStateTest.java
index 7c1ed0d..2e6867d 100644
--- a/tests/app/src/android/app/cts/ActivityManagerProcessStateTest.java
+++ b/tests/app/src/android/app/cts/ActivityManagerProcessStateTest.java
@@ -17,6 +17,7 @@
 package android.app.cts;
 
 import android.Manifest;
+import android.accessibilityservice.AccessibilityService;
 import android.app.Activity;
 import android.app.ActivityManager;
 import android.app.Instrumentation;
@@ -27,6 +28,7 @@
 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.content.ComponentName;
 import android.content.Context;
 import android.content.Intent;
 import android.content.pm.ApplicationInfo;
@@ -34,11 +36,20 @@
 import android.os.Parcel;
 import android.os.PowerManager;
 import android.os.RemoteException;
+import android.os.SystemClock;
+import android.server.am.WindowManagerState;
+import android.support.test.uiautomator.BySelector;
+import android.support.test.uiautomator.UiDevice;
+import android.support.test.uiautomator.UiSelector;
 import android.test.InstrumentationTestCase;
+import android.util.Log;
+import android.view.accessibility.AccessibilityEvent;
 
 import com.android.compatibility.common.util.SystemUtil;
 
 public class ActivityManagerProcessStateTest extends InstrumentationTestCase {
+    private static final String TAG = ActivityManagerProcessStateTest.class.getName();
+
     private static final String STUB_PACKAGE_NAME = "android.app.stubs";
     private static final int WAIT_TIME = 2000;
     // A secondary test activity from another APK.
@@ -50,6 +61,12 @@
     public static String ACTION_SIMPLE_ACTIVITY_START_SERVICE_RESULT =
             "com.android.cts.launcherapps.simpleapp.SimpleActivityStartService.RESULT";
 
+    // APKs for testing heavy weight app interactions.
+    static final String CANT_SAVE_STATE_1_PACKAGE_NAME = "com.android.test.cantsavestate1";
+    static final String CANT_SAVE_STATE_2_PACKAGE_NAME = "com.android.test.cantsavestate2";
+
+    private static final int TEMP_WHITELIST_DURATION_MS = 2000;
+
     private Context mContext;
     private Instrumentation mInstrumentation;
     private Intent mServiceIntent;
@@ -70,11 +87,75 @@
         mAllProcesses[1] = mService2Intent;
         mContext.stopService(mServiceIntent);
         mContext.stopService(mService2Intent);
+        removeTestAppFromWhitelists();
     }
 
-    @Override
-    protected void tearDown() throws Exception {
-        super.tearDown();
+    private void removeTestAppFromWhitelists() throws Exception {
+        executeShellCmd("cmd deviceidle whitelist -" + SIMPLE_PACKAGE_NAME);
+        executeShellCmd("cmd deviceidle tempwhitelist -r " + SIMPLE_PACKAGE_NAME);
+    }
+
+    private String executeShellCmd(String cmd) throws Exception {
+        final String result = SystemUtil.runShellCommand(getInstrumentation(), cmd);
+        Log.d(TAG, String.format("Output for '%s': %s", cmd, result));
+        return result;
+    }
+
+    private boolean isScreenInteractive() {
+        final PowerManager powerManager =
+                (PowerManager) mContext.getSystemService(Context.POWER_SERVICE);
+        return powerManager.isInteractive();
+    }
+
+    private boolean isKeyguardLocked() {
+        final KeyguardManager keyguardManager =
+                (KeyguardManager) mContext.getSystemService(Context.KEYGUARD_SERVICE);
+        return keyguardManager.isKeyguardLocked();
+    }
+
+    private void waitForAppFocus(String waitForApp, long waitTime) {
+        long waitUntil = SystemClock.elapsedRealtime() + waitTime;
+        while (true) {
+            WindowManagerState wms = new WindowManagerState();
+            wms.computeState();
+            String appName = wms.getFocusedApp();
+            if (appName != null) {
+                ComponentName comp = ComponentName.unflattenFromString(appName);
+                if (waitForApp.equals(comp.getPackageName())) {
+                    break;
+                }
+            }
+            if (SystemClock.elapsedRealtime() > waitUntil) {
+                throw new IllegalStateException("Timed out waiting for focus on app "
+                        + waitForApp + ", last was " + appName);
+            }
+            Log.i(TAG, "Waiting for app focus, current: " + appName);
+            try {
+                Thread.sleep(100);
+            } catch (InterruptedException e) {
+            }
+        };
+    }
+
+    private void startActivityAndWaitForShow(final Intent intent) throws Exception {
+        getInstrumentation().getUiAutomation().executeAndWaitForEvent(
+                () -> {
+                    try {
+                        mContext.startActivity(intent);
+                    } catch (Exception e) {
+                        fail("Cannot start activity: " + intent);
+                    }
+                }, (AccessibilityEvent event) -> event.getEventType()
+                        == AccessibilityEvent.TYPE_WINDOW_STATE_CHANGED
+                , WAIT_TIME);
+    }
+
+    private void maybeClick(UiDevice device, UiSelector sel) {
+        try { device.findObject(sel).click(); } catch (Throwable ignored) { }
+    }
+
+    private void maybeClick(UiDevice device, BySelector sel) {
+        try { device.findObject(sel).click(); } catch (Throwable ignored) { }
     }
 
     /**
@@ -82,14 +163,17 @@
      */
     public void testUidImportanceListener() throws Exception {
         final Parcel data = Parcel.obtain();
-        ServiceConnectionHandler conn = new ServiceConnectionHandler(mContext, mServiceIntent);
-        ServiceConnectionHandler conn2 = new ServiceConnectionHandler(mContext, mService2Intent);
+        ServiceConnectionHandler conn = new ServiceConnectionHandler(mContext, mServiceIntent,
+                WAIT_TIME);
+        ServiceConnectionHandler conn2 = new ServiceConnectionHandler(mContext, mService2Intent,
+                WAIT_TIME);
 
         ActivityManager am = mContext.getSystemService(ActivityManager.class);
 
         ApplicationInfo appInfo = mContext.getPackageManager().getApplicationInfo(
                 SIMPLE_PACKAGE_NAME, 0);
-        UidImportanceListener uidForegroundListener = new UidImportanceListener(appInfo.uid);
+        UidImportanceListener uidForegroundListener = new UidImportanceListener(appInfo.uid,
+                WAIT_TIME);
 
         String cmd = "pm revoke " + STUB_PACKAGE_NAME + " "
                 + Manifest.permission.PACKAGE_USAGE_STATS;
@@ -119,20 +203,21 @@
         am.addOnUidImportanceListener(uidForegroundListener,
                 ActivityManager.RunningAppProcessInfo.IMPORTANCE_SERVICE);
 
-        UidImportanceListener uidGoneListener = new UidImportanceListener(appInfo.uid);
+        UidImportanceListener uidGoneListener = new UidImportanceListener(appInfo.uid, WAIT_TIME);
         am.addOnUidImportanceListener(uidGoneListener,
                 ActivityManager.RunningAppProcessInfo.IMPORTANCE_CACHED);
 
-        WatchUidRunner uidWatcher = new WatchUidRunner(getInstrumentation(), appInfo.uid);
+        WatchUidRunner uidWatcher = new WatchUidRunner(getInstrumentation(), appInfo.uid,
+                WAIT_TIME);
 
         try {
             // First kill the processes to start out in a stable state.
-            conn.bind(WAIT_TIME);
-            conn2.bind(WAIT_TIME);
+            conn.bind();
+            conn2.bind();
             IBinder service1 = conn.getServiceIBinder();
             IBinder service2 = conn2.getServiceIBinder();
-            conn.unbind(WAIT_TIME);
-            conn2.unbind(WAIT_TIME);
+            conn.unbind();
+            conn2.unbind();
             try {
                 service1.transact(IBinder.FIRST_CALL_TRANSACTION, data, null, 0);
             } catch (RemoteException e) {
@@ -145,38 +230,38 @@
 
             // Wait for uid's processes to go away.
             uidGoneListener.waitForValue(ActivityManager.RunningAppProcessInfo.IMPORTANCE_GONE,
-                    ActivityManager.RunningAppProcessInfo.IMPORTANCE_GONE, WAIT_TIME);
+                    ActivityManager.RunningAppProcessInfo.IMPORTANCE_GONE);
             assertEquals(ActivityManager.RunningAppProcessInfo.IMPORTANCE_GONE,
                     am.getPackageImportance(SIMPLE_PACKAGE_NAME));
 
             // And wait for the uid report to be gone.
-            uidWatcher.waitFor(WatchUidRunner.CMD_GONE, null, WAIT_TIME);
+            uidWatcher.waitFor(WatchUidRunner.CMD_GONE, null);
 
             // Now bind and see if we get told about the uid coming in to the foreground.
-            conn.bind(WAIT_TIME);
+            conn.bind();
             uidForegroundListener.waitForValue(ActivityManager.RunningAppProcessInfo.IMPORTANCE_FOREGROUND,
-                    ActivityManager.RunningAppProcessInfo.IMPORTANCE_VISIBLE, WAIT_TIME);
+                    ActivityManager.RunningAppProcessInfo.IMPORTANCE_VISIBLE);
             assertEquals(ActivityManager.RunningAppProcessInfo.IMPORTANCE_FOREGROUND_SERVICE,
                     am.getPackageImportance(SIMPLE_PACKAGE_NAME));
 
             // Also make sure the uid state reports are as expected.  Wait for active because
             // there may be some intermediate states as the process comes up.
-            uidWatcher.waitFor(WatchUidRunner.CMD_ACTIVE, null, WAIT_TIME);
-            uidWatcher.expect(WatchUidRunner.CMD_UNCACHED, null, WAIT_TIME);
-            uidWatcher.expect(WatchUidRunner.CMD_PROCSTATE, "FGS", WAIT_TIME);
+            uidWatcher.waitFor(WatchUidRunner.CMD_ACTIVE, null);
+            uidWatcher.waitFor(WatchUidRunner.CMD_UNCACHED, null);
+            uidWatcher.expect(WatchUidRunner.CMD_PROCSTATE, WatchUidRunner.STATE_FG_SERVICE);
 
             // Pull out the service IBinder for a kludy hack...
             IBinder service = conn.getServiceIBinder();
 
             // Now unbind and see if we get told about it going to the background.
-            conn.unbind(WAIT_TIME);
+            conn.unbind();
             uidForegroundListener.waitForValue(ActivityManager.RunningAppProcessInfo.IMPORTANCE_CACHED,
-                    ActivityManager.RunningAppProcessInfo.IMPORTANCE_CACHED, WAIT_TIME);
+                    ActivityManager.RunningAppProcessInfo.IMPORTANCE_CACHED);
             assertEquals(ActivityManager.RunningAppProcessInfo.IMPORTANCE_CACHED,
                     am.getPackageImportance(SIMPLE_PACKAGE_NAME));
 
-            uidWatcher.waitFor(WatchUidRunner.CMD_CACHED, null, WAIT_TIME);
-            uidWatcher.expect(WatchUidRunner.CMD_PROCSTATE, "CEM", WAIT_TIME);
+            uidWatcher.waitFor(WatchUidRunner.CMD_CACHED, null);
+            uidWatcher.expect(WatchUidRunner.CMD_PROCSTATE, WatchUidRunner.STATE_CACHED_EMPTY);
 
             // Now kill the process and see if we are told about it being gone.
             try {
@@ -186,74 +271,74 @@
             }
 
             uidGoneListener.waitForValue(ActivityManager.RunningAppProcessInfo.IMPORTANCE_GONE,
-                    ActivityManager.RunningAppProcessInfo.IMPORTANCE_GONE, WAIT_TIME);
+                    ActivityManager.RunningAppProcessInfo.IMPORTANCE_GONE);
             assertEquals(ActivityManager.RunningAppProcessInfo.IMPORTANCE_GONE,
                     am.getPackageImportance(SIMPLE_PACKAGE_NAME));
 
-            uidWatcher.expect(WatchUidRunner.CMD_IDLE, null, WAIT_TIME);
-            uidWatcher.expect(WatchUidRunner.CMD_GONE, null, WAIT_TIME);
+            uidWatcher.expect(WatchUidRunner.CMD_IDLE, null);
+            uidWatcher.expect(WatchUidRunner.CMD_GONE, null);
 
             // Now we are going to try different combinations of binding to two processes to
             // see if they are correctly combined together for the app.
 
             // Bring up both services.
-            conn.bind(WAIT_TIME);
-            conn2.bind(WAIT_TIME);
+            conn.bind();
+            conn2.bind();
             uidForegroundListener.waitForValue(ActivityManager.RunningAppProcessInfo.IMPORTANCE_FOREGROUND,
-                    ActivityManager.RunningAppProcessInfo.IMPORTANCE_VISIBLE, WAIT_TIME);
+                    ActivityManager.RunningAppProcessInfo.IMPORTANCE_VISIBLE);
             assertEquals(ActivityManager.RunningAppProcessInfo.IMPORTANCE_FOREGROUND_SERVICE,
                     am.getPackageImportance(SIMPLE_PACKAGE_NAME));
 
             // Also make sure the uid state reports are as expected.
-            uidWatcher.waitFor(WatchUidRunner.CMD_ACTIVE, null, WAIT_TIME);
-            uidWatcher.expect(WatchUidRunner.CMD_UNCACHED, null, WAIT_TIME);
-            uidWatcher.expect(WatchUidRunner.CMD_PROCSTATE, "FGS", WAIT_TIME);
+            uidWatcher.waitFor(WatchUidRunner.CMD_ACTIVE, null);
+            uidWatcher.waitFor(WatchUidRunner.CMD_UNCACHED, null);
+            uidWatcher.expect(WatchUidRunner.CMD_PROCSTATE, WatchUidRunner.STATE_FG_SERVICE);
 
             // Bring down one service, app state should remain foreground.
-            conn2.unbind(WAIT_TIME);
+            conn2.unbind();
             assertEquals(ActivityManager.RunningAppProcessInfo.IMPORTANCE_FOREGROUND_SERVICE,
                     am.getPackageImportance(SIMPLE_PACKAGE_NAME));
 
             // Bring down other service, app state should now be cached.  (If the processes both
             // actually get killed immediately, this is also not a correctly behaving system.)
-            conn.unbind(WAIT_TIME);
+            conn.unbind();
             uidGoneListener.waitForValue(ActivityManager.RunningAppProcessInfo.IMPORTANCE_CACHED,
-                    ActivityManager.RunningAppProcessInfo.IMPORTANCE_CACHED, WAIT_TIME);
+                    ActivityManager.RunningAppProcessInfo.IMPORTANCE_CACHED);
             assertEquals(ActivityManager.RunningAppProcessInfo.IMPORTANCE_CACHED,
                     am.getPackageImportance(SIMPLE_PACKAGE_NAME));
 
-            uidWatcher.waitFor(WatchUidRunner.CMD_CACHED, null, WAIT_TIME);
-            uidWatcher.expect(WatchUidRunner.CMD_PROCSTATE, "CEM", WAIT_TIME);
+            uidWatcher.waitFor(WatchUidRunner.CMD_CACHED, null);
+            uidWatcher.expect(WatchUidRunner.CMD_PROCSTATE, WatchUidRunner.STATE_CACHED_EMPTY);
 
             // Bring up one service, this should be sufficient to become foreground.
-            conn2.bind(WAIT_TIME);
+            conn2.bind();
             uidForegroundListener.waitForValue(ActivityManager.RunningAppProcessInfo.IMPORTANCE_FOREGROUND,
-                    ActivityManager.RunningAppProcessInfo.IMPORTANCE_VISIBLE, WAIT_TIME);
+                    ActivityManager.RunningAppProcessInfo.IMPORTANCE_VISIBLE);
             assertEquals(ActivityManager.RunningAppProcessInfo.IMPORTANCE_FOREGROUND_SERVICE,
                     am.getPackageImportance(SIMPLE_PACKAGE_NAME));
 
-            uidWatcher.expect(WatchUidRunner.CMD_UNCACHED, null, WAIT_TIME);
-            uidWatcher.expect(WatchUidRunner.CMD_PROCSTATE, "FGS", WAIT_TIME);
+            uidWatcher.waitFor(WatchUidRunner.CMD_UNCACHED, null);
+            uidWatcher.expect(WatchUidRunner.CMD_PROCSTATE, WatchUidRunner.STATE_FG_SERVICE);
 
             // Bring up other service, should remain foreground.
-            conn.bind(WAIT_TIME);
+            conn.bind();
             assertEquals(ActivityManager.RunningAppProcessInfo.IMPORTANCE_FOREGROUND_SERVICE,
                     am.getPackageImportance(SIMPLE_PACKAGE_NAME));
 
             // Bring down one service, should remain foreground.
-            conn.unbind(WAIT_TIME);
+            conn.unbind();
             assertEquals(ActivityManager.RunningAppProcessInfo.IMPORTANCE_FOREGROUND_SERVICE,
                     am.getPackageImportance(SIMPLE_PACKAGE_NAME));
 
             // And bringing down other service should put us back to cached.
-            conn2.unbind(WAIT_TIME);
+            conn2.unbind();
             uidGoneListener.waitForValue(ActivityManager.RunningAppProcessInfo.IMPORTANCE_CACHED,
-                    ActivityManager.RunningAppProcessInfo.IMPORTANCE_CACHED, WAIT_TIME);
+                    ActivityManager.RunningAppProcessInfo.IMPORTANCE_CACHED);
             assertEquals(ActivityManager.RunningAppProcessInfo.IMPORTANCE_CACHED,
                     am.getPackageImportance(SIMPLE_PACKAGE_NAME));
 
-            uidWatcher.waitFor(WatchUidRunner.CMD_CACHED, null, WAIT_TIME);
-            uidWatcher.expect(WatchUidRunner.CMD_PROCSTATE, "CEM", WAIT_TIME);
+            uidWatcher.waitFor(WatchUidRunner.CMD_CACHED, null);
+            uidWatcher.expect(WatchUidRunner.CMD_PROCSTATE, WatchUidRunner.STATE_CACHED_EMPTY);
         } finally {
             data.recycle();
 
@@ -273,7 +358,8 @@
         Intent serviceIntent = new Intent();
         serviceIntent.setClassName(SIMPLE_PACKAGE_NAME,
                 SIMPLE_PACKAGE_NAME + SIMPLE_SERVICE);
-        ServiceConnectionHandler conn = new ServiceConnectionHandler(mContext, serviceIntent);
+        ServiceConnectionHandler conn = new ServiceConnectionHandler(mContext, serviceIntent,
+                WAIT_TIME);
 
         ActivityManager am = mContext.getSystemService(ActivityManager.class);
 
@@ -290,20 +376,22 @@
         ApplicationInfo appInfo = mContext.getPackageManager().getApplicationInfo(
                 SIMPLE_PACKAGE_NAME, 0);
 
-        UidImportanceListener uidForegroundListener = new UidImportanceListener(appInfo.uid);
+        UidImportanceListener uidForegroundListener = new UidImportanceListener(appInfo.uid,
+                WAIT_TIME);
         am.addOnUidImportanceListener(uidForegroundListener,
                 ActivityManager.RunningAppProcessInfo.IMPORTANCE_SERVICE);
-        UidImportanceListener uidGoneListener = new UidImportanceListener(appInfo.uid);
+        UidImportanceListener uidGoneListener = new UidImportanceListener(appInfo.uid, WAIT_TIME);
         am.addOnUidImportanceListener(uidGoneListener,
                 ActivityManager.RunningAppProcessInfo.IMPORTANCE_EMPTY);
 
-        WatchUidRunner uidWatcher = new WatchUidRunner(getInstrumentation(), appInfo.uid);
+        WatchUidRunner uidWatcher = new WatchUidRunner(getInstrumentation(), appInfo.uid,
+                WAIT_TIME);
 
         // First kill the process to start out in a stable state.
         mContext.stopService(serviceIntent);
-        conn.bind(WAIT_TIME);
+        conn.bind();
         IBinder service = conn.getServiceIBinder();
-        conn.unbind(WAIT_TIME);
+        conn.unbind();
         try {
             service.transact(IBinder.FIRST_CALL_TRANSACTION, data, null, 0);
         } catch (RemoteException e) {
@@ -312,19 +400,19 @@
 
         // Wait for uid's process to go away.
         uidGoneListener.waitForValue(ActivityManager.RunningAppProcessInfo.IMPORTANCE_GONE,
-                ActivityManager.RunningAppProcessInfo.IMPORTANCE_GONE, WAIT_TIME);
+                ActivityManager.RunningAppProcessInfo.IMPORTANCE_GONE);
         assertEquals(ActivityManager.RunningAppProcessInfo.IMPORTANCE_GONE,
                 am.getPackageImportance(SIMPLE_PACKAGE_NAME));
 
         // And wait for the uid report to be gone.
-        uidWatcher.waitFor(WatchUidRunner.CMD_GONE, null, WAIT_TIME);
+        uidWatcher.waitFor(WatchUidRunner.CMD_GONE, null);
 
         cmd = "appops set " + SIMPLE_PACKAGE_NAME + " RUN_IN_BACKGROUND deny";
         result = SystemUtil.runShellCommand(getInstrumentation(), cmd);
 
         // This is a side-effect of the app op command.
-        uidWatcher.expect(WatchUidRunner.CMD_IDLE, null, WAIT_TIME);
-        uidWatcher.expect(WatchUidRunner.CMD_PROCSTATE, "NONE", WAIT_TIME);
+        uidWatcher.expect(WatchUidRunner.CMD_IDLE, null);
+        uidWatcher.expect(WatchUidRunner.CMD_PROCSTATE, "NONE");
 
         // We don't want to wait for the uid to actually go idle, we can force it now.
         cmd = "am make-uid-idle " + SIMPLE_PACKAGE_NAME;
@@ -350,37 +438,38 @@
             }
 
             // Put app on temporary whitelist to see if this allows the service start.
-            cmd = "cmd deviceidle tempwhitelist -d 2000 " + SIMPLE_PACKAGE_NAME;
+            cmd = String.format("cmd deviceidle tempwhitelist -d %d %s",
+                    TEMP_WHITELIST_DURATION_MS, SIMPLE_PACKAGE_NAME);
             result = SystemUtil.runShellCommand(getInstrumentation(), cmd);
 
             // Try starting the service now that the app is whitelisted...  should work!
             mContext.startService(serviceIntent);
-            conn.waitForConnect(WAIT_TIME);
+            conn.waitForConnect();
 
             // Also make sure the uid state reports are as expected.
-            uidWatcher.waitFor(WatchUidRunner.CMD_ACTIVE, null, WAIT_TIME);
-            uidWatcher.expect(WatchUidRunner.CMD_UNCACHED, null, WAIT_TIME);
-            uidWatcher.expect(WatchUidRunner.CMD_PROCSTATE, "SVC", WAIT_TIME);
+            uidWatcher.waitFor(WatchUidRunner.CMD_ACTIVE, null);
+            uidWatcher.waitFor(WatchUidRunner.CMD_UNCACHED, null);
+            uidWatcher.expect(WatchUidRunner.CMD_PROCSTATE, WatchUidRunner.STATE_SERVICE);
 
             // Good, now stop the service and give enough time to get off the temp whitelist.
             mContext.stopService(serviceIntent);
-            conn.waitForDisconnect(WAIT_TIME);
+            conn.waitForDisconnect();
 
-            uidWatcher.expect(WatchUidRunner.CMD_CACHED, null, WAIT_TIME);
-            uidWatcher.expect(WatchUidRunner.CMD_PROCSTATE, "CEM", WAIT_TIME);
+            uidWatcher.expect(WatchUidRunner.CMD_CACHED, null);
+            uidWatcher.expect(WatchUidRunner.CMD_PROCSTATE, WatchUidRunner.STATE_CACHED_EMPTY);
 
-            Thread.sleep(3000);
+            executeShellCmd("cmd deviceidle tempwhitelist -r " + SIMPLE_PACKAGE_NAME);
 
             // Going off the temp whitelist causes a spurious proc state report...  that's
             // not ideal, but okay.
-            uidWatcher.expect(WatchUidRunner.CMD_PROCSTATE, "CEM", WAIT_TIME);
+            uidWatcher.expect(WatchUidRunner.CMD_PROCSTATE, WatchUidRunner.STATE_CACHED_EMPTY);
 
             // We don't want to wait for the uid to actually go idle, we can force it now.
             cmd = "am make-uid-idle " + SIMPLE_PACKAGE_NAME;
             result = SystemUtil.runShellCommand(getInstrumentation(), cmd);
 
-            uidWatcher.expect(WatchUidRunner.CMD_IDLE, null, WAIT_TIME);
-            uidWatcher.expect(WatchUidRunner.CMD_PROCSTATE, "CEM", WAIT_TIME);
+            uidWatcher.expect(WatchUidRunner.CMD_IDLE, null);
+            uidWatcher.expect(WatchUidRunner.CMD_PROCSTATE, WatchUidRunner.STATE_CACHED_EMPTY);
 
             // Now that we should be off the temp whitelist, make sure we again can't start.
             failed = false;
@@ -399,17 +488,17 @@
 
             // Try starting the service now that the app is whitelisted...  should work!
             mContext.startService(serviceIntent);
-            conn.waitForConnect(WAIT_TIME);
+            conn.waitForConnect();
 
-            uidWatcher.expect(WatchUidRunner.CMD_UNCACHED, null, WAIT_TIME);
-            uidWatcher.expect(WatchUidRunner.CMD_PROCSTATE, "SVC", WAIT_TIME);
+            uidWatcher.waitFor(WatchUidRunner.CMD_UNCACHED, null);
+            uidWatcher.expect(WatchUidRunner.CMD_PROCSTATE, WatchUidRunner.STATE_SERVICE);
 
             // Okay, bring down the service.
             mContext.stopService(serviceIntent);
-            conn.waitForDisconnect(WAIT_TIME);
+            conn.waitForDisconnect();
 
-            uidWatcher.expect(WatchUidRunner.CMD_CACHED, null, WAIT_TIME);
-            uidWatcher.expect(WatchUidRunner.CMD_PROCSTATE, "CEM", WAIT_TIME);
+            uidWatcher.expect(WatchUidRunner.CMD_CACHED, null);
+            uidWatcher.expect(WatchUidRunner.CMD_PROCSTATE, WatchUidRunner.STATE_CACHED_EMPTY);
 
         } finally {
             mContext.stopService(serviceIntent);
@@ -435,8 +524,10 @@
      */
     public void testBackgroundCheckStopsService() throws Exception {
         final Parcel data = Parcel.obtain();
-        ServiceConnectionHandler conn = new ServiceConnectionHandler(mContext, mServiceIntent);
-        ServiceConnectionHandler conn2 = new ServiceConnectionHandler(mContext, mService2Intent);
+        ServiceConnectionHandler conn = new ServiceConnectionHandler(mContext, mServiceIntent,
+                WAIT_TIME);
+        ServiceConnectionHandler conn2 = new ServiceConnectionHandler(mContext, mService2Intent,
+                WAIT_TIME);
 
         ActivityManager am = mContext.getSystemService(ActivityManager.class);
 
@@ -453,24 +544,26 @@
         ApplicationInfo appInfo = mContext.getPackageManager().getApplicationInfo(
                 SIMPLE_PACKAGE_NAME, 0);
 
-        UidImportanceListener uidServiceListener = new UidImportanceListener(appInfo.uid);
+        UidImportanceListener uidServiceListener = new UidImportanceListener(appInfo.uid,
+                WAIT_TIME);
         am.addOnUidImportanceListener(uidServiceListener,
                 ActivityManager.RunningAppProcessInfo.IMPORTANCE_SERVICE);
-        UidImportanceListener uidGoneListener = new UidImportanceListener(appInfo.uid);
+        UidImportanceListener uidGoneListener = new UidImportanceListener(appInfo.uid, WAIT_TIME);
         am.addOnUidImportanceListener(uidGoneListener,
                 ActivityManager.RunningAppProcessInfo.IMPORTANCE_CACHED);
 
-        WatchUidRunner uidWatcher = new WatchUidRunner(getInstrumentation(), appInfo.uid);
+        WatchUidRunner uidWatcher = new WatchUidRunner(getInstrumentation(), appInfo.uid,
+                WAIT_TIME);
 
         // First kill the process to start out in a stable state.
         mContext.stopService(mServiceIntent);
         mContext.stopService(mService2Intent);
-        conn.bind(WAIT_TIME);
-        conn2.bind(WAIT_TIME);
+        conn.bind();
+        conn2.bind();
         IBinder service = conn.getServiceIBinder();
         IBinder service2 = conn2.getServiceIBinder();
-        conn.unbind(WAIT_TIME);
-        conn2.unbind(WAIT_TIME);
+        conn.unbind();
+        conn2.unbind();
         try {
             service.transact(IBinder.FIRST_CALL_TRANSACTION, data, null, 0);
         } catch (RemoteException e) {
@@ -483,7 +576,7 @@
 
         // Wait for uid's process to go away.
         uidGoneListener.waitForValue(ActivityManager.RunningAppProcessInfo.IMPORTANCE_GONE,
-                ActivityManager.RunningAppProcessInfo.IMPORTANCE_GONE, WAIT_TIME);
+                ActivityManager.RunningAppProcessInfo.IMPORTANCE_GONE);
         assertEquals(ActivityManager.RunningAppProcessInfo.IMPORTANCE_GONE,
                 am.getPackageImportance(SIMPLE_PACKAGE_NAME));
 
@@ -494,8 +587,8 @@
         result = SystemUtil.runShellCommand(getInstrumentation(), cmd);
 
         // This is a side-effect of the app op command.
-        uidWatcher.expect(WatchUidRunner.CMD_IDLE, null, WAIT_TIME);
-        uidWatcher.expect(WatchUidRunner.CMD_PROCSTATE, "NONE", WAIT_TIME);
+        uidWatcher.expect(WatchUidRunner.CMD_IDLE, null);
+        uidWatcher.expect(WatchUidRunner.CMD_PROCSTATE, WatchUidRunner.STATE_NONEXISTENT);
 
         // We don't want to wait for the uid to actually go idle, we can force it now.
         cmd = "am make-uid-idle " + SIMPLE_PACKAGE_NAME;
@@ -521,43 +614,43 @@
             }
 
             // First poke the process into the foreground, so we can avoid background check.
-            conn2.bind(WAIT_TIME);
-            conn2.waitForConnect(WAIT_TIME);
+            conn2.bind();
+            conn2.waitForConnect();
 
             // Wait for process state to reflect running service.
             uidServiceListener.waitForValue(
                     ActivityManager.RunningAppProcessInfo.IMPORTANCE_FOREGROUND_SERVICE,
-                    ActivityManager.RunningAppProcessInfo.IMPORTANCE_PERCEPTIBLE, WAIT_TIME);
+                    ActivityManager.RunningAppProcessInfo.IMPORTANCE_PERCEPTIBLE);
             assertEquals(ActivityManager.RunningAppProcessInfo.IMPORTANCE_FOREGROUND_SERVICE,
                     am.getPackageImportance(SIMPLE_PACKAGE_NAME));
 
             // Also make sure the uid state reports are as expected.
-            uidWatcher.waitFor(WatchUidRunner.CMD_ACTIVE, null, WAIT_TIME);
-            uidWatcher.expect(WatchUidRunner.CMD_UNCACHED, null, WAIT_TIME);
-            uidWatcher.expect(WatchUidRunner.CMD_PROCSTATE, "FGS", WAIT_TIME);
+            uidWatcher.waitFor(WatchUidRunner.CMD_ACTIVE, null);
+            uidWatcher.waitFor(WatchUidRunner.CMD_UNCACHED, null);
+            uidWatcher.expect(WatchUidRunner.CMD_PROCSTATE, WatchUidRunner.STATE_FG_SERVICE);
 
-            conn2.unbind(WAIT_TIME);
+            conn2.unbind();
 
             // Wait for process to recover back down to being cached.
             uidServiceListener.waitForValue(ActivityManager.RunningAppProcessInfo.IMPORTANCE_CACHED,
-                    ActivityManager.RunningAppProcessInfo.IMPORTANCE_GONE, WAIT_TIME);
+                    ActivityManager.RunningAppProcessInfo.IMPORTANCE_GONE);
             assertEquals(ActivityManager.RunningAppProcessInfo.IMPORTANCE_CACHED,
                     am.getPackageImportance(SIMPLE_PACKAGE_NAME));
 
-            uidWatcher.waitFor(WatchUidRunner.CMD_CACHED, null, WAIT_TIME);
-            uidWatcher.expect(WatchUidRunner.CMD_PROCSTATE, "CEM", WAIT_TIME);
+            uidWatcher.waitFor(WatchUidRunner.CMD_CACHED, null);
+            uidWatcher.expect(WatchUidRunner.CMD_PROCSTATE, WatchUidRunner.STATE_CACHED_EMPTY);
 
             // Try starting the service now that the app is waiting to idle...  should work!
             mContext.startService(mServiceIntent);
-            conn.waitForConnect(WAIT_TIME);
+            conn.waitForConnect();
 
-            uidWatcher.expect(WatchUidRunner.CMD_UNCACHED, null, WAIT_TIME);
-            uidWatcher.expect(WatchUidRunner.CMD_PROCSTATE, "SVC", WAIT_TIME);
+            uidWatcher.waitFor(WatchUidRunner.CMD_UNCACHED, null);
+            uidWatcher.expect(WatchUidRunner.CMD_PROCSTATE, WatchUidRunner.STATE_SERVICE);
 
             // And also start the second service.
             conn2.startMonitoring();
             mContext.startService(mService2Intent);
-            conn2.waitForConnect(WAIT_TIME);
+            conn2.waitForConnect();
 
             // Force app to go idle now
             cmd = "am make-uid-idle " + SIMPLE_PACKAGE_NAME;
@@ -565,24 +658,24 @@
 
             // Wait for services to be stopped by system.
             uidServiceListener.waitForValue(ActivityManager.RunningAppProcessInfo.IMPORTANCE_CACHED,
-                    ActivityManager.RunningAppProcessInfo.IMPORTANCE_GONE, WAIT_TIME);
+                    ActivityManager.RunningAppProcessInfo.IMPORTANCE_GONE);
             assertEquals(ActivityManager.RunningAppProcessInfo.IMPORTANCE_CACHED,
                     am.getPackageImportance(SIMPLE_PACKAGE_NAME));
 
             // And service should be stopped by system, so just make sure it is disconnected.
-            conn.waitForDisconnect(WAIT_TIME);
-            conn2.waitForDisconnect(WAIT_TIME);
+            conn.waitForDisconnect();
+            conn2.waitForDisconnect();
 
-            uidWatcher.expect(WatchUidRunner.CMD_IDLE, null, WAIT_TIME);
+            uidWatcher.expect(WatchUidRunner.CMD_IDLE, null);
             // There may be a transient 'SVC' proc state here.
-            uidWatcher.waitFor(WatchUidRunner.CMD_CACHED, null, WAIT_TIME);
-            uidWatcher.expect(WatchUidRunner.CMD_PROCSTATE, "CEM", WAIT_TIME);
+            uidWatcher.waitFor(WatchUidRunner.CMD_CACHED, null);
+            uidWatcher.expect(WatchUidRunner.CMD_PROCSTATE, WatchUidRunner.STATE_CACHED_EMPTY);
 
         } finally {
             mContext.stopService(mServiceIntent);
             mContext.stopService(mService2Intent);
-            conn.cleanup(WAIT_TIME);
-            conn2.cleanup(WAIT_TIME);
+            conn.cleanup();
+            conn2.cleanup();
 
             uidWatcher.finish();
 
@@ -609,16 +702,17 @@
                 SIMPLE_PACKAGE_NAME + SIMPLE_RECEIVER_START_SERVICE);
 
         final ServiceProcessController controller = new ServiceProcessController(mContext,
-                getInstrumentation(), STUB_PACKAGE_NAME, mAllProcesses);
+                getInstrumentation(), STUB_PACKAGE_NAME, mAllProcesses, WAIT_TIME);
         final ServiceConnectionHandler conn = new ServiceConnectionHandler(mContext,
-                mServiceIntent);
+                mServiceIntent, WAIT_TIME);
+        final WatchUidRunner uidWatcher = controller.getUidWatcher();
 
         try {
             // First kill the process to start out in a stable state.
-            controller.ensureProcessGone(WAIT_TIME);
+            controller.ensureProcessGone();
 
             // Do initial setup.
-            controller.denyBackgroundOp(WAIT_TIME);
+            controller.denyBackgroundOp();
             controller.makeUidIdle();
             controller.removeFromWhitelist();
 
@@ -635,18 +729,18 @@
             }
 
             // Track the uid proc state changes from the broadcast (but not service execution)
-            controller.getUidWatcher().waitFor(WatchUidRunner.CMD_IDLE, null, WAIT_TIME);
-            controller.getUidWatcher().expect(WatchUidRunner.CMD_UNCACHED, null, WAIT_TIME);
-            controller.getUidWatcher().expect(WatchUidRunner.CMD_PROCSTATE, "RCVR", WAIT_TIME);
-            controller.getUidWatcher().expect(WatchUidRunner.CMD_CACHED, null, WAIT_TIME);
-            controller.getUidWatcher().expect(WatchUidRunner.CMD_PROCSTATE, "CEM", WAIT_TIME);
+            uidWatcher.waitFor(WatchUidRunner.CMD_IDLE, null, WAIT_TIME);
+            uidWatcher.waitFor(WatchUidRunner.CMD_UNCACHED, null, WAIT_TIME);
+            uidWatcher.expect(WatchUidRunner.CMD_PROCSTATE, WatchUidRunner.STATE_RECEIVER, WAIT_TIME);
+            uidWatcher.expect(WatchUidRunner.CMD_CACHED, null, WAIT_TIME);
+            uidWatcher.expect(WatchUidRunner.CMD_PROCSTATE, WatchUidRunner.STATE_CACHED_EMPTY, WAIT_TIME);
 
             // Put app on temporary whitelist to see if this allows the service start.
-            controller.tempWhitelist(2000);
+            controller.tempWhitelist(TEMP_WHITELIST_DURATION_MS);
 
             // Being on the whitelist means the uid is now active.
-            controller.getUidWatcher().expect(WatchUidRunner.CMD_ACTIVE, null, WAIT_TIME);
-            controller.getUidWatcher().expect(WatchUidRunner.CMD_PROCSTATE, "CEM", WAIT_TIME);
+            uidWatcher.expect(WatchUidRunner.CMD_ACTIVE, null, WAIT_TIME);
+            uidWatcher.expect(WatchUidRunner.CMD_PROCSTATE, WatchUidRunner.STATE_CACHED_EMPTY, WAIT_TIME);
 
             // Try starting the service now that the app is whitelisted...  should work!
             br.sendAndWait(mContext, broadcastIntent, Activity.RESULT_OK, null, null, WAIT_TIME);
@@ -654,34 +748,34 @@
             if (brCode != Activity.RESULT_FIRST_USER) {
                 fail("Failed starting service, result=" + brCode);
             }
-            conn.waitForConnect(WAIT_TIME);
+            conn.waitForConnect();
 
             // Also make sure the uid state reports are as expected.
-            controller.getUidWatcher().expect(WatchUidRunner.CMD_UNCACHED, null, WAIT_TIME);
+            uidWatcher.waitFor(WatchUidRunner.CMD_UNCACHED, null);
             // We are going to wait until 'SVC', because we may see an intermediate 'RCVR'
             // proc state depending on timing.
-            controller.getUidWatcher().waitFor(WatchUidRunner.CMD_PROCSTATE, "SVC", WAIT_TIME);
+            uidWatcher.waitFor(WatchUidRunner.CMD_PROCSTATE, WatchUidRunner.STATE_SERVICE);
 
             // Good, now stop the service and give enough time to get off the temp whitelist.
             mContext.stopService(mServiceIntent);
-            conn.waitForDisconnect(WAIT_TIME);
+            conn.waitForDisconnect();
 
-            controller.getUidWatcher().expect(WatchUidRunner.CMD_CACHED, null, WAIT_TIME);
-            controller.getUidWatcher().expect(WatchUidRunner.CMD_PROCSTATE, "CEM", WAIT_TIME);
+            uidWatcher.expect(WatchUidRunner.CMD_CACHED, null);
+            uidWatcher.expect(WatchUidRunner.CMD_PROCSTATE, WatchUidRunner.STATE_CACHED_EMPTY);
 
-            Thread.sleep(3000);
+            controller.removeFromTempWhitelist();
 
             // Going off the temp whitelist causes a spurious proc state report...  that's
             // not ideal, but okay.
-            controller.getUidWatcher().expect(WatchUidRunner.CMD_PROCSTATE, "CEM", WAIT_TIME);
+            uidWatcher.expect(WatchUidRunner.CMD_PROCSTATE, WatchUidRunner.STATE_CACHED_EMPTY);
 
             // We don't want to wait for the uid to actually go idle, we can force it now.
             controller.makeUidIdle();
 
-            controller.getUidWatcher().expect(WatchUidRunner.CMD_IDLE, null, WAIT_TIME);
+            uidWatcher.expect(WatchUidRunner.CMD_IDLE, null);
 
             // Make sure the process is gone so we start over fresh.
-            controller.ensureProcessGone(WAIT_TIME);
+            controller.ensureProcessGone();
 
             // Now that we should be off the temp whitelist, make sure we again can't start.
             br.sendAndWait(mContext, broadcastIntent, Activity.RESULT_OK, null, null, WAIT_TIME);
@@ -691,13 +785,13 @@
             }
 
             // Track the uid proc state changes from the broadcast (but not service execution)
-            controller.getUidWatcher().waitFor(WatchUidRunner.CMD_IDLE, null, WAIT_TIME);
+            uidWatcher.waitFor(WatchUidRunner.CMD_IDLE, null);
             // There could be a transient 'cached' state here before 'uncached' if uid state
             // changes are dispatched before receiver is started.
-            controller.getUidWatcher().waitFor(WatchUidRunner.CMD_UNCACHED, null, WAIT_TIME);
-            controller.getUidWatcher().expect(WatchUidRunner.CMD_PROCSTATE, "RCVR", WAIT_TIME);
-            controller.getUidWatcher().expect(WatchUidRunner.CMD_CACHED, null, WAIT_TIME);
-            controller.getUidWatcher().expect(WatchUidRunner.CMD_PROCSTATE, "CEM", WAIT_TIME);
+            uidWatcher.waitFor(WatchUidRunner.CMD_UNCACHED, null);
+            uidWatcher.expect(WatchUidRunner.CMD_PROCSTATE, WatchUidRunner.STATE_RECEIVER);
+            uidWatcher.expect(WatchUidRunner.CMD_CACHED, null);
+            uidWatcher.expect(WatchUidRunner.CMD_PROCSTATE, WatchUidRunner.STATE_CACHED_EMPTY);
 
             // Now put app on whitelist, should allow service to run.
             controller.addToWhitelist();
@@ -708,18 +802,18 @@
             if (brCode != Activity.RESULT_FIRST_USER) {
                 fail("Failed starting service, result=" + brCode);
             }
-            conn.waitForConnect(WAIT_TIME);
+            conn.waitForConnect();
 
             // Also make sure the uid state reports are as expected.
-            controller.getUidWatcher().expect(WatchUidRunner.CMD_UNCACHED, null, WAIT_TIME);
-            controller.getUidWatcher().waitFor(WatchUidRunner.CMD_PROCSTATE, "SVC", WAIT_TIME);
+            uidWatcher.waitFor(WatchUidRunner.CMD_UNCACHED, null);
+            uidWatcher.waitFor(WatchUidRunner.CMD_PROCSTATE, WatchUidRunner.STATE_SERVICE);
 
             // Okay, bring down the service.
             mContext.stopService(mServiceIntent);
-            conn.waitForDisconnect(WAIT_TIME);
+            conn.waitForDisconnect();
 
-            controller.getUidWatcher().expect(WatchUidRunner.CMD_CACHED, null, WAIT_TIME);
-            controller.getUidWatcher().expect(WatchUidRunner.CMD_PROCSTATE, "CEM", WAIT_TIME);
+            uidWatcher.expect(WatchUidRunner.CMD_CACHED, null);
+            uidWatcher.expect(WatchUidRunner.CMD_PROCSTATE, WatchUidRunner.STATE_CACHED_EMPTY);
 
         } finally {
             mContext.stopService(mServiceIntent);
@@ -738,16 +832,17 @@
                 SIMPLE_PACKAGE_NAME + SIMPLE_ACTIVITY_START_SERVICE);
 
         final ServiceProcessController controller = new ServiceProcessController(mContext,
-                getInstrumentation(), STUB_PACKAGE_NAME, mAllProcesses);
+                getInstrumentation(), STUB_PACKAGE_NAME, mAllProcesses, WAIT_TIME);
         final ServiceConnectionHandler conn = new ServiceConnectionHandler(mContext,
-                mServiceIntent);
+                mServiceIntent, WAIT_TIME);
+        final WatchUidRunner uidWatcher = controller.getUidWatcher();
 
         try {
             // First kill the process to start out in a stable state.
-            controller.ensureProcessGone(WAIT_TIME);
+            controller.ensureProcessGone();
 
             // Do initial setup.
-            controller.denyBackgroundOp(WAIT_TIME);
+            controller.denyBackgroundOp();
             controller.makeUidIdle();
             controller.removeFromWhitelist();
 
@@ -764,37 +859,36 @@
             if (brCode != Activity.RESULT_FIRST_USER) {
                 fail("Failed starting service, result=" + brCode);
             }
-            conn.waitForConnect(WAIT_TIME);
+            conn.waitForConnect();
 
             final String expectedActivityState = (isScreenInteractive() && !isKeyguardLocked())
-                    ? "TOP" : "TPSL";
+                    ? WatchUidRunner.STATE_TOP : WatchUidRunner.STATE_TOP_SLEEPING;
             // Also make sure the uid state reports are as expected.
-            controller.getUidWatcher().waitFor(WatchUidRunner.CMD_ACTIVE, null, WAIT_TIME);
-            controller.getUidWatcher().expect(WatchUidRunner.CMD_UNCACHED, null, WAIT_TIME);
-            controller.getUidWatcher().expect(WatchUidRunner.CMD_PROCSTATE,
-                    expectedActivityState, WAIT_TIME);
-            controller.getUidWatcher().expect(WatchUidRunner.CMD_PROCSTATE, "SVC", WAIT_TIME);
+            uidWatcher.waitFor(WatchUidRunner.CMD_ACTIVE, null);
+            uidWatcher.waitFor(WatchUidRunner.CMD_UNCACHED, null);
+            uidWatcher.expect(WatchUidRunner.CMD_PROCSTATE, expectedActivityState);
+            uidWatcher.expect(WatchUidRunner.CMD_PROCSTATE, WatchUidRunner.STATE_SERVICE);
 
             // Okay, bring down the service.
             mContext.stopService(mServiceIntent);
-            conn.waitForDisconnect(WAIT_TIME);
+            conn.waitForDisconnect();
 
-            controller.getUidWatcher().expect(WatchUidRunner.CMD_CACHED, null, WAIT_TIME);
-            controller.getUidWatcher().expect(WatchUidRunner.CMD_PROCSTATE, "CEM", WAIT_TIME);
+            uidWatcher.expect(WatchUidRunner.CMD_CACHED, null);
+            uidWatcher.expect(WatchUidRunner.CMD_PROCSTATE, WatchUidRunner.STATE_CACHED_EMPTY);
 
             // App isn't yet idle, so we should be able to start the service again.
             mContext.startService(mServiceIntent);
-            conn.waitForConnect(WAIT_TIME);
-            controller.getUidWatcher().expect(WatchUidRunner.CMD_UNCACHED, null, WAIT_TIME);
-            controller.getUidWatcher().expect(WatchUidRunner.CMD_PROCSTATE, "SVC", WAIT_TIME);
+            conn.waitForConnect();
+            uidWatcher.waitFor(WatchUidRunner.CMD_UNCACHED, null);
+            uidWatcher.expect(WatchUidRunner.CMD_PROCSTATE, WatchUidRunner.STATE_SERVICE);
 
             // And now fast-forward to the app going idle, service should be stopped.
             controller.makeUidIdle();
-            controller.getUidWatcher().waitFor(WatchUidRunner.CMD_IDLE, null, WAIT_TIME);
+            uidWatcher.waitFor(WatchUidRunner.CMD_IDLE, null);
 
-            conn.waitForDisconnect(WAIT_TIME);
-            controller.getUidWatcher().waitFor(WatchUidRunner.CMD_CACHED, null, WAIT_TIME);
-            controller.getUidWatcher().expect(WatchUidRunner.CMD_PROCSTATE, "CEM", WAIT_TIME);
+            conn.waitForDisconnect();
+            uidWatcher.waitFor(WatchUidRunner.CMD_CACHED, null);
+            uidWatcher.expect(WatchUidRunner.CMD_PROCSTATE, WatchUidRunner.STATE_CACHED_EMPTY);
 
             // No longer should be able to start service.
             boolean failed = false;
@@ -814,15 +908,271 @@
         }
     }
 
-    private boolean isScreenInteractive() {
-        final PowerManager powerManager =
-                (PowerManager) mContext.getSystemService(Context.POWER_SERVICE);
-        return powerManager.isInteractive();
+    /**
+     * Test that a single "can't save state" app has the proper process management
+     * semantics.
+     */
+    public void testCantSaveStateLaunchAndBackground() throws Exception {
+        final Intent activityIntent = new Intent();
+        activityIntent.setPackage(CANT_SAVE_STATE_1_PACKAGE_NAME);
+        activityIntent.setAction(Intent.ACTION_MAIN);
+        activityIntent.addCategory(Intent.CATEGORY_LAUNCHER);
+        activityIntent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
+
+        final Intent homeIntent = new Intent();
+        homeIntent.setAction(Intent.ACTION_MAIN);
+        homeIntent.addCategory(Intent.CATEGORY_HOME);
+        homeIntent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
+
+        ActivityManager am = mContext.getSystemService(ActivityManager.class);
+
+        String cmd = "pm grant " + STUB_PACKAGE_NAME + " "
+                + Manifest.permission.PACKAGE_USAGE_STATS;
+        String result = SystemUtil.runShellCommand(getInstrumentation(), cmd);
+
+        // We don't want to wait for the uid to actually go idle, we can force it now.
+        cmd = "am make-uid-idle " + CANT_SAVE_STATE_1_PACKAGE_NAME;
+        result = SystemUtil.runShellCommand(getInstrumentation(), cmd);
+
+        ApplicationInfo appInfo = mContext.getPackageManager().getApplicationInfo(
+                CANT_SAVE_STATE_1_PACKAGE_NAME, 0);
+
+        // This test is also using UidImportanceListener to make sure the correct
+        // heavy-weight state is reported there.
+        UidImportanceListener uidForegroundListener = new UidImportanceListener(appInfo.uid,
+                WAIT_TIME);
+        am.addOnUidImportanceListener(uidForegroundListener,
+                ActivityManager.RunningAppProcessInfo.IMPORTANCE_FOREGROUND);
+        UidImportanceListener uidBackgroundListener = new UidImportanceListener(appInfo.uid,
+                WAIT_TIME);
+        am.addOnUidImportanceListener(uidBackgroundListener,
+                ActivityManager.RunningAppProcessInfo.IMPORTANCE_CANT_SAVE_STATE-1);
+
+        WatchUidRunner uidWatcher = new WatchUidRunner(getInstrumentation(), appInfo.uid,
+                WAIT_TIME);
+
+        try {
+            // Start the heavy-weight app, should launch like a normal app.
+            mContext.startActivity(activityIntent);
+
+            // Wait for process state to reflect running activity.
+            uidForegroundListener.waitForValue(
+                    ActivityManager.RunningAppProcessInfo.IMPORTANCE_FOREGROUND,
+                    ActivityManager.RunningAppProcessInfo.IMPORTANCE_FOREGROUND);
+            assertEquals(ActivityManager.RunningAppProcessInfo.IMPORTANCE_FOREGROUND,
+                    am.getPackageImportance(CANT_SAVE_STATE_1_PACKAGE_NAME));
+
+            // Also make sure the uid state reports are as expected.
+            uidWatcher.waitFor(WatchUidRunner.CMD_ACTIVE, null);
+            uidWatcher.waitFor(WatchUidRunner.CMD_UNCACHED, null);
+            uidWatcher.expect(WatchUidRunner.CMD_PROCSTATE, WatchUidRunner.STATE_TOP);
+
+            // Now go to home, leaving the app.  It should be put in the heavy weight state.
+            mContext.startActivity(homeIntent);
+
+            // Wait for process to go down to background heavy-weight.
+            uidBackgroundListener.waitForValue(
+                    ActivityManager.RunningAppProcessInfo.IMPORTANCE_CANT_SAVE_STATE,
+                    ActivityManager.RunningAppProcessInfo.IMPORTANCE_CANT_SAVE_STATE);
+            assertEquals(ActivityManager.RunningAppProcessInfo.IMPORTANCE_CANT_SAVE_STATE,
+                    am.getPackageImportance(CANT_SAVE_STATE_1_PACKAGE_NAME));
+
+            uidWatcher.expect(WatchUidRunner.CMD_CACHED, null);
+            uidWatcher.expect(WatchUidRunner.CMD_PROCSTATE, WatchUidRunner.STATE_HEAVY_WEIGHT);
+
+            // While in background, should go in to normal idle state.
+            // Force app to go idle now
+            cmd = "am make-uid-idle " + CANT_SAVE_STATE_1_PACKAGE_NAME;
+            result = SystemUtil.runShellCommand(getInstrumentation(), cmd);
+            uidWatcher.expect(WatchUidRunner.CMD_IDLE, null);
+
+            // Switch back to heavy-weight app to see if it correctly returns to foreground.
+            mContext.startActivity(activityIntent);
+
+            // Wait for process state to reflect running activity.
+            uidForegroundListener.waitForValue(
+                    ActivityManager.RunningAppProcessInfo.IMPORTANCE_FOREGROUND,
+                    ActivityManager.RunningAppProcessInfo.IMPORTANCE_FOREGROUND);
+            assertEquals(ActivityManager.RunningAppProcessInfo.IMPORTANCE_FOREGROUND,
+                    am.getPackageImportance(CANT_SAVE_STATE_1_PACKAGE_NAME));
+
+            // Also make sure the uid state reports are as expected.
+            uidWatcher.waitFor(WatchUidRunner.CMD_ACTIVE, null);
+            uidWatcher.waitFor(WatchUidRunner.CMD_UNCACHED, null);
+            uidWatcher.expect(WatchUidRunner.CMD_PROCSTATE, WatchUidRunner.STATE_TOP);
+
+            waitForAppFocus(CANT_SAVE_STATE_1_PACKAGE_NAME, WAIT_TIME);
+
+            // Exit activity, check to see if we are now cached.
+            getInstrumentation().getUiAutomation().performGlobalAction(
+                    AccessibilityService.GLOBAL_ACTION_BACK);
+
+            // Wait for process to become cached
+            uidBackgroundListener.waitForValue(
+                    ActivityManager.RunningAppProcessInfo.IMPORTANCE_CACHED,
+                    ActivityManager.RunningAppProcessInfo.IMPORTANCE_CACHED);
+            assertEquals(ActivityManager.RunningAppProcessInfo.IMPORTANCE_CACHED,
+                    am.getPackageImportance(CANT_SAVE_STATE_1_PACKAGE_NAME));
+
+            uidWatcher.expect(WatchUidRunner.CMD_CACHED, null);
+            uidWatcher.expect(WatchUidRunner.CMD_PROCSTATE, WatchUidRunner.STATE_CACHED_RECENT);
+
+            // While in background, should go in to normal idle state.
+            // Force app to go idle now
+            cmd = "am make-uid-idle " + CANT_SAVE_STATE_1_PACKAGE_NAME;
+            result = SystemUtil.runShellCommand(getInstrumentation(), cmd);
+            uidWatcher.expect(WatchUidRunner.CMD_IDLE, null);
+
+        } finally {
+            uidWatcher.finish();
+
+            am.removeOnUidImportanceListener(uidForegroundListener);
+            am.removeOnUidImportanceListener(uidBackgroundListener);
+        }
     }
 
-    private boolean isKeyguardLocked() {
-        final KeyguardManager keyguardManager =
-                (KeyguardManager) mContext.getSystemService(Context.KEYGUARD_SERVICE);
-        return keyguardManager.isKeyguardLocked();
+    /**
+     * Test that switching between two "can't save state" apps is handled properly.
+     */
+    public void testCantSaveStateLaunchAndSwitch() throws Exception {
+        final Intent activity1Intent = new Intent();
+        activity1Intent.setPackage(CANT_SAVE_STATE_1_PACKAGE_NAME);
+        activity1Intent.setAction(Intent.ACTION_MAIN);
+        activity1Intent.addCategory(Intent.CATEGORY_LAUNCHER);
+        activity1Intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
+
+        final Intent activity2Intent = new Intent();
+        activity2Intent.setPackage(CANT_SAVE_STATE_2_PACKAGE_NAME);
+        activity2Intent.setAction(Intent.ACTION_MAIN);
+        activity2Intent.addCategory(Intent.CATEGORY_LAUNCHER);
+        activity2Intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
+
+        final Intent homeIntent = new Intent();
+        homeIntent.setAction(Intent.ACTION_MAIN);
+        homeIntent.addCategory(Intent.CATEGORY_HOME);
+        homeIntent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
+
+        ActivityManager am = mContext.getSystemService(ActivityManager.class);
+        UiDevice device = UiDevice.getInstance(getInstrumentation());
+
+        String cmd = "pm grant " + STUB_PACKAGE_NAME + " "
+                + Manifest.permission.PACKAGE_USAGE_STATS;
+        String result = SystemUtil.runShellCommand(getInstrumentation(), cmd);
+
+        // We don't want to wait for the uid to actually go idle, we can force it now.
+        cmd = "am make-uid-idle " + CANT_SAVE_STATE_1_PACKAGE_NAME;
+        result = SystemUtil.runShellCommand(getInstrumentation(), cmd);
+        cmd = "am make-uid-idle " + CANT_SAVE_STATE_2_PACKAGE_NAME;
+        result = SystemUtil.runShellCommand(getInstrumentation(), cmd);
+
+        ApplicationInfo app1Info = mContext.getPackageManager().getApplicationInfo(
+                CANT_SAVE_STATE_1_PACKAGE_NAME, 0);
+        WatchUidRunner uid1Watcher = new WatchUidRunner(getInstrumentation(), app1Info.uid,
+                WAIT_TIME);
+
+        ApplicationInfo app2Info = mContext.getPackageManager().getApplicationInfo(
+                CANT_SAVE_STATE_2_PACKAGE_NAME, 0);
+        WatchUidRunner uid2Watcher = new WatchUidRunner(getInstrumentation(), app2Info.uid,
+                WAIT_TIME);
+
+        try {
+            // Start the first heavy-weight app, should launch like a normal app.
+            mContext.startActivity(activity1Intent);
+
+            // Make sure the uid state reports are as expected.
+            uid1Watcher.waitFor(WatchUidRunner.CMD_ACTIVE, null);
+            uid1Watcher.waitFor(WatchUidRunner.CMD_UNCACHED, null);
+            uid1Watcher.expect(WatchUidRunner.CMD_PROCSTATE, WatchUidRunner.STATE_TOP);
+
+            // Now go to home, leaving the app.  It should be put in the heavy weight state.
+            mContext.startActivity(homeIntent);
+
+            // Wait for process to go down to background heavy-weight.
+            uid1Watcher.expect(WatchUidRunner.CMD_CACHED, null);
+            uid1Watcher.expect(WatchUidRunner.CMD_PROCSTATE, WatchUidRunner.STATE_HEAVY_WEIGHT);
+
+            // Start the second heavy-weight app, should ask us what to do with the two apps
+            startActivityAndWaitForShow(activity2Intent);
+
+            // First, let's try returning to the original app.
+            maybeClick(device, new UiSelector().resourceId("android:id/switch_old"));
+            device.waitForIdle();
+
+            // App should now be back in foreground.
+            uid1Watcher.expect(WatchUidRunner.CMD_UNCACHED, null);
+            uid1Watcher.expect(WatchUidRunner.CMD_PROCSTATE, WatchUidRunner.STATE_TOP);
+
+            // Return to home.
+            mContext.startActivity(homeIntent);
+            uid1Watcher.expect(WatchUidRunner.CMD_CACHED, null);
+            uid1Watcher.expect(WatchUidRunner.CMD_PROCSTATE, WatchUidRunner.STATE_HEAVY_WEIGHT);
+
+            // Again try starting second heavy-weight app to get prompt.
+            startActivityAndWaitForShow(activity2Intent);
+
+            // Now we'll switch to the new app.
+            maybeClick(device, new UiSelector().resourceId("android:id/switch_new"));
+            device.waitForIdle();
+
+            // The original app should now become cached.
+            uid1Watcher.expect(WatchUidRunner.CMD_PROCSTATE, WatchUidRunner.STATE_CACHED_RECENT);
+
+            // And the new app should start.
+            uid2Watcher.waitFor(WatchUidRunner.CMD_ACTIVE, null);
+            uid2Watcher.waitFor(WatchUidRunner.CMD_UNCACHED, null);
+            uid2Watcher.expect(WatchUidRunner.CMD_PROCSTATE, WatchUidRunner.STATE_TOP);
+
+            // Make sure the original app is idle for cleanliness
+            cmd = "am make-uid-idle " + CANT_SAVE_STATE_1_PACKAGE_NAME;
+            result = SystemUtil.runShellCommand(getInstrumentation(), cmd);
+            uid1Watcher.expect(WatchUidRunner.CMD_IDLE, null);
+
+            // Return to home.
+            mContext.startActivity(homeIntent);
+            uid2Watcher.waitFor(WatchUidRunner.CMD_CACHED, null);
+            uid2Watcher.expect(WatchUidRunner.CMD_PROCSTATE, WatchUidRunner.STATE_HEAVY_WEIGHT);
+
+            // Try starting the first heavy weight app, but return to the existing second.
+            startActivityAndWaitForShow(activity1Intent);
+            maybeClick(device, new UiSelector().resourceId("android:id/switch_old"));
+            device.waitForIdle();
+            uid2Watcher.waitFor(WatchUidRunner.CMD_UNCACHED, null);
+            uid2Watcher.expect(WatchUidRunner.CMD_PROCSTATE, WatchUidRunner.STATE_TOP);
+
+            // Return to home.
+            mContext.startActivity(homeIntent);
+            uid2Watcher.waitFor(WatchUidRunner.CMD_CACHED, null);
+            uid2Watcher.expect(WatchUidRunner.CMD_PROCSTATE, WatchUidRunner.STATE_HEAVY_WEIGHT);
+
+            // Again start the first heavy weight app, this time actually switching to it
+            startActivityAndWaitForShow(activity1Intent);
+            maybeClick(device, new UiSelector().resourceId("android:id/switch_new"));
+            device.waitForIdle();
+
+            // The second app should now become cached.
+            uid2Watcher.expect(WatchUidRunner.CMD_PROCSTATE, WatchUidRunner.STATE_CACHED_RECENT);
+
+            // And the first app should start.
+            uid1Watcher.waitFor(WatchUidRunner.CMD_ACTIVE, null);
+            uid1Watcher.waitFor(WatchUidRunner.CMD_UNCACHED, null);
+            uid1Watcher.expect(WatchUidRunner.CMD_PROCSTATE, WatchUidRunner.STATE_TOP);
+
+            // Exit activity, check to see if we are now cached.
+            waitForAppFocus(CANT_SAVE_STATE_1_PACKAGE_NAME, WAIT_TIME);
+            getInstrumentation().getUiAutomation().performGlobalAction(
+                    AccessibilityService.GLOBAL_ACTION_BACK);
+            uid1Watcher.expect(WatchUidRunner.CMD_CACHED, null);
+            uid1Watcher.expect(WatchUidRunner.CMD_PROCSTATE, WatchUidRunner.STATE_CACHED_RECENT);
+
+            // Make both apps idle for cleanliness.
+            cmd = "am make-uid-idle " + CANT_SAVE_STATE_1_PACKAGE_NAME;
+            result = SystemUtil.runShellCommand(getInstrumentation(), cmd);
+            cmd = "am make-uid-idle " + CANT_SAVE_STATE_2_PACKAGE_NAME;
+            result = SystemUtil.runShellCommand(getInstrumentation(), cmd);
+
+        } finally {
+            uid2Watcher.finish();
+            uid1Watcher.finish();
+        }
     }
 }
diff --git a/tests/app/src/android/app/cts/ActivityManagerTest.java b/tests/app/src/android/app/cts/ActivityManagerTest.java
index e718a32..5e77221 100644
--- a/tests/app/src/android/app/cts/ActivityManagerTest.java
+++ b/tests/app/src/android/app/cts/ActivityManagerTest.java
@@ -369,7 +369,6 @@
         final RunningAppProcessInfo ra = new RunningAppProcessInfo();
         ActivityManager.getMyMemoryState(ra);
 
-        assertEquals(mContext.getApplicationInfo().processName, ra.processName);
         assertEquals(android.os.Process.myUid(), ra.uid);
 
         // When an instrumentation test is running, the importance is high.
diff --git a/tests/app/src/android/app/cts/AlertWindowsTests.java b/tests/app/src/android/app/cts/AlertWindowsTests.java
deleted file mode 100644
index b6b4ce4..0000000
--- a/tests/app/src/android/app/cts/AlertWindowsTests.java
+++ /dev/null
@@ -1,288 +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.app.cts;
-
-import static android.app.ActivityManager.RunningAppProcessInfo.IMPORTANCE_PERCEPTIBLE;
-import static android.app.ActivityManager.RunningAppProcessInfo.IMPORTANCE_PERCEPTIBLE_PRE_26;
-import static android.app.ActivityManager.RunningAppProcessInfo.IMPORTANCE_VISIBLE;
-import static android.content.Context.BIND_ALLOW_OOM_MANAGEMENT;
-import static android.content.Context.BIND_AUTO_CREATE;
-import static android.content.Context.BIND_NOT_FOREGROUND;
-
-import static com.android.app2.AlertWindowService.MSG_ADD_ALERT_WINDOW;
-import static com.android.app2.AlertWindowService.MSG_ON_ALERT_WINDOW_ADDED;
-import static com.android.app2.AlertWindowService.MSG_ON_ALERT_WINDOW_REMOVED;
-import static com.android.app2.AlertWindowService.MSG_REMOVE_ALERT_WINDOW;
-import static com.android.app2.AlertWindowService.MSG_REMOVE_ALL_ALERT_WINDOWS;
-import static com.android.app2.AlertWindowService.NOTIFICATION_MESSENGER_EXTRA;
-
-import static org.junit.Assert.assertEquals;
-import static org.junit.Assert.fail;
-
-import android.app.ActivityManager;
-import android.app.ActivityManager.RunningAppProcessInfo;
-import android.content.ComponentName;
-import android.content.Context;
-import android.content.Intent;
-import android.content.res.Configuration;
-import android.content.ServiceConnection;
-import android.content.pm.PackageManager;
-import android.content.pm.PackageManager.NameNotFoundException;
-import android.os.Handler;
-import android.os.IBinder;
-import android.os.Looper;
-import android.os.Message;
-import android.os.Messenger;
-import android.os.SystemClock;
-import android.platform.test.annotations.Presubmit;
-import android.support.test.InstrumentationRegistry;
-import android.support.test.runner.AndroidJUnit4;
-import android.util.Log;
-
-import com.android.app2.AlertWindowService;
-import com.android.compatibility.common.util.SystemUtil;
-
-import org.junit.After;
-import org.junit.Before;
-import org.junit.Test;
-import org.junit.runner.RunWith;
-
-import java.util.concurrent.TimeUnit;
-import java.util.function.Function;
-
-/**
- * Build: mmma -j32 cts/tests/app
- * Run: cts/hostsidetests/services/activityandwindowmanager/util/run-test CtsAppTestCases android.app.cts.AlertWindowsTests
- */
-@Presubmit
-@RunWith(AndroidJUnit4.class)
-public class AlertWindowsTests {
-
-    private static final String TAG = "AlertWindowsTests";
-
-    private static final boolean DEBUG = false;
-    private static final long WAIT_TIME_MS = 2 * 1000;
-
-    private static final String SDK25_PACKAGE_NAME = "com.android.appSdk25";
-
-    private Messenger mService;
-    private String mServicePackageName;
-    private int mServiceUid;
-
-    private PackageManager mPm;
-
-    private ActivityManager mAm;
-    private ActivityManager mAm25; // ActivityManager created for an SDK 25 app context.
-
-    private final Messenger mMessenger = new Messenger(new IncomingHandler(Looper.getMainLooper()));
-    private final Object mAddedLock = new Object();
-    private final Object mRemoveLock = new Object();
-
-    @Before
-    public void setUp() throws Exception {
-        if (DEBUG) Log.e(TAG, "setUp");
-        final Context context = InstrumentationRegistry.getTargetContext();
-
-        mPm = context.getPackageManager();
-
-        mAm = context.getSystemService(ActivityManager.class);
-        mAm25 = context.createPackageContext(SDK25_PACKAGE_NAME, 0)
-                .getSystemService(ActivityManager.class);
-
-        final Intent intent = new Intent();
-        intent.setClassName(AlertWindowService.class.getPackage().getName(),
-                AlertWindowService.class.getName());
-        intent.putExtra(NOTIFICATION_MESSENGER_EXTRA, mMessenger);
-        // Needs to be both BIND_NOT_FOREGROUND and BIND_ALLOW_OOM_MANAGEMENT to avoid the binding
-        // to this instrumentation test from increasing its importance.
-        context.bindService(intent, mConnection,
-                BIND_AUTO_CREATE | BIND_NOT_FOREGROUND | BIND_ALLOW_OOM_MANAGEMENT);
-        synchronized (mConnection) {
-            // Wait for alert window service to be connection before processing.
-            mConnection.wait(WAIT_TIME_MS);
-        }
-    }
-
-    @After
-    public void tearDown() throws Exception {
-        if (DEBUG) Log.e(TAG, "tearDown");
-        if (mService != null) {
-            mService.send(Message.obtain(null, MSG_REMOVE_ALL_ALERT_WINDOWS));
-        }
-        final Context context = InstrumentationRegistry.getTargetContext();
-        context.unbindService(mConnection);
-        mAm = null;
-    }
-
-    @Test
-    public void testAlertWindowOomAdj() throws Exception {
-        // Alert windows are always hidden when running in VR.
-        if (isRunningInVR()) {
-            return;
-        }
-        setAlertWindowPermission(true /* allow */);
-
-
-        assertPackageImportance(IMPORTANCE_PERCEPTIBLE, IMPORTANCE_PERCEPTIBLE_PRE_26);
-
-        // TODO AM.getUidImportance() sometimes return a different value from what
-        // getPackageImportance() returns... b/37950472
-        // assertUidImportance(IMPORTANCE_PERCEPTIBLE, IMPORTANCE_PERCEPTIBLE_PRE_26);
-
-        addAlertWindow();
-        // Process importance should be increased to visible when the service has an alert window.
-        assertPackageImportance(IMPORTANCE_VISIBLE, IMPORTANCE_VISIBLE);
-
-        addAlertWindow();
-        assertPackageImportance(IMPORTANCE_VISIBLE, IMPORTANCE_VISIBLE);
-
-        setAlertWindowPermission(false /* allow */);
-        // Process importance should no longer be visible since its alert windows are not allowed to
-        // be visible.
-        assertPackageImportance(IMPORTANCE_PERCEPTIBLE, IMPORTANCE_PERCEPTIBLE_PRE_26);
-
-        setAlertWindowPermission(true /* allow */);
-        // They can show again so importance should be visible again.
-        assertPackageImportance(IMPORTANCE_VISIBLE, IMPORTANCE_VISIBLE);
-
-        removeAlertWindow();
-        assertPackageImportance(IMPORTANCE_VISIBLE, IMPORTANCE_VISIBLE);
-
-        removeAlertWindow();
-        // Process importance should no longer be visible when the service no longer as alert
-        // windows.
-        assertPackageImportance(IMPORTANCE_PERCEPTIBLE, IMPORTANCE_PERCEPTIBLE_PRE_26);
-    }
-
-    private void addAlertWindow() throws Exception {
-        mService.send(Message.obtain(null, MSG_ADD_ALERT_WINDOW));
-        synchronized (mAddedLock) {
-            // Wait for window addition confirmation before proceeding.
-            mAddedLock.wait(WAIT_TIME_MS);
-        }
-    }
-
-    private void removeAlertWindow() throws Exception {
-        mService.send(Message.obtain(null, MSG_REMOVE_ALERT_WINDOW));
-        synchronized (mRemoveLock) {
-            // Wait for window removal confirmation before proceeding.
-            mRemoveLock.wait(WAIT_TIME_MS);
-        }
-    }
-
-    private void setAlertWindowPermission(boolean allow) throws Exception {
-        final String cmd = "appops set " + mServicePackageName
-                + " android:system_alert_window " + (allow ? "allow" : "deny");
-        SystemUtil.runShellCommand(InstrumentationRegistry.getInstrumentation(), cmd);
-    }
-
-    private void assertImportance(Function<ActivityManager, Integer> apiCaller,
-            int expectedForO, int expectedForPreO) throws Exception {
-        final long TIMEOUT = SystemClock.uptimeMillis() + TimeUnit.SECONDS.toMillis(30);
-        int actual;
-
-        do {
-            // TODO: We should try to use ActivityManagerTest.UidImportanceListener here to listen
-            // for changes in the uid importance. However, the way it is currently structured
-            // doesn't really work for this use case right now...
-            Thread.sleep(500);
-            actual = apiCaller.apply(mAm);
-        } while (actual != expectedForO && (SystemClock.uptimeMillis() < TIMEOUT));
-
-        assertEquals(expectedForO, actual);
-
-        // Check the result for pre-O apps.
-        assertEquals(expectedForPreO, (int) apiCaller.apply(mAm25));
-    }
-
-    /**
-     * Make sure {@link ActivityManager#getPackageImportance} returns the expected value.
-     */
-    private void assertPackageImportance(int expectedForO, int expectedForPreO) throws Exception {
-        assertImportance(am -> am.getPackageImportance(mServicePackageName),
-                expectedForO, expectedForPreO);
-    }
-
-    /**
-     * Make sure {@link ActivityManager#getUidImportance(int)} returns the expected value.
-     */
-    private void assertUidImportance(int expectedForO, int expectedForPreO) throws Exception {
-        assertImportance(am -> am.getUidImportance(mServiceUid),
-                expectedForO, expectedForPreO);
-    }
-
-    private final ServiceConnection mConnection = new ServiceConnection() {
-        @Override
-        public void onServiceConnected(ComponentName name, IBinder service) {
-            if (DEBUG) Log.e(TAG, "onServiceConnected");
-            mService = new Messenger(service);
-            mServicePackageName = name.getPackageName();
-            try {
-                mServiceUid = mPm.getPackageUid(mServicePackageName, 0);
-            } catch (NameNotFoundException e) {
-                throw new RuntimeException("getPackageUid() failed.", e);
-            }
-            synchronized (mConnection) {
-                notifyAll();
-            }
-        }
-
-        @Override
-        public void onServiceDisconnected(ComponentName name) {
-            if (DEBUG) Log.e(TAG, "onServiceDisconnected");
-            mService = null;
-            mServicePackageName = null;
-            mServiceUid = 0;
-        }
-    };
-
-    private class IncomingHandler extends Handler {
-
-        IncomingHandler(Looper looper) {
-            super(looper);
-        }
-
-        @Override
-        public void handleMessage(Message msg) {
-            switch (msg.what) {
-                case MSG_ON_ALERT_WINDOW_ADDED:
-                    synchronized (mAddedLock) {
-                        if (DEBUG) Log.e(TAG, "MSG_ON_ALERT_WINDOW_ADDED");
-                        mAddedLock.notifyAll();
-                    }
-                    break;
-                case MSG_ON_ALERT_WINDOW_REMOVED:
-                    synchronized (mRemoveLock) {
-                        if (DEBUG) Log.e(TAG, "MSG_ON_ALERT_WINDOW_REMOVED");
-                        mRemoveLock.notifyAll();
-                    }
-                    break;
-                default:
-                    super.handleMessage(msg);
-            }
-        }
-    }
-
-    private boolean isRunningInVR() {
-        final Context context = InstrumentationRegistry.getTargetContext();
-        if ((context.getResources().getConfiguration().uiMode & Configuration.UI_MODE_TYPE_MASK)
-             == Configuration.UI_MODE_TYPE_VR_HEADSET) {
-            return true;
-        }
-        return false;
-    }
-}
diff --git a/tests/app/src/android/app/cts/AspectRatioTests.java b/tests/app/src/android/app/cts/AspectRatioTests.java
deleted file mode 100644
index fa6154e..0000000
--- a/tests/app/src/android/app/cts/AspectRatioTests.java
+++ /dev/null
@@ -1,256 +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.app.cts;
-
-import android.app.stubs.MetaDataMaxAspectRatioActivity;
-import com.android.appSdk25.Sdk25MaxAspectRatioActivity;
-
-import org.junit.After;
-import org.junit.Before;
-import org.junit.Ignore;
-import org.junit.Rule;
-import org.junit.Test;
-import org.junit.runner.RunWith;
-
-import android.app.Activity;
-import android.app.stubs.MaxAspectRatioActivity;
-import android.app.stubs.MaxAspectRatioResizeableActivity;
-import android.app.stubs.MaxAspectRatioUnsetActivity;
-import android.content.Context;
-import android.content.res.Configuration;
-import android.graphics.Point;
-import android.platform.test.annotations.Presubmit;
-import android.support.test.InstrumentationRegistry;
-import android.support.test.rule.ActivityTestRule;
-import android.support.test.runner.AndroidJUnit4;
-import android.util.DisplayMetrics;
-import android.view.Display;
-import android.view.View;
-import android.view.WindowManager;
-
-import com.android.compatibility.common.util.PollingCheck;
-
-import static android.content.Context.WINDOW_SERVICE;
-import static android.content.pm.ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE;
-import static android.content.pm.ActivityInfo.SCREEN_ORIENTATION_PORTRAIT;
-import static android.content.pm.PackageManager.FEATURE_WATCH;
-import static org.junit.Assert.fail;
-
-/**
- * Build: mmma -j32 cts/tests/app
- * Run: cts/hostsidetests/services/activityandwindowmanager/util/run-test CtsAppTestCases android.app.cts.AspectRatioTests
- */
-@RunWith(AndroidJUnit4.class)
-public class AspectRatioTests {
-    private static final String TAG = "AspectRatioTests";
-
-    // The max. aspect ratio the test activities are using.
-    private static final float MAX_ASPECT_RATIO = 1.0f;
-
-    // Max supported aspect ratio for pre-O apps.
-    private static final float MAX_PRE_O_ASPECT_RATIO = 1.86f;
-
-    // The minimum supported device aspect ratio.
-    private static final float MIN_DEVICE_ASPECT_RATIO = 1.333f;
-
-    // The minimum supported device aspect ratio for watches.
-    private static final float MIN_WATCH_DEVICE_ASPECT_RATIO = 1.0f;
-
-    @Rule
-    public ActivityTestRule<MaxAspectRatioActivity> mMaxAspectRatioActivity =
-            new ActivityTestRule<>(MaxAspectRatioActivity.class,
-                    false /* initialTouchMode */, false /* launchActivity */);
-
-    @Rule
-    public ActivityTestRule<MaxAspectRatioResizeableActivity> mMaxAspectRatioResizeableActivity =
-            new ActivityTestRule<>(MaxAspectRatioResizeableActivity.class,
-                    false /* initialTouchMode */, false /* launchActivity */);
-
-    @Rule
-    public ActivityTestRule<MetaDataMaxAspectRatioActivity> mMetaDataMaxAspectRatioActivity =
-        new ActivityTestRule<>(MetaDataMaxAspectRatioActivity.class,
-            false /* initialTouchMode */, false /* launchActivity */);
-
-    @Rule
-    public ActivityTestRule<MaxAspectRatioUnsetActivity> mMaxAspectRatioUnsetActivity =
-            new ActivityTestRule<>(MaxAspectRatioUnsetActivity.class,
-                    false /* initialTouchMode */, false /* launchActivity */);
-
-    // TODO: Can't use this to start an activity in a different process...sigh.
-    @Rule
-    public ActivityTestRule<Sdk25MaxAspectRatioActivity> mSdk25MaxAspectRatioActivity =
-            new ActivityTestRule<>(Sdk25MaxAspectRatioActivity.class, "com.android.appSdk25",
-                    268435456, false /* initialTouchMode */, false /* launchActivity */);
-
-    private interface AssertAspectRatioCallback {
-        void assertAspectRatio(float actual);
-    }
-
-    @Before
-    public void setUp() throws Exception {
-
-    }
-
-    @After
-    public void tearDown() throws Exception {
-        finishActivity(mMaxAspectRatioActivity);
-        finishActivity(mMaxAspectRatioResizeableActivity);
-        finishActivity(mSdk25MaxAspectRatioActivity);
-        finishActivity(mMaxAspectRatioUnsetActivity);
-        finishActivity(mMetaDataMaxAspectRatioActivity);
-    }
-
-    @Test
-    @Presubmit
-    public void testDeviceAspectRatio() throws Exception {
-        final Context context = InstrumentationRegistry.getInstrumentation().getContext();
-        final WindowManager wm = (WindowManager) context.getSystemService(WINDOW_SERVICE);
-        final Display display = wm.getDefaultDisplay();
-        final DisplayMetrics metrics = new DisplayMetrics();
-        display.getRealMetrics(metrics);
-
-        float longSide = Math.max(metrics.widthPixels, metrics.heightPixels);
-        float shortSide = Math.min(metrics.widthPixels, metrics.heightPixels);
-        float deviceAspectRatio = longSide / shortSide;
-        float expectedMinAspectRatio = context.getPackageManager().hasSystemFeature(FEATURE_WATCH)
-                ? MIN_WATCH_DEVICE_ASPECT_RATIO : MIN_DEVICE_ASPECT_RATIO;
-
-        if (deviceAspectRatio < expectedMinAspectRatio) {
-            fail("deviceAspectRatio=" + deviceAspectRatio
-                    + " is less than expectedMinAspectRatio=" + expectedMinAspectRatio);
-        }
-    }
-
-    @Test
-    @Presubmit
-    public void testMaxAspectRatio() throws Exception {
-        runTest(launchActivity(mMaxAspectRatioActivity),
-                actual -> {
-                    if (MAX_ASPECT_RATIO >= actual) return;
-                    fail("actual=" + actual + " is greater than expected=" + MAX_ASPECT_RATIO);
-                });
-    }
-
-    @Test
-    @Presubmit
-    public void testMetaDataMaxAspectRatio() throws Exception {
-        runTest(launchActivity(mMetaDataMaxAspectRatioActivity),
-            actual -> {
-                if (MAX_ASPECT_RATIO >= actual) return;
-                fail("actual=" + actual + " is greater than expected=" + MAX_ASPECT_RATIO);
-            });
-    }
-
-    @Test
-    // TODO: Currently 10% flaky so not part of pre-submit for now
-    public void testMaxAspectRatioResizeableActivity() throws Exception {
-        final Context context = InstrumentationRegistry.getInstrumentation().getContext();
-        final float expected = getAspectRatio(context);
-        final Activity testActivity = launchActivity(mMaxAspectRatioResizeableActivity);
-        PollingCheck.waitFor(testActivity::hasWindowFocus);
-
-        Display testDisplay = testActivity.findViewById(android.R.id.content).getDisplay();
-
-        // TODO(b/69982434): Fix DisplayManager NPE when getting display from Instrumentation
-        // context, then can use DisplayManager to get the aspect ratio of the correct display.
-        if (testDisplay.getDisplayId() != Display.DEFAULT_DISPLAY) {
-            return;
-        }
-
-        // Since this activity is resizeable, its aspect ratio shouldn't be less than the device's
-        runTest(testActivity,
-                actual -> {
-                    if (aspectRatioEqual(expected, actual) || expected < actual) return;
-                    fail("actual=" + actual + " is less than expected=" + expected);
-                });
-    }
-
-    @Test
-    @Presubmit
-    public void testMaxAspectRatioUnsetActivity() throws Exception {
-        final Context context = InstrumentationRegistry.getInstrumentation().getContext();
-        final float expected = getAspectRatio(context);
-
-        // Since this activity didn't set an aspect ratio, its aspect ratio shouldn't be less than
-        // the device's
-        runTest(launchActivity(mMaxAspectRatioUnsetActivity),
-                actual -> {
-                    if (aspectRatioEqual(expected, actual) || expected < actual) return;
-                    fail("actual=" + actual + " is less than expected=" + expected);
-                });
-    }
-
-    @Test
-    // TODO(b/35810513): Can't use rule to start an activity in a different process. Need a
-    // different way to make this test happen...host side? Sigh...
-    @Ignore
-    public void testMaxAspectRatioPreOActivity() throws Exception {
-        runTest(launchActivity(mSdk25MaxAspectRatioActivity),
-                actual -> {
-                    if (MAX_PRE_O_ASPECT_RATIO >= actual) return;
-                    fail("actual=" + actual + " is greater than expected=" + MAX_PRE_O_ASPECT_RATIO);
-                });
-    }
-
-    private void runTest(Activity activity, AssertAspectRatioCallback callback) {
-        callback.assertAspectRatio(getAspectRatio(activity));
-
-        // TODO(b/35810513): All this rotation stuff doesn't really work yet. Need to make sure
-        // context is updated correctly here. Also, what does it mean to be holding a reference to
-        // this activity if changing the orientation will cause a relaunch?
-//        activity.setRequestedOrientation(SCREEN_ORIENTATION_LANDSCAPE);
-//        waitForIdle();
-//        callback.assertAspectRatio(getAspectRatio(activity));
-//
-//        activity.setRequestedOrientation(SCREEN_ORIENTATION_PORTRAIT);
-//        waitForIdle();
-//        callback.assertAspectRatio(getAspectRatio(activity));
-    }
-
-    private float getAspectRatio(Context context) {
-        final Display display =
-                ((WindowManager) context.getSystemService(WINDOW_SERVICE)).getDefaultDisplay();
-        final Point size = new Point();
-        display.getSize(size);
-        final float longSide = Math.max(size.x, size.y);
-        final float shortSide = Math.min(size.x, size.y);
-        return longSide / shortSide;
-    }
-
-    private Activity launchActivity(ActivityTestRule activityRule) {
-        final Activity activity = activityRule.launchActivity(null);
-        waitForIdle();
-        return activity;
-    }
-
-    private void finishActivity(ActivityTestRule activityRule) {
-        final Activity activity = activityRule.getActivity();
-        if (activity != null) {
-            activity.finish();
-        }
-    }
-
-    private void waitForIdle() {
-        InstrumentationRegistry.getInstrumentation().waitForIdleSync();
-    }
-
-    private static boolean aspectRatioEqual(float a, float b) {
-        // Aspect ratios are considered equal if they ware within to significant digits.
-        float diff = Math.abs(a - b);
-        return diff < 0.01f;
-    }
-}
diff --git a/tests/app/src/android/app/cts/NotificationChannelGroupTest.java b/tests/app/src/android/app/cts/NotificationChannelGroupTest.java
index 162815f..7931a1f 100644
--- a/tests/app/src/android/app/cts/NotificationChannelGroupTest.java
+++ b/tests/app/src/android/app/cts/NotificationChannelGroupTest.java
@@ -37,10 +37,21 @@
         NotificationChannelGroup group =  new NotificationChannelGroup("1", "one");
         assertEquals("1", group.getId());
         assertEquals("one", group.getName());
+        assertFalse(group.isBlocked());
+        assertNull(group.getDescription());
+        assertEquals(0, group.getChannels().size());
+    }
+
+    public void testIsBlocked() {
+        NotificationChannelGroup group =  new NotificationChannelGroup("1", "one");
+        group.setBlocked(true);
+        assertTrue(group.isBlocked());
     }
 
     public void testWriteToParcel() {
         NotificationChannelGroup group = new NotificationChannelGroup("1", "one");
+        group.setBlocked(true);
+        group.setDescription("bananas!");
         Parcel parcel = Parcel.obtain();
         group.writeToParcel(parcel, 0);
         parcel.setDataPosition(0);
@@ -51,8 +62,12 @@
 
     public void testClone() {
         NotificationChannelGroup group =  new NotificationChannelGroup("1", "one");
+        group.setBlocked(true);
+        group.setDescription("bananas");
         NotificationChannelGroup cloned = group.clone();
         assertEquals("1", cloned.getId());
         assertEquals("one", cloned.getName());
+        assertTrue(cloned.isBlocked());
+        assertEquals("bananas", cloned.getDescription());
     }
 }
diff --git a/tests/app/src/android/app/cts/NotificationManagerTest.java b/tests/app/src/android/app/cts/NotificationManagerTest.java
index b6077ca..7b716ac 100644
--- a/tests/app/src/android/app/cts/NotificationManagerTest.java
+++ b/tests/app/src/android/app/cts/NotificationManagerTest.java
@@ -80,6 +80,12 @@
             }
             mNotificationManager.deleteNotificationChannel(nc.getId());
         }
+
+        List<NotificationChannelGroup> groups = mNotificationManager.getNotificationChannelGroups();
+        // Delete all groups.
+        for (NotificationChannelGroup ncg : groups) {
+            mNotificationManager.deleteNotificationChannelGroup(ncg.getId());
+        }
     }
 
     public void testCreateChannelGroup() throws Exception {
@@ -88,18 +94,43 @@
                 new NotificationChannel(mId, "name", NotificationManager.IMPORTANCE_DEFAULT);
         channel.setGroup(ncg.getId());
         mNotificationManager.createNotificationChannelGroup(ncg);
+        final NotificationChannel ungrouped =
+                new NotificationChannel(mId + "!", "name", NotificationManager.IMPORTANCE_DEFAULT);
         try {
             mNotificationManager.createNotificationChannel(channel);
+            mNotificationManager.createNotificationChannel(ungrouped);
 
             List<NotificationChannelGroup> ncgs =
                     mNotificationManager.getNotificationChannelGroups();
             assertEquals(1, ncgs.size());
-            assertEquals(ncg, ncgs.get(0));
+            assertEquals(ncg.getName(), ncgs.get(0).getName());
+            assertEquals(ncg.getDescription(), ncgs.get(0).getDescription());
+            assertEquals(channel.getId(), ncgs.get(0).getChannels().get(0).getId());
         } finally {
             mNotificationManager.deleteNotificationChannelGroup(ncg.getId());
         }
     }
 
+    public void testGetChannelGroup() throws Exception {
+        final NotificationChannelGroup ncg = new NotificationChannelGroup("a group", "a label");
+        ncg.setDescription("bananas");
+        final NotificationChannelGroup ncg2 = new NotificationChannelGroup("group 2", "label 2");
+        final NotificationChannel channel =
+                new NotificationChannel(mId, "name", NotificationManager.IMPORTANCE_DEFAULT);
+        channel.setGroup(ncg.getId());
+
+        mNotificationManager.createNotificationChannelGroup(ncg);
+        mNotificationManager.createNotificationChannelGroup(ncg2);
+        mNotificationManager.createNotificationChannel(channel);
+
+        NotificationChannelGroup actual =
+                mNotificationManager.getNotificationChannelGroup(ncg.getId());
+        assertEquals(ncg.getId(), actual.getId());
+        assertEquals(ncg.getName(), actual.getName());
+        assertEquals(ncg.getDescription(), actual.getDescription());
+        assertEquals(channel.getId(), actual.getChannels().get(0).getId());
+    }
+
     public void testDeleteChannelGroup() throws Exception {
         final NotificationChannelGroup ncg = new NotificationChannelGroup("a group", "a label");
         final NotificationChannel channel =
@@ -151,6 +182,46 @@
                 mNotificationManager.getNotificationChannel(mId).getImportance());
     }
 
+    public void testCreateChannel_addToGroup() throws Exception {
+        String oldGroup = null;
+        String newGroup = "new group";
+        mNotificationManager.createNotificationChannelGroup(
+                new NotificationChannelGroup(newGroup, newGroup));
+
+        NotificationChannel channel =
+                new NotificationChannel(mId, "name", NotificationManager.IMPORTANCE_DEFAULT);
+        channel.setGroup(oldGroup);
+        mNotificationManager.createNotificationChannel(channel);
+
+        channel.setGroup(newGroup);
+        mNotificationManager.createNotificationChannel(channel);
+
+        final NotificationChannel updatedChannel =
+                mNotificationManager.getNotificationChannel(mId);
+        assertEquals("Failed to add non-grouped channel to a group on update ",
+                newGroup, updatedChannel.getGroup());
+    }
+
+    public void testCreateChannel_cannotChangeGroup() throws Exception {
+        String oldGroup = "old group";
+        String newGroup = "new group";
+        mNotificationManager.createNotificationChannelGroup(
+                new NotificationChannelGroup(oldGroup, oldGroup));
+        mNotificationManager.createNotificationChannelGroup(
+                new NotificationChannelGroup(newGroup, newGroup));
+
+        NotificationChannel channel =
+                new NotificationChannel(mId, "name", NotificationManager.IMPORTANCE_DEFAULT);
+        channel.setGroup(oldGroup);
+        mNotificationManager.createNotificationChannel(channel);
+        channel.setGroup(newGroup);
+        mNotificationManager.createNotificationChannel(channel);
+        final NotificationChannel updatedChannel =
+                mNotificationManager.getNotificationChannel(mId);
+        assertEquals("Channels should not be allowed to change groups",
+                oldGroup, updatedChannel.getGroup());
+    }
+
     public void testCreateSameChannelDoesNotUpdate() throws Exception {
         final NotificationChannel channel =
                 new NotificationChannel(mId, "name", NotificationManager.IMPORTANCE_DEFAULT);
@@ -328,6 +399,54 @@
         }
     }
 
+    public void testNotify_blockedChannel() throws Exception {
+        mNotificationManager.cancelAll();
+
+        NotificationChannel channel =
+                new NotificationChannel(mId, "name", NotificationManager.IMPORTANCE_NONE);
+        mNotificationManager.createNotificationChannel(channel);
+
+        int id = 1;
+        final Notification notification =
+                new Notification.Builder(mContext, NOTIFICATION_CHANNEL_ID)
+                        .setSmallIcon(R.drawable.black)
+                        .setWhen(System.currentTimeMillis())
+                        .setContentTitle("notify#" + id)
+                        .setContentText("This is #" + id + "notification  ")
+                        .build();
+        mNotificationManager.notify(id, notification);
+
+        if (!checkNotificationExistence(id, /*shouldExist=*/ false)) {
+            fail("found unexpected notification id=" + id);
+        }
+    }
+
+    public void testNotify_blockedChannelGroup() throws Exception {
+        mNotificationManager.cancelAll();
+
+        NotificationChannelGroup group = new NotificationChannelGroup(mId, "group name");
+        group.setBlocked(true);
+        mNotificationManager.createNotificationChannelGroup(group);
+        NotificationChannel channel =
+                new NotificationChannel(mId, "name", NotificationManager.IMPORTANCE_DEFAULT);
+        channel.setGroup(mId);
+        mNotificationManager.createNotificationChannel(channel);
+
+        int id = 1;
+        final Notification notification =
+                new Notification.Builder(mContext, NOTIFICATION_CHANNEL_ID)
+                        .setSmallIcon(R.drawable.black)
+                        .setWhen(System.currentTimeMillis())
+                        .setContentTitle("notify#" + id)
+                        .setContentText("This is #" + id + "notification  ")
+                        .build();
+        mNotificationManager.notify(id, notification);
+
+        if (!checkNotificationExistence(id, /*shouldExist=*/ false)) {
+            fail("found unexpected notification id=" + id);
+        }
+    }
+
     public void testCancel() throws Exception {
         final int id = 9;
         sendNotification(id, R.drawable.black);
@@ -683,6 +802,7 @@
             found = false;
             final StatusBarNotification[] sbns = mNotificationManager.getActiveNotifications();
             for (StatusBarNotification sbn : sbns) {
+                Log.d(TAG, "Found " + sbn.getKey());
                 if (sbn.getId() == id) {
                     found = true;
                     break;
diff --git a/tests/app/src/android/app/cts/NotificationTest.java b/tests/app/src/android/app/cts/NotificationTest.java
index 25c25e3..a69a92a 100644
--- a/tests/app/src/android/app/cts/NotificationTest.java
+++ b/tests/app/src/android/app/cts/NotificationTest.java
@@ -25,12 +25,11 @@
 import android.content.Context;
 import android.content.Intent;
 import android.net.Uri;
+import android.os.Build;
 import android.os.Parcel;
 import android.test.AndroidTestCase;
 import android.widget.RemoteViews;
 
-import org.mockito.internal.matchers.Not;
-
 public class NotificationTest extends AndroidTestCase {
     private static final String TEXT_RESULT_KEY = "text";
     private static final String DATA_RESULT_KEY = "data";
@@ -252,6 +251,68 @@
                 mNotification.extras.getParcelableArray(Notification.EXTRA_HISTORIC_MESSAGES));
     }
 
+    public void testMessagingStyle_isGroupConversation() {
+        mContext.getApplicationInfo().targetSdkVersion = Build.VERSION_CODES.P;
+        Notification.MessagingStyle messagingStyle = new Notification.MessagingStyle("self name")
+                .setGroupConversation(true)
+                .setConversationTitle("test conversation title");
+        Notification notification = new Notification.Builder(mContext, "test id")
+                .setSmallIcon(1)
+                .setContentTitle("test title")
+                .setStyle(messagingStyle)
+                .build();
+
+        assertTrue(messagingStyle.isGroupConversation());
+        assertTrue(notification.extras.getBoolean(Notification.EXTRA_IS_GROUP_CONVERSATION));
+    }
+
+    public void testMessagingStyle_isGroupConversation_noConversationTitle() {
+        mContext.getApplicationInfo().targetSdkVersion = Build.VERSION_CODES.P;
+        Notification.MessagingStyle messagingStyle = new Notification.MessagingStyle("self name")
+                .setGroupConversation(true)
+                .setConversationTitle(null);
+        Notification notification = new Notification.Builder(mContext, "test id")
+                .setSmallIcon(1)
+                .setContentTitle("test title")
+                .setStyle(messagingStyle)
+                .build();
+
+        assertTrue(messagingStyle.isGroupConversation());
+        assertTrue(notification.extras.getBoolean(Notification.EXTRA_IS_GROUP_CONVERSATION));
+    }
+
+    public void testMessagingStyle_isGroupConversation_withConversationTitle_legacy() {
+        // In legacy (version < P), isGroupConversation is controlled by conversationTitle.
+        mContext.getApplicationInfo().targetSdkVersion = Build.VERSION_CODES.O;
+        Notification.MessagingStyle messagingStyle = new Notification.MessagingStyle("self name")
+                .setGroupConversation(false)
+                .setConversationTitle("test conversation title");
+        Notification notification = new Notification.Builder(mContext, "test id")
+                .setSmallIcon(1)
+                .setContentTitle("test title")
+                .setStyle(messagingStyle)
+                .build();
+
+        assertTrue(messagingStyle.isGroupConversation());
+        assertFalse(notification.extras.getBoolean(Notification.EXTRA_IS_GROUP_CONVERSATION));
+    }
+
+    public void testMessagingStyle_isGroupConversation_withoutConversationTitle_legacy() {
+        // In legacy (version < P), isGroupConversation is controlled by conversationTitle.
+        mContext.getApplicationInfo().targetSdkVersion = Build.VERSION_CODES.O;
+        Notification.MessagingStyle messagingStyle = new Notification.MessagingStyle("self name")
+                .setGroupConversation(true)
+                .setConversationTitle(null);
+        Notification notification = new Notification.Builder(mContext, "test id")
+                .setSmallIcon(1)
+                .setContentTitle("test title")
+                .setStyle(messagingStyle)
+                .build();
+
+        assertFalse(messagingStyle.isGroupConversation());
+        assertTrue(notification.extras.getBoolean(Notification.EXTRA_IS_GROUP_CONVERSATION));
+    }
+
     public void testToString() {
         mNotification = new Notification();
         assertNotNull(mNotification.toString());
diff --git a/tests/app/src/android/app/cts/ServiceTest.java b/tests/app/src/android/app/cts/ServiceTest.java
index 5f8202d..d660040 100644
--- a/tests/app/src/android/app/cts/ServiceTest.java
+++ b/tests/app/src/android/app/cts/ServiceTest.java
@@ -26,6 +26,7 @@
 import android.app.stubs.LocalForegroundService;
 import android.app.stubs.LocalGrantedService;
 import android.app.stubs.LocalService;
+import android.app.stubs.NullService;
 import android.content.ComponentName;
 import android.content.Context;
 import android.content.Intent;
@@ -36,6 +37,7 @@
 import android.os.IBinder;
 import android.os.Parcel;
 import android.os.RemoteException;
+import android.os.SystemClock;
 import android.service.notification.StatusBarNotification;
 import android.test.suitebuilder.annotation.MediumTest;
 import android.util.Log;
@@ -85,6 +87,41 @@
         }
     }
 
+    private static class NullServiceConnection implements ServiceConnection {
+        boolean mNullBinding = false;
+
+        @Override public void onServiceConnected(ComponentName name, IBinder service) {}
+        @Override public void onServiceDisconnected(ComponentName name) {}
+
+        @Override
+        public void onNullBinding(ComponentName name) {
+            synchronized (this) {
+                mNullBinding = true;
+                this.notifyAll();
+            }
+        }
+
+        public void waitForNullBinding(final long timeout) {
+            long now = SystemClock.uptimeMillis();
+            final long end = now + timeout;
+            synchronized (this) {
+                while (!mNullBinding && (now < end)) {
+                    try {
+                        this.wait(end - now);
+                    } catch (InterruptedException e) {
+                    }
+                    now = SystemClock.uptimeMillis();
+                }
+            }
+        }
+
+        public boolean nullBindingReceived() {
+            synchronized (this) {
+                return mNullBinding;
+            }
+        }
+    }
+
     private class TestConnection implements ServiceConnection {
         private final boolean mExpectDisconnect;
         private final boolean mSetReporter;
@@ -831,4 +868,28 @@
             // expected
         }
     }
+
+    /**
+     * Verify that when the requested service's onBind() returns null,
+     * the connection's onNullBinding() method is invoked.
+     */
+    @MediumTest
+    public void testNullServiceBinder() throws Exception {
+        Intent intent = new Intent(mContext, NullService.class);
+        intent.setAction("testNullServiceBinder");
+        NullServiceConnection conn1 = new NullServiceConnection();
+        NullServiceConnection conn2 = new NullServiceConnection();
+        try {
+            assertTrue(mContext.bindService(intent, conn1, Context.BIND_AUTO_CREATE));
+            conn1.waitForNullBinding(DELAY);
+            assertTrue(conn1.nullBindingReceived());
+
+            assertTrue(mContext.bindService(intent, conn2, Context.BIND_AUTO_CREATE));
+            conn2.waitForNullBinding(DELAY);
+            assertTrue(conn2.nullBindingReceived());
+        } finally {
+            mContext.unbindService(conn1);
+            mContext.unbindService(conn2);
+        }
+    }
 }
diff --git a/tests/app/src/android/app/cts/TaskDescriptionTest.java b/tests/app/src/android/app/cts/TaskDescriptionTest.java
new file mode 100644
index 0000000..408930e
--- /dev/null
+++ b/tests/app/src/android/app/cts/TaskDescriptionTest.java
@@ -0,0 +1,99 @@
+/*
+ * 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.app.cts;
+
+import static android.content.Context.ACTIVITY_SERVICE;
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertNull;
+import static org.junit.Assert.fail;
+
+import android.app.ActivityManager;
+import android.app.ActivityManager.RecentTaskInfo;
+import android.app.ActivityManager.TaskDescription;
+import android.graphics.Bitmap;
+import android.graphics.Color;
+import android.platform.test.annotations.Presubmit;
+import java.util.List;
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+import android.app.Activity;
+import android.app.stubs.MockActivity;
+import android.support.test.InstrumentationRegistry;
+import android.support.test.rule.ActivityTestRule;
+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
+ */
+@RunWith(AndroidJUnit4.class)
+@Presubmit
+public class TaskDescriptionTest {
+    private static final String TEST_LABEL = "test-label";
+    private static final int TEST_NO_DATA = 0;
+    private static final int TEST_RES_DATA = 777;
+    private static final int TEST_COLOR = Color.BLACK;
+
+    @Rule
+    public ActivityTestRule<MockActivity> mTaskDescriptionActivity =
+        new ActivityTestRule<>(MockActivity.class,
+            false /* initialTouchMode */, false /* launchActivity */);
+
+    @Test
+    public void testBitmapConstructor() throws Exception {
+        final Activity activity = mTaskDescriptionActivity.launchActivity(null);
+        final Bitmap bitmap = Bitmap.createBitmap(1, 1, Bitmap.Config.ARGB_8888);
+        bitmap.eraseColor(0);
+        activity.setTaskDescription(new TaskDescription(TEST_LABEL, bitmap, TEST_COLOR));
+        assertTaskDescription(activity, TEST_LABEL, TEST_NO_DATA);
+    }
+
+    @Test
+    public void testResourceConstructor() throws Exception {
+        final Activity activity = mTaskDescriptionActivity.launchActivity(null);
+        activity.setTaskDescription(new TaskDescription(TEST_LABEL, TEST_RES_DATA, TEST_COLOR));
+        assertTaskDescription(activity, TEST_LABEL, TEST_RES_DATA);
+    }
+
+    private void assertTaskDescription(Activity activity, String label, int resId) {
+        final ActivityManager am = (ActivityManager) activity.getSystemService(ACTIVITY_SERVICE);
+        List<RecentTaskInfo> recentsTasks = am.getRecentTasks(1 /* maxNum */, 0 /* flags */);
+        if (!recentsTasks.isEmpty()) {
+            final RecentTaskInfo info = recentsTasks.get(0);
+            if (activity.getTaskId() == info.id) {
+                final TaskDescription td = info.taskDescription;
+                assertNotNull(td);
+                if (resId == TEST_NO_DATA) {
+                    assertNotNull(td.getIcon());
+                    assertNotNull(td.getIconFilename());
+                } else {
+                    assertNull(td.getIconFilename());
+                    assertNull(td.getIcon());
+                }
+                assertEquals(resId, td.getIconResource());
+                assertEquals(label, td.getLabel());
+                return;
+            }
+        }
+        fail("Did not find activity (id=" + activity.getTaskId() + ") in recent tasks list");
+    }
+}
diff --git a/tests/app/src/android/app/cts/android/app/cts/tools/ServiceConnectionHandler.java b/tests/app/src/android/app/cts/android/app/cts/tools/ServiceConnectionHandler.java
index f5bb5a3..5a00932 100644
--- a/tests/app/src/android/app/cts/android/app/cts/tools/ServiceConnectionHandler.java
+++ b/tests/app/src/android/app/cts/android/app/cts/tools/ServiceConnectionHandler.java
@@ -32,6 +32,7 @@
 
     final Context mContext;
     final Intent mIntent;
+    final long mDefaultWaitTime;
     boolean mMonitoring;
     boolean mBound;
     IBinder mService;
@@ -47,8 +48,13 @@
     };
 
     public ServiceConnectionHandler(Context context, Intent intent) {
+        this(context, intent, 5*1000);
+    }
+
+    public ServiceConnectionHandler(Context context, Intent intent, long defaultWaitTime) {
         mContext = context;
         mIntent = intent;
+        mDefaultWaitTime = defaultWaitTime;
     }
 
     public void startMonitoring() {
@@ -64,6 +70,10 @@
         }
     }
 
+    public void waitForConnect() {
+        waitForConnect(mDefaultWaitTime);
+    }
+
     public void waitForConnect(long timeout) {
         final long endTime = SystemClock.uptimeMillis() + timeout;
 
@@ -85,6 +95,10 @@
         return mService;
     }
 
+    public void waitForDisconnect() {
+        waitForDisconnect(mDefaultWaitTime);
+    }
+
     public void waitForDisconnect(long timeout) {
         final long endTime = SystemClock.uptimeMillis() + timeout;
 
@@ -120,6 +134,10 @@
         }
     }
 
+    public void bind() {
+        bind(mDefaultWaitTime);
+    }
+
     public void bind(long timeout) {
         synchronized (this) {
             if (mBound) {
@@ -137,6 +155,10 @@
         }
     }
 
+    public void unbind() {
+        unbind(mDefaultWaitTime);
+    }
+
     public void unbind(long timeout) {
         synchronized (this) {
             if (!mBound) {
@@ -155,6 +177,10 @@
         }
     }
 
+    public void cleanup() {
+        cleanup(mDefaultWaitTime);
+    }
+
     public void cleanup(long timeout) {
         synchronized (this) {
             if (mBound) {
diff --git a/tests/app/src/android/app/cts/android/app/cts/tools/ServiceProcessController.java b/tests/app/src/android/app/cts/android/app/cts/tools/ServiceProcessController.java
index 9cad7a3..e8008df 100644
--- a/tests/app/src/android/app/cts/android/app/cts/tools/ServiceProcessController.java
+++ b/tests/app/src/android/app/cts/android/app/cts/tools/ServiceProcessController.java
@@ -42,6 +42,7 @@
     final String mMyPackageName;
     final Intent[] mServiceIntents;
     final String mServicePackage;
+    final long mDefaultWaitTime;
 
     final ActivityManager mAm;
     final Parcel mData;
@@ -54,11 +55,18 @@
     public ServiceProcessController(Context context, Instrumentation instrumentation,
             String myPackageName, Intent[] serviceIntents)
             throws IOException, PackageManager.NameNotFoundException {
+        this(context, instrumentation, myPackageName, serviceIntents, 5*1000);
+    }
+
+    public ServiceProcessController(Context context, Instrumentation instrumentation,
+            String myPackageName, Intent[] serviceIntents, long defaultWaitTime)
+            throws IOException, PackageManager.NameNotFoundException {
         mContext = context;
         mInstrumentation = instrumentation;
         mMyPackageName = myPackageName;
         mServiceIntents = serviceIntents;
         mServicePackage = mServiceIntents[0].getComponent().getPackageName();
+        mDefaultWaitTime = defaultWaitTime;
         String cmd = "pm grant " + mMyPackageName + " " + Manifest.permission.PACKAGE_USAGE_STATS;
         String result = SystemUtil.runShellCommand(mInstrumentation, cmd);
         /*
@@ -72,21 +80,26 @@
         mData = Parcel.obtain();
         mConnections = new ServiceConnectionHandler[serviceIntents.length];
         for (int i=0; i<serviceIntents.length; i++) {
-            mConnections[i] = new ServiceConnectionHandler(mContext, serviceIntents[i]);
+            mConnections[i] = new ServiceConnectionHandler(mContext, serviceIntents[i],
+                    mDefaultWaitTime);
         }
 
         ApplicationInfo appInfo = mContext.getPackageManager().getApplicationInfo(
                 mServicePackage, 0);
         mUid = appInfo.uid;
 
-        mUidForegroundListener = new UidImportanceListener(appInfo.uid);
+        mUidForegroundListener = new UidImportanceListener(appInfo.uid, mDefaultWaitTime);
         mAm.addOnUidImportanceListener(mUidForegroundListener,
                 ActivityManager.RunningAppProcessInfo.IMPORTANCE_SERVICE);
-        mUidGoneListener = new UidImportanceListener(appInfo.uid);
+        mUidGoneListener = new UidImportanceListener(appInfo.uid, mDefaultWaitTime);
         mAm.addOnUidImportanceListener(mUidGoneListener,
                 ActivityManager.RunningAppProcessInfo.IMPORTANCE_EMPTY);
 
-        mUidWatcher = new WatchUidRunner(instrumentation, appInfo.uid);
+        mUidWatcher = new WatchUidRunner(instrumentation, appInfo.uid, mDefaultWaitTime);
+    }
+
+    public void denyBackgroundOp() throws IOException {
+        denyBackgroundOp(mDefaultWaitTime);
     }
 
     public void denyBackgroundOp(long timeout) throws IOException {
@@ -123,6 +136,11 @@
         String result = SystemUtil.runShellCommand(mInstrumentation, cmd);
     }
 
+    public void removeFromTempWhitelist() throws IOException {
+        String cmd = "cmd deviceidle tempwhitelist -r " + mServicePackage;
+        SystemUtil.runShellCommand(mInstrumentation, cmd);
+    }
+
     public void cleanup() throws IOException {
         removeFromWhitelist();
         allowBackgroundOp();
@@ -152,6 +170,10 @@
         return mUidWatcher;
     }
 
+    public void ensureProcessGone() {
+        ensureProcessGone(mDefaultWaitTime);
+    }
+
     public void ensureProcessGone(long timeout) {
         for (int i=0; i<mConnections.length; i++) {
             mConnections[i].bind(timeout);
diff --git a/tests/app/src/android/app/cts/android/app/cts/tools/UidImportanceListener.java b/tests/app/src/android/app/cts/android/app/cts/tools/UidImportanceListener.java
index 3eb402a..f015441 100644
--- a/tests/app/src/android/app/cts/android/app/cts/tools/UidImportanceListener.java
+++ b/tests/app/src/android/app/cts/android/app/cts/tools/UidImportanceListener.java
@@ -25,11 +25,17 @@
  */
 public final class UidImportanceListener implements ActivityManager.OnUidImportanceListener {
     final int mUid;
+    final long mDefaultWaitTime;
 
     int mLastValue = -1;
 
     public UidImportanceListener(int uid) {
+        this(uid, 5*1000);
+    }
+
+    public UidImportanceListener(int uid, long defaultWaitTime) {
         mUid = uid;
+        mDefaultWaitTime = defaultWaitTime;
     }
 
     @Override
@@ -43,6 +49,10 @@
         }
     }
 
+    public int waitForValue(int minValue, int maxValue) {
+        return waitForValue(minValue, maxValue, mDefaultWaitTime);
+    }
+
     public int waitForValue(int minValue, int maxValue, long timeout) {
         final long endTime = SystemClock.uptimeMillis()+timeout;
 
diff --git a/tests/app/src/android/app/cts/android/app/cts/tools/WatchUidRunner.java b/tests/app/src/android/app/cts/android/app/cts/tools/WatchUidRunner.java
index e2abf67..c077d32 100644
--- a/tests/app/src/android/app/cts/android/app/cts/tools/WatchUidRunner.java
+++ b/tests/app/src/android/app/cts/android/app/cts/tools/WatchUidRunner.java
@@ -39,6 +39,8 @@
  * bit CtsAppTestCases:ActivityManagerProcessStateTest
  */
 public class WatchUidRunner {
+    static final String TAG = "WatchUidRunner";
+
     public static final int CMD_PROCSTATE = 0;
     public static final int CMD_ACTIVE = 1;
     public static final int CMD_IDLE = 2;
@@ -46,6 +48,27 @@
     public static final int CMD_CACHED = 4;
     public static final int CMD_GONE = 5;
 
+    public static final String STATE_PERSISTENT = "PER";
+    public static final String STATE_PERSISTENT_UI = "PERU";
+    public static final String STATE_TOP = "TOP";
+    public static final String STATE_BOUND_FG_SERVICE = "BFGS";
+    public static final String STATE_FG_SERVICE = "FGS";
+    public static final String STATE_TOP_SLEEPING = "TPSL";
+    public static final String STATE_IMPORTANT_FG = "IMPF";
+    public static final String STATE_IMPORTANT_BG = "IMPB";
+    public static final String STATE_TRANSIENT_BG = "TRNB";
+    public static final String STATE_BACKUP = "BKUP";
+    public static final String STATE_HEAVY_WEIGHT = "HVY";
+    public static final String STATE_SERVICE = "SVC";
+    public static final String STATE_RECEIVER = "RCVR";
+    public static final String STATE_HOME = "HOME";
+    public static final String STATE_LAST = "LAST";
+    public static final String STATE_CACHED_ACTIVITY = "CAC";
+    public static final String STATE_CACHED_ACTIVITY_CLIENT = "CACC";
+    public static final String STATE_CACHED_RECENT = "CRE";
+    public static final String STATE_CACHED_EMPTY = "CEM";
+    public static final String STATE_NONEXISTENT = "NONE";
+
     static final String[] COMMAND_TO_STRING = new String[] {
             "procstate", "active", "idle", "uncached", "cached", "gone"
     };
@@ -53,6 +76,7 @@
     final Instrumentation mInstrumentation;
     final int mUid;
     final String mUidStr;
+    final long mDefaultWaitTime;
     final Pattern mSpaceSplitter;
     final ParcelFileDescriptor mReadFd;
     final FileInputStream mReadStream;
@@ -68,9 +92,14 @@
     boolean mStopping;
 
     public WatchUidRunner(Instrumentation instrumentation, int uid) {
+        this(instrumentation, uid, 5*1000);
+    }
+
+    public WatchUidRunner(Instrumentation instrumentation, int uid, long defaultWaitTime) {
         mInstrumentation = instrumentation;
         mUid = uid;
         mUidStr = Integer.toString(uid);
+        mDefaultWaitTime = defaultWaitTime;
         mSpaceSplitter = Pattern.compile("\\s+");
         ParcelFileDescriptor[] pfds = instrumentation.getUiAutomation().executeShellCommandRw(
                 "am watch-uids");
@@ -96,6 +125,10 @@
         }
     }
 
+    public void expect(int cmd, String procState) {
+        expect(cmd, procState, mDefaultWaitTime);
+    }
+
     public void expect(int cmd, String procState, long timeout) {
         long waitUntil = SystemClock.uptimeMillis() + timeout;
         String[] line = waitForNextLine(waitUntil);
@@ -109,6 +142,10 @@
         }
     }
 
+    public void waitFor(int cmd, String procState) {
+        waitFor(cmd, procState, mDefaultWaitTime);
+    }
+
     public void waitFor(int cmd, String procState, long timeout) {
         long waitUntil = SystemClock.uptimeMillis() + timeout;
         while (true) {
@@ -120,11 +157,11 @@
                 if (line.length >= 3 && procState.equals(line[2])) {
                     return;
                 } else {
-                    Log.d("XXXX", "Skipping because procstate not " + procState + ": "
+                    Log.d(TAG, "Skipping because procstate not " + procState + ": "
                             + Arrays.toString(line));
                 }
             } else {
-                Log.d("XXXX", "Skipping because not " + COMMAND_TO_STRING[cmd] + ": "
+                Log.d(TAG, "Skipping because not " + COMMAND_TO_STRING[cmd] + ": "
                         + Arrays.toString(line));
             }
         }
@@ -170,14 +207,14 @@
             try {
                 while ((line = readNextLine()) != null) {
                     if (line.length < 2) {
-                        Log.d("XXXXX", "Skipping: " + mLastReadLine);
+                        Log.d(TAG, "Skipping too short: " + mLastReadLine);
                         continue;
                     }
                     if (!line[0].equals(mUidStr)) {
-                        Log.d("XXXXX", "Skipping: " + mLastReadLine);
+                        Log.d(TAG, "Skipping ignored uid: " + mLastReadLine);
                         continue;
                     }
-                    Log.d("XXXXX", "Enqueueing: " + mLastReadLine);
+                    Log.d(TAG, "Enqueueing: " + mLastReadLine);
                     synchronized (mPendingLines) {
                         if (mStopping) {
                             return;
@@ -187,7 +224,7 @@
                     }
                 }
             } catch (IOException e) {
-                Log.w("WatchUidRunner", "Failed reading", e);
+                Log.w(TAG, "Failed reading", e);
             }
         }
 
diff --git a/tests/aslr/AndroidTest.xml b/tests/aslr/AndroidTest.xml
index 8b9c893..87c5dfb 100644
--- a/tests/aslr/AndroidTest.xml
+++ b/tests/aslr/AndroidTest.xml
@@ -14,6 +14,7 @@
      limitations under the License.
 -->
 <configuration description="Config for CTS Aslr Malloc test cases">
+    <option name="test-suite-tag" value="cts" />
     <option name="config-descriptor:metadata" key="component" value="security" />
     <target_preparer class="com.android.compatibility.common.tradefed.targetprep.FilePusher">
         <option name="cleanup" value="true" />
diff --git a/tests/autofillservice/AndroidManifest.xml b/tests/autofillservice/AndroidManifest.xml
index b8ab1f9..0f1402a 100644
--- a/tests/autofillservice/AndroidManifest.xml
+++ b/tests/autofillservice/AndroidManifest.xml
@@ -32,6 +32,7 @@
             </intent-filter>
         </activity>
         <activity android:name=".PreFilledLoginActivity" />
+        <activity android:name=".LoginWithStringsActivity" />
         <activity android:name=".WelcomeActivity"/>
         <activity android:name=".ViewAttributesTestActivity" />
         <activity android:name=".AuthenticationActivity" />
@@ -66,6 +67,14 @@
         <activity android:name=".WebViewActivity"/>
         <activity android:name=".TrampolineWelcomeActivity"/>
         <activity android:name=".AttachedContextActivity"/>
+        <activity android:name=".DialogLauncherActivity" >
+            <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>
 
         <service
             android:name=".InstrumentedAutoFillService"
@@ -83,6 +92,15 @@
                 <action android:name="android.service.autofill.AutofillService" />
             </intent-filter>
         </service>
+        <!--  BadAutofillService does not declare the proper permission -->
+        <service
+            android:name=".BadAutofillService"
+            android:label="BadAutofillService"
+            android:permission="android.permission.BIND_AUTOFILL" >
+            <intent-filter>
+                <action android:name="android.service.autofill.AutofillService" />
+            </intent-filter>
+        </service>
     </application>
 
     <instrumentation
diff --git a/tests/autofillservice/res/layout/dialog_launcher_activity.xml b/tests/autofillservice/res/layout/dialog_launcher_activity.xml
new file mode 100644
index 0000000..62d6d9b
--- /dev/null
+++ b/tests/autofillservice/res/layout/dialog_launcher_activity.xml
@@ -0,0 +1,32 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT 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="wrap_content"
+    android:layout_height="match_parent"
+    android:focusable="true"
+    android:focusableInTouchMode="true"
+    android:importantForAutofill="noExcludeDescendants"
+    android:orientation="vertical" >
+
+    <Button
+        android:id="@+id/launch_button"
+        android:layout_width="wrap_content"
+        android:layout_height="wrap_content"
+        android:text="Launch" />
+</LinearLayout>
\ No newline at end of file
diff --git a/tests/autofillservice/res/layout/login_activity.xml b/tests/autofillservice/res/layout/login_activity.xml
index e16d1c4..2a971b1 100644
--- a/tests/autofillservice/res/layout/login_activity.xml
+++ b/tests/autofillservice/res/layout/login_activity.xml
@@ -37,6 +37,9 @@
 
         <EditText
             android:id="@+id/username"
+            android:minEms="2"
+            android:maxEms="5"
+            android:maxLength="25"
             android:layout_width="match_parent"
             android:layout_height="wrap_content" />
     </LinearLayout>
diff --git a/tests/autofillservice/res/layout/login_with_strings_activity.xml b/tests/autofillservice/res/layout/login_with_strings_activity.xml
new file mode 100644
index 0000000..2f90761
--- /dev/null
+++ b/tests/autofillservice/res/layout/login_with_strings_activity.xml
@@ -0,0 +1,104 @@
+<?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="@+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/third_line_only.xml b/tests/autofillservice/res/layout/third_line_only.xml
new file mode 100644
index 0000000..0267c94
--- /dev/null
+++ b/tests/autofillservice/res/layout/third_line_only.xml
@@ -0,0 +1,21 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+  * Copyright (C) 2017 The Android Open Source Project
+  *
+  * Licensed under the Apache License, Version 2.0 (the "License");
+  * you may not use this file except in compliance with the License.
+  * You may obtain a copy of the License at
+  *
+  *      http://www.apache.org/licenses/LICENSE-2.0
+  *
+  * Unless required by applicable law or agreed to in writing, software
+  * distributed under the License is distributed on an "AS IS" BASIS,
+  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+  * See the License for the specific language governing permissions and
+  * limitations under the License.
+  -->
+
+<TextView xmlns:android="http://schemas.android.com/apk/res/android"
+        android:id="@+id/third"
+        android:layout_width="wrap_content"
+        android:layout_height="match_parent"/>
diff --git a/tests/autofillservice/res/layout/three_horizontal_text_fields_last_two_invisible.xml b/tests/autofillservice/res/layout/three_horizontal_text_fields_last_two_invisible.xml
new file mode 100644
index 0000000..3a96389
--- /dev/null
+++ b/tests/autofillservice/res/layout/three_horizontal_text_fields_last_two_invisible.xml
@@ -0,0 +1,47 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+  * Copyright (C) 2017 The Android Open Source Project
+  *
+  * Licensed under the Apache License, Version 2.0 (the "License");
+  * you may not use this file except in compliance with the License.
+  * You may obtain a copy of the License at
+  *
+  *      http://www.apache.org/licenses/LICENSE-2.0
+  *
+  * Unless required by applicable law or agreed to in writing, software
+  * distributed under the License is distributed on an "AS IS" BASIS,
+  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+  * See the License for the specific language governing permissions and
+  * limitations under the License.
+  -->
+
+<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
+    android:orientation="horizontal"
+    android:layout_width="match_parent"
+    android:layout_height="wrap_content">
+
+    <TextView android:id="@+id/static_text"
+        android:layout_width="wrap_content"
+        android:layout_height="match_parent"
+        android:text="YO:"/>
+
+    <TextView android:id="@+id/first"
+        android:layout_width="wrap_content"
+        android:layout_height="match_parent"/>
+
+    <TextView android:id="@+id/second"
+        android:text="2ND, Y U NO HIDDEN?"
+        android:visibility="invisible"
+        android:layout_width="wrap_content"
+        android:layout_height="match_parent"/>
+
+    <TextView android:id="@+id/third"
+        android:text="3RD, Y U NO HIDDEN?"
+        android:visibility="invisible"
+        android:layout_width="wrap_content"
+        android:layout_height="match_parent"/>
+
+    <ImageView android:id="@+id/img"
+        android:layout_width="wrap_content"
+        android:layout_height="match_parent"/>
+</LinearLayout>
\ No newline at end of file
diff --git a/tests/autofillservice/res/layout/two_horizontal_text_fields.xml b/tests/autofillservice/res/layout/two_horizontal_text_fields.xml
index 944f926..773afae 100644
--- a/tests/autofillservice/res/layout/two_horizontal_text_fields.xml
+++ b/tests/autofillservice/res/layout/two_horizontal_text_fields.xml
@@ -16,6 +16,7 @@
   -->
 
 <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
+    android:id="@+id/parent"
     android:orientation="horizontal"
     android:layout_width="match_parent"
     android:layout_height="wrap_content">
diff --git a/tests/autofillservice/res/layout/webview_activity.xml b/tests/autofillservice/res/layout/webview_activity.xml
index a975186..8740273 100644
--- a/tests/autofillservice/res/layout/webview_activity.xml
+++ b/tests/autofillservice/res/layout/webview_activity.xml
@@ -14,8 +14,55 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
 -->
-<WebView  xmlns:android="http://schemas.android.com/apk/res/android"
-    android:id="@+id/webview"
-    android:layout_width="fill_parent"
-    android:layout_height="fill_parent"
-/>
+
+<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/outsideContainer1"
+        android:visibility="gone"
+        android:layout_width="match_parent"
+        android:layout_height="wrap_content"
+        android:orientation="horizontal" >
+
+        <TextView
+            android:layout_width="wrap_content"
+            android:layout_height="wrap_content"
+            android:text="Outside 1" />
+
+        <EditText
+            android:id="@+id/outside1"
+            android:layout_width="match_parent"
+            android:layout_height="wrap_content" />
+    </LinearLayout>
+
+    <LinearLayout
+        android:id="@+id/outsideContainer2"
+        android:visibility="gone"
+        android:layout_width="match_parent"
+        android:layout_height="wrap_content"
+        android:orientation="horizontal" >
+
+        <TextView
+            android:layout_width="wrap_content"
+            android:layout_height="wrap_content"
+            android:text="Outside 2" />
+
+        <EditText
+            android:id="@+id/outside2"
+            android:layout_width="match_parent"
+            android:layout_height="wrap_content" />
+    </LinearLayout>
+
+    <android.autofillservice.cts.MyWebView
+        android:id="@+id/webview"
+        android:layout_width="fill_parent"
+        android:layout_height="fill_parent"
+    />
+
+</LinearLayout>
diff --git a/tests/autofillservice/res/layout/welcome_activity.xml b/tests/autofillservice/res/layout/welcome_activity.xml
index 292aacd..a79752f 100644
--- a/tests/autofillservice/res/layout/welcome_activity.xml
+++ b/tests/autofillservice/res/layout/welcome_activity.xml
@@ -22,7 +22,7 @@
     android:orientation="vertical" >
 
     <TextView
-        android:id="@+id/output"
+        android:id="@+id/welcome"
         android:layout_width="wrap_content"
         android:layout_height="wrap_content"
         android:text="Welcome to the jungle!" />
diff --git a/tests/autofillservice/res/values/strings.xml b/tests/autofillservice/res/values/strings.xml
index 8720a71..785c7a5 100644
--- a/tests/autofillservice/res/values/strings.xml
+++ b/tests/autofillservice/res/values/strings.xml
@@ -26,4 +26,7 @@
         <item>never</item>
     </string-array>
 
+    <string name="username_string">Username</string>
+    <string name="password_string">Password</string>
+
 </resources>
\ No newline at end of file
diff --git a/tests/autofillservice/src/android/autofillservice/cts/AbstractAutoFillActivity.java b/tests/autofillservice/src/android/autofillservice/cts/AbstractAutoFillActivity.java
index efc0b2c..1de07cc 100644
--- a/tests/autofillservice/src/android/autofillservice/cts/AbstractAutoFillActivity.java
+++ b/tests/autofillservice/src/android/autofillservice/cts/AbstractAutoFillActivity.java
@@ -35,14 +35,14 @@
      * Run an action in the UI thread, and blocks caller until the action is finished.
      */
     public final void syncRunOnUiThread(Runnable action) {
-        syncRunOnUiThread(action, Helper.UI_TIMEOUT_MS);
+        syncRunOnUiThread(action, Timeouts.UI_TIMEOUT.ms());
     }
 
     /**
      * Run an action in the UI thread, and blocks caller until the action is finished or it times
      * out.
      */
-    public final void syncRunOnUiThread(Runnable action, int timeoutMs) {
+    public final void syncRunOnUiThread(Runnable action, long timeoutMs) {
         final CountDownLatch latch = new CountDownLatch(1);
         runOnUiThread(() -> {
             action.run();
diff --git a/tests/autofillservice/src/android/autofillservice/cts/AntiTrimmerTextWatcher.java b/tests/autofillservice/src/android/autofillservice/cts/AntiTrimmerTextWatcher.java
new file mode 100644
index 0000000..af713d3
--- /dev/null
+++ b/tests/autofillservice/src/android/autofillservice/cts/AntiTrimmerTextWatcher.java
@@ -0,0 +1,54 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package android.autofillservice.cts;
+
+import android.text.Editable;
+import android.text.TextWatcher;
+import android.widget.EditText;
+
+import java.util.regex.Pattern;
+
+/**
+ * A {@link TextWatcher} that appends pound signs ({@code #} at the beginning and end of the text.
+ */
+public final class AntiTrimmerTextWatcher implements TextWatcher {
+
+    /**
+     * Regex used to revert a String that was "anti-trimmed".
+     */
+    public static final Pattern TRIMMER_PATTERN = Pattern.compile("#(.*)#");
+
+    private final EditText mView;
+
+    public AntiTrimmerTextWatcher(EditText view) {
+        mView = view;
+        mView.addTextChangedListener(this);
+    }
+
+    @Override
+    public void onTextChanged(CharSequence s, int start, int before, int count) {
+        mView.removeTextChangedListener(this);
+        mView.setText("#" + s + "#");
+    }
+
+    @Override
+    public void beforeTextChanged(CharSequence s, int start, int count, int after) {
+    }
+
+    @Override
+    public void afterTextChanged(Editable s) {
+    }
+}
diff --git a/tests/autofillservice/src/android/autofillservice/cts/AttachedContextActivityTest.java b/tests/autofillservice/src/android/autofillservice/cts/AttachedContextActivityTest.java
index 45f7264..50526dd 100644
--- a/tests/autofillservice/src/android/autofillservice/cts/AttachedContextActivityTest.java
+++ b/tests/autofillservice/src/android/autofillservice/cts/AttachedContextActivityTest.java
@@ -56,7 +56,7 @@
         sReplier.getNextFillRequest();
 
         // Select dataset
-        sUiBot.selectDataset("fill me");
+        mUiBot.selectDataset("fill me");
 
         // Assert results
         fillExpectation.assertAutoFilled();
diff --git a/tests/autofillservice/src/android/autofillservice/cts/AuthenticationActivity.java b/tests/autofillservice/src/android/autofillservice/cts/AuthenticationActivity.java
index 570de4e..62072c3 100644
--- a/tests/autofillservice/src/android/autofillservice/cts/AuthenticationActivity.java
+++ b/tests/autofillservice/src/android/autofillservice/cts/AuthenticationActivity.java
@@ -50,6 +50,13 @@
     private static final String EXTRA_DATASET_ID = "dataset_id";
     private static final String EXTRA_RESPONSE_ID = "response_id";
 
+    /**
+     * When launched with this intent, it will pass it back to the
+     * {@link AutofillManager#EXTRA_CLIENT_STATE} of the result.
+     */
+    private static final String EXTRA_OUTPUT_CLIENT_STATE = "output_client_state";
+
+
     private static final int MSG_WAIT_FOR_LATCH = 1;
 
     private static Bundle sData;
@@ -84,10 +91,15 @@
      */
     public static IntentSender createSender(Context context, int id,
             CannedDataset dataset) {
+        return createSender(context, id, dataset, null);
+    }
+
+    public static IntentSender createSender(Context context, int id,
+            CannedDataset dataset, Bundle outClientState) {
         Preconditions.checkArgument(id > 0, "id must be positive");
         Preconditions.checkState(sDatasets.get(id) == null, "already have id");
         sDatasets.put(id, dataset);
-        return createSender(context, EXTRA_DATASET_ID, id);
+        return createSender(context, EXTRA_DATASET_ID, id, outClientState);
     }
 
     /**
@@ -95,15 +107,25 @@
      */
     public static IntentSender createSender(Context context, int id,
             CannedFillResponse response) {
+        return createSender(context, id, response, null);
+    }
+
+    public static IntentSender createSender(Context context, int id,
+            CannedFillResponse response, Bundle outData) {
         Preconditions.checkArgument(id > 0, "id must be positive");
         Preconditions.checkState(sResponses.get(id) == null, "already have id");
         sResponses.put(id, response);
-        return createSender(context, EXTRA_RESPONSE_ID, id);
+        return createSender(context, EXTRA_RESPONSE_ID, id, outData);
     }
 
-    private static IntentSender createSender(Context context, String extraName, int id) {
+    private static IntentSender createSender(Context context, String extraName, int id,
+            Bundle outClientState) {
         final Intent intent = new Intent(context, AuthenticationActivity.class);
         intent.putExtra(extraName, id);
+        if (outClientState != null) {
+            Log.d(TAG, "Create with " + outClientState + " as " + EXTRA_OUTPUT_CLIENT_STATE);
+            intent.putExtra(EXTRA_OUTPUT_CLIENT_STATE, outClientState);
+        }
         final PendingIntent pendingIntent = PendingIntent.getActivity(context, id, intent, 0);
         sPendingIntents.add(pendingIntent);
         return pendingIntent.getIntentSender();
@@ -213,6 +235,13 @@
         // Pass on the auth result
         final Intent intent = new Intent();
         intent.putExtra(AutofillManager.EXTRA_AUTHENTICATION_RESULT, result);
+
+        final Bundle outClientState = getIntent().getBundleExtra(EXTRA_OUTPUT_CLIENT_STATE);
+        if (outClientState != null) {
+            Log.d(TAG, "Adding " + outClientState + " as " + AutofillManager.EXTRA_CLIENT_STATE);
+            intent.putExtra(AutofillManager.EXTRA_CLIENT_STATE, outClientState);
+        }
+
         final int resultCode;
         synchronized (sLock) {
             resultCode = sResultCode;
diff --git a/tests/autofillservice/src/android/autofillservice/cts/AutoFillServiceTestCase.java b/tests/autofillservice/src/android/autofillservice/cts/AutoFillServiceTestCase.java
index 74e168b..a8d73c5 100644
--- a/tests/autofillservice/src/android/autofillservice/cts/AutoFillServiceTestCase.java
+++ b/tests/autofillservice/src/android/autofillservice/cts/AutoFillServiceTestCase.java
@@ -19,39 +19,60 @@
 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.runShellCommand;
 import static android.autofillservice.cts.Helper.setLoggingLevel;
 import static android.autofillservice.cts.InstrumentedAutoFillService.SERVICE_NAME;
-import static android.provider.Settings.Secure.AUTOFILL_SERVICE;
+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.pm.PackageManager;
+import android.provider.Settings;
 import android.support.test.InstrumentationRegistry;
 import android.support.test.runner.AndroidJUnit4;
 import android.util.Log;
 import android.widget.RemoteViews;
 
 import org.junit.After;
-import org.junit.AfterClass;
 import org.junit.Before;
 import org.junit.BeforeClass;
+import org.junit.ClassRule;
 import org.junit.Rule;
+import org.junit.rules.TestWatcher;
+import org.junit.runner.Description;
 import org.junit.runner.RunWith;
 
-import java.util.List;
-
 /**
  * Base class for all other tests.
  */
 @RunWith(AndroidJUnit4.class)
-abstract class AutoFillServiceTestCase {
+// NOTE: @ClassRule requires it to be public
+public abstract class AutoFillServiceTestCase {
     private static final String TAG = "AutoFillServiceTestCase";
 
-    protected static UiBot sUiBot;
+    static final UiBot sDefaultUiBot = new UiBot();
 
     protected static final Replier sReplier = InstrumentedAutoFillService.getReplier();
 
+    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());
+        }
+
+        @Override
+        protected void finished(Description description) {
+            JUnitHelper.setCurrentTestName(null);
+        }
+    };
+
     @Rule
     public final RetryRule mRetryRule = new RetryRule(2);
 
@@ -62,16 +83,28 @@
     public final RequiredFeatureRule mRequiredFeatureRule =
             new RequiredFeatureRule(PackageManager.FEATURE_AUTOFILL);
 
-    protected final Context mContext;
+    @Rule
+    public final SafeCleanerRule mSafeCleanerRule = new SafeCleanerRule()
+            .run(() -> sReplier.assertNumberUnhandledFillRequests(0))
+            .run(() -> sReplier.assertNumberUnhandledSaveRequests(0))
+            .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() {
-        mContext = InstrumentationRegistry.getTargetContext();
+        this(sDefaultUiBot);
+    }
+
+    protected AutoFillServiceTestCase(UiBot uiBot) {
         mPackageName = mContext.getPackageName();
+        mUiBot = uiBot;
     }
 
     @BeforeClass
@@ -85,22 +118,6 @@
         runShellCommand("cmd statusbar collapse");
     }
 
-    @BeforeClass
-    public static void setUiBot() throws Exception {
-        if (!hasAutofillFeature()) return;
-
-        sUiBot = new UiBot(InstrumentationRegistry.getInstrumentation());
-    }
-
-    @AfterClass
-    public static void resetSettings() {
-        if (!hasAutofillFeature()) return;
-
-        // Clean up only - no need to call disableService() because it doesn't need to fail if
-        // it's not reset.
-        runShellCommand("settings delete secure %s", AUTOFILL_SERVICE);
-    }
-
     @Before
     public void reset() {
         sReplier.reset();
@@ -121,6 +138,15 @@
         }
     }
 
+    /**
+     * Cleans up activities that might have been left over.
+     */
+    @Before
+    @After
+    public void finishActivities() {
+        WelcomeActivity.finishIt(mUiBot);
+    }
+
     @After
     public void resetVerboseLogging() {
         try {
@@ -130,24 +156,6 @@
         }
     }
 
-    // TODO: we shouldn't throw exceptions on @After / @AfterClass because if the test failed, these
-    // exceptions would mask the real cause. A better approach might be using a @Rule or some other
-    // visitor pattern.
-    @After
-    public void assertNothingIsPending() throws Throwable {
-        final MultipleExceptionsCatcher catcher = new MultipleExceptionsCatcher()
-            .run(() -> sReplier.assertNumberUnhandledFillRequests(0))
-            .run(() -> sReplier.assertNumberUnhandledSaveRequests(0));
-
-        final List<Exception> replierExceptions = sReplier.getExceptions();
-        if (replierExceptions != null) {
-            for (Exception e : replierExceptions) {
-                catcher.add(e);
-            }
-        }
-        catcher.throwIfAny();
-    }
-
     @After
     public void ignoreFurtherRequests() {
         InstrumentedAutoFillService.setIgnoreUnexpectedRequests(true);
diff --git a/tests/autofillservice/src/android/autofillservice/cts/AutoFinishSessionTest.java b/tests/autofillservice/src/android/autofillservice/cts/AutoFinishSessionTest.java
index b966078..43bc24f 100644
--- a/tests/autofillservice/src/android/autofillservice/cts/AutoFinishSessionTest.java
+++ b/tests/autofillservice/src/android/autofillservice/cts/AutoFinishSessionTest.java
@@ -36,6 +36,8 @@
 import org.junit.Rule;
 import org.junit.Test;
 
+import java.util.concurrent.atomic.AtomicReference;
+
 /**
  * Tests that the session finishes when the views and fragments go away
  */
@@ -62,13 +64,12 @@
 
     // firstRemove and secondRemove run in the UI Thread; firstCheck doesn't
     private void removeViewsBaseTest(@NonNull Runnable firstRemove, @Nullable Runnable firstCheck,
-            @Nullable Runnable secondRemove, String... viewsToSave)
-            throws Exception {
+            @Nullable Runnable secondRemove, String... viewsToSave) throws Exception {
         enableService();
 
         // Set expectations.
         sReplier.addResponse(new CannedFillResponse.Builder()
-                .setFlags(SaveInfo.FLAG_SAVE_ON_ALL_VIEWS_INVISIBLE)
+                .setSaveInfoFlags(SaveInfo.FLAG_SAVE_ON_ALL_VIEWS_INVISIBLE)
                 .setRequiredSavableIds(SAVE_DATA_TYPE_GENERIC, viewsToSave).build());
 
         // Trigger autofill
@@ -79,7 +80,7 @@
 
         sReplier.getNextFillRequest();
 
-        sUiBot.assertNoDatasets();
+        mUiBot.assertNoDatasets();
 
         // remove first set of views
         mActivity.syncRunOnUiThread(() -> {
@@ -99,7 +100,7 @@
         }
 
         // Save should be shows after all remove operations were executed
-        sUiBot.saveForAutofill(true, SAVE_DATA_TYPE_GENERIC);
+        mUiBot.saveForAutofill(true, SAVE_DATA_TYPE_GENERIC);
 
         SaveRequest saveRequest = sReplier.getNextSaveRequest();
         for (String view : viewsToSave) {
@@ -110,11 +111,24 @@
 
     @Test
     public void removeBothViewsToFinishSession() throws Exception {
+        final AtomicReference<Exception> ref = new AtomicReference<>();
         removeViewsBaseTest(
                 () -> ((ViewGroup) mEditText1.getParent()).removeView(mEditText1),
-                () -> sUiBot.assertSaveNotShowing(SAVE_DATA_TYPE_GENERIC),
+                () -> assertSaveNotShowing(ref),
                 () -> ((ViewGroup) mEditText2.getParent()).removeView(mEditText2),
                 "editText1", "editText2");
+        final Exception e = ref.get();
+        if (e != null) {
+            throw e;
+        }
+    }
+
+    private void assertSaveNotShowing(AtomicReference<Exception> ref) {
+        try {
+            mUiBot.assertSaveNotShowing(SAVE_DATA_TYPE_GENERIC);
+        } catch (Exception e) {
+            ref.set(e);
+        }
     }
 
     @Test
@@ -185,7 +199,7 @@
 
         // Set expectations.
         sReplier.addResponse(new CannedFillResponse.Builder()
-                .setFlags(SaveInfo.FLAG_SAVE_ON_ALL_VIEWS_INVISIBLE)
+                .setSaveInfoFlags(SaveInfo.FLAG_SAVE_ON_ALL_VIEWS_INVISIBLE)
                 .setRequiredSavableIds(SAVE_DATA_TYPE_GENERIC, "editText1").build());
 
         // Trigger autofill
@@ -196,7 +210,7 @@
 
         sReplier.getNextFillRequest();
 
-        sUiBot.assertNoDatasets();
+        mUiBot.assertNoDatasets();
 
         mActivity.syncRunOnUiThread(() -> {
             mEditText1.setText("editText1-filled");
@@ -212,20 +226,20 @@
             mActivity.syncRunOnUiThread(removeInBackGround);
         }
 
-        sUiBot.assertSaveNotShowing(SAVE_DATA_TYPE_GENERIC);
+        mUiBot.assertSaveNotShowing(SAVE_DATA_TYPE_GENERIC);
 
         // Remove previously started activity from top
-        sUiBot.selectById("android.autofillservice.cts:id/button");
+        mUiBot.selectById("android.autofillservice.cts:id/button");
         mActivity.waitUntilResumed();
 
         if (removeInForeGroup != null) {
-            sUiBot.assertSaveNotShowing(SAVE_DATA_TYPE_GENERIC);
+            mUiBot.assertSaveNotShowing(SAVE_DATA_TYPE_GENERIC);
 
             mActivity.syncRunOnUiThread(removeInForeGroup);
         }
 
         // Save should be shows after all remove operations were executed
-        sUiBot.saveForAutofill(true, SAVE_DATA_TYPE_GENERIC);
+        mUiBot.saveForAutofill(true, SAVE_DATA_TYPE_GENERIC);
 
         SaveRequest saveRequest = sReplier.getNextSaveRequest();
         assertThat(findNodeByResourceId(saveRequest.structure, "editText1")
diff --git a/tests/autofillservice/src/android/autofillservice/cts/AutofillLoggingTestRule.java b/tests/autofillservice/src/android/autofillservice/cts/AutofillLoggingTestRule.java
index 92a19a3..9c44f8f 100644
--- a/tests/autofillservice/src/android/autofillservice/cts/AutofillLoggingTestRule.java
+++ b/tests/autofillservice/src/android/autofillservice/cts/AutofillLoggingTestRule.java
@@ -16,13 +16,14 @@
 
 package android.autofillservice.cts;
 
-import static android.autofillservice.cts.Helper.runShellCommand;
+import static android.autofillservice.cts.common.ShellHelper.runShellCommand;
 
 import android.util.Log;
 
 import org.junit.rules.TestRule;
 import org.junit.runner.Description;
 import org.junit.runners.model.Statement;
+
 /**
  * Custom JUnit4 rule that improves autofill-related logging by:
  *
@@ -52,8 +53,12 @@
                 try {
                     base.evaluate();
                 } catch (Throwable t) {
-                    final String dump = runShellCommand("dumpsys autofill");
-                    Log.e(mTag, "dump for " + description.getDisplayName() + ": \n" + dump, t);
+                    final String name = description.getDisplayName();
+                    final String autofillDump = runShellCommand("dumpsys autofill");
+                    Log.e(mTag, "autofill dump for " + name + ": \n" + autofillDump, t);
+                    final String activityDump =
+                            runShellCommand("dumpsys activity android.autofillservice.cts");
+                    Log.e(mTag, "activity dump for " + name + ": \n" + activityDump, t);
                     throw t;
                 } finally {
                     if (!levelBefore.equals("verbose")) {
diff --git a/tests/autofillservice/src/android/autofillservice/cts/AutofillValueTest.java b/tests/autofillservice/src/android/autofillservice/cts/AutofillValueTest.java
index c319730..9fcf334 100644
--- a/tests/autofillservice/src/android/autofillservice/cts/AutofillValueTest.java
+++ b/tests/autofillservice/src/android/autofillservice/cts/AutofillValueTest.java
@@ -188,7 +188,7 @@
         startAutoFill(mEditText);
 
         // Autofill it.
-        sUiBot.selectDataset("dataset");
+        mUiBot.selectDataset("dataset");
 
         if (expectAutoFill) {
             // Check the results.
@@ -241,7 +241,7 @@
         startAutoFill(mCompoundButton);
 
         // Autofill it.
-        sUiBot.selectDataset("dataset");
+        mUiBot.selectDataset("dataset");
 
         if (expectAutoFill) {
             // Check the results.
@@ -294,7 +294,7 @@
         startAutoFill(mSpinner);
 
         // Autofill it.
-        sUiBot.selectDataset("dataset");
+        mUiBot.selectDataset("dataset");
 
         if (expectAutoFill) {
             // Check the results.
@@ -361,7 +361,7 @@
         startAutoFill(mEditText);
 
         // Autofill it.
-        sUiBot.selectDataset("dataset");
+        mUiBot.selectDataset("dataset");
 
         if (expectAutoFill) {
             // Check the results.
@@ -430,7 +430,7 @@
         startAutoFill(mEditText);
 
         // Autofill it.
-        sUiBot.selectDataset("dataset");
+        mUiBot.selectDataset("dataset");
 
         if (expectAutoFill) {
             // Check the results.
@@ -486,7 +486,7 @@
         startAutoFill(mEditText);
 
         // Autofill it.
-        sUiBot.selectDataset("dataset");
+        mUiBot.selectDataset("dataset");
 
         if (expectAutoFill) {
             // Check the results.
diff --git a/tests/autofillservice/src/android/autofillservice/cts/BadAutofillService.java b/tests/autofillservice/src/android/autofillservice/cts/BadAutofillService.java
new file mode 100644
index 0000000..0f862f5
--- /dev/null
+++ b/tests/autofillservice/src/android/autofillservice/cts/BadAutofillService.java
@@ -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.
+ */
+package android.autofillservice.cts;
+
+import android.os.CancellationSignal;
+import android.service.autofill.AutofillService;
+import android.service.autofill.FillCallback;
+import android.service.autofill.FillRequest;
+import android.service.autofill.SaveCallback;
+import android.service.autofill.SaveRequest;
+import android.util.Log;
+
+/**
+ * An {@link AutofillService} implementation that does fails if called upon.
+ */
+public class BadAutofillService extends AutofillService {
+
+    private static final String TAG = "BadAutofillService";
+
+    static final String SERVICE_NAME = BadAutofillService.class.getPackage().getName()
+            + "/." + BadAutofillService.class.getSimpleName();
+
+    @Override
+    public void onFillRequest(FillRequest request, CancellationSignal cancellationSignal,
+            FillCallback callback) {
+        Log.e(TAG, "onFillRequest() should never be called");
+        throw new UnsupportedOperationException("onFillRequest() should never be called");
+    }
+
+    @Override
+    public void onSaveRequest(SaveRequest request, SaveCallback callback) {
+        Log.e(TAG, "onSaveRequest() should never be called");
+        throw new UnsupportedOperationException("onSaveRequest() should never be called");
+    }
+}
diff --git a/tests/autofillservice/src/android/autofillservice/cts/BatchUpdatesTest.java b/tests/autofillservice/src/android/autofillservice/cts/BatchUpdatesTest.java
new file mode 100644
index 0000000..f17f7dc
--- /dev/null
+++ b/tests/autofillservice/src/android/autofillservice/cts/BatchUpdatesTest.java
@@ -0,0 +1,67 @@
+/*
+ * 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.autofillservice.cts;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import static org.mockito.Mockito.mock;
+import static org.testng.Assert.assertThrows;
+
+import android.service.autofill.BatchUpdates;
+import android.service.autofill.InternalTransformation;
+import android.service.autofill.Transformation;
+import android.support.test.runner.AndroidJUnit4;
+import android.widget.RemoteViews;
+
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+@RunWith(AndroidJUnit4.class)
+public class BatchUpdatesTest {
+
+    private final BatchUpdates.Builder mBuilder = new BatchUpdates.Builder();
+
+    @Test
+    public void testAddTransformation_null() {
+        assertThrows(IllegalArgumentException.class, () ->  mBuilder.transformChild(42, null));
+    }
+
+    @Test
+    public void testAddTransformation_invalidClass() {
+        assertThrows(IllegalArgumentException.class,
+                () ->  mBuilder.transformChild(42, mock(Transformation.class)));
+    }
+
+    @Test
+    public void testSetUpdateTemplate_null() {
+        assertThrows(NullPointerException.class, () ->  mBuilder.updateTemplate(null));
+    }
+
+    @Test
+    public void testEmptyObject() {
+        assertThrows(IllegalStateException.class, () ->  mBuilder.build());
+    }
+
+    @Test
+    public void testNoMoreChangesAfterBuild() {
+        assertThat(mBuilder.updateTemplate(mock(RemoteViews.class)).build()).isNotNull();
+        assertThrows(IllegalStateException.class,
+                () ->  mBuilder.updateTemplate(mock(RemoteViews.class)));
+        assertThrows(IllegalStateException.class,
+                () ->  mBuilder.transformChild(42, mock(InternalTransformation.class)));
+    }
+}
diff --git a/tests/autofillservice/src/android/autofillservice/cts/CannedFillResponse.java b/tests/autofillservice/src/android/autofillservice/cts/CannedFillResponse.java
index cf2cc6d..e51f957 100644
--- a/tests/autofillservice/src/android/autofillservice/cts/CannedFillResponse.java
+++ b/tests/autofillservice/src/android/autofillservice/cts/CannedFillResponse.java
@@ -27,8 +27,11 @@
 import android.service.autofill.Dataset;
 import android.service.autofill.FillCallback;
 import android.service.autofill.FillResponse;
+import android.service.autofill.Sanitizer;
 import android.service.autofill.SaveInfo;
+import android.service.autofill.UserData;
 import android.service.autofill.Validator;
+import android.util.Pair;
 import android.view.autofill.AutofillId;
 import android.view.autofill.AutofillValue;
 import android.widget.RemoteViews;
@@ -39,6 +42,7 @@
 import java.util.List;
 import java.util.Map;
 import java.util.function.Function;
+import java.util.regex.Pattern;
 
 /**
  * Helper class used to produce a {@link FillResponse} based on expected fields that should be
@@ -59,6 +63,7 @@
 
     private final ResponseType mResponseType;
     private final List<CannedDataset> mDatasets;
+    private final ArrayList<Pair<Sanitizer, AutofillId[]>> mSanitizers;
     private final String mFailureMessage;
     private final int mSaveType;
     private final Validator mValidator;
@@ -68,12 +73,19 @@
     private final CustomDescription mCustomDescription;
     private final Bundle mExtras;
     private final RemoteViews mPresentation;
+    private final RemoteViews mHeader;
+    private final RemoteViews mFooter;
     private final IntentSender mAuthentication;
     private final String[] mAuthenticationIds;
     private final String[] mIgnoredIds;
     private final int mNegativeActionStyle;
     private final IntentSender mNegativeActionListener;
-    private final int mFlags;
+    private final int mSaveInfoFlags;
+    private final int mFillResponseFlags;
+    private final AutofillId mSaveTriggerId;
+    private final long mDisableDuration;
+    private final AutofillId[] mFieldClassificationIds;
+    private final boolean mFieldClassificationIdsOverflow;
 
     private CannedFillResponse(Builder builder) {
         mResponseType = builder.mResponseType;
@@ -87,12 +99,20 @@
         mSaveType = builder.mSaveType;
         mExtras = builder.mExtras;
         mPresentation = builder.mPresentation;
+        mHeader = builder.mHeader;
+        mFooter = builder.mFooter;
         mAuthentication = builder.mAuthentication;
         mAuthenticationIds = builder.mAuthenticationIds;
         mIgnoredIds = builder.mIgnoredIds;
         mNegativeActionStyle = builder.mNegativeActionStyle;
         mNegativeActionListener = builder.mNegativeActionListener;
-        mFlags = builder.mFlags;
+        mSanitizers = builder.mSanitizers;
+        mSaveInfoFlags = builder.mSaveInfoFlags;
+        mFillResponseFlags = builder.mFillResponseFlags;
+        mSaveTriggerId = builder.mSaveTriggerId;
+        mDisableDuration = builder.mDisableDuration;
+        mFieldClassificationIds = builder.mFieldClassificationIds;
+        mFieldClassificationIdsOverflow = builder.mFieldClassificationIdsOverflow;
     }
 
     /**
@@ -122,7 +142,8 @@
      * structure.
      */
     FillResponse asFillResponse(Function<String, ViewNode> nodeResolver) {
-        final FillResponse.Builder builder = new FillResponse.Builder();
+        final FillResponse.Builder builder = new FillResponse.Builder()
+                .setFlags(mFillResponseFlags);
         if (mDatasets != null) {
             for (CannedDataset cannedDataset : mDatasets) {
                 final Dataset dataset = cannedDataset.asDataset(nodeResolver);
@@ -137,7 +158,7 @@
                             : new SaveInfo.Builder(mSaveType,
                                     getAutofillIds(nodeResolver, mRequiredSavableIds));
 
-            saveInfo.setFlags(mFlags);
+            saveInfo.setFlags(mSaveInfoFlags);
 
             if (mValidator != null) {
                 saveInfo.setValidator(mValidator);
@@ -153,6 +174,14 @@
             if (mCustomDescription != null) {
                 saveInfo.setCustomDescription(mCustomDescription);
             }
+
+            for (Pair<Sanitizer, AutofillId[]> sanitizer : mSanitizers) {
+                saveInfo.addSanitizer(sanitizer.first, sanitizer.second);
+            }
+
+            if (mSaveTriggerId != null) {
+                saveInfo.setTriggerId(mSaveTriggerId);
+            }
             builder.setSaveInfo(saveInfo.build());
         }
         if (mIgnoredIds != null) {
@@ -162,9 +191,29 @@
             builder.setAuthentication(getAutofillIds(nodeResolver, mAuthenticationIds),
                     mAuthentication, mPresentation);
         }
-        return builder
-                .setClientState(mExtras)
-                .build();
+        if (mDisableDuration > 0) {
+            builder.disableAutofill(mDisableDuration);
+        }
+        if (mFieldClassificationIdsOverflow) {
+            final int length = UserData.getMaxFieldClassificationIdsSize() + 1;
+            final AutofillId[] fieldIds = new AutofillId[length];
+            for (int i = 0; i < length; i++) {
+                fieldIds[i] = new AutofillId(i);
+            }
+            builder.setFieldClassificationIds(fieldIds);
+        } else if (mFieldClassificationIds != null) {
+            builder.setFieldClassificationIds(mFieldClassificationIds);
+        }
+        if (mExtras != null) {
+            builder.setClientState(mExtras);
+        }
+        if (mHeader != null) {
+            builder.setHeader(mHeader);
+        }
+        if (mFooter != null) {
+            builder.setFooter(mFooter);
+        }
+        return builder.build();
     }
 
     @Override
@@ -173,14 +222,22 @@
                 + ",datasets=" + mDatasets
                 + ", requiredSavableIds=" + Arrays.toString(mRequiredSavableIds)
                 + ", optionalSavableIds=" + Arrays.toString(mOptionalSavableIds)
-                + ", flags=" + mFlags
+                + ", saveInfoFlags=" + mSaveInfoFlags
+                + ", fillResponseFlags=" + mFillResponseFlags
                 + ", failureMessage=" + mFailureMessage
                 + ", saveDescription=" + mSaveDescription
                 + ", mCustomDescription=" + mCustomDescription
                 + ", hasPresentation=" + (mPresentation != null)
+                + ", hasHeader=" + (mHeader != null)
+                + ", hasFooter=" + (mFooter != null)
                 + ", hasAuthentication=" + (mAuthentication != null)
                 + ", authenticationIds=" + Arrays.toString(mAuthenticationIds)
                 + ", ignoredIds=" + Arrays.toString(mIgnoredIds)
+                + ", sanitizers =" + mSanitizers
+                + ", saveTriggerId=" + mSaveTriggerId
+                + ", disableDuration=" + mDisableDuration
+                + ", fieldClassificationIds=" + Arrays.toString(mFieldClassificationIds)
+                + ", fieldClassificationIdsOverflow=" + mFieldClassificationIdsOverflow
                 + "]";
     }
 
@@ -192,6 +249,7 @@
 
     static class Builder {
         private final List<CannedDataset> mDatasets = new ArrayList<>();
+        private final ArrayList<Pair<Sanitizer, AutofillId[]>> mSanitizers = new ArrayList<>();
         private final ResponseType mResponseType;
         private String mFailureMessage;
         private Validator mValidator;
@@ -202,12 +260,19 @@
         public int mSaveType = -1;
         private Bundle mExtras;
         private RemoteViews mPresentation;
+        private RemoteViews mFooter;
+        private RemoteViews mHeader;
         private IntentSender mAuthentication;
         private String[] mAuthenticationIds;
         private String[] mIgnoredIds;
         private int mNegativeActionStyle;
         private IntentSender mNegativeActionListener;
-        private int mFlags;
+        private int mSaveInfoFlags;
+        private int mFillResponseFlags;
+        private AutofillId mSaveTriggerId;
+        private long mDisableDuration;
+        private AutofillId[] mFieldClassificationIds;
+        private boolean mFieldClassificationIdsOverflow;
 
         public Builder(ResponseType type) {
             mResponseType = type;
@@ -240,8 +305,13 @@
             return this;
         }
 
-        public Builder setFlags(int flags) {
-            mFlags = flags;
+        public Builder setSaveInfoFlags(int flags) {
+            mSaveInfoFlags = flags;
+            return this;
+        }
+
+        public Builder setFillResponseFlags(int flags) {
+            mFillResponseFlags = flags;
             return this;
         }
 
@@ -312,6 +382,14 @@
             return this;
         }
 
+        /**
+         * Adds a save sanitizer.
+         */
+        public Builder addSanitizer(Sanitizer sanitizer, AutofillId... ids) {
+            mSanitizers.add(new Pair<>(sanitizer, ids));
+            return this;
+        }
+
         public CannedFillResponse build() {
             return new CannedFillResponse(this);
         }
@@ -324,6 +402,50 @@
             mFailureMessage = message;
             return this;
         }
+
+        /**
+         * Sets the view that explicitly triggers save.
+         */
+        public Builder setSaveTriggerId(AutofillId id) {
+            assertWithMessage("already set").that(mSaveTriggerId).isNull();
+            mSaveTriggerId = id;
+            return this;
+        }
+
+        public Builder disableAutofill(long duration) {
+            assertWithMessage("already set").that(mDisableDuration).isEqualTo(0L);
+            mDisableDuration = duration;
+            return this;
+        }
+
+        /**
+         * Sets the ids used for field classification.
+         */
+        public Builder setFieldClassificationIds(AutofillId... ids) {
+            assertWithMessage("already set").that(mFieldClassificationIds).isNull();
+            mFieldClassificationIds = ids;
+            return this;
+        }
+
+        /**
+         * Forces the service to throw an exception when setting the fields classification ids.
+         */
+        public Builder setFieldClassificationIdsOverflow() {
+            mFieldClassificationIdsOverflow = true;
+            return this;
+        }
+
+        public Builder setHeader(RemoteViews header) {
+            assertWithMessage("already set").that(mHeader).isNull();
+            mHeader = header;
+            return this;
+        }
+
+        public Builder setFooter(RemoteViews footer) {
+            assertWithMessage("already set").that(mFooter).isNull();
+            mFooter = footer;
+            return this;
+        }
     }
 
     /**
@@ -344,6 +466,7 @@
     static class CannedDataset {
         private final Map<String, AutofillValue> mFieldValues;
         private final Map<String, RemoteViews> mFieldPresentations;
+        private final Map<String, Pattern> mFieldFilters;
         private final RemoteViews mPresentation;
         private final IntentSender mAuthentication;
         private final String mId;
@@ -351,6 +474,7 @@
         private CannedDataset(Builder builder) {
             mFieldValues = builder.mFieldValues;
             mFieldPresentations = builder.mFieldPresentations;
+            mFieldFilters = builder.mFieldFilters;
             mPresentation = builder.mPresentation;
             mAuthentication = builder.mAuthentication;
             mId = builder.mId;
@@ -374,10 +498,19 @@
                     final AutofillId autofillid = node.getAutofillId();
                     final AutofillValue value = entry.getValue();
                     final RemoteViews presentation = mFieldPresentations.get(id);
+                    final Pattern filter = mFieldFilters.get(id);
                     if (presentation != null) {
-                        builder.setValue(autofillid, value, presentation);
+                        if (filter == null) {
+                            builder.setValue(autofillid, value, presentation);
+                        } else {
+                            builder.setValue(autofillid, value, filter, presentation);
+                        }
                     } else {
-                        builder.setValue(autofillid, value);
+                        if (filter == null) {
+                            builder.setValue(autofillid, value);
+                        } else {
+                            builder.setValue(autofillid, value, filter);
+                        }
                     }
                 }
             }
@@ -390,12 +523,15 @@
             return "CannedDataset " + mId + " : [hasPresentation=" + (mPresentation != null)
                     + ", fieldPresentations=" + (mFieldPresentations)
                     + ", hasAuthentication=" + (mAuthentication != null)
-                    + ", fieldValues=" + mFieldValues + "]";
+                    + ", fieldValues=" + mFieldValues
+                    + ", fieldFilters=" + mFieldFilters + "]";
         }
 
         static class Builder {
             private final Map<String, AutofillValue> mFieldValues = new HashMap<>();
             private final Map<String, RemoteViews> mFieldPresentations = new HashMap<>();
+            private final Map<String, Pattern> mFieldFilters = new HashMap<>();
+
             private RemoteViews mPresentation;
             private IntentSender mAuthentication;
             private String mId;
@@ -420,6 +556,17 @@
             }
 
             /**
+             * Sets the canned value of a text field based on its {@code id}.
+             *
+             * <p>The meaning of the id is defined by the object using the canned dataset.
+             * For example, {@link InstrumentedAutoFillService.Replier} resolves the id based on
+             * {@link IdMode}.
+             */
+            public Builder setField(String id, String text, Pattern filter) {
+                return setField(id, AutofillValue.forText(text), filter);
+            }
+
+            /**
              * Sets the canned value of a list field based on its its {@code id}.
              *
              * <p>The meaning of the id is defined by the object using the canned dataset.
@@ -465,6 +612,19 @@
             }
 
             /**
+             * Sets the canned value of a date field based on its {@code id}.
+             *
+             * <p>The meaning of the id is defined by the object using the canned dataset.
+             * For example, {@link InstrumentedAutoFillService.Replier} resolves the id based on
+             * {@link IdMode}.
+             */
+            public Builder setField(String id, AutofillValue value, Pattern filter) {
+                setField(id, value);
+                mFieldFilters.put(id, filter);
+                return this;
+            }
+
+            /**
              * Sets the canned value of a field based on its {@code id}.
              *
              * <p>The meaning of the id is defined by the object using the canned dataset.
@@ -478,6 +638,20 @@
             }
 
             /**
+             * Sets the canned value of a field based on its {@code id}.
+             *
+             * <p>The meaning of the id is defined by the object using the canned dataset.
+             * For example, {@link InstrumentedAutoFillService.Replier} resolves the id based on
+             * {@link IdMode}.
+             */
+            public Builder setField(String id, String text, RemoteViews presentation,
+                    Pattern filter) {
+                setField(id, text, presentation);
+                mFieldFilters.put(id, filter);
+                return this;
+            }
+
+            /**
              * Sets the view to present the response in the UI.
              */
             public Builder setPresentation(RemoteViews presentation) {
diff --git a/tests/autofillservice/src/android/autofillservice/cts/CheckoutActivity.java b/tests/autofillservice/src/android/autofillservice/cts/CheckoutActivity.java
index 84be355..141b8d0 100644
--- a/tests/autofillservice/src/android/autofillservice/cts/CheckoutActivity.java
+++ b/tests/autofillservice/src/android/autofillservice/cts/CheckoutActivity.java
@@ -21,6 +21,7 @@
 
 import android.content.Intent;
 import android.os.Bundle;
+import android.util.Log;
 import android.widget.ArrayAdapter;
 import android.widget.Button;
 import android.widget.CheckBox;
@@ -45,6 +46,7 @@
  * </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";
@@ -104,9 +106,18 @@
         mClearButton.setOnClickListener((v) -> resetFields());
     }
 
-    static void finishIt() {
+    @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);
         }
     }
 
diff --git a/tests/autofillservice/src/android/autofillservice/cts/CheckoutActivityTest.java b/tests/autofillservice/src/android/autofillservice/cts/CheckoutActivityTest.java
index 59b0b7c..a3ce455 100644
--- a/tests/autofillservice/src/android/autofillservice/cts/CheckoutActivityTest.java
+++ b/tests/autofillservice/src/android/autofillservice/cts/CheckoutActivityTest.java
@@ -44,13 +44,13 @@
 import android.autofillservice.cts.InstrumentedAutoFillService.SaveRequest;
 import android.service.autofill.CharSequenceTransformation;
 import android.service.autofill.CustomDescription;
+import android.service.autofill.ImageTransformation;
 import android.support.test.uiautomator.By;
 import android.support.test.uiautomator.UiObject2;
 import android.widget.ArrayAdapter;
 import android.widget.RemoteViews;
 import android.widget.Spinner;
 
-import org.junit.After;
 import org.junit.Before;
 import org.junit.Rule;
 import org.junit.Test;
@@ -74,11 +74,6 @@
         mActivity = mActivityRule.getActivity();
     }
 
-    @After
-    public void finishWelcomeActivity() {
-        WelcomeActivity.finishIt();
-    }
-
     @Test
     public void testAutofill() throws Exception {
         // Set service.
@@ -112,7 +107,7 @@
                 .inOrder();
 
         // Auto-fill it.
-        sUiBot.selectDataset("ACME CC");
+        mUiBot.selectDataset("ACME CC");
 
         // Check the results.
         mActivity.assertAutoFilled();
@@ -152,7 +147,7 @@
         assertWithMessage("ccExpirationNode.getAutoFillOptions()").that(options).isNull();
 
         // Auto-fill it.
-        sUiBot.selectDataset("ACME CC");
+        mUiBot.selectDataset("ACME CC");
 
         // Check the results.
         mActivity.assertAutoFilled();
@@ -195,7 +190,7 @@
                 .containsExactly("never", "today", "tomorrow", "yesterday").inOrder();
 
         // Auto-fill it.
-        sUiBot.selectDataset("ACME CC");
+        mUiBot.selectDataset("ACME CC");
 
         // Check the results.
         mActivity.assertAutoFilled();
@@ -235,7 +230,7 @@
         mActivity.onAddress((v) -> v.check(R.id.work_address));
         mActivity.onSaveCc((v) -> v.setChecked(false));
         mActivity.tapBuy();
-        sUiBot.saveForAutofill(true, SAVE_DATA_TYPE_CREDIT_CARD);
+        mUiBot.saveForAutofill(true, SAVE_DATA_TYPE_CREDIT_CARD);
         final SaveRequest saveRequest = sReplier.getNextSaveRequest();
 
         // Assert sanitization on save: everything should be available!
@@ -249,11 +244,20 @@
         assertToggleValue(findNodeByResourceId(saveRequest.structure, ID_SAVE_CC), false);
     }
 
+    @Test
+    public void testCustomizedSaveUi() throws Exception {
+        customizedSaveUi(false);
+    }
+
+    @Test
+    public void testCustomizedSaveUiWithContentDescription() throws Exception {
+        customizedSaveUi(true);
+    }
+
     /**
      * Tests that a spinner can be used on custom save descriptions.
      */
-    @Test
-    public void testCustomizedSaveUi() throws Exception {
+    private void customizedSaveUi(boolean withContentDescription) throws Exception {
         // Set service.
         enableService();
 
@@ -268,9 +272,18 @@
         final CharSequenceTransformation trans2 = new CharSequenceTransformation
                 .Builder(mActivity.getCcExpiration().getAutofillId(), Pattern.compile("(.*)"), "$1")
                 .build();
+        final ImageTransformation trans3 = (withContentDescription
+                ? new ImageTransformation.Builder(mActivity.getCcNumber().getAutofillId(),
+                        Pattern.compile("(.*)"), R.drawable.android,
+                        "One image is worth thousand words")
+                : new ImageTransformation.Builder(mActivity.getCcNumber().getAutofillId(),
+                        Pattern.compile("(.*)"), R.drawable.android))
+                .build();
+
         final CustomDescription customDescription = new CustomDescription.Builder(presentation)
                 .addChild(R.id.first, trans1)
                 .addChild(R.id.second, trans2)
+                .addChild(R.id.img, trans3)
                 .build();
 
         sReplier.addResponse(new CannedFillResponse.Builder()
@@ -291,10 +304,10 @@
         mActivity.tapBuy();
 
         // First make sure the UI is shown...
-        final UiObject2 saveUi = sUiBot.assertSaveShowing(SAVE_DATA_TYPE_CREDIT_CARD);
+        final UiObject2 saveUi = mUiBot.assertSaveShowing(SAVE_DATA_TYPE_CREDIT_CARD);
 
         // Then make sure it does have the custom views on it...
-        final UiObject2 staticText = saveUi.findObject(By.res(packageName, "static_text"));
+        final UiObject2 staticText = saveUi.findObject(By.res(packageName, Helper.ID_STATIC_TEXT));
         assertThat(staticText).isNotNull();
         assertThat(staticText.getText()).isEqualTo("YO:");
 
@@ -305,6 +318,15 @@
         final UiObject2 expiration = saveUi.findObject(By.res(packageName, "second"));
         assertThat(expiration).isNotNull();
         assertThat(expiration.getText()).isEqualTo("today");
+
+        final UiObject2 image = saveUi.findObject(By.res(packageName, "img"));
+        assertThat(image).isNotNull();
+        final String contentDescription = image.getContentDescription();
+        if (withContentDescription) {
+            assertThat(contentDescription).isEqualTo("One image is worth thousand words");
+        } else {
+            assertThat(contentDescription).isNull();
+        }
     }
 
     /**
@@ -353,9 +375,9 @@
         mActivity.tapBuy();
 
         // First make sure the UI is shown...
-        final UiObject2 saveUi = sUiBot.assertSaveShowing(SAVE_DATA_TYPE_CREDIT_CARD);
+        final UiObject2 saveUi = mUiBot.assertSaveShowing(SAVE_DATA_TYPE_CREDIT_CARD);
 
         // Then make sure it does not have the custom views on it...
-        assertThat(saveUi.findObject(By.res(packageName, "static_text"))).isNull();
+        assertThat(saveUi.findObject(By.res(packageName, Helper.ID_STATIC_TEXT))).isNull();
     }
 }
diff --git a/tests/autofillservice/src/android/autofillservice/cts/CustomDescriptionTest.java b/tests/autofillservice/src/android/autofillservice/cts/CustomDescriptionTest.java
index b7acfc6..92a379c 100644
--- a/tests/autofillservice/src/android/autofillservice/cts/CustomDescriptionTest.java
+++ b/tests/autofillservice/src/android/autofillservice/cts/CustomDescriptionTest.java
@@ -25,9 +25,12 @@
 import static com.google.common.truth.Truth.assertThat;
 import static com.google.common.truth.Truth.assertWithMessage;
 
+import android.service.autofill.BatchUpdates;
 import android.service.autofill.CharSequenceTransformation;
 import android.service.autofill.CustomDescription;
 import android.service.autofill.ImageTransformation;
+import android.service.autofill.RegexValidator;
+import android.service.autofill.Validator;
 import android.support.annotation.NonNull;
 import android.support.annotation.Nullable;
 import android.support.test.uiautomator.By;
@@ -36,7 +39,6 @@
 import android.view.autofill.AutofillId;
 import android.widget.RemoteViews;
 
-import org.junit.After;
 import org.junit.Before;
 import org.junit.Rule;
 import org.junit.Test;
@@ -47,7 +49,7 @@
 public class CustomDescriptionTest extends AutoFillServiceTestCase {
     @Rule
     public final AutofillActivityTestRule<LoginActivity> mActivityRule =
-        new AutofillActivityTestRule<>(LoginActivity.class);
+            new AutofillActivityTestRule<>(LoginActivity.class);
 
     private LoginActivity mActivity;
 
@@ -56,11 +58,6 @@
         mActivity = mActivityRule.getActivity();
     }
 
-    @After
-    public void finishWelcomeActivity() {
-        WelcomeActivity.finishIt();
-    }
-
     /**
      * Base test
      *
@@ -96,7 +93,7 @@
             uiVerifier.run();
         }
 
-        sUiBot.saveForAutofill(true, SAVE_DATA_TYPE_GENERIC);
+        mUiBot.saveForAutofill(true, SAVE_DATA_TYPE_GENERIC);
         sReplier.getNextSaveRequest();
 
         assertNoDanglingSessions();
@@ -105,13 +102,13 @@
     @Test
     public void validTransformation() throws Exception {
         testCustomDescription((usernameId, passwordId) -> {
-            RemoteViews presentation = new RemoteViews(getContext().getPackageName(),
-                    R.layout.two_horizontal_text_fields);
+            RemoteViews presentation = newTemplate(R.layout.two_horizontal_text_fields);
 
             CharSequenceTransformation trans1 = new CharSequenceTransformation
                     .Builder(usernameId, Pattern.compile("(.*)"), "$1")
                     .addField(passwordId, Pattern.compile(".*(..)"), "..$1")
                     .build();
+            @SuppressWarnings("deprecation")
             ImageTransformation trans2 = new ImageTransformation
                     .Builder(usernameId, Pattern.compile(".*"),
                     R.drawable.android).build();
@@ -120,31 +117,253 @@
                     .addChild(R.id.first, trans1)
                     .addChild(R.id.img, trans2)
                     .build();
-        }, () -> assertSaveUiWithCustomDescriptionIsShown("usernm..wd"));
+        }, () -> assertSaveUiIsShownWithTwoLines("usernm..wd"));
+    }
+
+    @Test
+    public void validTransformationWithOneTemplateUpdate() throws Exception {
+        testCustomDescription((usernameId, passwordId) -> {
+            RemoteViews presentation = newTemplate(R.layout.two_horizontal_text_fields);
+
+            CharSequenceTransformation trans1 = new CharSequenceTransformation
+                    .Builder(usernameId, Pattern.compile("(.*)"), "$1")
+                    .addField(passwordId, Pattern.compile(".*(..)"), "..$1")
+                    .build();
+            @SuppressWarnings("deprecation")
+            ImageTransformation trans2 = new ImageTransformation
+                    .Builder(usernameId, Pattern.compile(".*"),
+                    R.drawable.android).build();
+            RemoteViews update = newTemplate(0); // layout id not really used
+            update.setViewVisibility(R.id.second, View.GONE);
+            Validator condition = new RegexValidator(usernameId, Pattern.compile(".*"));
+
+            return new CustomDescription.Builder(presentation)
+                    .addChild(R.id.first, trans1)
+                    .addChild(R.id.img, trans2)
+                    .batchUpdate(condition,
+                            new BatchUpdates.Builder().updateTemplate(update).build())
+                    .build();
+        }, () -> assertSaveUiIsShownWithJustOneLine("usernm..wd"));
+    }
+
+    @Test
+    public void validTransformationWithMultipleTemplateUpdates() throws Exception {
+        testCustomDescription((usernameId, passwordId) -> {
+            RemoteViews presentation = newTemplate(R.layout.two_horizontal_text_fields);
+
+            CharSequenceTransformation trans1 = new CharSequenceTransformation.Builder(usernameId,
+                    Pattern.compile("(.*)"), "$1")
+                            .addField(passwordId, Pattern.compile(".*(..)"), "..$1")
+                            .build();
+            @SuppressWarnings("deprecation")
+            ImageTransformation trans2 = new ImageTransformation.Builder(usernameId,
+                    Pattern.compile(".*"), R.drawable.android)
+                    .build();
+
+            Validator validCondition = new RegexValidator(usernameId, Pattern.compile(".*"));
+            Validator invalidCondition = new RegexValidator(usernameId, Pattern.compile("D'OH"));
+
+            // Line 1 updates
+            RemoteViews update1 = newTemplate(666); // layout id not really used
+            update1.setContentDescription(R.id.first, "First am I"); // valid
+            RemoteViews update2 = newTemplate(0); // layout id not really used
+            update2.setViewVisibility(R.id.first, View.GONE); // invalid
+
+            // Line 2 updates
+            RemoteViews update3 = newTemplate(-666); // layout id not really used
+            update3.setTextViewText(R.id.second, "First of his second name"); // valid
+            RemoteViews update4 = newTemplate(0); // layout id not really used
+            update4.setTextViewText(R.id.second, "SECOND of his second name"); // invalid
+
+            return new CustomDescription.Builder(presentation)
+                    .addChild(R.id.first, trans1)
+                    .addChild(R.id.img, trans2)
+                    .batchUpdate(validCondition,
+                            new BatchUpdates.Builder().updateTemplate(update1).build())
+                    .batchUpdate(invalidCondition,
+                            new BatchUpdates.Builder().updateTemplate(update2).build())
+                    .batchUpdate(validCondition,
+                            new BatchUpdates.Builder().updateTemplate(update3).build())
+                    .batchUpdate(invalidCondition,
+                            new BatchUpdates.Builder().updateTemplate(update4).build())
+                    .build();
+        }, () -> assertSaveUiWithLinesIsShown(
+                (line1) -> assertWithMessage("Wrong content description for line1")
+                        .that(line1.getContentDescription()).isEqualTo("First am I"),
+                (line2) -> assertWithMessage("Wrong text for line2").that(line2.getText())
+                        .isEqualTo("First of his second name"),
+                null));
+    }
+
+    @Test
+    public void testMultipleBatchUpdates_noConditionPass() throws Exception {
+        multipleBatchUpdatesTest(BatchUpdatesConditionType.NONE_PASS);
+    }
+
+    @Test
+    public void testMultipleBatchUpdates_secondConditionPass() throws Exception {
+        multipleBatchUpdatesTest(BatchUpdatesConditionType.SECOND_PASS);
+    }
+
+    @Test
+    public void testMultipleBatchUpdates_thirdConditionPass() throws Exception {
+        multipleBatchUpdatesTest(BatchUpdatesConditionType.THIRD_PASS);
+    }
+
+    @Test
+    public void testMultipleBatchUpdates_allConditionsPass() throws Exception {
+        multipleBatchUpdatesTest(BatchUpdatesConditionType.ALL_PASS);
+    }
+
+    private enum BatchUpdatesConditionType {
+        NONE_PASS,
+        SECOND_PASS,
+        THIRD_PASS,
+        ALL_PASS
+    }
+
+    /**
+     * Tests a custom description that has 3 transformations, one applied directly and the other
+     * 2 in batch updates.
+     *
+     * @param conditionsType defines which batch updates conditions will pass.
+     */
+    private void multipleBatchUpdatesTest(BatchUpdatesConditionType conditionsType)
+            throws Exception {
+
+        final boolean line2Pass = conditionsType == BatchUpdatesConditionType.SECOND_PASS
+                || conditionsType == BatchUpdatesConditionType.ALL_PASS;
+        final boolean line3Pass = conditionsType == BatchUpdatesConditionType.THIRD_PASS
+                || conditionsType == BatchUpdatesConditionType.ALL_PASS;
+
+        final Visitor<UiObject2> line1Visitor = (line1) -> assertWithMessage("Wrong text for line1")
+                .that(line1.getText()).isEqualTo("L1-u");
+
+        final Visitor<UiObject2> line2Visitor;
+        if (line2Pass) {
+            line2Visitor = (line2) -> assertWithMessage("Wrong text for line2")
+                    .that(line2.getText()).isEqualTo("L2-u");
+        } else {
+            line2Visitor = null;
+        }
+
+        final Visitor<UiObject2> line3Visitor;
+        if (line3Pass) {
+            line3Visitor = (line3) -> assertWithMessage("Wrong text for line3")
+                    .that(line3.getText()).isEqualTo("L3-p");
+        } else {
+            line3Visitor = null;
+        }
+
+        testCustomDescription((usernameId, passwordId) -> {
+            Validator validCondition = new RegexValidator(usernameId, Pattern.compile(".*"));
+            Validator invalidCondition = new RegexValidator(usernameId, Pattern.compile("D'OH"));
+            Pattern firstCharGroupRegex = Pattern.compile("^(.).*$");
+
+            final RemoteViews presentation =
+                    newTemplate(R.layout.three_horizontal_text_fields_last_two_invisible);
+
+            final CharSequenceTransformation line1Transformation =
+                    new CharSequenceTransformation.Builder(usernameId, firstCharGroupRegex, "L1-$1")
+                        .build();
+
+            final CharSequenceTransformation line2Transformation =
+                    new CharSequenceTransformation.Builder(usernameId, firstCharGroupRegex, "L2-$1")
+                        .build();
+            final RemoteViews line2Updates = newTemplate(666); // layout id not really used
+            line2Updates.setViewVisibility(R.id.second, View.VISIBLE);
+
+            final CharSequenceTransformation line3Transformation =
+                    new CharSequenceTransformation.Builder(passwordId, firstCharGroupRegex, "L3-$1")
+                        .build();
+            final RemoteViews line3Updates = newTemplate(666); // layout id not really used
+            line3Updates.setViewVisibility(R.id.third, View.VISIBLE);
+
+            return new CustomDescription.Builder(presentation)
+                    .addChild(R.id.first, line1Transformation)
+                    .batchUpdate(line2Pass ? validCondition : invalidCondition,
+                            new BatchUpdates.Builder()
+                            .transformChild(R.id.second, line2Transformation)
+                            .updateTemplate(line2Updates)
+                            .build())
+                    .batchUpdate(line3Pass ? validCondition : invalidCondition,
+                            new BatchUpdates.Builder()
+                            .transformChild(R.id.third, line3Transformation)
+                            .updateTemplate(line3Updates)
+                            .build())
+                    .build();
+        }, () -> assertSaveUiWithLinesIsShown(line1Visitor, line2Visitor, line3Visitor));
+    }
+
+    @Test
+    public void testBatchUpdatesApplyUpdateFirstThenTransformations() throws Exception {
+
+        final Visitor<UiObject2> line1Visitor = (line1) -> assertWithMessage("Wrong text for line1")
+                .that(line1.getText()).isEqualTo("L1-u");
+        final Visitor<UiObject2> line2Visitor = (line2) -> assertWithMessage("Wrong text for line2")
+                .that(line2.getText()).isEqualTo("L2-u");
+        final Visitor<UiObject2> line3Visitor = (line3) -> assertWithMessage("Wrong text for line3")
+                .that(line3.getText()).isEqualTo("L3-p");
+
+        testCustomDescription((usernameId, passwordId) -> {
+            Validator validCondition = new RegexValidator(usernameId, Pattern.compile(".*"));
+            Validator invalidCondition = new RegexValidator(usernameId, Pattern.compile("D'OH"));
+            Pattern firstCharGroupRegex = Pattern.compile("^(.).*$");
+
+            final RemoteViews presentation =
+                    newTemplate(R.layout.two_horizontal_text_fields);
+
+            final CharSequenceTransformation line1Transformation =
+                    new CharSequenceTransformation.Builder(usernameId, firstCharGroupRegex, "L1-$1")
+                        .build();
+
+            final CharSequenceTransformation line2Transformation =
+                    new CharSequenceTransformation.Builder(usernameId, firstCharGroupRegex, "L2-$1")
+                        .build();
+
+            final CharSequenceTransformation line3Transformation =
+                    new CharSequenceTransformation.Builder(passwordId, firstCharGroupRegex, "L3-$1")
+                        .build();
+            final RemoteViews line3Presentation = newTemplate(R.layout.third_line_only);
+            final RemoteViews line3Updates = newTemplate(666); // layout id not really used
+            line3Updates.addView(R.id.parent, line3Presentation);
+
+            return new CustomDescription.Builder(presentation)
+                    .addChild(R.id.first, line1Transformation)
+                    .batchUpdate(validCondition,
+                            new BatchUpdates.Builder()
+                            .transformChild(R.id.second, line2Transformation)
+                            .build())
+                    .batchUpdate(validCondition,
+                            new BatchUpdates.Builder()
+                            .updateTemplate(line3Updates)
+                            .transformChild(R.id.third, line3Transformation)
+                            .build())
+                    .build();
+        }, () -> assertSaveUiWithLinesIsShown(line1Visitor, line2Visitor, line3Visitor));
     }
 
     @Test
     public void badImageTransformation() throws Exception {
         testCustomDescription((usernameId, passwordId) -> {
-            RemoteViews presentation = new RemoteViews(getContext().getPackageName(),
-                    R.layout.two_horizontal_text_fields);
+            RemoteViews presentation = newTemplate(R.layout.two_horizontal_text_fields);
 
-            ImageTransformation trans = new ImageTransformation
-                    .Builder(usernameId, Pattern.compile(".*"), 1)
-                    .build();
+            @SuppressWarnings("deprecation")
+            ImageTransformation trans = new ImageTransformation.Builder(usernameId,
+                    Pattern.compile(".*"), 1).build();
 
             return new CustomDescription.Builder(presentation)
                     .addChild(R.id.img, trans)
                     .build();
-        }, () -> assertSaveUiWithCustomDescriptionIsShown() );
+        }, () -> assertSaveUiWithCustomDescriptionIsShown());
     }
 
     @Test
     public void unusedImageTransformation() throws Exception {
         testCustomDescription((usernameId, passwordId) -> {
-            RemoteViews presentation = new RemoteViews(getContext().getPackageName(),
-                    R.layout.two_horizontal_text_fields);
+            RemoteViews presentation = newTemplate(R.layout.two_horizontal_text_fields);
 
+            @SuppressWarnings("deprecation")
             ImageTransformation trans = new ImageTransformation
                     .Builder(usernameId, Pattern.compile("invalid"), R.drawable.android)
                     .build();
@@ -158,9 +377,9 @@
     @Test
     public void applyImageTransformationToTextView() throws Exception {
         testCustomDescription((usernameId, passwordId) -> {
-            RemoteViews presentation = new RemoteViews(getContext().getPackageName(),
-                    R.layout.two_horizontal_text_fields);
+            RemoteViews presentation = newTemplate(R.layout.two_horizontal_text_fields);
 
+            @SuppressWarnings("deprecation")
             ImageTransformation trans = new ImageTransformation
                     .Builder(usernameId, Pattern.compile(".*"), R.drawable.android)
                     .build();
@@ -174,8 +393,7 @@
     @Test
     public void failFirstFailAll() throws Exception {
         testCustomDescription((usernameId, passwordId) -> {
-            RemoteViews presentation = new RemoteViews(getContext().getPackageName(),
-                    R.layout.two_horizontal_text_fields);
+            RemoteViews presentation = newTemplate(R.layout.two_horizontal_text_fields);
 
             CharSequenceTransformation trans = new CharSequenceTransformation
                     .Builder(usernameId, Pattern.compile("(.*)"), "$42")
@@ -191,8 +409,7 @@
     @Test
     public void failSecondFailAll() throws Exception {
         testCustomDescription((usernameId, passwordId) -> {
-            RemoteViews presentation = new RemoteViews(getContext().getPackageName(),
-                    R.layout.two_horizontal_text_fields);
+            RemoteViews presentation = newTemplate(R.layout.two_horizontal_text_fields);
 
             CharSequenceTransformation trans = new CharSequenceTransformation
                     .Builder(usernameId, Pattern.compile("(.*)"), "$1")
@@ -208,8 +425,7 @@
     @Test
     public void applyCharSequenceTransformationToImageView() throws Exception {
         testCustomDescription((usernameId, passwordId) -> {
-            RemoteViews presentation = new RemoteViews(getContext().getPackageName(),
-                    R.layout.two_horizontal_text_fields);
+            RemoteViews presentation = newTemplate(R.layout.two_horizontal_text_fields);
 
             CharSequenceTransformation trans = new CharSequenceTransformation
                     .Builder(usernameId, Pattern.compile("(.*)"), "$1")
@@ -232,8 +448,7 @@
         final CharSequenceTransformation secondTrans = new CharSequenceTransformation
                 .Builder(usernameId, Pattern.compile("(MARCO)"), "POLO")
                 .build();
-        final RemoteViews presentation = new RemoteViews(getContext().getPackageName(),
-                R.layout.two_horizontal_text_fields);
+        RemoteViews presentation = newTemplate(R.layout.two_horizontal_text_fields);
         final CustomDescription customDescription = new CustomDescription.Builder(presentation)
                 .addChild(R.id.first, firstTrans)
                 .addChild(R.id.first, secondTrans)
@@ -256,7 +471,7 @@
         mActivity.tapLogin();
 
         final String expectedText = matchFirst ? "polo" : "POLO";
-        assertSaveUiWithCustomDescriptionIsShown(expectedText);
+        assertSaveUiIsShownWithTwoLines(expectedText);
     }
 
     @Test
@@ -269,18 +484,30 @@
         multipleTransformationsForSameFieldTest(false);
     }
 
+    private RemoteViews newTemplate(int resourceId) {
+        return new RemoteViews(getContext().getPackageName(), resourceId);
+    }
+
+    private UiObject2 assertSaveUiShowing() {
+        try {
+            return mUiBot.assertSaveShowing(SAVE_DATA_TYPE_GENERIC);
+        } catch (Exception e) {
+            throw new RuntimeException(e);
+        }
+    }
+
     private void assertSaveUiWithoutCustomDescriptionIsShown() {
         // First make sure the UI is shown...
-        final UiObject2 saveUi = sUiBot.assertSaveShowing(SAVE_DATA_TYPE_GENERIC);
+        final UiObject2 saveUi = assertSaveUiShowing();
 
         // Then make sure it does not have the custom view on it.
-        assertWithMessage("found static_text on SaveUI (%s)", sUiBot.getChildrenAsText(saveUi))
+        assertWithMessage("found static_text on SaveUI (%s)", mUiBot.getChildrenAsText(saveUi))
             .that(saveUi.findObject(By.res(mPackageName, "static_text"))).isNull();
     }
 
     private UiObject2 assertSaveUiWithCustomDescriptionIsShown() {
         // First make sure the UI is shown...
-        final UiObject2 saveUi = sUiBot.assertSaveShowing(SAVE_DATA_TYPE_GENERIC);
+        final UiObject2 saveUi = assertSaveUiShowing();
 
         // Then make sure it does have the custom view on it...
         final UiObject2 staticText = saveUi.findObject(By.res(mPackageName, "static_text"));
@@ -290,10 +517,59 @@
         return saveUi;
     }
 
-    private void assertSaveUiWithCustomDescriptionIsShown(String expectedText) {
+    /**
+     * Asserts the save ui only has {@code first} and {@code second} lines (i.e, {@code third} is
+     * invisible), but only {@code first} has text.
+     */
+    private UiObject2 assertSaveUiIsShownWithTwoLines(String expectedTextOnFirst) {
+        return assertSaveUiWithLinesIsShown(
+                (line1) -> assertWithMessage("Wrong text for child with id 'first'")
+                        .that(line1.getText()).isEqualTo(expectedTextOnFirst),
+                (line2) -> assertWithMessage("Wrong text for child with id 'second'")
+                        .that(line2.getText()).isNull(),
+                null);
+    }
+
+    /**
+     * Asserts the save ui only has {@code first} line (i.e., {@code second} and {@code third} are
+     * invisible).
+     */
+    private void assertSaveUiIsShownWithJustOneLine(String expectedTextOnFirst) {
+        assertSaveUiWithLinesIsShown(
+                (line1) -> assertWithMessage("Wrong text for child with id 'first'")
+                        .that(line1.getText()).isEqualTo(expectedTextOnFirst),
+                null, null);
+    }
+
+    private UiObject2 assertSaveUiWithLinesIsShown(@Nullable Visitor<UiObject2> line1Visitor,
+            @Nullable Visitor<UiObject2> line2Visitor, @Nullable Visitor<UiObject2> line3Visitor) {
         final UiObject2 saveUi = assertSaveUiWithCustomDescriptionIsShown();
-        assertWithMessage("didn't find '%s' on SaveUI (%s)", expectedText,
-                sUiBot.getChildrenAsText(saveUi))
-                        .that(saveUi.findObject(By.text(expectedText))).isNotNull();
+        assertSaveUiChild(saveUi, "first", line1Visitor);
+        assertSaveUiChild(saveUi, "second", line2Visitor);
+        assertSaveUiChild(saveUi, "third", line3Visitor);
+        return saveUi;
+    }
+
+    /**
+     * Asserts the contents of save UI child element.
+     *
+     * @param saveUi save UI
+     * @param childId resource id of the child
+     * @param assertion if {@code null}, asserts the child does not exist; otherwise, asserts the
+     * child with it.
+     */
+    private void assertSaveUiChild(@NonNull UiObject2 saveUi, @NonNull String childId,
+            @Nullable Visitor<UiObject2> assertion) {
+        final UiObject2 child = saveUi.findObject(By.res(mPackageName, childId));
+        if (assertion != null) {
+            assertWithMessage("Didn't find child with id '%s'", childId).that(child).isNotNull();
+            try {
+                assertion.visit(child);
+            } catch (Throwable t) {
+                throw new AssertionError("Error on child '" + childId + "'", t);
+            }
+        } else {
+            assertWithMessage("Shouldn't find child with id '%s'", childId).that(child).isNull();
+        }
     }
 }
diff --git a/tests/autofillservice/src/android/autofillservice/cts/CustomDescriptionUnitTest.java b/tests/autofillservice/src/android/autofillservice/cts/CustomDescriptionUnitTest.java
new file mode 100644
index 0000000..2a50b6e
--- /dev/null
+++ b/tests/autofillservice/src/android/autofillservice/cts/CustomDescriptionUnitTest.java
@@ -0,0 +1,75 @@
+/*
+ * 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.autofillservice.cts;
+
+import static org.mockito.Mockito.mock;
+import static org.testng.Assert.assertThrows;
+
+import android.service.autofill.BatchUpdates;
+import android.service.autofill.CustomDescription;
+import android.service.autofill.InternalValidator;
+import android.service.autofill.Transformation;
+import android.service.autofill.Validator;
+import android.support.test.runner.AndroidJUnit4;
+import android.widget.RemoteViews;
+
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+@RunWith(AndroidJUnit4.class)
+public class CustomDescriptionUnitTest {
+
+    private final CustomDescription.Builder mBuilder =
+            new CustomDescription.Builder(mock(RemoteViews.class));
+    private final BatchUpdates mValidUpdate =
+            new BatchUpdates.Builder().updateTemplate(mock(RemoteViews.class)).build();
+    private final Validator mValidCondition = mock(InternalValidator.class);
+
+    @Test
+    public void testNullConstructor() {
+        assertThrows(NullPointerException.class, () ->  new CustomDescription.Builder(null));
+    }
+
+    @Test
+    public void testAddChild_null() {
+        assertThrows(IllegalArgumentException.class, () ->  mBuilder.addChild(42, null));
+    }
+
+    @Test
+    public void testAddChild_invalidTransformation() {
+        assertThrows(IllegalArgumentException.class,
+                () ->  mBuilder.addChild(42, mock(Transformation.class)));
+    }
+
+    @Test
+    public void testBatchUpdate_nullCondition() {
+        assertThrows(IllegalArgumentException.class,
+                () ->  mBuilder.batchUpdate(null, mValidUpdate));
+    }
+
+    @Test
+    public void testBatchUpdate_invalidCondition() {
+        assertThrows(IllegalArgumentException.class,
+                () ->  mBuilder.batchUpdate(mock(Validator.class), mValidUpdate));
+    }
+
+    @Test
+    public void testBatchUpdate_nullUpdates() {
+        assertThrows(NullPointerException.class,
+                () ->  mBuilder.batchUpdate(mValidCondition, null));
+    }
+}
diff --git a/tests/autofillservice/src/android/autofillservice/cts/CustomDescriptionWithLinkTestCase.java b/tests/autofillservice/src/android/autofillservice/cts/CustomDescriptionWithLinkTestCase.java
index 33f0943..7b8a42c 100644
--- a/tests/autofillservice/src/android/autofillservice/cts/CustomDescriptionWithLinkTestCase.java
+++ b/tests/autofillservice/src/android/autofillservice/cts/CustomDescriptionWithLinkTestCase.java
@@ -61,12 +61,13 @@
      */
     @Test
     public final void testTapLink_changeOrientationThenTapBack() throws Exception {
-        sUiBot.setScreenOrientation(UiBot.PORTRAIT);
+        mUiBot.setScreenOrientation(UiBot.PORTRAIT);
         try {
             saveUiRestoredAfterTappingLinkTest(
                     PostSaveLinkTappedAction.ROTATE_THEN_TAP_BACK_BUTTON);
         } finally {
-            sUiBot.setScreenOrientation(UiBot.PORTRAIT);
+            mUiBot.setScreenOrientation(UiBot.PORTRAIT);
+            cleanUpAfterScreenOrientationIsBackToPortrait();
         }
     }
 
@@ -83,6 +84,9 @@
     protected abstract void saveUiRestoredAfterTappingLinkTest(PostSaveLinkTappedAction type)
             throws Exception;
 
+    protected void cleanUpAfterScreenOrientationIsBackToPortrait() throws Exception {
+    }
+
     /**
      * Tests scenarios when user taps a link in the custom description, taps back to return to the
      * activity with the Save UI, and touch outside the Save UI to dismiss it.
@@ -192,6 +196,9 @@
         saveUiCancelledAfterTappingLinkTest(PostSaveLinkTappedAction.LAUNCH_NEW_ACTIVITY);
     }
 
+    protected abstract void saveUiCancelledAfterTappingLinkTest(PostSaveLinkTappedAction type)
+            throws Exception;
+
     @Test
     public final void testTapLink_launchTrampolineActivityThenTapBackAndStartNewSession()
             throws Exception {
@@ -201,6 +208,18 @@
     protected abstract void tapLinkLaunchTrampolineActivityThenTapBackAndStartNewSessionTest()
             throws Exception;
 
+    @Test
+    public final void testTapLinkAfterUpdateAppliedToLinkView() throws Exception {
+        tapLinkAfterUpdateAppliedTest(true);
+    }
+
+    @Test
+    public final void testTapLinkAfterUpdateAppliedToAnotherView() throws Exception {
+        tapLinkAfterUpdateAppliedTest(false);
+    }
+
+    protected abstract void tapLinkAfterUpdateAppliedTest(boolean updateLinkView) throws Exception;
+
     enum PostSaveLinkTappedAction {
         TAP_BACK_BUTTON,
         ROTATE_THEN_TAP_BACK_BUTTON,
@@ -213,41 +232,57 @@
         TAP_YES_ON_SAVE_UI
     }
 
-    protected abstract void saveUiCancelledAfterTappingLinkTest(PostSaveLinkTappedAction type)
-            throws Exception;
-
     protected final void startActivity(Class<?> clazz) {
         mContext.startActivity(new Intent(mContext, clazz));
     }
 
-    protected final CustomDescription newCustomDescription(
+    protected RemoteViews newTemplate() {
+        final RemoteViews presentation = new RemoteViews(mPackageName,
+                R.layout.custom_description_with_link);
+        return presentation;
+    }
+
+    protected final CustomDescription.Builder newCustomDescriptionBuilder(
             Class<? extends Activity> activityClass) {
         final Intent intent = new Intent(mContext, activityClass);
         intent.setFlags(Intent.FLAG_ACTIVITY_RETAIN_IN_RECENTS | Intent.FLAG_ACTIVITY_NEW_DOCUMENT);
-        return newCustomDescription(intent);
+        return newCustomDescriptionBuilder(intent);
+    }
+
+    protected final CustomDescription newCustomDescription(
+            Class<? extends Activity> activityClass) {
+        return newCustomDescriptionBuilder(activityClass).build();
+    }
+
+    protected final CustomDescription.Builder newCustomDescriptionBuilder(Intent intent) {
+        final RemoteViews presentation = newTemplate();
+        final PendingIntent pendingIntent = PendingIntent.getActivity(mContext, 0, intent, 0);
+        presentation.setOnClickPendingIntent(R.id.link, pendingIntent);
+        return new CustomDescription.Builder(presentation);
     }
 
     protected final CustomDescription newCustomDescription(Intent intent) {
-        final RemoteViews presentation = new RemoteViews(mPackageName,
-                R.layout.custom_description_with_link);
-        final PendingIntent pendingIntent = PendingIntent.getActivity(mContext, 0, intent, 0);
-        presentation.setOnClickPendingIntent(R.id.link, pendingIntent);
-        return new CustomDescription.Builder(presentation).build();
+        return newCustomDescriptionBuilder(intent).build();
     }
 
-    protected final UiObject2 assertSaveUiWithLinkIsShown(int saveType) {
+    protected final UiObject2 assertSaveUiWithLinkIsShown(int saveType) throws Exception {
+        return assertSaveUiWithLinkIsShown(saveType, "DON'T TAP ME!");
+    }
+
+    protected final UiObject2 assertSaveUiWithLinkIsShown(int saveType, String expectedText)
+            throws Exception {
         // First make sure the UI is shown...
-        final UiObject2 saveUi = sUiBot.assertSaveShowing(saveType);
+        final UiObject2 saveUi = mUiBot.assertSaveShowing(saveType);
         // Then make sure it does have the custom view with link on it...
-        getLink(saveUi);
+        final UiObject2 link = getLink(saveUi);
+        assertThat(link.getText()).isEqualTo(expectedText);
         return saveUi;
     }
 
     protected final UiObject2 getLink(final UiObject2 container) {
-        final UiObject2 button = container.findObject(By.res(mPackageName, ID_LINK));
-        assertThat(button).isNotNull();
-        assertThat(button.getText()).isEqualTo("DON'T TAP ME!");
-        return button;
+        final UiObject2 link = container.findObject(By.res(mPackageName, ID_LINK));
+        assertThat(link).isNotNull();
+        return link;
     }
 
     protected final void tapSaveUiLink(UiObject2 saveUi) {
diff --git a/tests/autofillservice/src/android/autofillservice/cts/DatasetTest.java b/tests/autofillservice/src/android/autofillservice/cts/DatasetTest.java
new file mode 100644
index 0000000..741fdda
--- /dev/null
+++ b/tests/autofillservice/src/android/autofillservice/cts/DatasetTest.java
@@ -0,0 +1,115 @@
+/*
+ * 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.autofillservice.cts;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import static org.mockito.Mockito.mock;
+import static org.testng.Assert.assertThrows;
+
+import android.service.autofill.Dataset;
+import android.support.test.runner.AndroidJUnit4;
+import android.view.autofill.AutofillId;
+import android.view.autofill.AutofillValue;
+import android.widget.RemoteViews;
+
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+import java.util.regex.Pattern;
+
+@RunWith(AndroidJUnit4.class)
+public class DatasetTest {
+
+    private final AutofillId mId = new AutofillId(42);
+    private final AutofillValue mValue = AutofillValue.forText("ValuableLikeGold");
+    private final Pattern mFilter = Pattern.compile("whatever");
+
+    private final RemoteViews mPresentation = mock(RemoteViews.class);
+
+    @Test
+    public void testBuilder_nullPresentation() {
+        assertThrows(NullPointerException.class, () -> new Dataset.Builder(null));
+    }
+
+    @Test
+    public void testBuilder_setValueNullId() {
+        final Dataset.Builder builder = new Dataset.Builder(mPresentation);
+        assertThrows(NullPointerException.class, () -> builder.setValue(null, mValue));
+    }
+
+    @Test
+    public void testBuilder_setValueWithoutPresentation() {
+        // Just assert that it builds without throwing an exception.
+        assertThat(new Dataset.Builder().setValue(mId, mValue).build()).isNotNull();
+    }
+
+    @Test
+    public void testBuilder_setValueWithNullPresentation() {
+        final Dataset.Builder builder = new Dataset.Builder();
+        assertThrows(NullPointerException.class, () -> builder.setValue(mId, mValue,
+                (RemoteViews) null));
+    }
+
+    @Test
+    public void testBuilder_setFilteredValueWithNullFilter() {
+        final Dataset.Builder builder = new Dataset.Builder();
+        assertThrows(NullPointerException.class, () -> builder.setValue(mId, mValue,
+                (Pattern) null));
+    }
+
+    @Test
+    public void testBuilder_setFilteredValueWithPresentationNullFilter() {
+        final Dataset.Builder builder = new Dataset.Builder();
+        assertThrows(NullPointerException.class, () -> builder.setValue(mId, mValue, null,
+                mPresentation));
+    }
+
+    @Test
+    public void testBuilder_setFilteredValueWithPresentationNullPresentation() {
+        final Dataset.Builder builder = new Dataset.Builder();
+        assertThrows(NullPointerException.class, () -> builder.setValue(mId, mValue, mFilter,
+                null));
+    }
+
+    @Test
+    public void testBuilder_setFilteredValueWithoutPresentation() {
+        final Dataset.Builder builder = new Dataset.Builder();
+        assertThrows(IllegalStateException.class, () -> builder.setValue(mId, mValue, mFilter));
+    }
+
+    @Test
+    public void testBuild_noValues() {
+        final Dataset.Builder builder = new Dataset.Builder();
+        assertThrows(IllegalStateException.class, () -> builder.build());
+    }
+
+    @Test
+    public void testNoMoreInteractionsAfterBuild() {
+        final Dataset.Builder builder = new Dataset.Builder();
+        builder.setValue(mId, mValue, mPresentation);
+        assertThat(builder.build()).isNotNull();
+        assertThrows(IllegalStateException.class, () -> builder.build());
+        assertThrows(IllegalStateException.class, () -> builder.setValue(mId, mValue));
+        assertThrows(IllegalStateException.class,
+                () -> builder.setValue(mId, mValue, mPresentation));
+        assertThrows(IllegalStateException.class,
+                () -> builder.setValue(mId, mValue, mFilter));
+        assertThrows(IllegalStateException.class,
+                () -> builder.setValue(mId, mValue, mFilter, mPresentation));
+    }
+}
diff --git a/tests/autofillservice/src/android/autofillservice/cts/DatePickerTestCase.java b/tests/autofillservice/src/android/autofillservice/cts/DatePickerTestCase.java
index d977ac6..55bc654 100644
--- a/tests/autofillservice/src/android/autofillservice/cts/DatePickerTestCase.java
+++ b/tests/autofillservice/src/android/autofillservice/cts/DatePickerTestCase.java
@@ -31,7 +31,6 @@
 import android.autofillservice.cts.InstrumentedAutoFillService.SaveRequest;
 import android.icu.util.Calendar;
 
-import org.junit.After;
 import org.junit.Test;
 
 /**
@@ -42,11 +41,6 @@
 
     protected abstract T getDatePickerActivity();
 
-    @After
-    public void finishWelcomeActivity() {
-        WelcomeActivity.finishIt();
-    }
-
     @Test
     public void testAutoFillAndSave() throws Exception {
         final T activity = getDatePickerActivity();
@@ -79,7 +73,7 @@
         assertNumberOfChildren(fillRequest.structure, ID_DATE_PICKER, 0);
 
         // Auto-fill it.
-        sUiBot.selectDataset("The end of the world");
+        mUiBot.selectDataset("The end of the world");
 
         // Check the results.
         activity.assertAutoFilled();
@@ -88,7 +82,7 @@
         activity.setDate(2010, 11, 12);
         activity.tapOk();
 
-        sUiBot.saveForAutofill(true, SAVE_DATA_TYPE_GENERIC);
+        mUiBot.saveForAutofill(true, SAVE_DATA_TYPE_GENERIC);
         final SaveRequest saveRequest = sReplier.getNextSaveRequest();
         assertWithMessage("onSave() not called").that(saveRequest).isNotNull();
 
diff --git a/tests/autofillservice/src/android/autofillservice/cts/DialogLauncherActivity.java b/tests/autofillservice/src/android/autofillservice/cts/DialogLauncherActivity.java
new file mode 100644
index 0000000..24cd5bf
--- /dev/null
+++ b/tests/autofillservice/src/android/autofillservice/cts/DialogLauncherActivity.java
@@ -0,0 +1,146 @@
+/*
+ * Copyright 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.autofillservice.cts;
+
+import static com.google.common.truth.Truth.assertWithMessage;
+
+import android.app.AlertDialog;
+import android.content.Context;
+import android.os.Bundle;
+import android.os.IBinder;
+import android.util.DisplayMetrics;
+import android.util.Log;
+import android.view.Display;
+import android.view.WindowManager;
+import android.view.WindowManager.LayoutParams;
+import android.widget.Button;
+import android.widget.EditText;
+
+/**
+ * Activity that has buttons to launch dialogs that should then be autofillable.
+ */
+public class DialogLauncherActivity extends AbstractAutoFillActivity {
+
+    private static final String TAG = "DialogLauncherActivity";
+
+    private FillExpectation mExpectation;
+    private LoginDialog mDialog;
+    Button mLaunchButton;
+
+
+    @Override
+    protected void onCreate(Bundle savedInstanceState) {
+        super.onCreate(savedInstanceState);
+
+        setContentView(R.layout.dialog_launcher_activity);
+        mLaunchButton = findViewById(R.id.launch_button);
+        mDialog = new LoginDialog(this);
+        mLaunchButton.setOnClickListener((v) -> mDialog.show());
+    }
+
+    void onUsername(Visitor<EditText> v) {
+        syncRunOnUiThread(() -> v.visit(mDialog.mUsernameEditText));
+    }
+
+    void launchDialog(UiBot uiBot) throws Exception {
+        syncRunOnUiThread(() -> mLaunchButton.performClick());
+        // TODO: should assert by id, but it's not working
+        uiBot.assertShownByText("Username");
+    }
+
+    void maximizeDialog() {
+        final WindowManager wm = getWindowManager();
+        final Display display = wm.getDefaultDisplay();
+        final DisplayMetrics metrics = new DisplayMetrics();
+        display.getMetrics(metrics);
+        syncRunOnUiThread(
+                () -> mDialog.getWindow().setLayout(metrics.widthPixels, metrics.heightPixels));
+    }
+
+    void expectAutofill(String username, String password) {
+        assertWithMessage("must call launchDialog first").that(mDialog.mUsernameEditText)
+                .isNotNull();
+        mExpectation = new FillExpectation(username, password);
+        mDialog.mUsernameEditText.addTextChangedListener(mExpectation.mCcUsernameWatcher);
+        mDialog.mPasswordEditText.addTextChangedListener(mExpectation.mCcPasswordWatcher);
+    }
+
+    void assertAutofilled() throws Exception {
+        assertWithMessage("expectAutoFill() not called").that(mExpectation).isNotNull();
+        if (mExpectation.mCcUsernameWatcher != null) {
+            mExpectation.mCcUsernameWatcher.assertAutoFilled();
+        }
+        if (mExpectation.mCcPasswordWatcher != null) {
+            mExpectation.mCcPasswordWatcher.assertAutoFilled();
+        }
+    }
+
+    private final class FillExpectation {
+        private final OneTimeTextWatcher mCcUsernameWatcher;
+        private final OneTimeTextWatcher mCcPasswordWatcher;
+
+        private FillExpectation(String username, String password) {
+            mCcUsernameWatcher = username == null ? null
+                    : new OneTimeTextWatcher("username", mDialog.mUsernameEditText, username);
+            mCcPasswordWatcher = password == null ? null
+                    : new OneTimeTextWatcher("password", mDialog.mPasswordEditText, password);
+        }
+
+        private FillExpectation(String username) {
+            this(username, null);
+        }
+    }
+
+    public final class LoginDialog extends AlertDialog {
+
+        private EditText mUsernameEditText;
+        private EditText mPasswordEditText;
+
+        public LoginDialog(Context context) {
+            super(context);
+        }
+
+        @Override
+        protected void onCreate(Bundle savedInstanceState) {
+            super.onCreate(savedInstanceState);
+
+            setContentView(R.layout.login_activity);
+            mUsernameEditText = findViewById(R.id.username);
+            mPasswordEditText = findViewById(R.id.password);
+        }
+
+        // TODO(b/68816440): temporary hack to make sure tests pass - everything below should be
+        // removed
+        private IBinder mToken;
+
+        @Override
+        public void onAttachedToWindow() {
+            super.onAttachedToWindow();
+
+            mToken = DialogLauncherActivity.this.getWindow().getAttributes().token;
+            Log.v(TAG, "onAttachedToWindow(): " + mToken);
+        }
+
+
+        @Override
+        public void onWindowAttributesChanged(LayoutParams params) {
+            Log.v(TAG, "onWindowAttributesChanged: p.token=" + params.token + "hack=" + mToken);
+            params.token = mToken;
+
+            super.onWindowAttributesChanged(params);
+        }
+    }
+}
diff --git a/tests/autofillservice/src/android/autofillservice/cts/DialogLauncherActivityTest.java b/tests/autofillservice/src/android/autofillservice/cts/DialogLauncherActivityTest.java
new file mode 100644
index 0000000..981655d
--- /dev/null
+++ b/tests/autofillservice/src/android/autofillservice/cts/DialogLauncherActivityTest.java
@@ -0,0 +1,114 @@
+/*
+ * Copyright 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.autofillservice.cts;
+
+import static android.autofillservice.cts.Helper.ID_USERNAME;
+import static android.autofillservice.cts.Helper.assertTextIsSanitized;
+import static android.autofillservice.cts.SimpleSaveActivity.ID_PASSWORD;
+
+import android.autofillservice.cts.CannedFillResponse.CannedDataset;
+import android.autofillservice.cts.InstrumentedAutoFillService.FillRequest;
+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);
+
+    private DialogLauncherActivity mActivity;
+
+    @Before
+    public void setActivity() {
+        mActivity = mActivityRule.getActivity();
+    }
+
+    @Test
+    public void testAutofill_noDatasets() throws Exception {
+        autofillNoDatasetsTest(false);
+    }
+
+    @Test
+    public void testAutofill_noDatasets_afterResizing() throws Exception {
+        autofillNoDatasetsTest(true);
+    }
+
+    private void autofillNoDatasetsTest(boolean resize) throws Exception {
+        enableService();
+        mActivity.launchDialog(mUiBot);
+
+        if (resize) {
+            mActivity.maximizeDialog();
+        }
+
+        // Set expectations.
+        sReplier.addResponse(CannedFillResponse.NO_RESPONSE);
+
+        // Trigger autofill.
+        mActivity.onUsername(View::requestFocus);
+        final FillRequest fillRequest = sReplier.getNextFillRequest();
+
+        // Asserts results.
+        try {
+            mUiBot.assertNoDatasets();
+            // Make sure nodes were properly generated.
+            assertTextIsSanitized(fillRequest.structure, ID_USERNAME);
+            assertTextIsSanitized(fillRequest.structure, ID_PASSWORD);
+        } catch (AssertionError e) {
+            Helper.dumpStructure("D'OH!", fillRequest.structure);
+            throw e;
+        }
+    }
+
+    @Test
+    public void testAutofill_oneDataset() throws Exception {
+        autofillOneDatasetTest(false);
+    }
+
+    @Test
+    public void testAutofill_oneDataset_afterResizing() throws Exception {
+        autofillOneDatasetTest(true);
+    }
+
+    private void autofillOneDatasetTest(boolean resize) throws Exception {
+        enableService();
+        mActivity.launchDialog(mUiBot);
+
+        if (resize) {
+            mActivity.maximizeDialog();
+        }
+
+        // Set expectations.
+        mActivity.expectAutofill("dude", "sweet");
+        sReplier.addResponse(new CannedDataset.Builder()
+                .setField(ID_USERNAME, "dude")
+                .setField(ID_PASSWORD, "sweet")
+                .setPresentation(createPresentation("The Dude"))
+                .build());
+
+        // Trigger autofill.
+        mActivity.onUsername(View::requestFocus);
+        sReplier.getNextFillRequest();
+
+        // Asserts results.
+        mUiBot.selectDataset("The Dude");
+        mActivity.assertAutofilled();
+    }
+}
diff --git a/tests/autofillservice/src/android/autofillservice/cts/DisableAutofillTest.java b/tests/autofillservice/src/android/autofillservice/cts/DisableAutofillTest.java
new file mode 100644
index 0000000..a7d3519
--- /dev/null
+++ b/tests/autofillservice/src/android/autofillservice/cts/DisableAutofillTest.java
@@ -0,0 +1,330 @@
+/*
+ * 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.autofillservice.cts;
+
+import android.autofillservice.cts.CannedFillResponse.CannedDataset;
+import android.content.Intent;
+import android.os.SystemClock;
+import android.service.autofill.FillResponse;
+import android.util.Log;
+
+import org.junit.Before;
+import org.junit.Test;
+
+/**
+ * Tests for the {@link android.service.autofill.FillResponse.Builder#disableAutofill(long)} API.
+ */
+public class DisableAutofillTest extends AutoFillServiceTestCase {
+
+    private static final String TAG = "DisableAutofillTest";
+
+    @Before
+    public void preTestCleanup() {
+        Helper.preTestCleanup();
+    }
+
+    private SimpleSaveActivity startSimpleSaveActivity() throws Exception {
+        final Intent intent = new Intent(mContext, SimpleSaveActivity.class);
+        mContext.startActivity(intent);
+        mUiBot.assertShownByRelativeId(SimpleSaveActivity.ID_LABEL);
+        return SimpleSaveActivity.getInstance();
+    }
+
+    private PreSimpleSaveActivity startPreSimpleSaveActivity() throws Exception {
+        final Intent intent = new Intent(mContext, PreSimpleSaveActivity.class);
+        mContext.startActivity(intent);
+        mUiBot.assertShownByRelativeId(PreSimpleSaveActivity.ID_PRE_LABEL);
+        return PreSimpleSaveActivity.getInstance();
+    }
+
+    /**
+     * Defines what to do after the activity being tested is launched.
+     */
+    enum PostLaunchAction {
+        /**
+         * Used when the service disables autofill in the fill response for this activty. As such:
+         *
+         * <ol>
+         *   <li>There should be a fill request on {@code sReplier}.
+         *   <li>The first UI focus should generate a
+         *   {@link android.view.autofill.AutofillManager.AutofillCallback#EVENT_INPUT_UNAVAILABLE}
+         *   event.
+         *   <li>Subsequent UI focus should not trigger events.
+         * </ol>
+         */
+        ASSERT_DISABLING,
+
+        /**
+         * Used when the service already disabled autofill prior to launching activty. As such:
+         *
+         * <ol>
+         *   <li>There should be no fill request on {@code sReplier}.
+         *   <li>There should be no callback calls when UI is focused
+         * </ol>
+         */
+        ASSERT_DISABLED,
+
+        /**
+         * Used when autofill is enabled, so it tries to autofill the activity.
+         */
+        ASSERT_ENABLED_AND_AUTOFILL
+    }
+
+    private void launchSimpleSaveActivity(PostLaunchAction action) throws Exception {
+        Log.v(TAG, "launchPreSimpleSaveActivity(): " + action);
+        sReplier.assertNumberUnhandledFillRequests(0);
+
+        if (action == PostLaunchAction.ASSERT_ENABLED_AND_AUTOFILL) {
+            sReplier.addResponse(new CannedFillResponse.Builder()
+                    .addDataset(new CannedDataset.Builder()
+                            .setField(SimpleSaveActivity.ID_INPUT, "id")
+                            .setField(SimpleSaveActivity.ID_PASSWORD, "pass")
+                            .setPresentation(createPresentation("YO"))
+                            .build())
+                    .build());
+
+        }
+
+        final SimpleSaveActivity activity = startSimpleSaveActivity();
+        final MyAutofillCallback callback = activity.registerCallback();
+
+        try {
+            // Trigger autofill
+            activity.syncRunOnUiThread(() -> activity.mInput.requestFocus());
+
+            if (action == PostLaunchAction.ASSERT_DISABLING) {
+                callback.assertUiUnavailableEvent(activity.mInput);
+                sReplier.getNextFillRequest();
+
+                // Make sure other fields are not triggered.
+                activity.syncRunOnUiThread(() -> activity.mPassword.requestFocus());
+                callback.assertNotCalled();
+            } else if (action == PostLaunchAction.ASSERT_DISABLED) {
+                // Make sure forced requests are ignored as well.
+                activity.getAutofillManager().requestAutofill(activity.mInput);
+                callback.assertNotCalled();
+            } else if (action == PostLaunchAction.ASSERT_ENABLED_AND_AUTOFILL) {
+                callback.assertUiShownEvent(activity.mInput);
+                sReplier.getNextFillRequest();
+                final SimpleSaveActivity.FillExpectation autofillExpectation =
+                        activity.expectAutoFill("id", "pass");
+                mUiBot.selectDataset("YO");
+                autofillExpectation.assertAutoFilled();
+            }
+
+            // Asserts isEnabled() status.
+            assertAutofillEnabled(activity, action == PostLaunchAction.ASSERT_ENABLED_AND_AUTOFILL);
+        } finally {
+            activity.unregisterCallback();
+            activity.finish();
+        }
+        sReplier.assertNumberUnhandledFillRequests(0);
+    }
+
+    private void launchPreSimpleSaveActivity(PostLaunchAction action) throws Exception {
+        Log.v(TAG, "launchPreSimpleSaveActivity(): " + action);
+        sReplier.assertNumberUnhandledFillRequests(0);
+
+        if (action == PostLaunchAction.ASSERT_ENABLED_AND_AUTOFILL) {
+            sReplier.addResponse(new CannedFillResponse.Builder()
+                    .addDataset(new CannedDataset.Builder()
+                            .setField(PreSimpleSaveActivity.ID_PRE_INPUT, "yo")
+                            .setPresentation(createPresentation("YO"))
+                            .build())
+                    .build());
+        }
+
+        final PreSimpleSaveActivity activity = startPreSimpleSaveActivity();
+        final MyAutofillCallback callback = activity.registerCallback();
+
+        try {
+            // Trigger autofill
+            activity.syncRunOnUiThread(() -> activity.mPreInput.requestFocus());
+
+            if (action == PostLaunchAction.ASSERT_DISABLING) {
+                callback.assertUiUnavailableEvent(activity.mPreInput);
+                sReplier.getNextFillRequest();
+            } else if (action == PostLaunchAction.ASSERT_DISABLED) {
+                activity.getAutofillManager().requestAutofill(activity.mPreInput);
+                callback.assertNotCalled();
+            } else if (action == PostLaunchAction.ASSERT_ENABLED_AND_AUTOFILL) {
+                callback.assertUiShownEvent(activity.mPreInput);
+                sReplier.getNextFillRequest();
+                final PreSimpleSaveActivity.FillExpectation autofillExpectation =
+                        activity.expectAutoFill("yo");
+                mUiBot.selectDataset("YO");
+                autofillExpectation.assertAutoFilled();
+            }
+
+            // Asserts isEnabled() status.
+            assertAutofillEnabled(activity, action == PostLaunchAction.ASSERT_ENABLED_AND_AUTOFILL);
+        } finally {
+            activity.unregisterCallback();
+            activity.finish();
+        }
+        sReplier.assertNumberUnhandledFillRequests(0);
+    }
+
+    @Test
+    public void testDisableApp() throws Exception {
+        // Set service.
+        enableService();
+
+        // Set expectations.
+        sReplier.addResponse(
+                new CannedFillResponse.Builder().disableAutofill(Long.MAX_VALUE).build());
+
+        // Trigger autofill for the first time.
+        launchSimpleSaveActivity(PostLaunchAction.ASSERT_DISABLING);
+
+        // Launch activity again.
+        launchSimpleSaveActivity(PostLaunchAction.ASSERT_DISABLED);
+
+        // Now try it using a different activity - should be disabled too.
+        launchPreSimpleSaveActivity(PostLaunchAction.ASSERT_DISABLED);
+    }
+
+    @Test
+    public void testDisableAppThenWaitToReenableIt() throws Exception {
+        // Set service.
+        enableService();
+
+        // Need to wait the equivalent of launching 2 activities, plus some extra legging room
+        final long duration = 2 * Timeouts.ACTIVITY_RESURRECTION.ms() + 500;
+
+        // Set expectations.
+        sReplier.addResponse(new CannedFillResponse.Builder().disableAutofill(duration).build());
+
+        // Trigger autofill for the first time.
+        launchSimpleSaveActivity(PostLaunchAction.ASSERT_DISABLING);
+
+        // Launch activity again.
+        launchSimpleSaveActivity(PostLaunchAction.ASSERT_DISABLED);
+
+        // Wait for the timeout, then try again, autofilling it this time.
+        SystemClock.sleep(duration + 1);
+        launchSimpleSaveActivity(PostLaunchAction.ASSERT_ENABLED_AND_AUTOFILL);
+
+        // Also try it on another activity.
+        launchPreSimpleSaveActivity(PostLaunchAction.ASSERT_ENABLED_AND_AUTOFILL);
+    }
+
+    @Test
+    public void testDisableAppThenResetServiceToReenableIt() throws Exception {
+        enableService();
+        sReplier.addResponse(new CannedFillResponse.Builder()
+                .disableAutofill(Long.MAX_VALUE).build());
+
+        // Trigger autofill for the first time.
+        launchSimpleSaveActivity(PostLaunchAction.ASSERT_DISABLING);
+        // Launch activity again.
+        launchSimpleSaveActivity(PostLaunchAction.ASSERT_DISABLED);
+
+        // Then "reset" service to re-enable autofill.
+        disableService();
+        enableService();
+
+        // Try again on activity that disabled it.
+        launchSimpleSaveActivity(PostLaunchAction.ASSERT_ENABLED_AND_AUTOFILL);
+
+        // Try again on other activity.
+        launchPreSimpleSaveActivity(PostLaunchAction.ASSERT_ENABLED_AND_AUTOFILL);
+    }
+
+    @Test
+    public void testDisableActivity() throws Exception {
+        // Set service.
+        enableService();
+
+        // Set expectations.
+        sReplier.addResponse(new CannedFillResponse.Builder()
+                .disableAutofill(Long.MAX_VALUE)
+                .setFillResponseFlags(FillResponse.FLAG_DISABLE_ACTIVITY_ONLY)
+                .build());
+
+        // Trigger autofill for the first time.
+        launchSimpleSaveActivity(PostLaunchAction.ASSERT_DISABLING);
+
+        // Launch activity again.
+        launchSimpleSaveActivity(PostLaunchAction.ASSERT_DISABLED);
+
+        // Now try it using a different activity - should work.
+        launchPreSimpleSaveActivity(PostLaunchAction.ASSERT_ENABLED_AND_AUTOFILL);
+    }
+
+    @Test
+    public void testDisableActivityThenWaitToReenableIt() throws Exception {
+        // Set service.
+        enableService();
+
+        // Need to wait the equivalent of launching 2 activities, plus some extra legging room
+        final long duration = 2 * Timeouts.ACTIVITY_RESURRECTION.ms() + 500;
+
+        // Set expectations.
+        sReplier.addResponse(new CannedFillResponse.Builder()
+                .disableAutofill(duration)
+                .setFillResponseFlags(FillResponse.FLAG_DISABLE_ACTIVITY_ONLY)
+                .build());
+
+        // Trigger autofill for the first time.
+        launchSimpleSaveActivity(PostLaunchAction.ASSERT_DISABLING);
+
+        // Launch activity again.
+        launchSimpleSaveActivity(PostLaunchAction.ASSERT_DISABLED);
+
+        // Make sure other app is working.
+        launchPreSimpleSaveActivity(PostLaunchAction.ASSERT_ENABLED_AND_AUTOFILL);
+
+        // Wait for the timeout, then try again, autofilling it this time.
+        SystemClock.sleep(duration + 1);
+        launchSimpleSaveActivity(PostLaunchAction.ASSERT_ENABLED_AND_AUTOFILL);
+    }
+
+    @Test
+    public void testDisableActivityThenResetServiceToReenableIt() throws Exception {
+        enableService();
+        sReplier.addResponse(new CannedFillResponse.Builder()
+                .disableAutofill(Long.MAX_VALUE)
+                .setFillResponseFlags(FillResponse.FLAG_DISABLE_ACTIVITY_ONLY)
+                .build());
+
+        // Trigger autofill for the first time.
+        launchSimpleSaveActivity(PostLaunchAction.ASSERT_DISABLING);
+        // Launch activity again.
+        launchSimpleSaveActivity(PostLaunchAction.ASSERT_DISABLED);
+
+        // Make sure other app is working.
+        launchPreSimpleSaveActivity(PostLaunchAction.ASSERT_ENABLED_AND_AUTOFILL);
+
+        // Then "reset" service to re-enable autofill.
+        disableService();
+        enableService();
+
+        // Try again on activity that disabled it.
+        launchSimpleSaveActivity(PostLaunchAction.ASSERT_ENABLED_AND_AUTOFILL);
+    }
+
+    private void assertAutofillEnabled(AbstractAutoFillActivity activity, boolean expected)
+            throws Exception {
+        Timeouts.ACTIVITY_RESURRECTION.run(
+                "assertAutofillEnabled(" + activity.getComponentName().flattenToShortString() + ")",
+                () -> {
+                    return activity.getAutofillManager().isEnabled() == expected
+                            ? Boolean.TRUE : null;
+                });
+    }
+}
diff --git a/tests/autofillservice/src/android/autofillservice/cts/DuplicateIdActivityTest.java b/tests/autofillservice/src/android/autofillservice/cts/DuplicateIdActivityTest.java
index 0e59e2f..e44f510 100644
--- a/tests/autofillservice/src/android/autofillservice/cts/DuplicateIdActivityTest.java
+++ b/tests/autofillservice/src/android/autofillservice/cts/DuplicateIdActivityTest.java
@@ -18,9 +18,9 @@
 
 import static android.autofillservice.cts.CannedFillResponse.NO_RESPONSE;
 import static android.autofillservice.cts.DuplicateIdActivity.DUPLICATE_ID;
-import static android.autofillservice.cts.Helper.runShellCommand;
 import static android.autofillservice.cts.InstrumentedAutoFillService.waitUntilConnected;
 import static android.autofillservice.cts.InstrumentedAutoFillService.waitUntilDisconnected;
+import static android.autofillservice.cts.common.ShellHelper.runShellCommand;
 
 import static com.google.common.truth.Truth.assertThat;
 
@@ -46,9 +46,9 @@
     private DuplicateIdActivity mActivity;
 
     @Before
-    public void setup() {
-        Helper.disableAutoRotation(sUiBot);
-        sUiBot.setScreenOrientation(0);
+    public void setup() throws Exception {
+        Helper.disableAutoRotation(mUiBot);
+        mUiBot.setScreenOrientation(0);
 
         mActivity = mActivityRule.getActivity();
     }
@@ -112,13 +112,21 @@
         sReplier.addResponse(NO_RESPONSE);
 
         // Force rotation to force onDestroy->onCreate cycle
-        sUiBot.setScreenOrientation(1);
+        mUiBot.setScreenOrientation(1);
+        // Wait context and Views being recreated in rotation
+        mUiBot.assertShownByRelativeId(DUPLICATE_ID);
+
+        // Because service returned a null response, rotation will trigger another request.
+        sReplier.addResponse(NO_RESPONSE);
 
         // Select other field to trigger new partition
         runShellCommand("input keyevent KEYCODE_TAB");
 
         request = sReplier.getNextFillRequest();
 
+        // Ignore 2nd request.
+        sReplier.getNextFillRequest();
+
         views = findViews(request);
         AutofillId recreatedId1 = views[0].getAutofillId();
         AutofillId recreatedId2 = views[1].getAutofillId();
diff --git a/tests/autofillservice/src/android/autofillservice/cts/EditDistanceScorerTest.java b/tests/autofillservice/src/android/autofillservice/cts/EditDistanceScorerTest.java
new file mode 100644
index 0000000..0afa747
--- /dev/null
+++ b/tests/autofillservice/src/android/autofillservice/cts/EditDistanceScorerTest.java
@@ -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.autofillservice.cts;
+
+import static android.autofillservice.cts.Helper.assertFloat;
+
+import android.service.autofill.EditDistanceScorer;
+import android.support.test.runner.AndroidJUnit4;
+import android.view.autofill.AutofillValue;
+
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+@RunWith(AndroidJUnit4.class)
+public class EditDistanceScorerTest {
+
+    private final EditDistanceScorer mScorer = EditDistanceScorer.getInstance();
+
+    @Test
+    public void testGetScore_nullValue() {
+        assertFloat(mScorer.getScore(null, "D'OH!"), 0);
+    }
+
+    @Test
+    public void testGetScore_nonTextValue() {
+        assertFloat(mScorer.getScore(AutofillValue.forToggle(true), "D'OH!"), 0);
+    }
+
+    @Test
+    public void testGetScore_nullUserData() {
+        assertFloat(mScorer.getScore(AutofillValue.forText("D'OH!"), null), 0);
+    }
+
+    @Test
+    public void testGetScore_fullMatch() {
+        assertFloat(mScorer.getScore(AutofillValue.forText("D'OH!"), "D'OH!"), 1);
+    }
+
+    @Test
+    public void testGetScore_fullMatchMixedCase() {
+        assertFloat(mScorer.getScore(AutofillValue.forText("D'OH!"), "D'oH!"), 1);
+    }
+
+    // TODO(b/70291841): might need to change it once it supports different sizes
+    @Test
+    public void testGetScore_mismatchDifferentSizes() {
+        assertFloat(mScorer.getScore(AutofillValue.forText("One"), "MoreThanOne"), 0);
+        assertFloat(mScorer.getScore(AutofillValue.forText("MoreThanOne"), "One"), 0);
+    }
+
+    @Test
+    public void testGetScore_partialMatch() {
+        assertFloat(mScorer.getScore(AutofillValue.forText("Dude"), "Dxxx"), 0.25F);
+        assertFloat(mScorer.getScore(AutofillValue.forText("Dude"), "DUxx"), 0.50F);
+        assertFloat(mScorer.getScore(AutofillValue.forText("Dude"), "DUDx"), 0.75F);
+        assertFloat(mScorer.getScore(AutofillValue.forText("Dxxx"), "Dude"), 0.25F);
+        assertFloat(mScorer.getScore(AutofillValue.forText("DUxx"), "Dude"), 0.50F);
+        assertFloat(mScorer.getScore(AutofillValue.forText("DUDx"), "Dude"), 0.75F);
+    }
+}
diff --git a/tests/autofillservice/src/android/autofillservice/cts/FatActivityTest.java b/tests/autofillservice/src/android/autofillservice/cts/FatActivityTest.java
index b2dd1d4..ba6f7a1 100644
--- a/tests/autofillservice/src/android/autofillservice/cts/FatActivityTest.java
+++ b/tests/autofillservice/src/android/autofillservice/cts/FatActivityTest.java
@@ -70,7 +70,7 @@
         // Trigger auto-fill.
         mFatActivity.onInput((v) -> v.requestFocus());
         final FillRequest fillRequest = sReplier.getNextFillRequest();
-        sUiBot.assertNoDatasets();
+        mUiBot.assertNoDatasets();
 
         // TODO: should only have 5 children, but there is an extra
         // TextView that's probably coming from the title. For now we're just ignoring it, but
diff --git a/tests/autofillservice/src/android/autofillservice/cts/FieldsClassificationTest.java b/tests/autofillservice/src/android/autofillservice/cts/FieldsClassificationTest.java
new file mode 100644
index 0000000..24bc2a3
--- /dev/null
+++ b/tests/autofillservice/src/android/autofillservice/cts/FieldsClassificationTest.java
@@ -0,0 +1,386 @@
+/*
+ * Copyright 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.autofillservice.cts;
+
+import static android.autofillservice.cts.Helper.assertFillEventForContextCommitted;
+import static android.autofillservice.cts.Helper.assertFillEventForFieldsClassification;
+import static android.provider.Settings.Secure.AUTOFILL_FEATURE_FIELD_CLASSIFICATION;
+import static android.provider.Settings.Secure.AUTOFILL_USER_DATA_MAX_FIELD_CLASSIFICATION_IDS_SIZE;
+import static android.provider.Settings.Secure.AUTOFILL_USER_DATA_MAX_USER_DATA_SIZE;
+import static android.provider.Settings.Secure.AUTOFILL_USER_DATA_MAX_VALUE_LENGTH;
+import static android.provider.Settings.Secure.AUTOFILL_USER_DATA_MIN_VALUE_LENGTH;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import android.autofillservice.cts.Helper.FieldClassificationResult;
+import android.autofillservice.cts.common.SettingsStateChangerRule;
+import android.content.Context;
+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;
+
+public class FieldsClassificationTest extends AutoFillServiceTestCase {
+
+    private static final Context sContext = InstrumentationRegistry.getContext();
+
+    @ClassRule
+    public static final SettingsStateChangerRule sFeatureEnabler =
+            new SettingsStateChangerRule(sContext, AUTOFILL_FEATURE_FIELD_CLASSIFICATION, "1");
+
+    @ClassRule
+    public static final SettingsStateChangerRule sUserDataMaxFcSizeChanger =
+            new SettingsStateChangerRule(sContext,
+                    AUTOFILL_USER_DATA_MAX_FIELD_CLASSIFICATION_IDS_SIZE, "10");
+
+    @ClassRule
+    public static final SettingsStateChangerRule sUserDataMaxUserSizeChanger =
+            new SettingsStateChangerRule(sContext, AUTOFILL_USER_DATA_MAX_USER_DATA_SIZE, "10");
+
+    @ClassRule
+    public static final SettingsStateChangerRule sUserDataMinValueChanger =
+            new SettingsStateChangerRule(sContext, AUTOFILL_USER_DATA_MIN_VALUE_LENGTH, "5");
+
+    @ClassRule
+    public static final SettingsStateChangerRule sUserDataMaxValueChanger =
+            new SettingsStateChangerRule(sContext, AUTOFILL_USER_DATA_MAX_VALUE_LENGTH, "50");
+
+    @Rule
+    public final AutofillActivityTestRule<GridActivity> mActivityRule =
+            new AutofillActivityTestRule<GridActivity>(GridActivity.class);
+
+
+    private GridActivity mActivity;
+    private AutofillManager mAfm;
+
+    @Before
+    public void setFixtures() {
+        mActivity = mActivityRule.getActivity();
+        mAfm = mActivity.getAutofillManager();
+    }
+
+    @Test
+    public void testFeatureIsEnabled() throws Exception {
+        enableService();
+        assertThat(mAfm.isFieldClassificationEnabled()).isTrue();
+
+        disableService();
+        assertThat(mAfm.isFieldClassificationEnabled()).isFalse();
+    }
+
+    @Test
+    public void testGetAlgorithm() throws Exception {
+        enableService();
+
+        // Check algorithms
+        final List<String> names = mAfm.getAvailableFieldClassificationAlgorithms();
+        assertThat(names.size()).isAtLeast(1);
+        final String defaultAlgorithm = getDefaultAlgorithm();
+        assertThat(defaultAlgorithm).isNotEmpty();
+        assertThat(names).contains(defaultAlgorithm);
+
+        // Checks invalid service
+        disableService();
+        assertThat(mAfm.getAvailableFieldClassificationAlgorithms()).isEmpty();
+    }
+
+    @Test
+    public void testHit_oneUserData_oneDetectableField() throws Exception {
+        simpleHitTest(false, null);
+    }
+
+    @Test
+    public void testHit_invalidAlgorithmIsIgnored() throws Exception {
+        // For simplicity's sake, let's assume that name will never be valid..
+        String invalidName = " ALGORITHM, Y NO INVALID? ";
+
+        simpleHitTest(true, invalidName);
+    }
+
+    @Test
+    public void testHit_userDataAlgorithmIsReset() throws Exception {
+        simpleHitTest(true, null);
+    }
+
+    private void simpleHitTest(boolean setAlgorithm, String algorithm) throws Exception {
+        // Set service.
+        enableService();
+
+        // Set expectations.
+        final UserData.Builder userData = new UserData.Builder("myId", "FULLY");
+        if (setAlgorithm) {
+            userData.setFieldClassificationAlgorithm(algorithm, null);
+        }
+        mAfm.setUserData(userData.build());
+        final MyAutofillCallback callback = mActivity.registerCallback();
+        final EditText field = mActivity.getCell(1, 1);
+        final AutofillId fieldId = field.getAutofillId();
+        sReplier.addResponse(new CannedFillResponse.Builder()
+                .setFieldClassificationIds(fieldId)
+                .build());
+
+        // Trigger autofill
+        mActivity.focusCell(1, 1);
+        sReplier.getNextFillRequest();
+
+        mUiBot.assertNoDatasets();
+        callback.assertUiUnavailableEvent(field);
+
+        // Simulate user input
+        mActivity.setText(1, 1, "fully");
+
+        // Finish context.
+        mAfm.commit();
+
+        // Assert results
+        final List<Event> events = InstrumentedAutoFillService.getFillEvents(1);
+        assertFillEventForFieldsClassification(events.get(0), fieldId, "myId", 1,
+                getDefaultAlgorithm());
+    }
+
+    @Test
+    public void testHit_manyUserData_oneDetectableField_bestMatchIsFirst() throws Exception {
+        manyUserData_oneDetectableField(true);
+    }
+
+    @Test
+    public void testHit_manyUserData_oneDetectableField_bestMatchIsSecond() throws Exception {
+        manyUserData_oneDetectableField(false);
+    }
+
+    private void manyUserData_oneDetectableField(boolean firstMatch) throws Exception {
+        // Set service.
+        enableService();
+
+        // Set expectations.
+        mAfm.setUserData(new UserData.Builder("1stId", "Iam1ST").add("2ndId", "Iam2ND").build());
+        final MyAutofillCallback callback = mActivity.registerCallback();
+        final EditText field = mActivity.getCell(1, 1);
+        final AutofillId fieldId = field.getAutofillId();
+        sReplier.addResponse(new CannedFillResponse.Builder()
+                .setFieldClassificationIds(fieldId)
+                .build());
+
+        // Trigger autofill
+        mActivity.focusCell(1, 1);
+        sReplier.getNextFillRequest();
+
+        mUiBot.assertNoDatasets();
+        callback.assertUiUnavailableEvent(field);
+
+        // Simulate user input
+        mActivity.setText(1, 1, firstMatch ? "IAM111" : "IAM222");
+
+        // Finish context.
+        mAfm.commit();
+
+        // Assert results
+        final List<Event> events = InstrumentedAutoFillService.getFillEvents(1);
+        final String algorithm = getDefaultAlgorithm();
+        final String[] algorithms = { algorithm, algorithm };
+        // Best match is 0.66 (4 of 6), worst is 0.5 (3 of 6)
+        if (firstMatch) {
+            assertFillEventForFieldsClassification(events.get(0), new FieldClassificationResult[] {
+                    new FieldClassificationResult(fieldId, new String[] { "1stId", "2ndId" },
+                            new float[] { 0.66F, 0.5F }, algorithms)});
+        } else {
+            assertFillEventForFieldsClassification(events.get(0), new FieldClassificationResult[] {
+                    new FieldClassificationResult(fieldId, new String[] { "2ndId", "1stId" },
+                            new float[] { 0.66F, 0.5F }, algorithms) });
+        }
+    }
+
+    @Test
+    public void testHit_oneUserData_manyDetectableFields() throws Exception {
+        // Set service.
+        enableService();
+
+        // Set expectations.
+        mAfm.setUserData(new UserData.Builder("myId", "FULLY").build());
+        final MyAutofillCallback callback = mActivity.registerCallback();
+        final EditText field1 = mActivity.getCell(1, 1);
+        final AutofillId fieldId1 = field1.getAutofillId();
+        final EditText field2 = mActivity.getCell(1, 2);
+        final AutofillId fieldId2 = field2.getAutofillId();
+        sReplier.addResponse(new CannedFillResponse.Builder()
+                .setFieldClassificationIds(fieldId1, fieldId2)
+                .build());
+
+        // Trigger autofill
+        mActivity.focusCell(1, 1);
+        sReplier.getNextFillRequest();
+
+        mUiBot.assertNoDatasets();
+        callback.assertUiUnavailableEvent(field1);
+
+        // Simulate user input
+        mActivity.setText(1, 1, "fully"); // 100%
+        mActivity.setText(1, 2, "fooly"); // 60%
+
+        // Finish context.
+        mAfm.commit();
+
+        // Assert results
+        final List<Event> events = InstrumentedAutoFillService.getFillEvents(1);
+        final String algorithm = getDefaultAlgorithm();
+        assertFillEventForFieldsClassification(events.get(0),
+                new FieldClassificationResult[] {
+                        new FieldClassificationResult(fieldId1, "myId", 1.0F, algorithm),
+                        new FieldClassificationResult(fieldId2, "myId", 1.0F, algorithm),
+                });
+    }
+
+    @Test
+    public void testHit_manyUserData_manyDetectableFields() throws Exception {
+        // Set service.
+        enableService();
+
+        // Set expectations.
+        mAfm.setUserData(new UserData.Builder("myId", "FULLY")
+                .add("totalMiss", "ZZZZZZZZZZ") // should not have matched any
+                .add("otherId", "EMPTY")
+                .build());
+        final MyAutofillCallback callback = mActivity.registerCallback();
+        final EditText field1 = mActivity.getCell(1, 1);
+        final AutofillId fieldId1 = field1.getAutofillId();
+        final EditText field2 = mActivity.getCell(1, 2);
+        final AutofillId fieldId2 = field2.getAutofillId();
+        final EditText field3 = mActivity.getCell(2, 1);
+        final AutofillId fieldId3 = field3.getAutofillId();
+        final EditText field4 = mActivity.getCell(2, 2);
+        final AutofillId fieldId4 = field4.getAutofillId();
+        sReplier.addResponse(new CannedFillResponse.Builder()
+                .setFieldClassificationIds(fieldId1, fieldId2)
+                .build());
+
+        // Trigger autofill
+        mActivity.focusCell(1, 1);
+        sReplier.getNextFillRequest();
+
+        mUiBot.assertNoDatasets();
+        callback.assertUiUnavailableEvent(field1);
+
+        // Simulate user input
+        mActivity.setText(1, 1, "fully"); // u1: 100% u2:  20%
+        mActivity.setText(1, 2, "empty"); // u1:  20% u2: 100%
+        mActivity.setText(2, 1, "fooly"); // u1:  60% u2:  20%
+        mActivity.setText(2, 2, "emppy"); // u1:  20% u2:  80%
+
+        // Finish context.
+        mAfm.commit();
+
+        // Assert results
+        final String algorithm = getDefaultAlgorithm();
+        final String[] algorithms = { algorithm, algorithm };
+        final List<Event> events = InstrumentedAutoFillService.getFillEvents(1);
+        assertFillEventForFieldsClassification(events.get(0),
+                new FieldClassificationResult[] {
+                        new FieldClassificationResult(fieldId1, new String[] { "myId", "otherId" },
+                                new float[] { 1.0F, 0.2F }, algorithms),
+                        new FieldClassificationResult(fieldId2, new String[] { "otherId", "myId" },
+                                new float[] { 1.0F, 0.2F }, algorithms),
+                        new FieldClassificationResult(fieldId3, new String[] { "myId", "otherId" },
+                                new float[] { 0.6F, 0.2F }, algorithms),
+                        new FieldClassificationResult(fieldId4, new String[] { "otherId", "myId"},
+                                new float[] { 0.80F, 0.2F }, algorithms)});
+    }
+
+    @Test
+    public void testMiss() throws Exception {
+        // Set service.
+        enableService();
+
+        // Set expectations.
+        mAfm.setUserData(new UserData.Builder("myId", "ABCDEF").build());
+        final MyAutofillCallback callback = mActivity.registerCallback();
+        final EditText field = mActivity.getCell(1, 1);
+        final AutofillId fieldId = field.getAutofillId();
+        sReplier.addResponse(new CannedFillResponse.Builder()
+                .setFieldClassificationIds(fieldId)
+                .build());
+
+        // Trigger autofill
+        mActivity.focusCell(1, 1);
+        sReplier.getNextFillRequest();
+
+        mUiBot.assertNoDatasets();
+        callback.assertUiUnavailableEvent(field);
+
+        // Simulate user input
+        mActivity.setText(1, 1, "xyz");
+
+        // Finish context.
+        mAfm.commit();
+
+        // Assert results
+        final List<Event> events = InstrumentedAutoFillService.getFillEvents(1);
+        assertFillEventForContextCommitted(events.get(0));
+    }
+
+    @Test
+    public void testNoUserInput() throws Exception {
+        // Set service.
+        enableService();
+
+        // Set expectations.
+        mAfm.setUserData(new UserData.Builder("myId", "FULLY").build());
+        final MyAutofillCallback callback = mActivity.registerCallback();
+        final EditText field = mActivity.getCell(1, 1);
+        final AutofillId fieldId = field.getAutofillId();
+        sReplier.addResponse(new CannedFillResponse.Builder()
+                .setFieldClassificationIds(fieldId)
+                .build());
+
+        // Trigger autofill
+        mActivity.focusCell(1, 1);
+        sReplier.getNextFillRequest();
+
+        mUiBot.assertNoDatasets();
+        callback.assertUiUnavailableEvent(field);
+
+        // Finish context.
+        mAfm.commit();
+
+        // Assert results
+        final List<Event> events = InstrumentedAutoFillService.getFillEvents(1);
+        assertFillEventForContextCommitted(events.get(0));
+    }
+
+    private String getDefaultAlgorithm() {
+        return mAfm.getDefaultFieldClassificationAlgorithm();
+    }
+
+    /*
+     * TODO(b/70407264): other scenarios:
+     *
+     * - Multipartition (for example, one response with FieldsDetection, others with datasets,
+     *   saveinfo, and/or ignoredIds)
+     * - make sure detectable fields don't trigger a new partition
+     * v test partial hit (for example, 'fool' instead of 'full'
+     * v multiple fields
+     * v multiple value
+     * - combinations of above items
+     */
+}
diff --git a/tests/autofillservice/src/android/autofillservice/cts/FillEventHistoryTest.java b/tests/autofillservice/src/android/autofillservice/cts/FillEventHistoryTest.java
new file mode 100644
index 0000000..3382161
--- /dev/null
+++ b/tests/autofillservice/src/android/autofillservice/cts/FillEventHistoryTest.java
@@ -0,0 +1,1182 @@
+/*
+ * 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.autofillservice.cts;
+
+import static android.autofillservice.cts.CannedFillResponse.DO_NOT_REPLY_RESPONSE;
+import static android.autofillservice.cts.CannedFillResponse.NO_RESPONSE;
+import static android.autofillservice.cts.CheckoutActivity.ID_CC_NUMBER;
+import static android.autofillservice.cts.Helper.ID_PASSWORD;
+import static android.autofillservice.cts.Helper.ID_USERNAME;
+import static android.autofillservice.cts.Helper.NULL_DATASET_ID;
+import static android.autofillservice.cts.Helper.assertDeprecatedClientState;
+import static android.autofillservice.cts.Helper.assertFillEventForAuthenticationSelected;
+import static android.autofillservice.cts.Helper.assertFillEventForDatasetAuthenticationSelected;
+import static android.autofillservice.cts.Helper.assertFillEventForDatasetSelected;
+import static android.autofillservice.cts.Helper.assertFillEventForSaveShown;
+import static android.autofillservice.cts.Helper.assertNoDeprecatedClientState;
+import static android.autofillservice.cts.InstrumentedAutoFillService.waitUntilConnected;
+import static android.autofillservice.cts.InstrumentedAutoFillService.waitUntilDisconnected;
+import static android.autofillservice.cts.LoginActivity.BACKDOOR_USERNAME;
+import static android.autofillservice.cts.LoginActivity.getWelcomeMessage;
+import static android.service.autofill.FillEventHistory.Event.TYPE_CONTEXT_COMMITTED;
+import static android.service.autofill.SaveInfo.SAVE_DATA_TYPE_GENERIC;
+import static android.service.autofill.SaveInfo.SAVE_DATA_TYPE_PASSWORD;
+
+import static com.google.common.truth.Truth.assertThat;
+import static com.google.common.truth.Truth.assertWithMessage;
+
+import android.autofillservice.cts.CannedFillResponse.CannedDataset;
+import android.content.Intent;
+import android.content.IntentSender;
+import android.os.Bundle;
+import android.service.autofill.FillEventHistory;
+import android.service.autofill.FillEventHistory.Event;
+import android.service.autofill.FillResponse;
+import android.support.test.uiautomator.UiObject2;
+import android.view.View;
+import android.view.autofill.AutofillId;
+
+import com.google.common.collect.ImmutableMap;
+
+import org.junit.Before;
+import org.junit.Rule;
+import org.junit.Test;
+
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+
+/**
+ * Test that uses {@link LoginActivity} to test {@link FillEventHistory}.
+ */
+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();
+    }
+
+    @Test
+    public void testDatasetAuthenticationSelected() throws Exception {
+        enableService();
+
+        // Set up FillResponse with dataset authentication
+        Bundle clientState = new Bundle();
+        clientState.putCharSequence("clientStateKey", "clientStateValue");
+
+        // Prepare the authenticated response
+        final IntentSender authentication = AuthenticationActivity.createSender(mContext, 1,
+                new CannedDataset.Builder()
+                        .setField(ID_USERNAME, "dude")
+                        .setField(ID_PASSWORD, "sweet")
+                        .setPresentation(createPresentation("Dataset"))
+                        .build());
+
+        sReplier.addResponse(new CannedFillResponse.Builder().addDataset(
+                new CannedDataset.Builder()
+                        .setField(ID_USERNAME, "username")
+                        .setId("name")
+                        .setPresentation(createPresentation("authentication"))
+                        .setAuthentication(authentication)
+                        .build())
+                .setExtras(clientState).build());
+        mActivity.expectAutoFill("dude", "sweet");
+
+        // Trigger autofill.
+        mActivity.onUsername(View::requestFocus);
+
+        // Authenticate
+        mUiBot.selectDataset("authentication");
+        sReplier.getNextFillRequest();
+        mActivity.assertAutoFilled();
+
+        // Verify fill selection
+        final List<Event> events = InstrumentedAutoFillService.getFillEvents(1);
+        assertFillEventForDatasetAuthenticationSelected(events.get(0), "name",
+                "clientStateKey", "clientStateValue");
+    }
+
+    @Test
+    public void testAuthenticationSelected() throws Exception {
+        enableService();
+
+        // Set up FillResponse with response wide authentication
+        Bundle clientState = new Bundle();
+        clientState.putCharSequence("clientStateKey", "clientStateValue");
+
+        // Prepare the authenticated response
+        final IntentSender authentication = AuthenticationActivity.createSender(mContext, 1,
+                new CannedFillResponse.Builder().addDataset(
+                        new CannedDataset.Builder()
+                                .setField(ID_USERNAME, "username")
+                                .setId("name")
+                                .setPresentation(createPresentation("dataset"))
+                                .build())
+                        .setExtras(clientState).build());
+
+        sReplier.addResponse(new CannedFillResponse.Builder().setExtras(clientState)
+                .setPresentation(createPresentation("authentication"))
+                .setAuthentication(authentication, ID_USERNAME)
+                .build());
+
+        // Trigger autofill.
+        mActivity.onUsername(View::requestFocus);
+
+        // Authenticate
+        mUiBot.selectDataset("authentication");
+        sReplier.getNextFillRequest();
+        mUiBot.assertDatasets("dataset");
+
+        // Verify fill selection
+        final FillEventHistory selection = InstrumentedAutoFillService.getFillEventHistory(1);
+        assertDeprecatedClientState(selection, "clientStateKey", "clientStateValue");
+        List<Event> events = selection.getEvents();
+        assertFillEventForAuthenticationSelected(events.get(0), NULL_DATASET_ID,
+                "clientStateKey", "clientStateValue");
+    }
+
+    @Test
+    public void testDatasetSelected_twoResponses() throws Exception {
+        enableService();
+
+        // Set up first partition with an anonymous dataset
+        Bundle clientState1 = new Bundle();
+        clientState1.putCharSequence("clientStateKey", "Value1");
+
+        sReplier.addResponse(new CannedFillResponse.Builder().addDataset(
+                new CannedDataset.Builder()
+                        .setField(ID_USERNAME, "username")
+                        .setPresentation(createPresentation("dataset1"))
+                        .build())
+                .setExtras(clientState1)
+                .build());
+        mActivity.expectAutoFill("username");
+
+        // Trigger autofill on username
+        mActivity.onUsername(View::requestFocus);
+        waitUntilConnected();
+        mUiBot.selectDataset("dataset1");
+        sReplier.getNextFillRequest();
+        mActivity.assertAutoFilled();
+
+        {
+            // Verify fill selection
+            final FillEventHistory selection = InstrumentedAutoFillService.getFillEventHistory(1);
+            assertDeprecatedClientState(selection, "clientStateKey", "Value1");
+            final List<Event> events = selection.getEvents();
+            assertFillEventForDatasetSelected(events.get(0), NULL_DATASET_ID,
+                    "clientStateKey", "Value1");
+        }
+
+        // Set up second partition with a named dataset
+        Bundle clientState2 = new Bundle();
+        clientState2.putCharSequence("clientStateKey", "Value2");
+
+        sReplier.addResponse(new CannedFillResponse.Builder()
+                .addDataset(
+                        new CannedDataset.Builder()
+                                .setField(ID_PASSWORD, "password2")
+                                .setPresentation(createPresentation("dataset2"))
+                                .setId("name2")
+                                .build())
+                .addDataset(
+                        new CannedDataset.Builder()
+                                .setField(ID_PASSWORD, "password3")
+                                .setPresentation(createPresentation("dataset3"))
+                                .setId("name3")
+                                .build())
+                .setExtras(clientState2)
+                .setRequiredSavableIds(SAVE_DATA_TYPE_GENERIC, ID_PASSWORD).build());
+        mActivity.expectPasswordAutoFill("password3");
+
+        // Trigger autofill on password
+        mActivity.onPassword(View::requestFocus);
+        mUiBot.selectDataset("dataset3");
+        sReplier.getNextFillRequest();
+        mActivity.assertAutoFilled();
+
+        {
+            // Verify fill selection
+            final FillEventHistory selection = InstrumentedAutoFillService.getFillEventHistory(1);
+            assertDeprecatedClientState(selection, "clientStateKey", "Value2");
+            final List<Event> events = selection.getEvents();
+            assertFillEventForDatasetSelected(events.get(0), "name3",
+                    "clientStateKey", "Value2");
+        }
+
+        mActivity.onPassword((v) -> v.setText("new password"));
+        mActivity.syncRunOnUiThread(() -> mActivity.finish());
+        waitUntilDisconnected();
+
+        {
+            // Verify fill selection
+            final FillEventHistory selection = InstrumentedAutoFillService.getFillEventHistory(2);
+            assertDeprecatedClientState(selection, "clientStateKey", "Value2");
+
+            final List<Event> events = selection.getEvents();
+            assertFillEventForDatasetSelected(events.get(0), "name3",
+                    "clientStateKey", "Value2");
+            assertFillEventForSaveShown(events.get(1), NULL_DATASET_ID,
+                    "clientStateKey", "Value2");
+        }
+    }
+
+    @Test
+    public void testNoEvents_whenServiceReturnsNullResponse() throws Exception {
+        enableService();
+
+        // First reset
+        sReplier.addResponse(new CannedFillResponse.Builder().addDataset(
+                new CannedDataset.Builder()
+                        .setField(ID_USERNAME, "username")
+                        .setPresentation(createPresentation("dataset1"))
+                        .build())
+                .build());
+        mActivity.expectAutoFill("username");
+
+        mActivity.onUsername(View::requestFocus);
+        waitUntilConnected();
+        sReplier.getNextFillRequest();
+        mUiBot.selectDataset("dataset1");
+        mActivity.assertAutoFilled();
+
+        {
+            // Verify fill selection
+            final FillEventHistory selection = InstrumentedAutoFillService.getFillEventHistory(1);
+            assertNoDeprecatedClientState(selection);
+            final List<Event> events = selection.getEvents();
+            assertFillEventForDatasetSelected(events.get(0), NULL_DATASET_ID);
+        }
+
+        // Second request
+        sReplier.addResponse(NO_RESPONSE);
+        mActivity.onPassword(View::requestFocus);
+        sReplier.getNextFillRequest();
+        mUiBot.assertNoDatasets();
+        waitUntilDisconnected();
+
+        InstrumentedAutoFillService.assertNoFillEventHistory();
+    }
+
+    @Test
+    public void testNoEvents_whenServiceReturnsFailure() throws Exception {
+        enableService();
+
+        // First reset
+        sReplier.addResponse(new CannedFillResponse.Builder().addDataset(
+                new CannedDataset.Builder()
+                        .setField(ID_USERNAME, "username")
+                        .setPresentation(createPresentation("dataset1"))
+                        .build())
+                .build());
+        mActivity.expectAutoFill("username");
+
+        mActivity.onUsername(View::requestFocus);
+        waitUntilConnected();
+        sReplier.getNextFillRequest();
+        mUiBot.selectDataset("dataset1");
+        mActivity.assertAutoFilled();
+
+        {
+            // Verify fill selection
+            final FillEventHistory selection = InstrumentedAutoFillService.getFillEventHistory(1);
+            assertNoDeprecatedClientState(selection);
+            final List<Event> events = selection.getEvents();
+            assertFillEventForDatasetSelected(events.get(0), NULL_DATASET_ID);
+        }
+
+        // Second request
+        sReplier.addResponse(new CannedFillResponse.Builder().returnFailure("D'OH!").build());
+        mActivity.onPassword(View::requestFocus);
+        sReplier.getNextFillRequest();
+        mUiBot.assertNoDatasets();
+        waitUntilDisconnected();
+
+        InstrumentedAutoFillService.assertNoFillEventHistory();
+    }
+
+    @Test
+    public void testNoEvents_whenServiceTimesout() throws Exception {
+        enableService();
+
+        // First reset
+        sReplier.addResponse(new CannedFillResponse.Builder().addDataset(
+                new CannedDataset.Builder()
+                        .setField(ID_USERNAME, "username")
+                        .setPresentation(createPresentation("dataset1"))
+                        .build())
+                .build());
+        mActivity.expectAutoFill("username");
+
+        mActivity.onUsername(View::requestFocus);
+        waitUntilConnected();
+        sReplier.getNextFillRequest();
+        mUiBot.selectDataset("dataset1");
+        mActivity.assertAutoFilled();
+
+        {
+            // Verify fill selection
+            final FillEventHistory selection = InstrumentedAutoFillService.getFillEventHistory(1);
+            assertNoDeprecatedClientState(selection);
+            final List<Event> events = selection.getEvents();
+            assertFillEventForDatasetSelected(events.get(0), NULL_DATASET_ID);
+        }
+
+        // Second request
+        sReplier.addResponse(DO_NOT_REPLY_RESPONSE);
+        mActivity.onPassword(View::requestFocus);
+        sReplier.getNextFillRequest();
+        waitUntilDisconnected();
+
+        InstrumentedAutoFillService.assertNoFillEventHistory();
+    }
+
+    private Bundle getBundle(String key, String value) {
+        final Bundle bundle = new Bundle();
+        bundle.putString(key, value);
+        return bundle;
+    }
+
+    /**
+     * Tests the following scenario:
+     *
+     * <ol>
+     *    <li>Activity A is launched.
+     *    <li>Activity A triggers autofill.
+     *    <li>Activity B is launched.
+     *    <li>Activity B triggers autofill.
+     *    <li>User goes back to Activity A.
+     *    <li>User triggers save on Activity A - at this point, service should have stats of
+     *        activity B, and stats for activity A should have beeen discarded.
+     * </ol>
+     */
+    @Test
+    public void testEventsFromPreviousSessionIsDiscarded() throws Exception {
+        enableService();
+
+        // Launch activity A
+        sReplier.addResponse(new CannedFillResponse.Builder()
+                .setExtras(getBundle("activity", "A"))
+                .setRequiredSavableIds(SAVE_DATA_TYPE_PASSWORD, ID_USERNAME, ID_PASSWORD)
+                .build());
+
+        // Trigger autofill on activity A
+        mActivity.onUsername(View::requestFocus);
+        waitUntilConnected();
+        sReplier.getNextFillRequest();
+
+        // Verify fill selection for Activity A
+        final FillEventHistory selectionA = InstrumentedAutoFillService.getFillEventHistory(0);
+        assertDeprecatedClientState(selectionA, "activity", "A");
+
+        // Launch activity B
+        mContext.startActivity(new Intent(mContext, CheckoutActivity.class));
+        mUiBot.assertShownByRelativeId(ID_CC_NUMBER);
+
+        // Trigger autofill on activity B
+        sReplier.addResponse(new CannedFillResponse.Builder()
+                .setExtras(getBundle("activity", "B"))
+                .addDataset(new CannedDataset.Builder()
+                        .setField(ID_CC_NUMBER, "4815162342")
+                        .setPresentation(createPresentation("datasetB"))
+                        .build())
+                .build());
+        mUiBot.focusByRelativeId(ID_CC_NUMBER);
+        sReplier.getNextFillRequest();
+
+        // Verify fill selection for Activity B
+        final FillEventHistory selectionB = InstrumentedAutoFillService.getFillEventHistory(0);
+        assertDeprecatedClientState(selectionB, "activity", "B");
+
+        // Now switch back to A...
+        mUiBot.pressBack(); // dismiss keyboard
+        mUiBot.pressBack(); // dismiss task
+        mUiBot.assertShownByRelativeId(ID_USERNAME);
+        // ...and trigger save
+        // Set credentials...
+        mActivity.onUsername((v) -> v.setText("malkovich"));
+        mActivity.onPassword((v) -> v.setText("malkovich"));
+        final String expectedMessage = getWelcomeMessage("malkovich");
+        final String actualMessage = mActivity.tapLogin();
+        assertWithMessage("Wrong welcome msg").that(actualMessage).isEqualTo(expectedMessage);
+        mUiBot.saveForAutofill(true, SAVE_DATA_TYPE_PASSWORD);
+        sReplier.getNextSaveRequest();
+
+        // Finally, make sure history is right
+        final FillEventHistory finalSelection = InstrumentedAutoFillService.getFillEventHistory(0);
+        assertDeprecatedClientState(finalSelection, "activity", "B");
+    }
+
+    @Test
+    public void testContextCommitted_whenServiceDidntDoAnything() throws Exception {
+        enableService();
+
+        sReplier.addResponse(CannedFillResponse.NO_RESPONSE);
+
+        // Trigger autofill on username
+        mActivity.onUsername(View::requestFocus);
+        sReplier.getNextFillRequest();
+        mUiBot.assertNoDatasets();
+
+        // Trigger save
+        mActivity.onUsername((v) -> v.setText("malkovich"));
+        mActivity.onPassword((v) -> v.setText("malkovich"));
+        final String expectedMessage = getWelcomeMessage("malkovich");
+        final String actualMessage = mActivity.tapLogin();
+        assertWithMessage("Wrong welcome msg").that(actualMessage).isEqualTo(expectedMessage);
+        mUiBot.assertSaveNotShowing(SAVE_DATA_TYPE_PASSWORD);
+
+        // Assert no events where generated
+        InstrumentedAutoFillService.assertNoFillEventHistory();
+    }
+
+    @Test
+    public void textContextCommitted_withoutDatasets() throws Exception {
+        enableService();
+
+        sReplier.addResponse(new CannedFillResponse.Builder()
+                .setFillResponseFlags(FillResponse.FLAG_TRACK_CONTEXT_COMMITED)
+                .setRequiredSavableIds(SAVE_DATA_TYPE_PASSWORD, ID_USERNAME, ID_PASSWORD)
+                .build());
+
+        // Trigger autofill on username
+        mActivity.onUsername(View::requestFocus);
+        sReplier.getNextFillRequest();
+        mUiBot.assertNoDatasets();
+
+        // Trigger save
+        mActivity.onUsername((v) -> v.setText("malkovich"));
+        mActivity.onPassword((v) -> v.setText("malkovich"));
+        final String expectedMessage = getWelcomeMessage("malkovich");
+        final String actualMessage = mActivity.tapLogin();
+        assertWithMessage("Wrong welcome msg").that(actualMessage).isEqualTo(expectedMessage);
+        mUiBot.saveForAutofill(true, SAVE_DATA_TYPE_PASSWORD);
+        sReplier.getNextSaveRequest();
+
+        // Assert it
+        final List<Event> events = InstrumentedAutoFillService.getFillEvents(1);
+        assertFillEventForSaveShown(events.get(0), NULL_DATASET_ID);
+    }
+
+    @Test
+    public void testContextCommitted_withoutFlagOnLastResponse() throws Exception {
+        enableService();
+        // Trigger 1st autofill request
+        sReplier.addResponse(new CannedFillResponse.Builder().addDataset(
+                new CannedDataset.Builder()
+                        .setId("id1")
+                        .setField(ID_USERNAME, BACKDOOR_USERNAME)
+                        .setPresentation(createPresentation("dataset1"))
+                        .build())
+                .setFillResponseFlags(FillResponse.FLAG_TRACK_CONTEXT_COMMITED)
+                .build());
+        mActivity.expectAutoFill(BACKDOOR_USERNAME);
+        mActivity.onUsername(View::requestFocus);
+        sReplier.getNextFillRequest();
+        mUiBot.selectDataset("dataset1");
+        mActivity.assertAutoFilled();
+        // Verify fill history
+        {
+            final List<Event> events = InstrumentedAutoFillService.getFillEvents(1);
+            assertFillEventForDatasetSelected(events.get(0), "id1");
+        }
+
+        // Trigger 2st autofill request (which will clear the fill event history)
+        sReplier.addResponse(new CannedFillResponse.Builder().addDataset(
+                new CannedDataset.Builder()
+                        .setId("id2")
+                        .setField(ID_PASSWORD, "whatever")
+                        .setPresentation(createPresentation("dataset2"))
+                        .build())
+                // don't set flags
+                .build());
+        mActivity.expectPasswordAutoFill("whatever");
+        mActivity.onPassword(View::requestFocus);
+        sReplier.getNextFillRequest();
+        mUiBot.selectDataset("dataset2");
+        mActivity.assertAutoFilled();
+        // Verify fill history
+        {
+            final List<Event> events = InstrumentedAutoFillService.getFillEvents(1);
+            assertFillEventForDatasetSelected(events.get(0), "id2");
+        }
+
+        // Finish the context by login in
+        final String expectedMessage = getWelcomeMessage(BACKDOOR_USERNAME);
+        final String actualMessage = mActivity.tapLogin();
+        assertWithMessage("Wrong welcome msg").that(actualMessage).isEqualTo(expectedMessage);
+        mUiBot.assertSaveNotShowing(SAVE_DATA_TYPE_PASSWORD);
+
+        {
+            // Verify fill history
+            final List<Event> events = InstrumentedAutoFillService.getFillEvents(1);
+
+            assertFillEventForDatasetSelected(events.get(0), "id2");
+        }
+    }
+
+    @Test
+    public void testContextCommitted_idlessDatasets() throws Exception {
+        enableService();
+
+        sReplier.addResponse(new CannedFillResponse.Builder().addDataset(
+                new CannedDataset.Builder()
+                        .setField(ID_USERNAME, "username1")
+                        .setField(ID_PASSWORD, "password1")
+                        .setPresentation(createPresentation("dataset1"))
+                        .build())
+                .addDataset(new CannedDataset.Builder()
+                        .setField(ID_USERNAME, "username2")
+                        .setField(ID_PASSWORD, "password2")
+                        .setPresentation(createPresentation("dataset2"))
+                        .build())
+                .setFillResponseFlags(FillResponse.FLAG_TRACK_CONTEXT_COMMITED)
+                .build());
+        mActivity.expectAutoFill("username1", "password1");
+
+        // Trigger autofill on username
+        mActivity.onUsername(View::requestFocus);
+        sReplier.getNextFillRequest();
+
+        final UiObject2 datasetPicker = mUiBot.assertDatasets("dataset1", "dataset2");
+        mUiBot.selectDataset(datasetPicker, "dataset1");
+        mActivity.assertAutoFilled();
+
+        // Verify dataset selection
+        {
+            final List<Event> events = InstrumentedAutoFillService.getFillEvents(1);
+            assertFillEventForDatasetSelected(events.get(0), NULL_DATASET_ID);
+        }
+
+        // Finish the context by login in
+        mActivity.onUsername((v) -> v.setText("USERNAME"));
+        mActivity.onPassword((v) -> v.setText("USERNAME"));
+
+        final String expectedMessage = getWelcomeMessage("USERNAME");
+        final String actualMessage = mActivity.tapLogin();
+        assertWithMessage("Wrong welcome msg").that(actualMessage).isEqualTo(expectedMessage);
+        mUiBot.assertSaveNotShowing(SAVE_DATA_TYPE_PASSWORD);
+
+        // ...and check again
+        {
+            final List<Event> events = InstrumentedAutoFillService.getFillEvents(1);
+            assertFillEventForDatasetSelected(events.get(0), NULL_DATASET_ID);
+        }
+    }
+
+    @Test
+    public void testContextCommitted_idlessDatasetSelected_datasetWithIdIgnored()
+            throws Exception {
+        enableService();
+
+        sReplier.addResponse(new CannedFillResponse.Builder().addDataset(
+                new CannedDataset.Builder()
+                        .setField(ID_USERNAME, "username1")
+                        .setField(ID_PASSWORD, "password1")
+                        .setPresentation(createPresentation("dataset1"))
+                        .build())
+                .addDataset(new CannedDataset.Builder()
+                        .setId("id2")
+                        .setField(ID_USERNAME, "username2")
+                        .setField(ID_PASSWORD, "password2")
+                        .setPresentation(createPresentation("dataset2"))
+                        .build())
+                .setFillResponseFlags(FillResponse.FLAG_TRACK_CONTEXT_COMMITED)
+                .build());
+        mActivity.expectAutoFill("username1", "password1");
+
+        // Trigger autofill on username
+        mActivity.onUsername(View::requestFocus);
+        sReplier.getNextFillRequest();
+
+        final UiObject2 datasetPicker = mUiBot.assertDatasets("dataset1", "dataset2");
+        mUiBot.selectDataset(datasetPicker, "dataset1");
+        mActivity.assertAutoFilled();
+
+        // Verify dataset selection
+        {
+            final List<Event> events = InstrumentedAutoFillService.getFillEvents(1);
+            assertFillEventForDatasetSelected(events.get(0), NULL_DATASET_ID);
+        }
+
+        // Finish the context by login in
+        mActivity.onPassword((v) -> v.setText("username1"));
+
+        final String expectedMessage = getWelcomeMessage("username1");
+        final String actualMessage = mActivity.tapLogin();
+        assertWithMessage("Wrong welcome msg").that(actualMessage).isEqualTo(expectedMessage);
+        mUiBot.assertSaveNotShowing(SAVE_DATA_TYPE_PASSWORD);
+
+        // ...and check again
+        {
+            final List<Event> events = InstrumentedAutoFillService.getFillEvents(2);
+            assertFillEventForDatasetSelected(events.get(0), NULL_DATASET_ID);
+
+            FillEventHistory.Event event2 = events.get(1);
+            assertThat(event2.getType()).isEqualTo(TYPE_CONTEXT_COMMITTED);
+            assertThat(event2.getDatasetId()).isNull();
+            assertThat(event2.getClientState()).isNull();
+            assertThat(event2.getSelectedDatasetIds()).isEmpty();
+            assertThat(event2.getIgnoredDatasetIds()).containsExactly("id2");
+            final AutofillId passwordId = mActivity.getPassword().getAutofillId();
+            final Map<AutofillId, String> changedFields = event2.getChangedFields();
+            assertThat(changedFields).containsExactly(passwordId, "id2");
+            assertThat(event2.getManuallyEnteredField()).isEmpty();
+        }
+    }
+
+    @Test
+    public void testContextCommitted_idlessDatasetIgnored_datasetWithIdSelected()
+            throws Exception {
+        enableService();
+
+        sReplier.addResponse(new CannedFillResponse.Builder().addDataset(
+                new CannedDataset.Builder()
+                        .setField(ID_USERNAME, "username1")
+                        .setField(ID_PASSWORD, "password1")
+                        .setPresentation(createPresentation("dataset1"))
+                        .build())
+                .addDataset(new CannedDataset.Builder()
+                        .setId("id2")
+                        .setField(ID_USERNAME, "username2")
+                        .setField(ID_PASSWORD, "password2")
+                        .setPresentation(createPresentation("dataset2"))
+                        .build())
+                .setFillResponseFlags(FillResponse.FLAG_TRACK_CONTEXT_COMMITED)
+                .build());
+        mActivity.expectAutoFill("username2", "password2");
+
+        // Trigger autofill on username
+        mActivity.onUsername(View::requestFocus);
+        sReplier.getNextFillRequest();
+
+        final UiObject2 datasetPicker = mUiBot.assertDatasets("dataset1", "dataset2");
+        mUiBot.selectDataset(datasetPicker, "dataset2");
+        mActivity.assertAutoFilled();
+
+        // Verify dataset selection
+        {
+            final List<Event> events = InstrumentedAutoFillService.getFillEvents(1);
+            assertFillEventForDatasetSelected(events.get(0), "id2");
+        }
+
+        // Finish the context by login in
+        mActivity.onPassword((v) -> v.setText("username2"));
+
+        final String expectedMessage = getWelcomeMessage("username2");
+        final String actualMessage = mActivity.tapLogin();
+        assertWithMessage("Wrong welcome msg").that(actualMessage).isEqualTo(expectedMessage);
+        mUiBot.assertSaveNotShowing(SAVE_DATA_TYPE_PASSWORD);
+
+        // ...and check again
+        {
+            final List<Event> events = InstrumentedAutoFillService.getFillEvents(2);
+            assertFillEventForDatasetSelected(events.get(0), "id2");
+
+            final FillEventHistory.Event event2 = events.get(1);
+            assertThat(event2.getType()).isEqualTo(TYPE_CONTEXT_COMMITTED);
+            assertThat(event2.getDatasetId()).isNull();
+            assertThat(event2.getClientState()).isNull();
+            assertThat(event2.getSelectedDatasetIds()).containsExactly("id2");
+            assertThat(event2.getIgnoredDatasetIds()).isEmpty();
+            final AutofillId passwordId = mActivity.getPassword().getAutofillId();
+            final Map<AutofillId, String> changedFields = event2.getChangedFields();
+            assertThat(changedFields).containsExactly(passwordId, "id2");
+            assertThat(event2.getManuallyEnteredField()).isEmpty();
+        }
+    }
+
+    /**
+     * Tests scenario where the context was committed, no dataset was selected by the user,
+     * neither the user entered values that were present in these datasets.
+     */
+    @Test
+    public void testContextCommitted_noDatasetSelected_valuesNotManuallyEntered() throws Exception {
+        enableService();
+
+        sReplier.addResponse(new CannedFillResponse.Builder().addDataset(
+                new CannedDataset.Builder()
+                        .setId("id1")
+                        .setField(ID_USERNAME, "username1")
+                        .setField(ID_PASSWORD, "password1")
+                        .setPresentation(createPresentation("dataset1"))
+                        .build())
+                .addDataset(new CannedDataset.Builder()
+                        .setId("id2")
+                        .setField(ID_USERNAME, "username2")
+                        .setField(ID_PASSWORD, "password2")
+                        .setPresentation(createPresentation("dataset2"))
+                        .build())
+                .setFillResponseFlags(FillResponse.FLAG_TRACK_CONTEXT_COMMITED)
+                .build());
+        // Trigger autofill on username
+        mActivity.onUsername(View::requestFocus);
+        sReplier.getNextFillRequest();
+        mUiBot.assertDatasets("dataset1", "dataset2");
+
+        // Verify history
+        InstrumentedAutoFillService.getFillEventHistory(0);
+
+        // Enter values not present at the datasets
+        mActivity.onUsername((v) -> v.setText("USERNAME"));
+        mActivity.onPassword((v) -> v.setText("USERNAME"));
+
+        // Finish the context by login in
+        final String expectedMessage = getWelcomeMessage("USERNAME");
+        final String actualMessage = mActivity.tapLogin();
+        assertWithMessage("Wrong welcome msg").that(actualMessage).isEqualTo(expectedMessage);
+        mUiBot.assertSaveNotShowing(SAVE_DATA_TYPE_PASSWORD);
+
+        // Verify history again
+        {
+            final List<Event> events = InstrumentedAutoFillService.getFillEvents(1);
+            final Event event = events.get(0);
+            assertThat(event.getType()).isEqualTo(TYPE_CONTEXT_COMMITTED);
+            assertThat(event.getDatasetId()).isNull();
+            assertThat(event.getClientState()).isNull();
+            assertThat(event.getIgnoredDatasetIds()).containsExactly("id1", "id2");
+            assertThat(event.getChangedFields()).isEmpty();
+            assertThat(event.getManuallyEnteredField()).isEmpty();
+        }
+    }
+
+    /**
+     * Tests scenario where the context was committed, just one dataset was selected by the user,
+     * and the user changed the values provided by the service.
+     */
+    @Test
+    public void testContextCommitted_oneDatasetSelected() throws Exception {
+        enableService();
+
+        sReplier.addResponse(new CannedFillResponse.Builder().addDataset(
+                new CannedDataset.Builder()
+                        .setId("id1")
+                        .setField(ID_USERNAME, "username1")
+                        .setField(ID_PASSWORD, "password1")
+                        .setPresentation(createPresentation("dataset1"))
+                        .build())
+                .addDataset(new CannedDataset.Builder()
+                        .setId("id2")
+                        .setField(ID_USERNAME, "username2")
+                        .setField(ID_PASSWORD, "password2")
+                        .setPresentation(createPresentation("dataset2"))
+                        .build())
+                .setFillResponseFlags(FillResponse.FLAG_TRACK_CONTEXT_COMMITED)
+                .build());
+        mActivity.expectAutoFill("username1", "password1");
+
+        // Trigger autofill on username
+        mActivity.onUsername(View::requestFocus);
+        sReplier.getNextFillRequest();
+
+        final UiObject2 datasetPicker = mUiBot.assertDatasets("dataset1", "dataset2");
+        mUiBot.selectDataset(datasetPicker, "dataset1");
+        mActivity.assertAutoFilled();
+
+        // Verify dataset selection
+        {
+            final List<Event> events = InstrumentedAutoFillService.getFillEvents(1);
+            assertFillEventForDatasetSelected(events.get(0), "id1");
+        }
+
+        // Finish the context by login in
+        mActivity.onUsername((v) -> v.setText("USERNAME"));
+        mActivity.onPassword((v) -> v.setText("USERNAME"));
+
+        final String expectedMessage = getWelcomeMessage("USERNAME");
+        final String actualMessage = mActivity.tapLogin();
+        assertWithMessage("Wrong welcome msg").that(actualMessage).isEqualTo(expectedMessage);
+        mUiBot.assertSaveNotShowing(SAVE_DATA_TYPE_PASSWORD);
+
+        // ...and check again
+        {
+            final List<Event> events = InstrumentedAutoFillService.getFillEvents(2);
+            assertFillEventForDatasetSelected(events.get(0), "id1");
+
+            final FillEventHistory.Event event2 = events.get(1);
+            assertThat(event2.getType()).isEqualTo(TYPE_CONTEXT_COMMITTED);
+            assertThat(event2.getDatasetId()).isNull();
+            assertThat(event2.getClientState()).isNull();
+            assertThat(event2.getSelectedDatasetIds()).containsExactly("id1");
+            assertThat(event2.getIgnoredDatasetIds()).containsExactly("id2");
+            final Map<AutofillId, String> changedFields = event2.getChangedFields();
+            final AutofillId usernameId = mActivity.getUsername().getAutofillId();
+            final AutofillId passwordId = mActivity.getPassword().getAutofillId();
+            assertThat(changedFields).containsExactlyEntriesIn(
+                    ImmutableMap.of(usernameId, "id1", passwordId, "id1"));
+            assertThat(event2.getManuallyEnteredField()).isEmpty();
+        }
+    }
+
+    /**
+     * Tests scenario where the context was committed, both datasets were selected by the user,
+     * and the user changed the values provided by the service.
+     */
+    @Test
+    public void testContextCommitted_multipleDatasetsSelected() throws Exception {
+        enableService();
+
+        sReplier.addResponse(new CannedFillResponse.Builder().addDataset(
+                new CannedDataset.Builder()
+                        .setId("id1")
+                        .setField(ID_USERNAME, "username")
+                        .setPresentation(createPresentation("dataset1"))
+                        .build())
+                .addDataset(new CannedDataset.Builder()
+                        .setId("id2")
+                        .setField(ID_PASSWORD, "password")
+                        .setPresentation(createPresentation("dataset2"))
+                        .build())
+                .setFillResponseFlags(FillResponse.FLAG_TRACK_CONTEXT_COMMITED)
+                .build());
+        mActivity.expectAutoFill("username");
+
+        // Trigger autofill
+        mActivity.onUsername(View::requestFocus);
+        sReplier.getNextFillRequest();
+
+        // Autofill username
+        mUiBot.selectDataset("dataset1");
+        mActivity.assertAutoFilled();
+        {
+            // Verify fill history
+            final List<Event> events = InstrumentedAutoFillService.getFillEvents(1);
+            assertFillEventForDatasetSelected(events.get(0), "id1");
+        }
+
+        // Autofill password
+        mActivity.expectPasswordAutoFill("password");
+
+        mActivity.onPassword(View::requestFocus);
+        mUiBot.selectDataset("dataset2");
+        mActivity.assertAutoFilled();
+
+        {
+            // Verify fill history
+            final List<Event> events = InstrumentedAutoFillService.getFillEvents(2);
+
+            assertFillEventForDatasetSelected(events.get(0), "id1");
+            assertFillEventForDatasetSelected(events.get(1), "id2");
+        }
+
+        // Finish the context by login in
+        mActivity.onPassword((v) -> v.setText("username"));
+
+        final String expectedMessage = getWelcomeMessage("username");
+        final String actualMessage = mActivity.tapLogin();
+        assertWithMessage("Wrong welcome msg").that(actualMessage).isEqualTo(expectedMessage);
+        mUiBot.assertSaveNotShowing(SAVE_DATA_TYPE_PASSWORD);
+
+        {
+            // Verify fill history
+            final List<Event> events = InstrumentedAutoFillService.getFillEvents(3);
+
+            assertFillEventForDatasetSelected(events.get(0), "id1");
+            assertFillEventForDatasetSelected(events.get(1), "id2");
+
+            final FillEventHistory.Event event3 = events.get(2);
+            assertThat(event3.getType()).isEqualTo(TYPE_CONTEXT_COMMITTED);
+            assertThat(event3.getDatasetId()).isNull();
+            assertThat(event3.getClientState()).isNull();
+            assertThat(event3.getSelectedDatasetIds()).containsExactly("id1", "id2");
+            assertThat(event3.getIgnoredDatasetIds()).isEmpty();
+            final Map<AutofillId, String> changedFields = event3.getChangedFields();
+            final AutofillId passwordId = mActivity.getPassword().getAutofillId();
+            assertThat(changedFields).containsExactly(passwordId, "id2");
+            assertThat(event3.getManuallyEnteredField()).isEmpty();
+        }
+    }
+
+    /**
+     * Tests scenario where the context was committed, both datasets were selected by the user,
+     * and the user didn't change the values provided by the service.
+     */
+    @Test
+    public void testContextCommitted_multipleDatasetsSelected_butNotChanged() throws Exception {
+        enableService();
+
+        sReplier.addResponse(new CannedFillResponse.Builder().addDataset(
+                new CannedDataset.Builder()
+                        .setId("id1")
+                        .setField(ID_USERNAME, BACKDOOR_USERNAME)
+                        .setPresentation(createPresentation("dataset1"))
+                        .build())
+                .addDataset(new CannedDataset.Builder()
+                        .setId("id2")
+                        .setField(ID_PASSWORD, "whatever")
+                        .setPresentation(createPresentation("dataset2"))
+                        .build())
+                .setFillResponseFlags(FillResponse.FLAG_TRACK_CONTEXT_COMMITED)
+                .build());
+        mActivity.expectAutoFill(BACKDOOR_USERNAME);
+
+        // Trigger autofill
+        mActivity.onUsername(View::requestFocus);
+        sReplier.getNextFillRequest();
+
+        // Autofill username
+        mUiBot.selectDataset("dataset1");
+        mActivity.assertAutoFilled();
+        {
+            // Verify fill history
+            final List<Event> events = InstrumentedAutoFillService.getFillEvents(1);
+            assertFillEventForDatasetSelected(events.get(0), "id1");
+        }
+
+        // Autofill password
+        mActivity.expectPasswordAutoFill("whatever");
+
+        mActivity.onPassword(View::requestFocus);
+        mUiBot.selectDataset("dataset2");
+        mActivity.assertAutoFilled();
+
+        {
+            // Verify fill history
+            final List<Event> events = InstrumentedAutoFillService.getFillEvents(2);
+
+            assertFillEventForDatasetSelected(events.get(0), "id1");
+            assertFillEventForDatasetSelected(events.get(1), "id2");
+        }
+
+        // Finish the context by login in
+        final String expectedMessage = getWelcomeMessage(BACKDOOR_USERNAME);
+        final String actualMessage = mActivity.tapLogin();
+        assertWithMessage("Wrong welcome msg").that(actualMessage).isEqualTo(expectedMessage);
+        mUiBot.assertSaveNotShowing(SAVE_DATA_TYPE_PASSWORD);
+
+        {
+            // Verify fill history
+            final List<Event> events = InstrumentedAutoFillService.getFillEvents(3);
+
+            assertFillEventForDatasetSelected(events.get(0), "id1");
+            assertFillEventForDatasetSelected(events.get(1), "id2");
+
+            final FillEventHistory.Event event3 = events.get(2);
+            assertThat(event3.getType()).isEqualTo(TYPE_CONTEXT_COMMITTED);
+            assertThat(event3.getDatasetId()).isNull();
+            assertThat(event3.getClientState()).isNull();
+            assertThat(event3.getSelectedDatasetIds()).containsExactly("id1", "id2");
+            assertThat(event3.getIgnoredDatasetIds()).isEmpty();
+            assertThat(event3.getChangedFields()).isEmpty();
+            assertThat(event3.getManuallyEnteredField()).isEmpty();
+        }
+    }
+
+    /**
+     * Tests scenario where the context was committed, the user selected the dataset, than changed
+     * the autofilled values, but then change the values again so they match what was provided by
+     * the service.
+     */
+    @Test
+    public void testContextCommitted_oneDatasetSelected_Changed_thenChangedBack()
+            throws Exception {
+        enableService();
+
+        sReplier.addResponse(new CannedFillResponse.Builder().addDataset(
+                new CannedDataset.Builder()
+                        .setId("id1")
+                        .setField(ID_USERNAME, "username")
+                        .setField(ID_PASSWORD, "username")
+                        .setPresentation(createPresentation("dataset1"))
+                        .build())
+                .setFillResponseFlags(FillResponse.FLAG_TRACK_CONTEXT_COMMITED)
+                .build());
+        mActivity.expectAutoFill("username", "username");
+
+        // Trigger autofill on username
+        mActivity.onUsername(View::requestFocus);
+        sReplier.getNextFillRequest();
+
+        mUiBot.selectDataset("dataset1");
+        mActivity.assertAutoFilled();
+
+        // Verify dataset selection
+        {
+            final List<Event> events = InstrumentedAutoFillService.getFillEvents(1);
+            assertFillEventForDatasetSelected(events.get(0), "id1");
+        }
+
+        // Change the fields to different values from datasets
+        mActivity.onUsername((v) -> v.setText("USERNAME"));
+        mActivity.onPassword((v) -> v.setText("USERNAME"));
+
+        // Then change back to dataset values
+        mActivity.onUsername((v) -> v.setText("username"));
+        mActivity.onPassword((v) -> v.setText("username"));
+
+        final String expectedMessage = getWelcomeMessage("username");
+        final String actualMessage = mActivity.tapLogin();
+        assertWithMessage("Wrong welcome msg").that(actualMessage).isEqualTo(expectedMessage);
+        mUiBot.assertSaveNotShowing(SAVE_DATA_TYPE_PASSWORD);
+
+        // ...and check again
+        {
+            final List<Event> events = InstrumentedAutoFillService.getFillEvents(2);
+            assertFillEventForDatasetSelected(events.get(0), "id1");
+
+            FillEventHistory.Event event2 = events.get(1);
+            assertThat(event2.getType()).isEqualTo(TYPE_CONTEXT_COMMITTED);
+            assertThat(event2.getDatasetId()).isNull();
+            assertThat(event2.getClientState()).isNull();
+            assertThat(event2.getSelectedDatasetIds()).containsExactly("id1");
+            assertThat(event2.getIgnoredDatasetIds()).isEmpty();
+            assertThat(event2.getChangedFields()).isEmpty();
+            assertThat(event2.getManuallyEnteredField()).isEmpty();
+        }
+    }
+
+    /**
+     * Tests scenario where the context was committed, the user did not selected any dataset, but
+     * the user manually entered values that match what was provided by the service.
+     */
+    @Test
+    public void testContextCommitted_noDatasetSelected_butManuallyEntered()
+            throws Exception {
+        enableService();
+
+        sReplier.addResponse(new CannedFillResponse.Builder().addDataset(
+                new CannedDataset.Builder()
+                        .setId("id1")
+                        .setField(ID_USERNAME, BACKDOOR_USERNAME)
+                        .setField(ID_PASSWORD, "NotUsedPassword")
+                        .setPresentation(createPresentation("dataset1"))
+                        .build())
+                .addDataset(new CannedDataset.Builder()
+                        .setId("id2")
+                        .setField(ID_USERNAME, "NotUserUsername")
+                        .setField(ID_PASSWORD, "whatever")
+                        .setPresentation(createPresentation("dataset2"))
+                        .build())
+                .setFillResponseFlags(FillResponse.FLAG_TRACK_CONTEXT_COMMITED)
+                .build());
+        // Trigger autofill on username
+        mActivity.onUsername(View::requestFocus);
+        sReplier.getNextFillRequest();
+        mUiBot.assertDatasets("dataset1", "dataset2");
+
+        // Verify history
+        InstrumentedAutoFillService.getFillEventHistory(0);
+
+        // Enter values present at the datasets
+        mActivity.onUsername((v) -> v.setText(BACKDOOR_USERNAME));
+        mActivity.onPassword((v) -> v.setText("whatever"));
+
+        // Finish the context by login in
+        final String expectedMessage = getWelcomeMessage(BACKDOOR_USERNAME);
+        final String actualMessage = mActivity.tapLogin();
+        assertWithMessage("Wrong welcome msg").that(actualMessage).isEqualTo(expectedMessage);
+        mUiBot.assertSaveNotShowing(SAVE_DATA_TYPE_PASSWORD);
+
+        // Verify history
+        {
+            final List<Event> events = InstrumentedAutoFillService.getFillEvents(1);
+            FillEventHistory.Event event = events.get(0);
+            assertThat(event.getType()).isEqualTo(TYPE_CONTEXT_COMMITTED);
+            assertThat(event.getDatasetId()).isNull();
+            assertThat(event.getClientState()).isNull();
+            assertThat(event.getSelectedDatasetIds()).isEmpty();
+            assertThat(event.getIgnoredDatasetIds()).containsExactly("id1", "id2");
+            assertThat(event.getChangedFields()).isEmpty();
+            final AutofillId usernameId = mActivity.getUsername().getAutofillId();
+            final AutofillId passwordId = mActivity.getPassword().getAutofillId();
+
+            final Map<AutofillId, Set<String>> manuallyEnteredFields =
+                    event.getManuallyEnteredField();
+            assertThat(manuallyEnteredFields).isNotNull();
+            assertThat(manuallyEnteredFields.size()).isEqualTo(2);
+            assertThat(manuallyEnteredFields.get(usernameId)).containsExactly("id1");
+            assertThat(manuallyEnteredFields.get(passwordId)).containsExactly("id2");
+        }
+    }
+
+    /**
+     * Tests scenario where the context was committed, the user did not selected any dataset, but
+     * the user manually entered values that match what was provided by the service on different
+     * datasets.
+     */
+    @Test
+    public void testContextCommitted_noDatasetSelected_butManuallyEntered_matchingMultipleDatasets()
+            throws Exception {
+        enableService();
+
+        sReplier.addResponse(new CannedFillResponse.Builder().addDataset(
+                new CannedDataset.Builder()
+                        .setId("id1")
+                        .setField(ID_USERNAME, BACKDOOR_USERNAME)
+                        .setField(ID_PASSWORD, "NotUsedPassword")
+                        .setPresentation(createPresentation("dataset1"))
+                        .build())
+                .addDataset(new CannedDataset.Builder()
+                        .setId("id2")
+                        .setField(ID_USERNAME, "NotUserUsername")
+                        .setField(ID_PASSWORD, "whatever")
+                        .setPresentation(createPresentation("dataset2"))
+                        .build())
+                .addDataset(new CannedDataset.Builder()
+                        .setId("id3")
+                        .setField(ID_USERNAME, BACKDOOR_USERNAME)
+                        .setField(ID_PASSWORD, "whatever")
+                        .setPresentation(createPresentation("dataset3"))
+                        .build())
+                .setFillResponseFlags(FillResponse.FLAG_TRACK_CONTEXT_COMMITED)
+                .build());
+        // Trigger autofill on username
+        mActivity.onUsername(View::requestFocus);
+        sReplier.getNextFillRequest();
+        mUiBot.assertDatasets("dataset1", "dataset2", "dataset3");
+
+        // Verify history
+        InstrumentedAutoFillService.getFillEventHistory(0);
+
+        // Enter values present at the datasets
+        mActivity.onUsername((v) -> v.setText(BACKDOOR_USERNAME));
+        mActivity.onPassword((v) -> v.setText("whatever"));
+
+        // Finish the context by login in
+        final String expectedMessage = getWelcomeMessage(BACKDOOR_USERNAME);
+        final String actualMessage = mActivity.tapLogin();
+        assertWithMessage("Wrong welcome msg").that(actualMessage).isEqualTo(expectedMessage);
+        mUiBot.assertSaveNotShowing(SAVE_DATA_TYPE_PASSWORD);
+
+        // Verify history
+        {
+            final List<Event> events = InstrumentedAutoFillService.getFillEvents(1);
+
+            final FillEventHistory.Event event = events.get(0);
+            assertThat(event.getType()).isEqualTo(TYPE_CONTEXT_COMMITTED);
+            assertThat(event.getDatasetId()).isNull();
+            assertThat(event.getClientState()).isNull();
+            assertThat(event.getSelectedDatasetIds()).isEmpty();
+            assertThat(event.getIgnoredDatasetIds()).containsExactly("id1", "id2", "id3");
+            assertThat(event.getChangedFields()).isEmpty();
+            final AutofillId usernameId = mActivity.getUsername().getAutofillId();
+            final AutofillId passwordId = mActivity.getPassword().getAutofillId();
+
+            final Map<AutofillId, Set<String>> manuallyEnteredFields =
+                    event.getManuallyEnteredField();
+            assertThat(manuallyEnteredFields.size()).isEqualTo(2);
+            assertThat(manuallyEnteredFields.get(usernameId)).containsExactly("id1", "id3");
+            assertThat(manuallyEnteredFields.get(passwordId)).containsExactly("id2", "id3");
+        }
+    }
+}
diff --git a/tests/autofillservice/src/android/autofillservice/cts/FillResponseTest.java b/tests/autofillservice/src/android/autofillservice/cts/FillResponseTest.java
new file mode 100644
index 0000000..67771f4
--- /dev/null
+++ b/tests/autofillservice/src/android/autofillservice/cts/FillResponseTest.java
@@ -0,0 +1,242 @@
+/*
+ * 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.autofillservice.cts;
+
+import static android.service.autofill.FillResponse.FLAG_DISABLE_ACTIVITY_ONLY;
+import static android.service.autofill.FillResponse.FLAG_TRACK_CONTEXT_COMMITED;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import static org.testng.Assert.assertThrows;
+
+import android.content.IntentSender;
+import android.os.Bundle;
+import android.service.autofill.Dataset;
+import android.service.autofill.FillResponse;
+import android.service.autofill.SaveInfo;
+import android.service.autofill.UserData;
+import android.view.autofill.AutofillId;
+import android.view.autofill.AutofillValue;
+import android.widget.RemoteViews;
+
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.Mock;
+import org.mockito.junit.MockitoJUnitRunner;
+
+@RunWith(MockitoJUnitRunner.class)
+public class FillResponseTest {
+
+    private final AutofillId mAutofillId = new AutofillId(42);
+    private final FillResponse.Builder mBuilder = new FillResponse.Builder();
+    private final AutofillId[] mIds = new AutofillId[] { mAutofillId };
+    private final SaveInfo mSaveInfo = new SaveInfo.Builder(0, mIds).build();
+    private final Bundle mClientState = new Bundle();
+    private final Dataset mDataset = new Dataset.Builder()
+            .setValue(mAutofillId, AutofillValue.forText("forty-two"))
+            .build();
+    private final long mDisableDuration = 666;
+    @Mock private RemoteViews mPresentation;
+    @Mock private RemoteViews mHeader;
+    @Mock private RemoteViews mFooter;
+    @Mock private IntentSender mIntentSender;
+
+    @Test
+    public void testBuilder_setAuthentication_invalid() {
+        // null ids
+        assertThrows(IllegalArgumentException.class,
+                () -> mBuilder.setAuthentication(null, mIntentSender, mPresentation));
+        // empty ids
+        assertThrows(IllegalArgumentException.class,
+                () -> mBuilder.setAuthentication(new AutofillId[] {}, mIntentSender,
+                        mPresentation));
+        // null intent sender
+        assertThrows(IllegalArgumentException.class,
+                () -> mBuilder.setAuthentication(mIds, null, mPresentation));
+        // null presentation
+        assertThrows(IllegalArgumentException.class,
+                () -> mBuilder.setAuthentication(mIds, mIntentSender, null));
+    }
+
+    @Test
+    public void testBuilder_setAuthentication_valid() {
+        new FillResponse.Builder().setAuthentication(mIds, null, null);
+        new FillResponse.Builder().setAuthentication(mIds, mIntentSender, mPresentation);
+    }
+
+    @Test
+    public void testBuilder_setAuthentication_illegalState() {
+        assertThrows(IllegalStateException.class,
+                () -> new FillResponse.Builder().setHeader(mHeader).setAuthentication(mIds,
+                        mIntentSender, mPresentation));
+        assertThrows(IllegalStateException.class,
+                () -> new FillResponse.Builder().setFooter(mFooter).setAuthentication(mIds,
+                        mIntentSender, mPresentation));
+    }
+
+    @Test
+    public void testBuilder_setHeaderOrFooterInvalid() {
+        assertThrows(NullPointerException.class, () -> new FillResponse.Builder().setHeader(null));
+        assertThrows(NullPointerException.class, () -> new FillResponse.Builder().setFooter(null));
+    }
+
+    @Test
+    public void testBuilder_setHeaderOrFooterAfterAuthentication() {
+        FillResponse.Builder builder =
+                new FillResponse.Builder().setAuthentication(mIds, mIntentSender, mPresentation);
+        assertThrows(IllegalStateException.class, () -> builder.setHeader(mHeader));
+        assertThrows(IllegalStateException.class, () -> builder.setHeader(mFooter));
+    }
+
+    @Test
+    public void testBuilder_setFlag_invalid() {
+        assertThrows(IllegalArgumentException.class, () -> mBuilder.setFlags(-1));
+    }
+
+    @Test
+    public void testBuilder_setFlag_valid() {
+        mBuilder.setFlags(0);
+        mBuilder.setFlags(FLAG_TRACK_CONTEXT_COMMITED);
+        mBuilder.setFlags(FLAG_DISABLE_ACTIVITY_ONLY);
+    }
+
+    @Test
+    public void testBuilder_disableAutofill_invalid() {
+        assertThrows(IllegalArgumentException.class, () -> mBuilder.disableAutofill(0));
+        assertThrows(IllegalArgumentException.class, () -> mBuilder.disableAutofill(-1));
+    }
+
+    @Test
+    public void testBuilder_disableAutofill_valid() {
+        mBuilder.disableAutofill(mDisableDuration);
+        mBuilder.disableAutofill(Long.MAX_VALUE);
+    }
+
+    @Test
+    public void testBuilder_disableAutofill_mustBeTheOnlyMethodCalled() {
+        // No method can be called after disableAutofill()
+        mBuilder.disableAutofill(mDisableDuration);
+        assertThrows(IllegalStateException.class, () -> mBuilder.setSaveInfo(mSaveInfo));
+        assertThrows(IllegalStateException.class, () -> mBuilder.addDataset(mDataset));
+        assertThrows(IllegalStateException.class,
+                () -> mBuilder.setAuthentication(mIds, mIntentSender, mPresentation));
+        assertThrows(IllegalStateException.class,
+                () -> mBuilder.setFieldClassificationIds(mAutofillId));
+        assertThrows(IllegalStateException.class,
+                () -> mBuilder.setClientState(mClientState));
+
+        // And vice-versa...
+        final FillResponse.Builder builder1 = new FillResponse.Builder().setSaveInfo(mSaveInfo);
+        assertThrows(IllegalStateException.class, () -> builder1.disableAutofill(mDisableDuration));
+        final FillResponse.Builder builder2 = new FillResponse.Builder().addDataset(mDataset);
+        assertThrows(IllegalStateException.class, () -> builder2.disableAutofill(mDisableDuration));
+        final FillResponse.Builder builder3 =
+                new FillResponse.Builder().setAuthentication(mIds, mIntentSender, mPresentation);
+        assertThrows(IllegalStateException.class, () -> builder3.disableAutofill(mDisableDuration));
+        final FillResponse.Builder builder4 =
+                new FillResponse.Builder().setFieldClassificationIds(mAutofillId);
+        assertThrows(IllegalStateException.class, () -> builder4.disableAutofill(mDisableDuration));
+        final FillResponse.Builder builder5 =
+                new FillResponse.Builder().setClientState(mClientState);
+        assertThrows(IllegalStateException.class, () -> builder5.disableAutofill(mDisableDuration));
+    }
+
+    @Test
+    public void testBuilder_setFieldClassificationIds_invalid() {
+        assertThrows(NullPointerException.class,
+                () -> mBuilder.setFieldClassificationIds((AutofillId) null));
+        assertThrows(NullPointerException.class,
+                () -> mBuilder.setFieldClassificationIds((AutofillId[]) null));
+        final AutofillId[] oneTooMany =
+                new AutofillId[UserData.getMaxFieldClassificationIdsSize() + 1];
+        for (int i = 0; i < oneTooMany.length; i++) {
+            oneTooMany[i] = new AutofillId(i);
+        }
+        assertThrows(IllegalArgumentException.class,
+                () -> mBuilder.setFieldClassificationIds(oneTooMany));
+    }
+
+    @Test
+    public void testBuilder_setFieldClassificationIds_valid() {
+        mBuilder.setFieldClassificationIds(mAutofillId);
+    }
+
+    @Test
+    public void testBuilder_setFieldClassificationIds_setsFlag() {
+        mBuilder.setFieldClassificationIds(mAutofillId);
+        assertThat(mBuilder.build().getFlags()).isEqualTo(FLAG_TRACK_CONTEXT_COMMITED);
+    }
+
+    @Test
+    public void testBuilder_setFieldClassificationIds_addsFlag() {
+        mBuilder.setFlags(FLAG_DISABLE_ACTIVITY_ONLY).setFieldClassificationIds(mAutofillId);
+        assertThat(mBuilder.build().getFlags())
+                .isEqualTo(FLAG_TRACK_CONTEXT_COMMITED | FLAG_DISABLE_ACTIVITY_ONLY);
+    }
+
+    @Test
+    public void testBuild_invalid() {
+        assertThrows(IllegalStateException.class, () -> mBuilder.build());
+    }
+
+    @Test
+    public void testBuild_valid() {
+        // authentication only
+        assertThat(new FillResponse.Builder().setAuthentication(mIds, mIntentSender, mPresentation)
+                .build()).isNotNull();
+        // save info only
+        assertThat(new FillResponse.Builder().setSaveInfo(mSaveInfo).build()).isNotNull();
+        // dataset only
+        assertThat(new FillResponse.Builder().addDataset(mDataset).build()).isNotNull();
+        // disable autofill only
+        assertThat(new FillResponse.Builder().disableAutofill(mDisableDuration).build())
+                .isNotNull();
+        // fill detection only
+        assertThat(new FillResponse.Builder().setFieldClassificationIds(mAutofillId).build())
+                .isNotNull();
+        // client state only
+        assertThat(new FillResponse.Builder().setClientState(mClientState).build())
+                .isNotNull();
+    }
+
+    @Test
+    public void testBuilder_build_headerOrFooterWithoutDatasets() {
+        assertThrows(IllegalStateException.class,
+                () -> new FillResponse.Builder().setHeader(mHeader).build());
+        assertThrows(IllegalStateException.class,
+                () -> new FillResponse.Builder().setFooter(mFooter).build());
+    }
+
+    @Test
+    public void testNoMoreInteractionsAfterBuild() {
+        assertThat(mBuilder.setAuthentication(mIds, mIntentSender, mPresentation).build())
+                .isNotNull();
+
+        assertThrows(IllegalStateException.class, () -> mBuilder.build());
+        assertThrows(IllegalStateException.class,
+                () -> mBuilder.setAuthentication(mIds, mIntentSender, mPresentation).build());
+        assertThrows(IllegalStateException.class, () -> mBuilder.setIgnoredIds(mIds));
+        assertThrows(IllegalStateException.class, () -> mBuilder.addDataset(null));
+        assertThrows(IllegalStateException.class, () -> mBuilder.setSaveInfo(mSaveInfo));
+        assertThrows(IllegalStateException.class, () -> mBuilder.setClientState(mClientState));
+        assertThrows(IllegalStateException.class, () -> mBuilder.setFlags(0));
+        assertThrows(IllegalStateException.class,
+                () -> mBuilder.setFieldClassificationIds(mAutofillId));
+        assertThrows(IllegalStateException.class, () -> mBuilder.setHeader(mHeader));
+        assertThrows(IllegalStateException.class, () -> mBuilder.setFooter(mFooter));
+    }
+}
diff --git a/tests/autofillservice/src/android/autofillservice/cts/FragmentContainerActivity.java b/tests/autofillservice/src/android/autofillservice/cts/FragmentContainerActivity.java
index 7be4496..2e84a32 100644
--- a/tests/autofillservice/src/android/autofillservice/cts/FragmentContainerActivity.java
+++ b/tests/autofillservice/src/android/autofillservice/cts/FragmentContainerActivity.java
@@ -71,10 +71,10 @@
     }
 
     public boolean waitUntilResumed() throws InterruptedException {
-        return mResumed.await(Helper.UI_TIMEOUT_MS, TimeUnit.MILLISECONDS);
+        return mResumed.await(Timeouts.UI_TIMEOUT.ms(), TimeUnit.MILLISECONDS);
     }
 
     public boolean waitUntilStopped() throws InterruptedException {
-        return mStopped.await(Helper.UI_TIMEOUT_MS, TimeUnit.MILLISECONDS);
+        return mStopped.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 33321c7..29af4a2 100644
--- a/tests/autofillservice/src/android/autofillservice/cts/GridActivity.java
+++ b/tests/autofillservice/src/android/autofillservice/cts/GridActivity.java
@@ -83,7 +83,7 @@
         getSystemService(AutofillManager.class).cancel();
     }
 
-    private EditText getCell(int row, int column) {
+    EditText getCell(int row, int column) {
         return mCells[row - 1][column - 1];
     }
 
diff --git a/tests/autofillservice/src/android/autofillservice/cts/Helper.java b/tests/autofillservice/src/android/autofillservice/cts/Helper.java
index f1d02dd..741c029 100644
--- a/tests/autofillservice/src/android/autofillservice/cts/Helper.java
+++ b/tests/autofillservice/src/android/autofillservice/cts/Helper.java
@@ -18,8 +18,14 @@
 
 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;
 import static android.provider.Settings.Secure.USER_SETUP_COMPLETE;
+import static android.service.autofill.FillEventHistory.Event.TYPE_AUTHENTICATION_SELECTED;
+import static android.service.autofill.FillEventHistory.Event.TYPE_CONTEXT_COMMITTED;
+import static android.service.autofill.FillEventHistory.Event.TYPE_DATASET_AUTHENTICATION_SELECTED;
+import static android.service.autofill.FillEventHistory.Event.TYPE_DATASET_SELECTED;
+import static android.service.autofill.FillEventHistory.Event.TYPE_SAVE_SHOWN;
 
 import static com.google.common.truth.Truth.assertThat;
 import static com.google.common.truth.Truth.assertWithMessage;
@@ -27,10 +33,16 @@
 import android.app.assist.AssistStructure;
 import android.app.assist.AssistStructure.ViewNode;
 import android.app.assist.AssistStructure.WindowNode;
+import android.autofillservice.cts.common.SettingsHelper;
+import android.content.ComponentName;
 import android.content.Context;
 import android.content.pm.PackageManager;
 import android.icu.util.Calendar;
+import android.os.Bundle;
+import android.service.autofill.FieldClassification;
+import android.service.autofill.FieldClassification.Match;
 import android.service.autofill.FillContext;
+import android.service.autofill.FillEventHistory;
 import android.support.annotation.NonNull;
 import android.support.annotation.Nullable;
 import android.support.test.InstrumentationRegistry;
@@ -43,10 +55,10 @@
 import android.view.autofill.AutofillValue;
 import android.webkit.WebView;
 
-import com.android.compatibility.common.util.SystemUtil;
-
 import java.lang.reflect.Field;
 import java.util.List;
+import java.util.Map;
+import java.util.Map.Entry;
 import java.util.function.Function;
 
 /**
@@ -54,7 +66,7 @@
  */
 final class Helper {
 
-    private static final String TAG = "AutoFillCtsHelper";
+    static final String TAG = "AutoFillCtsHelper";
 
     static final boolean VERBOSE = false;
 
@@ -64,54 +76,18 @@
     static final String ID_PASSWORD = "password";
     static final String ID_LOGIN = "login";
     static final String ID_OUTPUT = "output";
+    static final String ID_STATIC_TEXT = "static_text";
+
+    public static final String NULL_DATASET_ID = null;
+
+    /**
+     * 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";
 
-    /**
-     * Timeout (in milliseconds) until framework binds / unbinds from service.
-     */
-    static final long CONNECTION_TIMEOUT_MS = 2000;
-
-    /**
-     * Timeout (in milliseconds) until framework unbinds from a service.
-     */
-    static final long IDLE_UNBIND_TIMEOUT_MS = 5000;
-
-    /**
-     * Timeout (in milliseconds) for expected auto-fill requests.
-     */
-    static final long FILL_TIMEOUT_MS = 2000;
-
-    /**
-     * Timeout (in milliseconds) for expected save requests.
-     */
-    static final long SAVE_TIMEOUT_MS = 5000;
-
-    /**
-     * Time to wait if a UI change is not expected
-     */
-    static final long NOT_SHOWING_TIMEOUT_MS = 500;
-
-    /**
-     * Timeout (in milliseconds) for UI operations. Typically used by {@link UiBot}.
-     */
-    static final int UI_TIMEOUT_MS = 2000;
-
-    /**
-     * Timeout (in milliseconds) for an activity to be brought out to top.
-     */
-    static final int ACTIVITY_RESURRECTION_MS = 5000;
-
-    /**
-     * Timeout (in milliseconds) for changing the screen orientation.
-     */
-    static final int UI_SCREEN_ORIENTATION_TIMEOUT_MS = 5000;
-
-    /**
-     * Time to wait in between retries
-     */
-    static final int RETRY_MS = 100;
-
     private final static String ACCELLEROMETER_CHANGE =
             "content insert --uri content://settings/system --bind name:s:accelerometer_rotation "
                     + "--bind value:i:%d";
@@ -134,74 +110,24 @@
         return id.equals(getHtmlName(node));
     };
 
+    private static final NodeFilter HTML_NAME_OR_RESOURCE_ID_FILTER = (node, id) -> {
+        return id.equals(getHtmlName(node)) || id.equals(node.getIdEntry());
+    };
+
     private static final NodeFilter TEXT_FILTER = (node, id) -> {
         return id.equals(node.getText());
     };
 
-    private static final NodeFilter WEBVIEW_ROOT_FILTER = (node, id) -> {
-        // TODO(b/66953802): class name should be android.webkit.WebView, and form name should be
-        // inside HtmlInfo, but Chromium 61 does not implement that.
+    private static final NodeFilter WEBVIEW_FORM_FILTER = (node, id) -> {
         final String className = node.getClassName();
-        final String formName;
-        if (className.equals("android.webkit.WebView")) {
-            final HtmlInfo htmlInfo = assertHasHtmlTag(node, "form");
-            formName = getAttributeValue(htmlInfo, "name");
-        } else {
-            formName = className;
-        }
+        if (!className.equals("android.webkit.WebView")) return false;
+
+        final HtmlInfo htmlInfo = assertHasHtmlTag(node, "form");
+        final String formName = getAttributeValue(htmlInfo, "name");
         return id.equals(formName);
     };
 
     /**
-     * Runs a {@code r}, ignoring all {@link RuntimeException} and {@link Error} until the
-     * {@link #UI_TIMEOUT_MS} is reached.
-     */
-    static void eventually(Runnable r) throws Exception {
-        eventually(r, UI_TIMEOUT_MS);
-    }
-
-    /**
-     * Runs a {@code r}, ignoring all {@link RuntimeException} and {@link Error} until the
-     * {@code timeout} is reached.
-     */
-    static void eventually(Runnable r, int timeout) throws Exception {
-        long startTime = System.currentTimeMillis();
-
-        while (true) {
-            try {
-                r.run();
-                break;
-            } catch (RuntimeException | Error e) {
-                if (System.currentTimeMillis() - startTime < timeout) {
-                    if (VERBOSE) Log.v(TAG, "Ignoring", e);
-                    Thread.sleep(RETRY_MS);
-                } else {
-                    if (e instanceof RetryableException) {
-                        throw e;
-                    } else {
-                        throw new RetryableException(e, "Timedout out after %d ms", timeout);
-                    }
-                }
-            }
-        }
-    }
-
-    /**
-     * Runs a Shell command, returning a trimmed response.
-     */
-    static String runShellCommand(String template, Object...args) {
-        final String command = String.format(template, args);
-        Log.d(TAG, "runShellCommand(): " + command);
-        try {
-            final String result = SystemUtil
-                    .runShellCommand(InstrumentationRegistry.getInstrumentation(), command);
-            return TextUtils.isEmpty(result) ? "" : result.trim();
-        } catch (Exception e) {
-            throw new RuntimeException("Command '" + command + "' failed: ", e);
-        }
-    }
-
-    /**
      * Dump the assist structure on logcat.
      */
     static void dumpStructure(String message, AssistStructure structure) {
@@ -236,32 +162,7 @@
      * Sets whether the user completed the initial setup.
      */
     static void setUserComplete(Context context, boolean complete) {
-        if (isUserComplete() == complete) return;
-
-        final OneTimeSettingsListener observer = new OneTimeSettingsListener(context,
-                USER_SETUP_COMPLETE);
-        final String newValue = complete ? "1" : null;
-        runShellCommand("settings put secure %s %s default", USER_SETUP_COMPLETE, newValue);
-        observer.assertCalled();
-
-        assertIsUserComplete(complete);
-    }
-
-    /**
-     * Gets whether the user completed the initial setup.
-     */
-    static boolean isUserComplete() {
-        final String isIt = runShellCommand("settings get secure %s", USER_SETUP_COMPLETE);
-        return "1".equals(isIt);
-    }
-
-    /**
-     * Assets that user completed (or not) the initial setup.
-     */
-    static void assertIsUserComplete(boolean expected) {
-        final boolean actual = isUserComplete();
-        assertWithMessage("Invalid value for secure setting %s", USER_SETUP_COMPLETE)
-                .that(actual).isEqualTo(expected);
+        SettingsHelper.syncSet(context, USER_SETUP_COMPLETE, complete ? "1" : null);
     }
 
     private static void dump(StringBuffer buffer, ViewNode node, String prefix, int childId) {
@@ -391,6 +292,14 @@
     }
 
     /**
+     * Gets a node given the name of its HTML INPUT tag or Android resoirce id, or {@code null} if
+     * not found.
+     */
+    static ViewNode findNodeByHtmlNameOrResourceId(List<FillContext> contexts, String id) {
+        return findNodeByFilter(contexts, id, HTML_NAME_OR_RESOURCE_ID_FILTER);
+    }
+
+    /**
      * Gets the {@code name} attribute of a node representing an HTML input tag.
      */
     @Nullable
@@ -428,7 +337,7 @@
     }
 
     /**
-     * Asserts a text-base node is sanitized.
+     * Asserts a text-based node is sanitized.
      */
     static void assertTextIsSanitized(ViewNode node) {
         final CharSequence text = node.getText();
@@ -436,9 +345,15 @@
         if (!TextUtils.isEmpty(text)) {
             throw new AssertionError("text on sanitized field " + resourceId + ": " + text);
         }
+
+        assertNotFromResources(node);
         assertNodeHasNoAutofillValue(node);
     }
 
+    private static void assertNotFromResources(ViewNode node) {
+        assertThat(node.getTextIdEntry()).isNull();
+    }
+
     static void assertNodeHasNoAutofillValue(ViewNode node) {
         final AutofillValue value = node.getAutofillValue();
         if (value != null) {
@@ -449,22 +364,31 @@
 
     /**
      * Asserts the contents of a text-based node that is also auto-fillable.
-     *
      */
     static void assertTextOnly(ViewNode node, String expectedValue) {
         assertText(node, expectedValue, false);
+        assertNotFromResources(node);
     }
 
     /**
      * Asserts the contents of a text-based node that is also auto-fillable.
-     *
      */
-    static void assertTextAndValue(ViewNode node, String expectedValue) {
-        assertText(node, expectedValue, true);
+    static void assertTextOnly(AssistStructure structure, String resourceId, String expectedValue) {
+        final ViewNode node = findNodeByResourceId(structure, resourceId);
+        assertText(node, expectedValue, false);
+        assertNotFromResources(node);
     }
 
     /**
-     * Asserts a text-base node exists and verify its values.
+     * Asserts the contents of a text-based node that is also auto-fillable.
+     */
+    static void assertTextAndValue(ViewNode node, String expectedValue) {
+        assertText(node, expectedValue, true);
+        assertNotFromResources(node);
+    }
+
+    /**
+     * Asserts a text-based node exists and verify its values.
      */
     static ViewNode assertTextAndValue(AssistStructure structure, String resourceId,
             String expectedValue) {
@@ -474,7 +398,7 @@
     }
 
     /**
-     * Asserts a text-base node exists and is sanitized.
+     * Asserts a text-based node exists and is sanitized.
      */
     static ViewNode assertValue(AssistStructure structure, String resourceId,
             String expectedValue) {
@@ -483,16 +407,28 @@
         return node;
     }
 
+    /**
+     * Asserts the values of a text-based node whose string come from resoruces.
+     */
+    static ViewNode assertTextFromResouces(AssistStructure structure, String resourceId,
+            String expectedValue, boolean isAutofillable, String expectedTextIdEntry) {
+        final ViewNode node = findNodeByResourceId(structure, resourceId);
+        assertText(node, expectedValue, isAutofillable);
+        assertThat(node.getTextIdEntry()).isEqualTo(expectedTextIdEntry);
+        return node;
+    }
+
     private static void assertText(ViewNode node, String expectedValue, boolean isAutofillable) {
-        assertWithMessage("wrong text on %s", node).that(node.getText().toString())
+        assertWithMessage("wrong text on %s", node.getAutofillId()).that(node.getText().toString())
                 .isEqualTo(expectedValue);
         final AutofillValue value = node.getAutofillValue();
+        final AutofillId id = node.getAutofillId();
         if (isAutofillable) {
-            assertWithMessage("null auto-fill value on %s", node).that(value).isNotNull();
-            assertWithMessage("wrong auto-fill value on %s", node)
+            assertWithMessage("null auto-fill value on %s", id).that(value).isNotNull();
+            assertWithMessage("wrong auto-fill value on %s", id)
                     .that(value.getTextValue().toString()).isEqualTo(expectedValue);
         } else {
-            assertWithMessage("node %s should not have AutofillValue", node).that(value).isNull();
+            assertWithMessage("node %s should not have AutofillValue", id).that(value).isNull();
         }
     }
 
@@ -501,9 +437,10 @@
      */
     static ViewNode assertTextValue(ViewNode node, String expectedText) {
         final AutofillValue value = node.getAutofillValue();
-        assertWithMessage("null autofill value on %s", node).that(value).isNotNull();
-        assertWithMessage("wrong autofill type on %s", node).that(value.isText()).isTrue();
-        assertWithMessage("wrong autofill value on %s", node).that(value.getTextValue().toString())
+        final AutofillId id = node.getAutofillId();
+        assertWithMessage("null autofill value on %s", id).that(value).isNotNull();
+        assertWithMessage("wrong autofill type on %s", id).that(value.isText()).isTrue();
+        assertWithMessage("wrong autofill value on %s", id).that(value.getTextValue().toString())
                 .isEqualTo(expectedText);
         return node;
     }
@@ -513,9 +450,10 @@
      */
     static ViewNode assertListValue(ViewNode node, int expectedIndex) {
         final AutofillValue value = node.getAutofillValue();
-        assertWithMessage("null autofill value on %s", node).that(value).isNotNull();
-        assertWithMessage("wrong autofill type on %s", node).that(value.isList()).isTrue();
-        assertWithMessage("wrong autofill value on %s", node).that(value.getListValue())
+        final AutofillId id = node.getAutofillId();
+        assertWithMessage("null autofill value on %s", id).that(value).isNotNull();
+        assertWithMessage("wrong autofill type on %s", id).that(value.isList()).isTrue();
+        assertWithMessage("wrong autofill value on %s", id).that(value.getListValue())
                 .isEqualTo(expectedIndex);
         return node;
     }
@@ -525,9 +463,10 @@
      */
     static void assertToggleValue(ViewNode node, boolean expectedToggle) {
         final AutofillValue value = node.getAutofillValue();
-        assertWithMessage("null autofill value on %s", node).that(value).isNotNull();
-        assertWithMessage("wrong autofill type on %s", node).that(value.isToggle()).isTrue();
-        assertWithMessage("wrong autofill value on %s", node).that(value.getToggleValue())
+        final AutofillId id = node.getAutofillId();
+        assertWithMessage("null autofill value on %s", id).that(value).isNotNull();
+        assertWithMessage("wrong autofill type on %s", id).that(value.isToggle()).isTrue();
+        assertWithMessage("wrong autofill value on %s", id).that(value.getToggleValue())
                 .isEqualTo(expectedToggle);
     }
 
@@ -594,7 +533,7 @@
     }
 
     /**
-     * Asserts a text-base node exists and is sanitized.
+     * Asserts a text-based node exists and is sanitized.
      */
     static ViewNode assertTextIsSanitized(AssistStructure structure, String resourceId) {
         final ViewNode node = findNodeByResourceId(structure, resourceId);
@@ -701,7 +640,7 @@
     /**
      * Prevents the screen to rotate by itself
      */
-    public static void disableAutoRotation(UiBot uiBot) {
+    public static void disableAutoRotation(UiBot uiBot) throws Exception {
         runShellCommand(ACCELLEROMETER_CHANGE, 0);
         uiBot.setScreenOrientation(PORTRAIT);
     }
@@ -718,28 +657,21 @@
      *
      * @return The pid of the process
      */
-    public static int getOutOfProcessPid(@NonNull String processName) {
-        long startTime = System.currentTimeMillis();
+    public static int getOutOfProcessPid(@NonNull String processName, @NonNull Timeout timeout)
+            throws Exception {
 
-        while (System.currentTimeMillis() - startTime <= UI_TIMEOUT_MS) {
-            String[] allProcessDescs = runShellCommand("ps -eo PID,ARGS=CMD").split("\n");
+        return timeout.run("getOutOfProcessPid(" + processName + ")", () -> {
+            final String[] allProcessDescs = runShellCommand("ps -eo PID,ARGS=CMD").split("\n");
 
             for (String processDesc : allProcessDescs) {
-                String[] pidAndName = processDesc.trim().split(" ");
+                final String[] pidAndName = processDesc.trim().split(" ");
 
                 if (pidAndName[1].equals(processName)) {
                     return Integer.parseInt(pidAndName[0]);
                 }
             }
-
-            try {
-                Thread.sleep(RETRY_MS);
-            } catch (InterruptedException e) {
-                Thread.currentThread().interrupt();
-            }
-        }
-
-        throw new IllegalStateException("process not found");
+            return null;
+        });
     }
 
     /**
@@ -782,43 +714,37 @@
      * Uses Settings to enable the given autofill service for the default user, and checks the
      * value was properly check, throwing an exception if it was not.
      */
-    public static void enableAutofillService(Context context, String serviceName) {
+    public static void enableAutofillService(@NonNull Context context,
+            @NonNull String serviceName) {
         if (isAutofillServiceEnabled(serviceName)) return;
 
-        final OneTimeSettingsListener observer = new OneTimeSettingsListener(context,
-                AUTOFILL_SERVICE);
-        runShellCommand("settings put secure %s %s default", AUTOFILL_SERVICE, serviceName);
-        observer.assertCalled();
-        assertAutofillServiceStatus(serviceName, true);
+        SettingsHelper.syncSet(context, AUTOFILL_SERVICE, serviceName);
     }
 
     /**
      * 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.
      */
-    public static void disableAutofillService(Context context, String serviceName) {
+    public static void disableAutofillService(@NonNull Context context,
+            @NonNull String serviceName) {
         if (!isAutofillServiceEnabled(serviceName)) return;
 
-        final OneTimeSettingsListener observer = new OneTimeSettingsListener(context,
-                AUTOFILL_SERVICE);
-        runShellCommand("settings delete secure %s", AUTOFILL_SERVICE);
-        observer.assertCalled();
-        assertAutofillServiceStatus(serviceName, false);
+        SettingsHelper.syncDelete(context, AUTOFILL_SERVICE);
     }
 
     /**
      * Checks whether the given service is set as the autofill service for the default user.
      */
-    public static boolean isAutofillServiceEnabled(String serviceName) {
-        final String actualName = runShellCommand("settings get secure %s", AUTOFILL_SERVICE);
+    private static boolean isAutofillServiceEnabled(@NonNull String serviceName) {
+        final String actualName = SettingsHelper.get(AUTOFILL_SERVICE);
         return serviceName.equals(actualName);
     }
 
     /**
      * Asserts whether the given service is enabled as the autofill service for the default user.
      */
-    public static void assertAutofillServiceStatus(String serviceName, boolean enabled) {
-        final String actual = runShellCommand("settings get secure %s", AUTOFILL_SERVICE);
+    public static void assertAutofillServiceStatus(@NonNull String serviceName, boolean enabled) {
+        final String actual = SettingsHelper.get(AUTOFILL_SERVICE);
         final String expected = enabled ? serviceName : "null";
         assertWithMessage("Invalid value for secure setting %s", AUTOFILL_SERVICE)
                 .that(actual).isEqualTo(expected);
@@ -828,11 +754,15 @@
      * Asserts that there is no session left in the service.
      */
     public static void assertNoDanglingSessions() {
-        final String result = runShellCommand(CMD_LIST_SESSIONS);
+        final String result = listSessions();
         assertWithMessage("Dangling sessions ('%s'): %s'", CMD_LIST_SESSIONS, result).that(result)
                 .isEmpty();
     }
 
+    public static String listSessions() {
+        return runShellCommand(CMD_LIST_SESSIONS);
+    }
+
     /**
      * Asserts that there is a pending session for the given package.
      */
@@ -952,17 +882,267 @@
     /**
      * Finds a {@link WebView} node given its expected form name.
      */
-    public static ViewNode findWebViewNode(AssistStructure structure, String formName) {
-        return findNodeByFilter(structure, formName, WEBVIEW_ROOT_FILTER);
+    public static ViewNode findWebViewNodeByFormName(AssistStructure structure, String formName) {
+        return findNodeByFilter(structure, formName, WEBVIEW_FORM_FILTER);
+    }
+
+    private static void assertClientState(Object container, Bundle clientState,
+            String key, String value) {
+        assertWithMessage("'%s' should have client state", container)
+            .that(clientState).isNotNull();
+        assertWithMessage("Wrong number of client state extras on '%s'", container)
+            .that(clientState.keySet().size()).isEqualTo(1);
+        assertWithMessage("Wrong value for client state key (%s) on '%s'", key, container)
+            .that(clientState.getString(key)).isEqualTo(value);
     }
 
     /**
-     * Finds a {@link WebView} node given its expected form name.
+     * Asserts the content of a {@link FillEventHistory#getClientState()}.
+     *
+     * @param history event to be asserted
+     * @param key the only key expected in the client state bundle
+     * @param value the only value expected in the client state bundle
      */
-    public static ViewNode findWebViewNode(ViewNode node, String formName) {
-        return findNodeByFilter(node, formName, WEBVIEW_ROOT_FILTER);
+    @SuppressWarnings("javadoc")
+    public static void assertDeprecatedClientState(@NonNull FillEventHistory history,
+            @NonNull String key, @NonNull String value) {
+        assertThat(history).isNotNull();
+        @SuppressWarnings("deprecation")
+        final Bundle clientState = history.getClientState();
+        assertClientState(history, clientState, key, value);
+    }
+
+    /**
+     * Asserts the {@link FillEventHistory#getClientState()} is not set.
+     *
+     * @param history event to be asserted
+     */
+    @SuppressWarnings("javadoc")
+    public static void assertNoDeprecatedClientState(@NonNull FillEventHistory history) {
+        assertThat(history).isNotNull();
+        @SuppressWarnings("deprecation")
+        final Bundle clientState = history.getClientState();
+        assertWithMessage("History '%s' should not have client state", history)
+             .that(clientState).isNull();
+    }
+
+    /**
+     * Asserts the content of a {@link android.service.autofill.FillEventHistory.Event}.
+     *
+     * @param event event to be asserted
+     * @param eventType expected type
+     * @param datasetId dataset set id expected in the event
+     * @param key the only key expected in the client state bundle (or {@code null} if it shouldn't
+     * have client state)
+     * @param value the only value expected in the client state bundle (or {@code null} if it
+     * shouldn't have client state)
+     * @param fieldClassificationResults expected results when asserting field classification
+     */
+    private static void assertFillEvent(@NonNull FillEventHistory.Event event,
+            int eventType, @Nullable String datasetId,
+            @Nullable String key, @Nullable String value,
+            @Nullable FieldClassificationResult[] fieldClassificationResults) {
+        assertThat(event).isNotNull();
+        assertWithMessage("Wrong type for %s", event).that(event.getType()).isEqualTo(eventType);
+        if (datasetId == null) {
+            assertWithMessage("Event %s should not have dataset id", event)
+                .that(event.getDatasetId()).isNull();
+        } else {
+            assertWithMessage("Wrong dataset id for %s", event)
+                .that(event.getDatasetId()).isEqualTo(datasetId);
+        }
+        final Bundle clientState = event.getClientState();
+        if (key == null) {
+            assertWithMessage("Event '%s' should not have client state", event)
+                .that(clientState).isNull();
+        } else {
+            assertClientState(event, clientState, key, value);
+        }
+        assertWithMessage("Event '%s' should not have selected datasets", event)
+                .that(event.getSelectedDatasetIds()).isEmpty();
+        assertWithMessage("Event '%s' should not have ignored datasets", event)
+                .that(event.getIgnoredDatasetIds()).isEmpty();
+        assertWithMessage("Event '%s' should not have changed fields", event)
+                .that(event.getChangedFields()).isEmpty();
+        assertWithMessage("Event '%s' should not have manually-entered fields", event)
+                .that(event.getManuallyEnteredField()).isEmpty();
+        final Map<AutofillId, FieldClassification> detectedFields = event.getFieldsClassification();
+        if (fieldClassificationResults == null) {
+            assertThat(detectedFields).isEmpty();
+        } else {
+            assertThat(detectedFields).hasSize(fieldClassificationResults.length);
+            int i = 0;
+            for (Entry<AutofillId, FieldClassification> entry : detectedFields.entrySet()) {
+                assertMatches(i, entry, fieldClassificationResults[i]);
+                i++;
+            }
+        }
+    }
+
+    private static void assertMatches(int i, Entry<AutofillId, FieldClassification> actualResult,
+            FieldClassificationResult expectedResult) {
+        assertWithMessage("Wrong field id at index %s", i).that(actualResult.getKey())
+                .isEqualTo(expectedResult.id);
+        final List<Match> matches = actualResult.getValue().getMatches();
+        assertWithMessage("Wrong number of matches: " + matches).that(matches.size())
+                .isEqualTo(expectedResult.remoteIds.length);
+        for (int j = 0; j < matches.size(); j++) {
+            final Match match = matches.get(j);
+            assertWithMessage("Wrong remoteId at (%s, %s): %s", i, j, match)
+                .that(match.getRemoteId()).isEqualTo(expectedResult.remoteIds[j]);
+            assertWithMessage("Wrong score at (%s, %s): %s", i, j, match)
+                .that(match.getScore()).isWithin(expectedResult.scores[j]);
+            assertWithMessage("Wrong algorithm at (%s, %s): %s", i, j, match)
+                .that(match.getAlgorithm()).isEqualTo(expectedResult.algorithms[j]);
+        }
+    }
+
+    /**
+     * Asserts the content of a
+     * {@link android.service.autofill.FillEventHistory.Event#TYPE_DATASET_SELECTED} event.
+     *
+     * @param event event to be asserted
+     * @param datasetId dataset set id expected in the event
+     */
+    public static void assertFillEventForDatasetSelected(@NonNull FillEventHistory.Event event,
+            @Nullable String datasetId) {
+        assertFillEvent(event, TYPE_DATASET_SELECTED, datasetId, null, null, null);
+    }
+
+    /**
+     * Asserts the content of a
+     * {@link android.service.autofill.FillEventHistory.Event#TYPE_DATASET_SELECTED} event.
+     *
+     * @param event event to be asserted
+     * @param datasetId dataset set id expected in the event
+     * @param key the only key expected in the client state bundle
+     * @param value the only value expected in the client state bundle
+     */
+    public static void assertFillEventForDatasetSelected(@NonNull FillEventHistory.Event event,
+            @Nullable String datasetId, @Nullable String key, @Nullable String value) {
+        assertFillEvent(event, TYPE_DATASET_SELECTED, datasetId, key, value, null);
+    }
+
+    /**
+     * Asserts the content of a
+     * {@link android.service.autofill.FillEventHistory.Event#TYPE_SAVE_SHOWN} event.
+     *
+     * @param event event to be asserted
+     * @param datasetId dataset set id expected in the event
+     * @param key the only key expected in the client state bundle
+     * @param value the only value expected in the client state bundle
+     */
+    public static void assertFillEventForSaveShown(@NonNull FillEventHistory.Event event,
+            @NonNull String datasetId, @NonNull String key, @NonNull String value) {
+        assertFillEvent(event, TYPE_SAVE_SHOWN, datasetId, key, value, null);
+    }
+
+    /**
+     * Asserts the content of a
+     * {@link android.service.autofill.FillEventHistory.Event#TYPE_SAVE_SHOWN} event.
+     *
+     * @param event event to be asserted
+     * @param datasetId dataset set id expected in the event
+     */
+    public static void assertFillEventForSaveShown(@NonNull FillEventHistory.Event event,
+            @NonNull String datasetId) {
+        assertFillEvent(event, TYPE_SAVE_SHOWN, datasetId, null, null, null);
+    }
+
+    /**
+     * Asserts the content of a
+     * {@link android.service.autofill.FillEventHistory.Event#TYPE_DATASET_AUTHENTICATION_SELECTED}
+     * event.
+     *
+     * @param event event to be asserted
+     * @param datasetId dataset set id expected in the event
+     * @param key the only key expected in the client state bundle
+     * @param value the only value expected in the client state bundle
+     */
+    public static void assertFillEventForDatasetAuthenticationSelected(
+            @NonNull FillEventHistory.Event event,
+            @Nullable String datasetId, @NonNull String key, @NonNull String value) {
+        assertFillEvent(event, TYPE_DATASET_AUTHENTICATION_SELECTED, datasetId, key, value, null);
+    }
+
+    /**
+     * Asserts the content of a
+     * {@link android.service.autofill.FillEventHistory.Event#TYPE_AUTHENTICATION_SELECTED} event.
+     *
+     * @param event event to be asserted
+     * @param datasetId dataset set id expected in the event
+     * @param key the only key expected in the client state bundle
+     * @param value the only value expected in the client state bundle
+     */
+    public static void assertFillEventForAuthenticationSelected(
+            @NonNull FillEventHistory.Event event,
+            @Nullable String datasetId, @NonNull String key, @NonNull String value) {
+        assertFillEvent(event, TYPE_AUTHENTICATION_SELECTED, datasetId, key, value, null);
+    }
+
+    public static void assertFillEventForFieldsClassification(@NonNull FillEventHistory.Event event,
+            @NonNull AutofillId fieldId, @NonNull String remoteId, float score,
+            @NonNull String algorithm) {
+        assertFillEvent(event, TYPE_CONTEXT_COMMITTED, null, null, null,
+                new FieldClassificationResult[] {
+                        new FieldClassificationResult(fieldId, remoteId, score, algorithm)
+                });
+    }
+
+    public static void assertFillEventForFieldsClassification(@NonNull FillEventHistory.Event event,
+            @NonNull FieldClassificationResult[] results) {
+        assertFillEvent(event, TYPE_CONTEXT_COMMITTED, null, null, null, results);
+    }
+
+    public static void assertFillEventForContextCommitted(@NonNull FillEventHistory.Event event) {
+        assertFillEvent(event, TYPE_CONTEXT_COMMITTED, null, null, null, null);
+    }
+
+    @NonNull
+    public static String getActivityName(List<FillContext> contexts) {
+        if (contexts == null) return "N/A (null contexts)";
+
+        if (contexts.isEmpty()) return "N/A (empty contexts)";
+
+        final AssistStructure structure = contexts.get(contexts.size() - 1).getStructure();
+        if (structure == null) return "N/A (no AssistStructure)";
+
+        final ComponentName componentName = structure.getActivityComponent();
+        if (componentName == null) return "N/A (no component name)";
+
+        return componentName.flattenToShortString();
+    }
+
+    public static void assertFloat(float actualValue, float expectedValue) {
+        assertThat(actualValue).isWithin(1.0e-10f).of(expectedValue);
+    }
+
+    public static void assertHasFlags(int actualFlags, int expectedFlags) {
+        assertWithMessage("Flags %s not in %s", expectedFlags, actualFlags)
+                .that(actualFlags & expectedFlags).isEqualTo(expectedFlags);
     }
 
     private Helper() {
+        throw new UnsupportedOperationException("contain static methods only");
+    }
+
+    static class FieldClassificationResult {
+        public final AutofillId id;
+        public final String[] remoteIds;
+        public final float[] scores;
+        public final String[] algorithms;
+
+        FieldClassificationResult(@NonNull AutofillId id, @NonNull String remoteId, float score,
+                String algorithm) {
+            this(id, new String[] { remoteId }, new float[] { score }, new String[] { algorithm });
+        }
+
+        FieldClassificationResult(@NonNull AutofillId id, @NonNull String[] remoteIds,
+                float[] scores, String[] algorithms) {
+            this.id = id;
+            this.remoteIds = remoteIds;
+            this.scores = scores;
+            this.algorithms = algorithms;
+        }
     }
 }
diff --git a/tests/autofillservice/src/android/autofillservice/cts/IdMode.java b/tests/autofillservice/src/android/autofillservice/cts/IdMode.java
index 01878ab..66e857b 100644
--- a/tests/autofillservice/src/android/autofillservice/cts/IdMode.java
+++ b/tests/autofillservice/src/android/autofillservice/cts/IdMode.java
@@ -21,5 +21,6 @@
  */
 enum IdMode {
     RESOURCE_ID,
-    HTML_NAME
+    HTML_NAME,
+    HTML_NAME_OR_RESOURCE_ID
 }
diff --git a/tests/autofillservice/src/android/autofillservice/cts/ImageTransformationTest.java b/tests/autofillservice/src/android/autofillservice/cts/ImageTransformationTest.java
index e96df05..935be23 100644
--- a/tests/autofillservice/src/android/autofillservice/cts/ImageTransformationTest.java
+++ b/tests/autofillservice/src/android/autofillservice/cts/ImageTransformationTest.java
@@ -39,30 +39,35 @@
 public class ImageTransformationTest {
 
     @Test
+    @SuppressWarnings("deprecation")
     public void testAllNullBuilder() {
         assertThrows(NullPointerException.class,
                 () ->  new ImageTransformation.Builder(null, null, 0));
     }
 
     @Test
+    @SuppressWarnings("deprecation")
     public void testNullAutofillIdBuilder() {
         assertThrows(NullPointerException.class,
                 () ->  new ImageTransformation.Builder(null, Pattern.compile(""), 1));
     }
 
     @Test
+    @SuppressWarnings("deprecation")
     public void testNullRegexBuilder() {
         assertThrows(NullPointerException.class,
                 () ->  new ImageTransformation.Builder(new AutofillId(1), null, 1));
     }
 
     @Test
+    @SuppressWarnings("deprecation")
     public void testNullSubstBuilder() {
         assertThrows(IllegalArgumentException.class,
                 () ->  new ImageTransformation.Builder(new AutofillId(1), Pattern.compile(""), 0));
     }
 
     @Test
+    @SuppressWarnings("deprecation")
     public void fieldCannotBeFound() throws Exception {
         AutofillId unknownId = new AutofillId(42);
 
@@ -82,6 +87,7 @@
     }
 
     @Test
+    @SuppressWarnings("deprecation")
     public void theOneOptionsMatches() throws Exception {
         AutofillId id = new AutofillId(1);
         ImageTransformation trans = new ImageTransformation
@@ -99,6 +105,25 @@
     }
 
     @Test
+    public void theOneOptionsMatchesWithContentDescription() throws Exception {
+        AutofillId id = new AutofillId(1);
+        ImageTransformation trans = new ImageTransformation
+                .Builder(id, Pattern.compile(".*"), 42, "Are you content?")
+                .build();
+
+        ValueFinder finder = mock(ValueFinder.class);
+        RemoteViews template = mock(RemoteViews.class);
+
+        when(finder.findByAutofillId(id)).thenReturn("val");
+
+        trans.apply(finder, template, 0);
+
+        verify(template).setImageViewResource(0, 42);
+        verify(template).setContentDescription(0, "Are you content?");
+    }
+
+    @Test
+    @SuppressWarnings("deprecation")
     public void noOptionsMatches() throws Exception {
         AutofillId id = new AutofillId(1);
         ImageTransformation trans = new ImageTransformation
@@ -116,6 +141,7 @@
     }
 
     @Test
+    @SuppressWarnings("deprecation")
     public void multipleOptionsOneMatches() throws Exception {
         AutofillId id = new AutofillId(1);
         ImageTransformation trans = new ImageTransformation
@@ -134,6 +160,26 @@
     }
 
     @Test
+    public void multipleOptionsOneMatchesWithContentDescription() throws Exception {
+        AutofillId id = new AutofillId(1);
+        ImageTransformation trans = new ImageTransformation
+                .Builder(id, Pattern.compile(".*1"), 1, "Are you content?")
+                .addOption(Pattern.compile(".*2"), 2, "I am content")
+                .build();
+
+        ValueFinder finder = mock(ValueFinder.class);
+        RemoteViews template = mock(RemoteViews.class);
+
+        when(finder.findByAutofillId(id)).thenReturn("val-2");
+
+        trans.apply(finder, template, 0);
+
+        verify(template).setImageViewResource(0, 2);
+        verify(template).setContentDescription(0, "I am content");
+    }
+
+    @Test
+    @SuppressWarnings("deprecation")
     public void twoOptionsMatch() throws Exception {
         AutofillId id = new AutofillId(1);
         ImageTransformation trans = new ImageTransformation
@@ -151,4 +197,24 @@
         // If two options match, the first one is picked
         verify(template, only()).setImageViewResource(0, 1);
     }
+
+    @Test
+    public void twoOptionsMatchWithContentDescription() throws Exception {
+        AutofillId id = new AutofillId(1);
+        ImageTransformation trans = new ImageTransformation
+                .Builder(id, Pattern.compile(".*a.*"), 1, "Are you content?")
+                .addOption(Pattern.compile(".*b.*"), 2, "No, I'm not")
+                .build();
+
+        ValueFinder finder = mock(ValueFinder.class);
+        RemoteViews template = mock(RemoteViews.class);
+
+        when(finder.findByAutofillId(id)).thenReturn("ab");
+
+        trans.apply(finder, template, 0);
+
+        // If two options match, the first one is picked
+        verify(template).setImageViewResource(0, 1);
+        verify(template).setContentDescription(0, "Are you content?");
+    }
 }
diff --git a/tests/autofillservice/src/android/autofillservice/cts/InstrumentedAutoFillService.java b/tests/autofillservice/src/android/autofillservice/cts/InstrumentedAutoFillService.java
index 6694e5a..0f2dc82 100644
--- a/tests/autofillservice/src/android/autofillservice/cts/InstrumentedAutoFillService.java
+++ b/tests/autofillservice/src/android/autofillservice/cts/InstrumentedAutoFillService.java
@@ -18,24 +18,31 @@
 
 import static android.autofillservice.cts.CannedFillResponse.ResponseType.NULL;
 import static android.autofillservice.cts.CannedFillResponse.ResponseType.TIMEOUT;
-import static android.autofillservice.cts.Helper.CONNECTION_TIMEOUT_MS;
-import static android.autofillservice.cts.Helper.FILL_TIMEOUT_MS;
-import static android.autofillservice.cts.Helper.IDLE_UNBIND_TIMEOUT_MS;
-import static android.autofillservice.cts.Helper.SAVE_TIMEOUT_MS;
 import static android.autofillservice.cts.Helper.dumpAutofillService;
 import static android.autofillservice.cts.Helper.dumpStructure;
+import static android.autofillservice.cts.Helper.getActivityName;
+import static android.autofillservice.cts.Timeouts.CONNECTION_TIMEOUT;
+import static android.autofillservice.cts.Timeouts.FILL_EVENTS_TIMEOUT;
+import static android.autofillservice.cts.Timeouts.FILL_TIMEOUT;
+import static android.autofillservice.cts.Timeouts.IDLE_UNBIND_TIMEOUT;
+import static android.autofillservice.cts.Timeouts.SAVE_TIMEOUT;
 
+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.CannedFillResponse.CannedDataset;
 import android.content.ComponentName;
+import android.content.IntentSender;
 import android.os.Bundle;
 import android.os.CancellationSignal;
+import android.os.SystemClock;
 import android.service.autofill.AutofillService;
 import android.service.autofill.Dataset;
 import android.service.autofill.FillCallback;
 import android.service.autofill.FillContext;
+import android.service.autofill.FillEventHistory;
+import android.service.autofill.FillEventHistory.Event;
 import android.service.autofill.FillResponse;
 import android.service.autofill.SaveCallback;
 import android.support.annotation.Nullable;
@@ -53,8 +60,10 @@
  */
 public class InstrumentedAutoFillService extends AutofillService {
 
-    static final String SERVICE_NAME = InstrumentedAutoFillService.class.getPackage()
-            .getName() + "/." + InstrumentedAutoFillService.class.getSimpleName();
+    static final String SERVICE_PACKAGE = "android.autofillservice.cts";
+    static final String SERVICE_CLASS = "InstrumentedAutoFillService";
+
+    static final String SERVICE_NAME = SERVICE_PACKAGE + "/." + SERVICE_CLASS;
 
     private static final String TAG = "InstrumentedAutoFillService";
 
@@ -78,10 +87,69 @@
         sInstance.set(this);
     }
 
-    public static AutofillService peekInstance() {
+    public static InstrumentedAutoFillService peekInstance() {
         return sInstance.get();
     }
 
+    /**
+     * Gets the list of fill events in the {@link FillEventHistory}, waiting until it has the
+     * expected size.
+     */
+    public static List<Event> getFillEvents(int expectedSize) throws Exception {
+        final List<Event> events = getFillEventHistory(expectedSize).getEvents();
+        // Sanity check
+        if (expectedSize > 0 && events == null || events.size() != expectedSize) {
+            throw new IllegalStateException("INTERNAL ERROR: events should have " + expectedSize
+                    + ", but it is: " + events);
+        }
+        return events;
+    }
+
+    /**
+     * Gets the {@link FillEventHistory}, waiting until it has the expected size.
+     */
+    public static FillEventHistory getFillEventHistory(int expectedSize) throws Exception {
+        final InstrumentedAutoFillService service = peekInstance();
+
+        if (expectedSize == 0) {
+            // Need to always sleep as there is no condition / callback to be used to wait until
+            // expected number of events is set.
+            SystemClock.sleep(FILL_EVENTS_TIMEOUT.ms());
+            final FillEventHistory history = service.getFillEventHistory();
+            assertThat(history.getEvents()).isNull();
+            return history;
+        }
+
+        return FILL_EVENTS_TIMEOUT.run("getFillEvents(" + expectedSize + ")", () -> {
+            final FillEventHistory history = service.getFillEventHistory();
+            if (history == null) {
+                return null;
+            }
+            final List<Event> events = history.getEvents();
+            if (events != null) {
+                if (events.size() != expectedSize) {
+                    Log.v(TAG, "Didn't get " + expectedSize + " events yet: " + events);
+                    return null;
+                }
+            } else {
+                Log.v(TAG, "Events is still null (expecting " + expectedSize + ")");
+                return null;
+            }
+            return history;
+        });
+    }
+
+    /**
+     * Asserts there is no {@link FillEventHistory}.
+     */
+    public static void assertNoFillEventHistory() {
+        // Need to always sleep as there is no condition / callback to be used to wait until
+        // expected number of events is set.
+        SystemClock.sleep(FILL_EVENTS_TIMEOUT.ms());
+        assertThat(peekInstance().getFillEventHistory()).isNull();
+
+    }
+
     @Override
     public void onConnected() {
         Log.v(TAG, "onConnected(): " + sConnectionStates);
@@ -118,7 +186,8 @@
                 return;
             }
         }
-        sReplier.onSaveRequest(request.getFillContexts(), request.getClientState(), callback);
+        sReplier.onSaveRequest(request.getFillContexts(), request.getClientState(), callback,
+                request.getDatasetIds());
     }
 
     private boolean fromSamePackage(List<FillContext> contexts) {
@@ -152,12 +221,15 @@
      * block until the service receives a callback, it should use
      * {@link Replier#getNextFillRequest()} instead.
      */
-    static void waitUntilConnected() throws InterruptedException {
-        final String state = sConnectionStates.poll(CONNECTION_TIMEOUT_MS, TimeUnit.MILLISECONDS);
-        if (state == null) {
-            dumpAutofillService();
-            throw new RetryableException("not connected in %d ms", CONNECTION_TIMEOUT_MS);
-        }
+    static void waitUntilConnected() throws Exception {
+        final String state = CONNECTION_TIMEOUT.run("waitUntilConnected()", () -> {
+            final String polled =
+                    sConnectionStates.poll(CONNECTION_TIMEOUT.ms(), TimeUnit.MILLISECONDS);
+            if (polled == null) {
+                dumpAutofillService();
+            }
+            return polled;
+        });
         assertWithMessage("Invalid connection state").that(state).isEqualTo(STATE_CONNECTED);
     }
 
@@ -167,12 +239,10 @@
      * <p>This method is useful on tests that explicitly verifies the connection, but should be
      * avoided in other tests, as it adds extra time to the test execution.
      */
-    static void waitUntilDisconnected() throws InterruptedException {
-        final String state = sConnectionStates.poll(2 * IDLE_UNBIND_TIMEOUT_MS,
-                TimeUnit.MILLISECONDS);
-        if (state == null) {
-            throw new RetryableException("not disconnected in %d ms", IDLE_UNBIND_TIMEOUT_MS);
-        }
+    static void waitUntilDisconnected() throws Exception {
+        final String state = IDLE_UNBIND_TIMEOUT.run("waitUntilDisconnected()", () -> {
+            return sConnectionStates.poll(2 * IDLE_UNBIND_TIMEOUT.ms(), TimeUnit.MILLISECONDS);
+        });
         assertWithMessage("Invalid connection state").that(state).isEqualTo(STATE_DISCONNECTED);
     }
 
@@ -217,12 +287,14 @@
      * that can be asserted at the end of a test case.
      */
     static final class SaveRequest {
-        final List<FillContext> contexts;
-        final AssistStructure structure;
-        final Bundle data;
-        final SaveCallback callback;
+        public final List<FillContext> contexts;
+        public final AssistStructure structure;
+        public final Bundle data;
+        public final SaveCallback callback;
+        public final List<String> datasetIds;
 
-        private SaveRequest(List<FillContext> contexts, Bundle data, SaveCallback callback) {
+        private SaveRequest(List<FillContext> contexts, Bundle data, SaveCallback callback,
+                List<String> datasetIds) {
             if (contexts != null && contexts.size() > 0) {
                 structure = contexts.get(contexts.size() - 1).getStructure();
             } else {
@@ -231,6 +303,7 @@
             this.contexts = contexts;
             this.data = data;
             this.callback = callback;
+            this.datasetIds = datasetIds;
         }
     }
 
@@ -246,13 +319,13 @@
         private final BlockingQueue<FillRequest> mFillRequests = new LinkedBlockingQueue<>();
         private final BlockingQueue<SaveRequest> mSaveRequests = new LinkedBlockingQueue<>();
 
-        private List<Exception> mExceptions;
+        private List<Throwable> mExceptions;
+        private IntentSender mOnSaveIntentSender;
         private String mAcceptedPackageName;
 
         private Replier() {
         }
 
-
         private IdMode mIdMode = IdMode.RESOURCE_ID;
 
         public void setIdMode(IdMode mode) {
@@ -266,11 +339,11 @@
         /**
          * Gets the exceptions thrown asynchronously, if any.
          */
-        @Nullable List<Exception> getExceptions() {
+        @Nullable List<Throwable> getExceptions() {
             return mExceptions;
         }
 
-        private void addException(@Nullable Exception e) {
+        private void addException(@Nullable Throwable e) {
             if (e == null) return;
 
             if (mExceptions == null) {
@@ -301,15 +374,23 @@
         }
 
         /**
+         * Sets the {@link IntentSender} that is passed to
+         * {@link SaveCallback#onSuccess(IntentSender)}.
+         */
+        void setOnSave(IntentSender intentSender) {
+            mOnSaveIntentSender = intentSender;
+        }
+
+        /**
          * Gets the next fill request, in the order received.
          *
          * <p>Typically called at the end of a test case, to assert the initial request.
          */
         FillRequest getNextFillRequest() throws InterruptedException {
-            final FillRequest request = mFillRequests.poll(FILL_TIMEOUT_MS, TimeUnit.MILLISECONDS);
+            final FillRequest request =
+                    mFillRequests.poll(FILL_TIMEOUT.ms(), TimeUnit.MILLISECONDS);
             if (request == null) {
-                throw new RetryableException("onFillRequest() not called in %s ms",
-                        FILL_TIMEOUT_MS);
+                throw new RetryableException(FILL_TIMEOUT, "onFillRequest() not called");
             }
             return request;
         }
@@ -337,10 +418,10 @@
          * <p>Typically called at the end of a test case, to assert the initial request.
          */
         SaveRequest getNextSaveRequest() throws InterruptedException {
-            final SaveRequest request = mSaveRequests.poll(SAVE_TIMEOUT_MS, TimeUnit.MILLISECONDS);
+            final SaveRequest request =
+                    mSaveRequests.poll(SAVE_TIMEOUT.ms(), TimeUnit.MILLISECONDS);
             if (request == null) {
-                throw new RetryableException(
-                        "onSaveRequest() not called in %d ms", SAVE_TIMEOUT_MS);
+                throw new RetryableException(SAVE_TIMEOUT, "onSaveRequest() not called");
             }
             return request;
         }
@@ -363,6 +444,7 @@
             mFillRequests.clear();
             mSaveRequests.clear();
             mExceptions = null;
+            mOnSaveIntentSender = null;
             mAcceptedPackageName = null;
         }
 
@@ -371,7 +453,7 @@
             try {
                 CannedFillResponse response = null;
                 try {
-                    response = mResponses.poll(CONNECTION_TIMEOUT_MS, TimeUnit.MILLISECONDS);
+                    response = mResponses.poll(CONNECTION_TIMEOUT.ms(), TimeUnit.MILLISECONDS);
                 } catch (InterruptedException e) {
                     Log.w(TAG, "Interrupted getting CannedResponse: " + e);
                     Thread.currentThread().interrupt();
@@ -379,7 +461,8 @@
                     return;
                 }
                 if (response == null) {
-                    final String msg = "onFillRequest() received when no CannedResponse was set";
+                    final String msg = "onFillRequest() for activity " + getActivityName(contexts)
+                            + " received when no canned response was set.";
                     dumpStructure(msg, contexts);
                     addException(new RetryableException(msg));
                     return;
@@ -413,6 +496,10 @@
                         fillResponse = response.asFillResponse(
                                 (name) -> Helper.findNodeByHtmlName(contexts, name));
                         break;
+                    case HTML_NAME_OR_RESOURCE_ID:
+                        fillResponse = response.asFillResponse(
+                                (id) -> Helper.findNodeByHtmlNameOrResourceId(contexts, id));
+                        break;
                     default:
                         throw new IllegalStateException("Unknown id mode: " + mIdMode);
                 }
@@ -427,10 +514,15 @@
             }
         }
 
-        private void onSaveRequest(List<FillContext> contexts, Bundle data, SaveCallback callback) {
-            Log.d(TAG, "onSaveRequest()");
-            mSaveRequests.offer(new SaveRequest(contexts, data, callback));
-            callback.onSuccess();
+        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();
+            }
         }
     }
 }
diff --git a/tests/autofillservice/src/android/autofillservice/cts/JUnitHelper.java b/tests/autofillservice/src/android/autofillservice/cts/JUnitHelper.java
new file mode 100644
index 0000000..3d70bd0
--- /dev/null
+++ b/tests/autofillservice/src/android/autofillservice/cts/JUnitHelper.java
@@ -0,0 +1,40 @@
+/*
+ * 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.autofillservice.cts;
+
+import android.support.annotation.NonNull;
+
+/**
+ * Generic helper for JUnit needs.
+ */
+public final class JUnitHelper {
+
+    private static String sCurrentTestNamer;
+
+    @NonNull
+    static String getCurrentTestName() {
+        return sCurrentTestNamer != null ? sCurrentTestNamer : "N/A";
+    }
+
+    public static void setCurrentTestName(String name) {
+        sCurrentTestNamer = name;
+    }
+
+    private JUnitHelper() {
+        throw new UnsupportedOperationException("contain static methods only");
+    }
+}
diff --git a/tests/autofillservice/src/android/autofillservice/cts/LoginActivity.java b/tests/autofillservice/src/android/autofillservice/cts/LoginActivity.java
index b41cef8..2a3f019 100644
--- a/tests/autofillservice/src/android/autofillservice/cts/LoginActivity.java
+++ b/tests/autofillservice/src/android/autofillservice/cts/LoginActivity.java
@@ -22,6 +22,9 @@
 import android.os.Bundle;
 import android.text.TextUtils;
 import android.util.Log;
+import android.view.ActionMode;
+import android.view.Menu;
+import android.view.MenuItem;
 import android.view.View.OnClickListener;
 import android.view.inputmethod.InputMethodManager;
 import android.widget.Button;
@@ -99,6 +102,42 @@
             getAutofillManager().cancel();
         });
         mCancelButton.setOnClickListener((OnClickListener) v -> finish());
+
+        // Create a custom insertion callback so it just show the AUTOFILL item, otherwise CTS
+        // testAutofillManuallyOneDataset() will fail if a previous test set the clipboard
+        // TODO(b/71711122): remove once there's a proper way to reset the clipboard
+        mUsernameEditText.setCustomInsertionActionModeCallback(new ActionMode.Callback() {
+
+            @Override
+            public boolean onPrepareActionMode(ActionMode mode, Menu menu) {
+                final String autofillTitle = AutoFillServiceTestCase.sDefaultUiBot
+                        .getAutofillContextualMenuTitle();
+                for (int i = 0; i < menu.size(); i++) {
+                    final MenuItem item = menu.getItem(i);
+                    final String title = item.getTitle().toString();
+                    if (!title.equals(autofillTitle)) {
+                        Log.v(TAG, "onPrepareActionMode(): ignoring " + title);
+                        menu.removeItem(item.getItemId());
+                    }
+                }
+                return true;
+            }
+
+            @Override
+            public void onDestroyActionMode(ActionMode mode) {
+
+            }
+
+            @Override
+            public boolean onCreateActionMode(ActionMode mode, Menu menu) {
+                return true;
+            }
+
+            @Override
+            public boolean onActionItemClicked(ActionMode mode, MenuItem item) {
+                return false;
+            }
+        });
     }
 
     protected int getContentView() {
diff --git a/tests/autofillservice/src/android/autofillservice/cts/LoginActivityTest.java b/tests/autofillservice/src/android/autofillservice/cts/LoginActivityTest.java
index 898ca84..8ab6d3b 100644
--- a/tests/autofillservice/src/android/autofillservice/cts/LoginActivityTest.java
+++ b/tests/autofillservice/src/android/autofillservice/cts/LoginActivityTest.java
@@ -20,29 +20,30 @@
 import static android.app.Activity.RESULT_OK;
 import static android.autofillservice.cts.CannedFillResponse.DO_NOT_REPLY_RESPONSE;
 import static android.autofillservice.cts.CannedFillResponse.NO_RESPONSE;
-import static android.autofillservice.cts.CheckoutActivity.ID_CC_NUMBER;
 import static android.autofillservice.cts.Helper.ID_PASSWORD;
 import static android.autofillservice.cts.Helper.ID_PASSWORD_LABEL;
 import static android.autofillservice.cts.Helper.ID_USERNAME;
+import static android.autofillservice.cts.Helper.ID_USERNAME_LABEL;
+import static android.autofillservice.cts.Helper.UNUSED_AUTOFILL_VALUE;
+import static android.autofillservice.cts.Helper.assertHasFlags;
 import static android.autofillservice.cts.Helper.assertNoDanglingSessions;
 import static android.autofillservice.cts.Helper.assertNumberOfChildren;
 import static android.autofillservice.cts.Helper.assertTextAndValue;
 import static android.autofillservice.cts.Helper.assertTextIsSanitized;
+import static android.autofillservice.cts.Helper.assertTextOnly;
 import static android.autofillservice.cts.Helper.assertValue;
 import static android.autofillservice.cts.Helper.dumpStructure;
 import static android.autofillservice.cts.Helper.findNodeByResourceId;
-import static android.autofillservice.cts.Helper.runShellCommand;
 import static android.autofillservice.cts.Helper.setUserComplete;
+import static android.autofillservice.cts.InstrumentedAutoFillService.SERVICE_CLASS;
+import static android.autofillservice.cts.InstrumentedAutoFillService.SERVICE_PACKAGE;
 import static android.autofillservice.cts.InstrumentedAutoFillService.waitUntilConnected;
 import static android.autofillservice.cts.InstrumentedAutoFillService.waitUntilDisconnected;
 import static android.autofillservice.cts.LoginActivity.AUTHENTICATION_MESSAGE;
 import static android.autofillservice.cts.LoginActivity.BACKDOOR_USERNAME;
 import static android.autofillservice.cts.LoginActivity.ID_USERNAME_CONTAINER;
 import static android.autofillservice.cts.LoginActivity.getWelcomeMessage;
-import static android.service.autofill.FillEventHistory.Event.TYPE_AUTHENTICATION_SELECTED;
-import static android.service.autofill.FillEventHistory.Event.TYPE_DATASET_AUTHENTICATION_SELECTED;
-import static android.service.autofill.FillEventHistory.Event.TYPE_DATASET_SELECTED;
-import static android.service.autofill.FillEventHistory.Event.TYPE_SAVE_SHOWN;
+import static android.autofillservice.cts.common.ShellHelper.runShellCommand;
 import static android.service.autofill.FillRequest.FLAG_MANUAL_REQUEST;
 import static android.service.autofill.SaveInfo.SAVE_DATA_TYPE_ADDRESS;
 import static android.service.autofill.SaveInfo.SAVE_DATA_TYPE_CREDIT_CARD;
@@ -64,13 +65,13 @@
 import android.autofillservice.cts.InstrumentedAutoFillService.FillRequest;
 import android.autofillservice.cts.InstrumentedAutoFillService.SaveRequest;
 import android.content.BroadcastReceiver;
+import android.content.ComponentName;
 import android.content.Context;
 import android.content.Intent;
 import android.content.IntentFilter;
 import android.content.IntentSender;
 import android.graphics.Color;
 import android.os.Bundle;
-import android.service.autofill.FillEventHistory;
 import android.service.autofill.SaveInfo;
 import android.support.test.uiautomator.UiObject2;
 import android.util.Log;
@@ -84,7 +85,6 @@
 import android.view.autofill.AutofillValue;
 import android.widget.RemoteViews;
 
-import org.junit.After;
 import org.junit.Before;
 import org.junit.Rule;
 import org.junit.Test;
@@ -92,6 +92,7 @@
 import java.util.concurrent.CountDownLatch;
 import java.util.concurrent.TimeUnit;
 import java.util.concurrent.atomic.AtomicInteger;
+import java.util.regex.Pattern;
 
 /**
  * This is the test case covering most scenarios - other test cases will cover characteristics
@@ -112,11 +113,6 @@
         mActivity = mActivityRule.getActivity();
     }
 
-    @After
-    public void finishWelcomeActivity() {
-        WelcomeActivity.finishIt();
-    }
-
     @Test
     public void testAutoFillNoDatasets() throws Exception {
         // Set service.
@@ -133,14 +129,7 @@
         sReplier.getNextFillRequest();
 
         // Make sure UI is not shown.
-        sUiBot.assertNoDatasets();
-
-        // Try to trigger it again...
-
-        mActivity.onPassword(View::requestFocus);
-        // ...and make sure it didn't
-        sUiBot.assertNoDatasets();
-        sReplier.assertNumberUnhandledFillRequests(0);
+        mUiBot.assertNoDatasets();
 
         // Test connection lifecycle.
         waitUntilDisconnected();
@@ -148,18 +137,28 @@
 
     @Test
     public void testAutofillManuallyAfterServiceReturnedNoDatasets() throws Exception {
+        autofillAfterServiceReturnedNoDatasets(true);
+    }
+
+    @Test
+    public void testAutofillAutomaticallyAfterServiceReturnedNoDatasets() throws Exception {
+        autofillAfterServiceReturnedNoDatasets(false);
+    }
+
+    private void autofillAfterServiceReturnedNoDatasets(boolean manually) throws Exception {
         // Set service.
         enableService();
 
         // Set expectations.
         sReplier.addResponse(NO_RESPONSE);
+        mActivity.expectAutoFill("dude", "sweet");
 
         // Trigger autofill.
         mActivity.onUsername(View::requestFocus);
         sReplier.getNextFillRequest();
 
         // Make sure UI is not shown.
-        sUiBot.assertNoDatasets();
+        mUiBot.assertNoDatasets();
 
         // Try again, forcing it
         sReplier.addResponse(new CannedDataset.Builder()
@@ -167,15 +166,21 @@
                 .setField(ID_PASSWORD, "sweet")
                 .setPresentation(createPresentation("The Dude"))
                 .build());
-        mActivity.expectAutoFill("dude", "sweet");
 
-        mActivity.forceAutofillOnUsername();
+        final int expectedFlags;
+        if (manually) {
+            expectedFlags = FLAG_MANUAL_REQUEST;
+            mActivity.forceAutofillOnUsername();
+        } else {
+            expectedFlags = 0;
+            mActivity.onPassword(View::requestFocus);
+        }
 
         final FillRequest fillRequest = sReplier.getNextFillRequest();
-        assertThat(fillRequest.flags).isEqualTo(FLAG_MANUAL_REQUEST);
+        assertHasFlags(fillRequest.flags, expectedFlags);
 
-        // Selects the dataset.
-        sUiBot.selectDataset("The Dude");
+        // Select the dataset.
+        mUiBot.selectDataset("The Dude");
 
         // Check the results.
         mActivity.assertAutoFilled();
@@ -183,6 +188,15 @@
 
     @Test
     public void testAutofillManuallyAndSaveAfterServiceReturnedNoDatasets() throws Exception {
+        autofillAndSaveAfterServiceReturnedNoDatasets(true);
+    }
+
+    @Test
+    public void testAutofillAutomaticallyAndSaveAfterServiceReturnedNoDatasets() throws Exception {
+        autofillAndSaveAfterServiceReturnedNoDatasets(false);
+    }
+
+    private void autofillAndSaveAfterServiceReturnedNoDatasets(boolean manually) throws Exception {
         // Set service.
         enableService();
 
@@ -190,31 +204,98 @@
         sReplier.addResponse(NO_RESPONSE);
 
         // Trigger autofill.
-        mActivity.onUsername(View::requestFocus);
+        // NOTE: must be on password, as saveOnlyTest() will trigger on username
+        mActivity.onPassword(View::requestFocus);
         sReplier.getNextFillRequest();
 
         // Make sure UI is not shown.
-        sUiBot.assertNoDatasets();
+        mUiBot.assertNoDatasets();
         sReplier.assertNumberUnhandledFillRequests(0);
         mActivity.onPassword(View::requestFocus);
-        sUiBot.assertNoDatasets();
+        mUiBot.assertNoDatasets();
         sReplier.assertNumberUnhandledFillRequests(0);
 
         // Try again, forcing it
-        saveOnlyTest(true);
+        saveOnlyTest(manually);
     }
 
     @Test
-    public void testAutoFillOneDataset() throws Exception {
+    public void testAutofillManuallyAlwaysCallServiceAgain() throws Exception {
         // Set service.
         enableService();
 
-        // Set expectations.
+        // First request
         sReplier.addResponse(new CannedDataset.Builder()
                 .setField(ID_USERNAME, "dude")
                 .setField(ID_PASSWORD, "sweet")
                 .setPresentation(createPresentation("The Dude"))
                 .build());
+        mActivity.onUsername(View::requestFocus);
+        sReplier.getNextFillRequest();
+        mUiBot.assertDatasets("The Dude");
+
+        // Second request
+        sReplier.addResponse(new CannedDataset.Builder()
+                .setField(ID_USERNAME, "DUDE")
+                .setField(ID_PASSWORD, "SWEET")
+                .setPresentation(createPresentation("THE DUDE"))
+                .build());
+
+        mActivity.forceAutofillOnUsername();
+        final FillRequest secondRequest = sReplier.getNextFillRequest();
+        assertHasFlags(secondRequest.flags, FLAG_MANUAL_REQUEST);
+        mUiBot.assertDatasets("THE DUDE");
+    }
+
+    @Test
+    public void testAutoFillOneDataset() throws Exception {
+        autofillOneDatasetTest(BorderType.NONE);
+    }
+
+    @Test
+    public void testAutoFillOneDataset_withHeader() throws Exception {
+        autofillOneDatasetTest(BorderType.HEADER_ONLY);
+    }
+
+    @Test
+    public void testAutoFillOneDataset_withFooter() throws Exception {
+        autofillOneDatasetTest(BorderType.FOOTER_ONLY);
+    }
+
+    @Test
+    public void testAutoFillOneDataset_withHeaderAndFooter() throws Exception {
+        autofillOneDatasetTest(BorderType.BOTH);
+    }
+
+    private enum BorderType {
+        NONE,
+        HEADER_ONLY,
+        FOOTER_ONLY,
+        BOTH
+    }
+
+    private void autofillOneDatasetTest(BorderType borderType) throws Exception {
+        // Set service.
+        enableService();
+
+        // Set expectations.
+        String expectedHeader = null, expectedFooter = null;
+
+        final CannedFillResponse.Builder builder = new CannedFillResponse.Builder()
+                .addDataset(new CannedDataset.Builder()
+                        .setField(ID_USERNAME, "dude")
+                        .setField(ID_PASSWORD, "sweet")
+                        .setPresentation(createPresentation("The Dude"))
+                        .build());
+        if (borderType == BorderType.BOTH || borderType == BorderType.HEADER_ONLY) {
+            expectedHeader = "Head";
+            builder.setHeader(createPresentation(expectedHeader));
+        }
+        if (borderType == BorderType.BOTH || borderType == BorderType.FOOTER_ONLY) {
+            expectedFooter = "Tails";
+            builder.setFooter(createPresentation(expectedFooter));
+        }
+        sReplier.addResponse(builder.build());
         mActivity.expectAutoFill("dude", "sweet");
 
         // Dynamically set password to make sure it's sanitized.
@@ -224,7 +305,10 @@
         mActivity.onUsername(View::requestFocus);
 
         // Auto-fill it.
-        sUiBot.selectDataset("The Dude");
+        final UiObject2 picker = mUiBot.assertDatasetsWithBorders(expectedHeader, expectedFooter,
+                "The Dude");
+
+        mUiBot.selectDataset(picker, "The Dude");
 
         // Check the results.
         mActivity.assertAutoFilled();
@@ -268,14 +352,14 @@
         sReplier.getNextFillRequest();
 
         // Make sure all datasets are available...
-        sUiBot.assertDatasets("The Dude", "THE DUDE");
+        mUiBot.assertDatasets("The Dude", "THE DUDE");
 
         // ... on all fields.
         mActivity.onPassword(View::requestFocus);
-        sUiBot.assertDatasets("The Dude", "THE DUDE");
+        mUiBot.assertDatasets("The Dude", "THE DUDE");
 
         // Auto-fill it.
-        sUiBot.selectDataset("The Dude");
+        mUiBot.selectDataset("The Dude");
 
         // Check the results.
         mActivity.assertAutoFilled();
@@ -318,19 +402,19 @@
         sReplier.getNextFillRequest();
 
         // Make sure all datasets are available on username...
-        sUiBot.assertDatasets("The Dude", "THE DUDE");
+        mUiBot.assertDatasets("The Dude", "THE DUDE");
 
         // ... but just one for password
         mActivity.onPassword(View::requestFocus);
-        sUiBot.assertDatasets("The Dude");
+        mUiBot.assertDatasets("The Dude");
 
         // Auto-fill it.
         mActivity.onUsername(View::requestFocus);
-        sUiBot.assertDatasets("The Dude", "THE DUDE");
+        mUiBot.assertDatasets("The Dude", "THE DUDE");
         if (fillsAll) {
-            sUiBot.selectDataset("The Dude");
+            mUiBot.selectDataset("The Dude");
         } else {
-            sUiBot.selectDataset("THE DUDE");
+            mUiBot.selectDataset("THE DUDE");
         }
 
         // Check the results.
@@ -338,6 +422,43 @@
     }
 
     @Test
+    public void testAutoFillDatasetWithoutFieldIsIgnored() throws Exception {
+        // Set service.
+        enableService();
+
+        // Set expectations.
+        sReplier.addResponse(new CannedFillResponse.Builder()
+                .addDataset(new CannedDataset.Builder()
+                        .setField(ID_USERNAME, "dude")
+                        .setField(ID_PASSWORD, "sweet")
+                        .setPresentation(createPresentation("The Dude"))
+                        .build())
+                .addDataset(new CannedDataset.Builder()
+                        .setField(ID_USERNAME, "DUDE")
+                        .setField(ID_PASSWORD, "SWEET")
+                        .build())
+                .build());
+        mActivity.expectAutoFill("dude", "sweet");
+
+        // Trigger auto-fill.
+        mActivity.onUsername(View::requestFocus);
+        sReplier.getNextFillRequest();
+
+        // Make sure all datasets are available...
+        mUiBot.assertDatasets("The Dude");
+
+        // ... on all fields.
+        mActivity.onPassword(View::requestFocus);
+        mUiBot.assertDatasets("The Dude");
+
+        // Auto-fill it.
+        mUiBot.selectDataset("The Dude");
+
+        // Check the results.
+        mActivity.assertAutoFilled();
+    }
+
+    @Test
     public void testAutoFillWhenViewHasChildAccessibilityNodes() throws Exception {
         mActivity.onUsername((v) -> v.setAccessibilityDelegate(new AccessibilityDelegate() {
             @Override
@@ -383,7 +504,7 @@
         sReplier.assertNumberUnhandledFillRequests(0);
 
         // Auto-fill it.
-        sUiBot.selectDataset("The Dude");
+        mUiBot.selectDataset("The Dude");
 
         // Check the results.
         mActivity.assertAutoFilled();
@@ -409,17 +530,17 @@
         // Trigger auto-fill.
         mActivity.onUsername(View::requestFocus);
         sReplier.getNextFillRequest();
-        sUiBot.selectDataset("The Dude");
+        mUiBot.selectDataset("The Dude");
 
         // Check the results.
         mActivity.assertAutoFilled();
 
         // Make sure tapping on autofilled field does not trigger it again
         mActivity.onPassword(View::requestFocus);
-        sUiBot.assertNoDatasets();
+        mUiBot.assertNoDatasets();
 
         mActivity.onUsername(View::requestFocus);
-        sUiBot.assertNoDatasets();
+        mUiBot.assertNoDatasets();
     }
 
     @Test
@@ -453,7 +574,7 @@
         callback.assertNumberUnhandledEvents(0);
 
         // Auto-fill it.
-        sUiBot.selectDataset("The Dude");
+        mUiBot.selectDataset("The Dude");
 
         // Check the results.
         mActivity.assertAutoFilled();
@@ -499,7 +620,7 @@
         sReplier.getNextFillRequest();
 
         // Auto-fill it.
-        sUiBot.assertNoDatasets();
+        mUiBot.assertNoDatasets();
 
         // Assert callback was called
         final View username = mActivity.getUsername();
@@ -517,6 +638,7 @@
 
         sReplier.addResponse(new CannedFillResponse.Builder()
                 .addDataset(new CannedDataset.Builder()
+                        .setId("I'm the alpha and the omega")
                         .setField(ID_USERNAME, "dude")
                         .setField(ID_PASSWORD, "sweet")
                         .setPresentation(createPresentation("The Dude"))
@@ -532,10 +654,18 @@
         // Since this is a Presubmit test, wait for connection to avoid flakiness.
         waitUntilConnected();
 
-        sReplier.getNextFillRequest();
+        final FillRequest fillRequest = sReplier.getNextFillRequest();
+
+        // Make sure input was sanitized...
+        assertTextIsSanitized(fillRequest.structure, ID_USERNAME);
+        assertTextIsSanitized(fillRequest.structure, ID_PASSWORD);
+
+        // ...but labels weren't
+        assertTextOnly(fillRequest.structure, ID_USERNAME_LABEL, "Username");
+        assertTextOnly(fillRequest.structure, ID_PASSWORD_LABEL, "Password");
 
         // Auto-fill it.
-        sUiBot.selectDataset("The Dude");
+        mUiBot.selectDataset("The Dude");
 
         // Check the results.
         mActivity.assertAutoFilled();
@@ -554,15 +684,17 @@
         assertWithMessage("Wrong welcome msg").that(actualMessage).isEqualTo(expectedMessage);
 
         // Assert the snack bar is shown and tap "Save".
-        sUiBot.saveForAutofill(true, SAVE_DATA_TYPE_PASSWORD);
+        mUiBot.saveForAutofill(true, SAVE_DATA_TYPE_PASSWORD);
 
         final SaveRequest saveRequest = sReplier.getNextSaveRequest();
 
+        assertThat(saveRequest.datasetIds).containsExactly("I'm the alpha and the omega");
+
         // Assert value of expected fields - should not be sanitized.
-        final ViewNode username = findNodeByResourceId(saveRequest.structure, ID_USERNAME);
-        assertTextAndValue(username, "dude");
-        final ViewNode password = findNodeByResourceId(saveRequest.structure, ID_USERNAME);
-        assertTextAndValue(password, "dude");
+        assertTextAndValue(saveRequest.structure, ID_USERNAME, "dude");
+        assertTextAndValue(saveRequest.structure, ID_PASSWORD, "dude");
+        assertTextOnly(saveRequest.structure, ID_USERNAME_LABEL, "Username");
+        assertTextOnly(saveRequest.structure, ID_PASSWORD_LABEL, "Password");
 
         // Make sure extras were passed back on onSave()
         assertThat(saveRequest.data).isNotNull();
@@ -608,7 +740,7 @@
             runShellCommand("appops set %s SYSTEM_ALERT_WINDOW allow", mPackageName);
 
             // Make sure the fill UI is shown.
-            sUiBot.assertDatasets("The Dude");
+            mUiBot.assertDatasets("The Dude");
 
             final CountDownLatch latch = new CountDownLatch(1);
 
@@ -638,7 +770,7 @@
             assertThat(latch.await(5, TimeUnit.SECONDS)).isTrue();
 
             // Auto-fill it.
-            sUiBot.selectDataset("The Dude");
+            mUiBot.selectDataset("The Dude");
 
             // Check the results.
             mActivity.assertAutoFilled();
@@ -658,14 +790,14 @@
             assertWithMessage("Wrong welcome msg").that(actualMessage).isEqualTo(expectedMessage);
 
             // Assert the snack bar is shown and tap "Save".
-            sUiBot.saveForAutofill(true, SAVE_DATA_TYPE_PASSWORD);
+            mUiBot.saveForAutofill(true, SAVE_DATA_TYPE_PASSWORD);
 
             final SaveRequest saveRequest = sReplier.getNextSaveRequest();
 
             // Assert value of expected fields - should not be sanitized.
             final ViewNode username = findNodeByResourceId(saveRequest.structure, ID_USERNAME);
             assertTextAndValue(username, "dude");
-            final ViewNode password = findNodeByResourceId(saveRequest.structure, ID_USERNAME);
+            final ViewNode password = findNodeByResourceId(saveRequest.structure, ID_PASSWORD);
             assertTextAndValue(password, "dude");
 
             // Make sure extras were passed back on onSave()
@@ -747,10 +879,10 @@
         sReplier.getNextFillRequest();
 
         // Make sure all datasets are shown.
-        final UiObject2 picker = sUiBot.assertDatasets("Mr Plow", "El Barto", "Mr Sparkle");
+        final UiObject2 picker = mUiBot.assertDatasets("Mr Plow", "El Barto", "Mr Sparkle");
 
         // Auto-fill it.
-        sUiBot.selectDataset(picker, name);
+        mUiBot.selectDataset(picker, name);
 
         // Check the results.
         mActivity.assertAutoFilled();
@@ -779,17 +911,17 @@
         sReplier.getNextFillRequest();
 
         // Check initial field.
-        sUiBot.assertDatasets("The Dude");
+        mUiBot.assertDatasets("The Dude");
 
         // Then move around...
         mActivity.onPassword(View::requestFocus);
-        sUiBot.assertDatasets("Dude's password");
+        mUiBot.assertDatasets("Dude's password");
         mActivity.onUsername(View::requestFocus);
-        sUiBot.assertDatasets("The Dude");
+        mUiBot.assertDatasets("The Dude");
 
         // Auto-fill it.
         mActivity.onPassword(View::requestFocus);
-        sUiBot.selectDataset("Dude's password");
+        mUiBot.selectDataset("Dude's password");
 
         // Check the results.
         mActivity.assertAutoFilled();
@@ -823,17 +955,17 @@
         sReplier.getNextFillRequest();
 
         // Check initial field.
-        sUiBot.assertDatasets("Dataset1", "User2");
+        mUiBot.assertDatasets("Dataset1", "User2");
 
         // Then move around...
         mActivity.onPassword(View::requestFocus);
-        sUiBot.assertDatasets("Pass1", "Dataset2");
+        mUiBot.assertDatasets("Pass1", "Dataset2");
         mActivity.onUsername(View::requestFocus);
-        sUiBot.assertDatasets("Dataset1", "User2");
+        mUiBot.assertDatasets("Dataset1", "User2");
 
         // Auto-fill it.
         mActivity.onPassword(View::requestFocus);
-        sUiBot.selectDataset("Pass1");
+        mUiBot.selectDataset("Pass1");
 
         // Check the results.
         mActivity.assertAutoFilled();
@@ -866,17 +998,17 @@
         sReplier.getNextFillRequest();
 
         // Check initial field.
-        sUiBot.assertDatasets("User1", "User2");
+        mUiBot.assertDatasets("User1", "User2");
 
         // Then move around...
         mActivity.onPassword(View::requestFocus);
-        sUiBot.assertDatasets("Pass1", "Pass2");
+        mUiBot.assertDatasets("Pass1", "Pass2");
         mActivity.onUsername(View::requestFocus);
-        sUiBot.assertDatasets("User1", "User2");
+        mUiBot.assertDatasets("User1", "User2");
 
         // Auto-fill it.
         mActivity.onPassword(View::requestFocus);
-        sUiBot.selectDataset("Pass1");
+        mUiBot.selectDataset("Pass1");
 
         // Check the results.
         mActivity.assertAutoFilled();
@@ -909,16 +1041,16 @@
         sReplier.getNextFillRequest();
 
         // Check initial field.
-        sUiBot.assertDatasets("User1", "User2");
+        mUiBot.assertDatasets("User1", "User2");
 
         // Then move around...
         mActivity.onPassword(View::requestFocus);
-        sUiBot.assertDatasets("Pass2");
+        mUiBot.assertDatasets("Pass2");
         mActivity.onUsername(View::requestFocus);
-        sUiBot.assertDatasets("User1", "User2");
+        mUiBot.assertDatasets("User1", "User2");
 
         // Auto-fill it.
-        sUiBot.selectDataset("User2");
+        mUiBot.selectDataset("User2");
 
         // Check the results.
         mActivity.assertAutoFilled();
@@ -951,16 +1083,16 @@
         sReplier.getNextFillRequest();
 
         // Check initial field.
-        sUiBot.assertDatasets("User1");
+        mUiBot.assertDatasets("User1");
 
         // Then move around...
         mActivity.onPassword(View::requestFocus);
-        sUiBot.assertDatasets("Pass1", "Pass2");
+        mUiBot.assertDatasets("Pass1", "Pass2");
         mActivity.onUsername(View::requestFocus);
-        sUiBot.assertDatasets("User1");
+        mUiBot.assertDatasets("User1");
 
         // Auto-fill it.
-        sUiBot.selectDataset("User1");
+        mUiBot.selectDataset("User1");
 
         // Check the results.
         mActivity.assertAutoFilled();
@@ -995,29 +1127,29 @@
         sReplier.getNextFillRequest();
 
         // With no filter text all datasets should be shown
-        sUiBot.assertDatasets(AA, AB, B);
+        mUiBot.assertDatasets(AA, AB, B);
 
         // Only two datasets start with 'a'
         runShellCommand("input keyevent KEYCODE_A");
-        sUiBot.assertDatasets(AA, AB);
+        mUiBot.assertDatasets(AA, AB);
 
         // Only one dataset start with 'aa'
         runShellCommand("input keyevent KEYCODE_A");
-        sUiBot.assertDatasets(AA);
+        mUiBot.assertDatasets(AA);
 
         // Only two datasets start with 'a'
         runShellCommand("input keyevent KEYCODE_DEL");
-        sUiBot.assertDatasets(AA, AB);
+        mUiBot.assertDatasets(AA, AB);
 
         // With no filter text all datasets should be shown
         runShellCommand("input keyevent KEYCODE_DEL");
-        sUiBot.assertDatasets(AA, AB, B);
+        mUiBot.assertDatasets(AA, AB, B);
 
         // No dataset start with 'aaa'
         runShellCommand("input keyevent KEYCODE_A");
         runShellCommand("input keyevent KEYCODE_A");
         runShellCommand("input keyevent KEYCODE_A");
-        sUiBot.assertNoDatasets();
+        mUiBot.assertNoDatasets();
     }
 
     @Test
@@ -1049,29 +1181,29 @@
         sReplier.getNextFillRequest();
 
         // With no filter text all datasets should be shown
-        sUiBot.assertDatasets(AA, AB, B);
+        mUiBot.assertDatasets(AA, AB, B);
 
         // Two datasets start with 'a' and one with null value always shown
         runShellCommand("input keyevent KEYCODE_A");
-        sUiBot.assertDatasets(AA, AB, B);
+        mUiBot.assertDatasets(AA, AB, B);
 
         // One dataset start with 'aa' and one with null value always shown
         runShellCommand("input keyevent KEYCODE_A");
-        sUiBot.assertDatasets(AA, B);
+        mUiBot.assertDatasets(AA, B);
 
         // Two datasets start with 'a' and one with null value always shown
         runShellCommand("input keyevent KEYCODE_DEL");
-        sUiBot.assertDatasets(AA, AB, B);
+        mUiBot.assertDatasets(AA, AB, B);
 
         // With no filter text all datasets should be shown
         runShellCommand("input keyevent KEYCODE_DEL");
-        sUiBot.assertDatasets(AA, AB, B);
+        mUiBot.assertDatasets(AA, AB, B);
 
         // No dataset start with 'aaa' and one with null value always shown
         runShellCommand("input keyevent KEYCODE_A");
         runShellCommand("input keyevent KEYCODE_A");
         runShellCommand("input keyevent KEYCODE_A");
-        sUiBot.assertDatasets(B);
+        mUiBot.assertDatasets(B);
     }
 
     @Test
@@ -1103,16 +1235,71 @@
         sReplier.getNextFillRequest();
 
         // With no filter text all datasets should be shown
-        sUiBot.assertDatasets(A, B, C);
+        mUiBot.assertDatasets(A, B, C);
 
         mActivity.onUsername((v) -> v.setText("a"));
-        sUiBot.assertDatasets(A);
+        mUiBot.assertDatasets(A);
 
         mActivity.onUsername((v) -> v.setText("b"));
-        sUiBot.assertDatasets(B);
+        mUiBot.assertDatasets(B);
 
         mActivity.onUsername((v) -> v.setText("c"));
-        sUiBot.assertDatasets(C);
+        mUiBot.assertDatasets(C);
+    }
+
+    @Test
+    public void filterTextUsingRegex() throws Exception {
+        // Dataset presentations.
+        final String aa = "Two A's";
+        final String ab = "A and B";
+        final String b = "Only B";
+
+        enableService();
+
+        // Set expectations.
+        sReplier.addResponse(new CannedFillResponse.Builder()
+                .addDataset(new CannedDataset.Builder()
+                        .setField(ID_USERNAME, "whatever", Pattern.compile("a|aa"))
+                        .setPresentation(createPresentation(aa))
+                        .build())
+                .addDataset(new CannedDataset.Builder()
+                        .setField(ID_USERNAME, "whatsoever", createPresentation(ab),
+                                Pattern.compile("a|ab"))
+                        .build())
+                .addDataset(new CannedDataset.Builder()
+                        .setField(ID_USERNAME, (String) null, Pattern.compile("b"))
+                        .setPresentation(createPresentation(b))
+                        .build())
+                .build());
+
+        // Trigger auto-fill.
+        mActivity.onUsername(View::requestFocus);
+        sReplier.getNextFillRequest();
+
+        // With no filter text all datasets should be shown
+        mUiBot.assertDatasets(aa, ab, b);
+
+        // Only two datasets start with 'a'
+        runShellCommand("input keyevent KEYCODE_A");
+        mUiBot.assertDatasets(aa, ab);
+
+        // Only one dataset start with 'aa'
+        runShellCommand("input keyevent KEYCODE_A");
+        mUiBot.assertDatasets(aa);
+
+        // Only two datasets start with 'a'
+        runShellCommand("input keyevent KEYCODE_DEL");
+        mUiBot.assertDatasets(aa, ab);
+
+        // With no filter text all datasets should be shown
+        runShellCommand("input keyevent KEYCODE_DEL");
+        mUiBot.assertDatasets(aa, ab, b);
+
+        // No dataset start with 'aaa'
+        runShellCommand("input keyevent KEYCODE_A");
+        runShellCommand("input keyevent KEYCODE_A");
+        runShellCommand("input keyevent KEYCODE_A");
+        mUiBot.assertNoDatasets();
     }
 
     @Test
@@ -1141,7 +1328,7 @@
         }
 
         // Sanity check.
-        sUiBot.assertNoDatasets();
+        mUiBot.assertNoDatasets();
 
         // Wait for onFill() before proceeding, otherwise the fields might be changed before
         // the session started
@@ -1157,10 +1344,11 @@
         assertWithMessage("Wrong welcome msg").that(actualMessage).isEqualTo(expectedMessage);
 
         // Assert the snack bar is shown and tap "Save".
-        sUiBot.saveForAutofill(true, SAVE_DATA_TYPE_PASSWORD);
+        mUiBot.saveForAutofill(true, SAVE_DATA_TYPE_PASSWORD);
 
         final SaveRequest saveRequest = sReplier.getNextSaveRequest();
         sReplier.assertNumberUnhandledSaveRequests(0);
+        assertThat(saveRequest.datasetIds).isNull();
 
         // Assert value of expected fields - should not be sanitized.
         try {
@@ -1193,12 +1381,12 @@
         startCheckoutActivityAsNewTask();
         try {
             // .. then the real activity being tested.
-            sUiBot.switchAppsUsingRecents();
-            sUiBot.assertShownByRelativeId(ID_USERNAME_CONTAINER);
+            mUiBot.switchAppsUsingRecents();
+            mUiBot.assertShownByRelativeId(ID_USERNAME_CONTAINER);
 
             saveGoesAway(DismissType.RECENTS_BUTTON);
         } finally {
-            CheckoutActivity.finishIt();
+            CheckoutActivity.finishIt(mUiBot);
         }
     }
 
@@ -1207,12 +1395,12 @@
         saveGoesAway(DismissType.TOUCH_OUTSIDE);
     }
 
-    private void startCheckoutActivityAsNewTask() {
+    private void startCheckoutActivityAsNewTask() throws Exception {
         final Intent intent = new Intent(mContext, CheckoutActivity.class);
         intent.setFlags(
                 Intent.FLAG_ACTIVITY_NEW_DOCUMENT | Intent.FLAG_ACTIVITY_RETAIN_IN_RECENTS);
         mContext.startActivity(intent);
-        sUiBot.assertShownByRelativeId(CheckoutActivity.ID_ADDRESS);
+        mUiBot.assertShownByRelativeId(CheckoutActivity.ID_ADDRESS);
     }
 
     private void saveGoesAway(DismissType dismissType) throws Exception {
@@ -1227,7 +1415,7 @@
         mActivity.onUsername(View::requestFocus);
 
         // Sanity check.
-        sUiBot.assertNoDatasets();
+        mUiBot.assertNoDatasets();
 
         // Wait for onFill() before proceeding, otherwise the fields might be changed before
         // the session started
@@ -1243,27 +1431,27 @@
         assertWithMessage("Wrong welcome msg").that(actualMessage).isEqualTo(expectedMessage);
 
         // Assert the snack bar is shown and tap "Save".
-        sUiBot.assertSaveShowing(SAVE_DATA_TYPE_PASSWORD);
+        mUiBot.assertSaveShowing(SAVE_DATA_TYPE_PASSWORD);
 
         // Then make sure it goes away when user doesn't want it..
         switch (dismissType) {
             case BACK_BUTTON:
-                sUiBot.pressBack();
+                mUiBot.pressBack();
                 break;
             case HOME_BUTTON:
-                sUiBot.pressHome();
+                mUiBot.pressHome();
                 break;
             case TOUCH_OUTSIDE:
-                sUiBot.assertShownByText(expectedMessage).click();
+                mUiBot.assertShownByText(expectedMessage).click();
                 break;
             case RECENTS_BUTTON:
-                sUiBot.switchAppsUsingRecents();
-                sUiBot.assertShownByRelativeId(CheckoutActivity.ID_ADDRESS);
+                mUiBot.switchAppsUsingRecents();
+                mUiBot.assertShownByRelativeId(CheckoutActivity.ID_ADDRESS);
                 break;
             default:
                 throw new IllegalArgumentException("invalid dismiss type: " + dismissType);
         }
-        sUiBot.assertSaveNotShowing(SAVE_DATA_TYPE_PASSWORD);
+        mUiBot.assertSaveNotShowing(SAVE_DATA_TYPE_PASSWORD);
     }
 
     @Test
@@ -1296,7 +1484,7 @@
         }
 
         // Sanity check.
-        sUiBot.assertNoDatasets();
+        mUiBot.assertNoDatasets();
 
         // Wait for onFill() before proceeding, otherwise the fields might be changed before
         // the session started
@@ -1312,7 +1500,7 @@
         assertWithMessage("Wrong welcome msg").that(actualMessage).isEqualTo(expectedMessage);
 
         // Assert the snack bar is shown and tap "Save".
-        sUiBot.saveForAutofill(true, SAVE_DATA_TYPE_PASSWORD);
+        mUiBot.saveForAutofill(true, SAVE_DATA_TYPE_PASSWORD);
 
         final SaveRequest saveRequest = sReplier.getNextSaveRequest();
         sReplier.assertNumberUnhandledSaveRequests(0);
@@ -1350,7 +1538,7 @@
         // Wait for onFill() before changing value, otherwise the fields might be changed before
         // the session started
         sReplier.getNextFillRequest();
-        sUiBot.assertNoDatasets();
+        mUiBot.assertNoDatasets();
 
         // Set credentials...
         mActivity.onPassword((v) -> v.setText("thou should pass")); // contains pass
@@ -1361,7 +1549,7 @@
         assertWithMessage("Wrong welcome msg").that(actualMessage).isEqualTo(expectedMessage);
 
         // Assert the snack bar is shown and tap "Save".
-        sUiBot.saveForAutofill(true, SAVE_DATA_TYPE_PASSWORD);
+        mUiBot.saveForAutofill(true, SAVE_DATA_TYPE_PASSWORD);
 
         final SaveRequest saveRequest = sReplier.getNextSaveRequest();
         sReplier.assertNumberUnhandledSaveRequests(0);
@@ -1395,7 +1583,7 @@
         mActivity.onUsername(View::requestFocus);
 
         // Sanity check.
-        sUiBot.assertNoDatasets();
+        mUiBot.assertNoDatasets();
 
         // Wait for onFill() before proceeding, otherwise the fields might be changed before
         // the session started
@@ -1412,7 +1600,7 @@
         assertWithMessage("Wrong welcome msg").that(actualMessage).isEqualTo(expectedMessage);
 
         // Assert the snack bar is shown and tap "Save".
-        sUiBot.saveForAutofill(true, SAVE_DATA_TYPE_PASSWORD);
+        mUiBot.saveForAutofill(true, SAVE_DATA_TYPE_PASSWORD);
 
         final SaveRequest saveRequest = sReplier.getNextSaveRequest();
 
@@ -1460,7 +1648,7 @@
         mActivity.onUsername(View::requestFocus);
 
         // Sanity check.
-        sUiBot.assertNoDatasets();
+        mUiBot.assertNoDatasets();
 
         // Wait for onFill() before proceeding, otherwise the fields might be changed before
         // the session started
@@ -1485,13 +1673,13 @@
         assertWithMessage("Wrong welcome msg").that(actualMessage).isEqualTo(expectedMessage);
 
         if (filledFields == FilledFields.NONE) {
-            sUiBot.assertSaveNotShowing(SAVE_DATA_TYPE_PASSWORD);
+            mUiBot.assertSaveNotShowing(SAVE_DATA_TYPE_PASSWORD);
             assertNoDanglingSessions();
             return;
         }
 
         // Assert the snack bar is shown and tap "Save".
-        sUiBot.saveForAutofill(true, SAVE_DATA_TYPE_PASSWORD);
+        mUiBot.saveForAutofill(true, SAVE_DATA_TYPE_PASSWORD);
 
         final SaveRequest saveRequest = sReplier.getNextSaveRequest();
 
@@ -1553,7 +1741,7 @@
         mActivity.onUsername(View::requestFocus);
 
         // Sanity check.
-        sUiBot.assertNoDatasets();
+        mUiBot.assertNoDatasets();
 
         // Wait for onFill() before proceeding, otherwise the fields might be changed before
         // the session started.
@@ -1569,14 +1757,50 @@
         assertWithMessage("Wrong welcome msg").that(actualMessage).isEqualTo(expectedMessage);
 
         // Assert the snack bar is shown and tap "Save".
-        final UiObject2 saveSnackBar = sUiBot.assertSaveShowing(saveDescription, type);
-        sUiBot.saveForAutofill(saveSnackBar, true);
+        final UiObject2 saveSnackBar = mUiBot.assertSaveShowing(saveDescription, type);
+        mUiBot.saveForAutofill(saveSnackBar, true);
 
         // Assert save was called.
         sReplier.getNextSaveRequest();
     }
 
     @Test
+    public void testDontTriggerSaveOnFinishWhenRequestedByFlag() throws Exception {
+        enableService();
+
+        // Set expectations.
+        sReplier.addResponse(new CannedFillResponse.Builder()
+                .setRequiredSavableIds(SAVE_DATA_TYPE_PASSWORD, ID_USERNAME, ID_PASSWORD)
+                .setSaveInfoFlags(SaveInfo.FLAG_DONT_SAVE_ON_FINISH)
+                .build());
+
+        // Trigger auto-fill.
+        mActivity.onUsername(View::requestFocus);
+
+        // Sanity check.
+        mUiBot.assertNoDatasets();
+
+        // Wait for onFill() before proceeding, otherwise the fields might be changed before
+        // the session started
+        sReplier.getNextFillRequest();
+
+        // Set credentials...
+        mActivity.onUsername((v) -> v.setText("malkovich"));
+        mActivity.onPassword((v) -> v.setText("malkovich"));
+
+        // ...and login
+        final String expectedMessage = getWelcomeMessage("malkovich");
+        final String actualMessage = mActivity.tapLogin();
+        assertWithMessage("Wrong welcome msg").that(actualMessage).isEqualTo(expectedMessage);
+
+        // Make sure it didn't trigger save.
+        mUiBot.assertSaveNotShowing(SAVE_DATA_TYPE_PASSWORD);
+
+        // Sanity check: session should have been canceled
+        assertNoDanglingSessions();
+    }
+
+    @Test
     public void testAutoFillOneDatasetAndSaveWhenFlagSecure() throws Exception {
         mActivity.setFlags(FLAG_SECURE);
         testAutoFillOneDatasetAndSave();
@@ -1633,14 +1857,14 @@
         sReplier.getNextFillRequest();
         final View username = mActivity.getUsername();
         callback.assertUiShownEvent(username);
-        sUiBot.assertDatasets("Tap to auth response");
+        mUiBot.assertDatasets("Tap to auth response");
 
         // Make sure UI is show on 2nd field as well
         final View password = mActivity.getPassword();
         mActivity.onPassword(View::requestFocus);
         callback.assertUiHiddenEvent(username);
         callback.assertUiShownEvent(password);
-        sUiBot.assertDatasets("Tap to auth response");
+        mUiBot.assertDatasets("Tap to auth response");
 
         // Now tap on 1st field to show it again...
         mActivity.onUsername(View::requestFocus);
@@ -1650,16 +1874,16 @@
         if (cancelFirstAttempt) {
             // Trigger the auth dialog, but emulate cancel.
             AuthenticationActivity.setResultCode(RESULT_CANCELED);
-            sUiBot.selectDataset("Tap to auth response");
+            mUiBot.selectDataset("Tap to auth response");
             callback.assertUiHiddenEvent(username);
             callback.assertUiShownEvent(username);
-            sUiBot.assertDatasets("Tap to auth response");
+            mUiBot.assertDatasets("Tap to auth response");
 
             // Make sure it's still shown on other fields...
             mActivity.onPassword(View::requestFocus);
             callback.assertUiHiddenEvent(username);
             callback.assertUiShownEvent(password);
-            sUiBot.assertDatasets("Tap to auth response");
+            mUiBot.assertDatasets("Tap to auth response");
 
             // Tap on 1st field to show it again...
             mActivity.onUsername(View::requestFocus);
@@ -1669,13 +1893,13 @@
 
         // ...and select it this time
         AuthenticationActivity.setResultCode(RESULT_OK);
-        sUiBot.selectDataset("Tap to auth response");
+        mUiBot.selectDataset("Tap to auth response");
         callback.assertUiHiddenEvent(username);
         callback.assertUiShownEvent(username);
-        final UiObject2 picker = sUiBot.assertDatasets("Dataset");
-        sUiBot.selectDataset(picker, "Dataset");
+        final UiObject2 picker = mUiBot.assertDatasets("Dataset");
+        mUiBot.selectDataset(picker, "Dataset");
         callback.assertUiHiddenEvent(username);
-        sUiBot.assertNoDatasets();
+        mUiBot.assertNoDatasets();
 
         // Check the results.
         mActivity.assertAutoFilled();
@@ -1722,25 +1946,25 @@
         sReplier.getNextFillRequest();
         final View username = mActivity.getUsername();
         callback.assertUiShownEvent(username);
-        sUiBot.assertDatasets("Tap to auth response");
+        mUiBot.assertDatasets("Tap to auth response");
 
         // Make sure UI is not show on 2nd field
         mActivity.onPassword(View::requestFocus);
         callback.assertUiHiddenEvent(username);
-        sUiBot.assertNoDatasets();
+        mUiBot.assertNoDatasets();
         // Now tap on 1st field to show it again...
         mActivity.onUsername(View::requestFocus);
         callback.assertUiShownEvent(username);
 
         // ...and select it this time
-        sUiBot.selectDataset("Tap to auth response");
+        mUiBot.selectDataset("Tap to auth response");
         callback.assertUiHiddenEvent(username);
-        final UiObject2 picker = sUiBot.assertDatasets("Dataset");
+        final UiObject2 picker = mUiBot.assertDatasets("Dataset");
 
         callback.assertUiShownEvent(username);
-        sUiBot.selectDataset(picker, "Dataset");
+        mUiBot.selectDataset(picker, "Dataset");
         callback.assertUiHiddenEvent(username);
-        sUiBot.assertNoDatasets();
+        mUiBot.assertNoDatasets();
 
         // Check the results.
         mActivity.assertAutoFilled();
@@ -1780,7 +2004,7 @@
         sReplier.getNextFillRequest();
         final View username = mActivity.getUsername();
         callback.assertUiShownEvent(username);
-        sUiBot.assertDatasets("Tap to auth response");
+        mUiBot.assertDatasets("Tap to auth response");
 
         // Disables autofill so it's not triggered again after the auth activity is finished
         // (and current session is canceled) and the login activity is resumed.
@@ -1790,7 +2014,7 @@
         final CountDownLatch latch = new CountDownLatch(1);
         AuthenticationActivity.setResultCode(latch, RESULT_OK);
 
-        sUiBot.selectDataset("Tap to auth response");
+        mUiBot.selectDataset("Tap to auth response");
         callback.assertUiHiddenEvent(username);
 
         // Cancel session...
@@ -1799,7 +2023,7 @@
         // ...before finishing the Auth UI.
         latch.countDown();
 
-        sUiBot.assertNoDatasets();
+        mUiBot.assertNoDatasets();
     }
 
     @Test
@@ -1842,9 +2066,112 @@
         callback.assertUiShownEvent(username);
 
         // Select the authentication dialog.
-        sUiBot.selectDataset("Tap to auth response");
+        mUiBot.selectDataset("Tap to auth response");
         callback.assertUiHiddenEvent(username);
-        sUiBot.assertNoDatasets();
+        mUiBot.assertNoDatasets();
+    }
+
+    @Test
+    public void testFillResponseAuthClientStateSetOnIntentOnly() throws Exception {
+        fillResponseAuthWithClientState(ClientStateLocation.INTENT_ONLY);
+    }
+
+    @Test
+    public void testFillResponseAuthClientStateSetOnFillResponseOnly() throws Exception {
+        fillResponseAuthWithClientState(ClientStateLocation.FILL_RESPONSE_ONLY);
+    }
+
+    @Test
+    public void testFillResponseAuthClientStateSetOnIntentAndFillResponse() throws Exception {
+        fillResponseAuthWithClientState(ClientStateLocation.BOTH);
+    }
+
+    enum ClientStateLocation {
+        INTENT_ONLY,
+        FILL_RESPONSE_ONLY,
+        BOTH
+    }
+
+    private void fillResponseAuthWithClientState(ClientStateLocation where) throws Exception {
+        // Set service.
+        enableService();
+
+        // Prepare the authenticated response
+        final CannedFillResponse.Builder authenticatedResponseBuilder =
+                new CannedFillResponse.Builder()
+                .setRequiredSavableIds(SAVE_DATA_TYPE_PASSWORD, ID_USERNAME, ID_PASSWORD)
+                .addDataset(new CannedDataset.Builder()
+                        .setField(ID_USERNAME, "dude")
+                        .setField(ID_PASSWORD, "sweet")
+                        .setPresentation(createPresentation("Dataset"))
+                        .build());
+
+        if (where == ClientStateLocation.FILL_RESPONSE_ONLY || where == ClientStateLocation.BOTH) {
+            authenticatedResponseBuilder.setExtras(newClientState("CSI", "FromAuthResponse"));
+        }
+
+        final IntentSender authentication = where == ClientStateLocation.FILL_RESPONSE_ONLY
+                ? AuthenticationActivity.createSender(mContext, 1,
+                        authenticatedResponseBuilder.build())
+                : AuthenticationActivity.createSender(mContext, 1,
+                        authenticatedResponseBuilder.build(), newClientState("CSI", "FromIntent"));
+
+        // Configure the service behavior
+        sReplier.addResponse(new CannedFillResponse.Builder()
+                .setAuthentication(authentication, ID_USERNAME)
+                .setIgnoreFields(ID_PASSWORD)
+                .setPresentation(createPresentation("Tap to auth response"))
+                .setExtras(newClientState("CSI", "FromResponse"))
+                .build());
+
+        // Set expectation for the activity
+        mActivity.expectAutoFill("dude", "sweet");
+
+        // Trigger autofill.
+        mActivity.onUsername(View::requestFocus);
+        sReplier.getNextFillRequest();
+
+        // Tap authentication request.
+        mUiBot.selectDataset("Tap to auth response");
+
+        // Tap dataset.
+        mUiBot.selectDataset("Dataset");
+
+        // Check the results.
+        mActivity.assertAutoFilled();
+
+        // Now trigger save.
+        mActivity.onUsername((v) -> v.setText("malkovich"));
+        mActivity.onPassword((v) -> v.setText("malkovich"));
+        final String expectedMessage = getWelcomeMessage("malkovich");
+        final String actualMessage = mActivity.tapLogin();
+        assertWithMessage("Wrong welcome msg").that(actualMessage).isEqualTo(expectedMessage);
+        mUiBot.saveForAutofill(true, SAVE_DATA_TYPE_PASSWORD);
+
+        // Assert client state on authentication activity.
+        assertClientState("auth activity", AuthenticationActivity.getData(), "CSI", "FromResponse");
+
+        // Assert client state on save request.
+        final SaveRequest saveRequest = sReplier.getNextSaveRequest();
+        final String expectedValue = where == ClientStateLocation.FILL_RESPONSE_ONLY
+                ? "FromAuthResponse" : "FromIntent";
+        assertClientState("on save", saveRequest.data, "CSI", expectedValue);
+    }
+
+    // TODO(on master): move to helper / reuse in other places
+    private void assertClientState(String where, Bundle data, String expectedKey,
+            String expectedValue) {
+        assertWithMessage("no client state on %s", where).that(data).isNotNull();
+        final String extraValue = data.getString(expectedKey);
+        assertWithMessage("invalid value for %s on %s", expectedKey, where)
+            .that(extraValue).isEqualTo(expectedValue);
+    }
+
+    // TODO(on master): move to helper / reuse in other places
+    private Bundle newClientState(String key, String value) {
+        final Bundle clientState = new Bundle();
+        clientState.putString(key, value);
+        return clientState;
     }
 
     @Test
@@ -1885,27 +2212,27 @@
 
         // Make sure it's showing initially...
         callback.assertUiShownEvent(username);
-        sUiBot.assertDatasets("Tap to auth response");
+        mUiBot.assertDatasets("Tap to auth response");
 
         // ..then type something to hide it.
         runShellCommand("input keyevent KEYCODE_A");
         callback.assertUiHiddenEvent(username);
-        sUiBot.assertNoDatasets();
+        mUiBot.assertNoDatasets();
 
         // Now delete the char and assert it's shown again...
         runShellCommand("input keyevent KEYCODE_DEL");
         callback.assertUiShownEvent(username);
-        sUiBot.assertDatasets("Tap to auth response");
+        mUiBot.assertDatasets("Tap to auth response");
 
         // ...and select it this time
         AuthenticationActivity.setResultCode(RESULT_OK);
-        sUiBot.selectDataset("Tap to auth response");
+        mUiBot.selectDataset("Tap to auth response");
         callback.assertUiHiddenEvent(username);
         callback.assertUiShownEvent(username);
-        final UiObject2 picker = sUiBot.assertDatasets("Dataset");
-        sUiBot.selectDataset(picker, "Dataset");
+        final UiObject2 picker = mUiBot.assertDatasets("Dataset");
+        mUiBot.selectDataset(picker, "Dataset");
         callback.assertUiHiddenEvent(username);
-        sUiBot.assertNoDatasets();
+        mUiBot.assertNoDatasets();
 
         // Check the results.
         mActivity.assertAutoFilled();
@@ -1927,10 +2254,6 @@
     }
 
     private void datasetAuthTwoFields(boolean cancelFirstAttempt) throws Exception {
-        // TODO: current API requires these fields...
-        final RemoteViews bogusPresentation = createPresentation("Whatever man, I'm not used...");
-        final String bogusValue = "Y U REQUIRE IT?";
-
         // Set service.
         enableService();
         final MyAutofillCallback callback = mActivity.registerCallback();
@@ -1940,14 +2263,13 @@
                 new CannedDataset.Builder()
                         .setField(ID_USERNAME, "dude")
                         .setField(ID_PASSWORD, "sweet")
-                        .setPresentation(bogusPresentation)
                         .build());
 
         // Configure the service behavior
         sReplier.addResponse(new CannedFillResponse.Builder()
                 .addDataset(new CannedDataset.Builder()
-                        .setField(ID_USERNAME, bogusValue)
-                        .setField(ID_PASSWORD, bogusValue)
+                        .setField(ID_USERNAME, UNUSED_AUTOFILL_VALUE)
+                        .setField(ID_PASSWORD, UNUSED_AUTOFILL_VALUE)
                         .setPresentation(createPresentation("Tap to auth dataset"))
                         .setAuthentication(authentication)
                         .build())
@@ -1963,34 +2285,34 @@
         sReplier.getNextFillRequest();
         final View username = mActivity.getUsername();
         callback.assertUiShownEvent(username);
-        sUiBot.assertDatasets("Tap to auth dataset");
+        mUiBot.assertDatasets("Tap to auth dataset");
 
         // Make sure UI is show on 2nd field as well
         final View password = mActivity.getPassword();
         mActivity.onPassword(View::requestFocus);
         callback.assertUiHiddenEvent(username);
         callback.assertUiShownEvent(password);
-        sUiBot.assertDatasets("Tap to auth dataset");
+        mUiBot.assertDatasets("Tap to auth dataset");
 
         // Now tap on 1st field to show it again...
         mActivity.onUsername(View::requestFocus);
         callback.assertUiHiddenEvent(password);
         callback.assertUiShownEvent(username);
-        sUiBot.assertDatasets("Tap to auth dataset");
+        mUiBot.assertDatasets("Tap to auth dataset");
 
         if (cancelFirstAttempt) {
             // Trigger the auth dialog, but emulate cancel.
             AuthenticationActivity.setResultCode(RESULT_CANCELED);
-            sUiBot.selectDataset("Tap to auth dataset");
+            mUiBot.selectDataset("Tap to auth dataset");
             callback.assertUiHiddenEvent(username);
             callback.assertUiShownEvent(username);
-            sUiBot.assertDatasets("Tap to auth dataset");
+            mUiBot.assertDatasets("Tap to auth dataset");
 
             // Make sure it's still shown on other fields...
             mActivity.onPassword(View::requestFocus);
             callback.assertUiHiddenEvent(username);
             callback.assertUiShownEvent(password);
-            sUiBot.assertDatasets("Tap to auth dataset");
+            mUiBot.assertDatasets("Tap to auth dataset");
 
             // Tap on 1st field to show it again...
             mActivity.onUsername(View::requestFocus);
@@ -2000,9 +2322,9 @@
 
         // ...and select it this time
         AuthenticationActivity.setResultCode(RESULT_OK);
-        sUiBot.selectDataset("Tap to auth dataset");
+        mUiBot.selectDataset("Tap to auth dataset");
         callback.assertUiHiddenEvent(username);
-        sUiBot.assertNoDatasets();
+        mUiBot.assertNoDatasets();
 
         // Check the results.
         mActivity.assertAutoFilled();
@@ -2051,14 +2373,14 @@
 
         // Authenticate
         callback.assertUiShownEvent(username);
-        sUiBot.selectDataset("Tap to auth dataset");
+        mUiBot.selectDataset("Tap to auth dataset");
         callback.assertUiHiddenEvent(username);
 
         // Select a dataset from the new response
         callback.assertUiShownEvent(username);
-        sUiBot.selectDataset("Dataset");
+        mUiBot.selectDataset("Dataset");
         callback.assertUiHiddenEvent(username);
-        sUiBot.assertNoDatasets();
+        mUiBot.assertNoDatasets();
 
         // Check the results.
         mActivity.assertAutoFilled();
@@ -2080,7 +2402,6 @@
                 new CannedDataset.Builder()
                         .setField(ID_USERNAME, "dude")
                         .setField(ID_PASSWORD, "sweet")
-                        .setPresentation(createPresentation("Dataset"))
                         .build());
 
         // Configure the service behavior
@@ -2105,9 +2426,9 @@
 
         // Authenticate
         callback.assertUiShownEvent(username);
-        sUiBot.selectDataset("Tap to auth dataset");
+        mUiBot.selectDataset("Tap to auth dataset");
         callback.assertUiHiddenEvent(username);
-        sUiBot.assertNoDatasets();
+        mUiBot.assertNoDatasets();
 
         // Check the results.
         mActivity.assertAutoFilled();
@@ -2115,10 +2436,6 @@
 
     @Test
     public void testDatasetAuthTwoDatasets() throws Exception {
-        // TODO: current API requires these fields...
-        final RemoteViews bogusPresentation = createPresentation("Whatever man, I'm not used...");
-        final String bogusValue = "Y U REQUIRE IT?";
-
         // Set service.
         enableService();
         final MyAutofillCallback callback = mActivity.registerCallback();
@@ -2127,7 +2444,6 @@
         final CannedDataset unlockedDataset = new CannedDataset.Builder()
                 .setField(ID_USERNAME, "dude")
                 .setField(ID_PASSWORD, "sweet")
-                .setPresentation(bogusPresentation)
                 .build();
         final IntentSender authentication1 = AuthenticationActivity.createSender(mContext, 1,
                 unlockedDataset);
@@ -2137,14 +2453,14 @@
         // Configure the service behavior
         sReplier.addResponse(new CannedFillResponse.Builder()
                 .addDataset(new CannedDataset.Builder()
-                        .setField(ID_USERNAME, bogusValue)
-                        .setField(ID_PASSWORD, bogusValue)
+                        .setField(ID_USERNAME, UNUSED_AUTOFILL_VALUE)
+                        .setField(ID_PASSWORD, UNUSED_AUTOFILL_VALUE)
                         .setPresentation(createPresentation("Tap to auth dataset 1"))
                         .setAuthentication(authentication1)
                         .build())
                 .addDataset(new CannedDataset.Builder()
-                        .setField(ID_USERNAME, bogusValue)
-                        .setField(ID_PASSWORD, bogusValue)
+                        .setField(ID_USERNAME, UNUSED_AUTOFILL_VALUE)
+                        .setField(ID_PASSWORD, UNUSED_AUTOFILL_VALUE)
                         .setPresentation(createPresentation("Tap to auth dataset 2"))
                         .setAuthentication(authentication2)
                         .build())
@@ -2162,11 +2478,11 @@
 
         // Authenticate
         callback.assertUiShownEvent(username);
-        sUiBot.assertDatasets("Tap to auth dataset 1", "Tap to auth dataset 2");
+        mUiBot.assertDatasets("Tap to auth dataset 1", "Tap to auth dataset 2");
 
-        sUiBot.selectDataset("Tap to auth dataset 1");
+        mUiBot.selectDataset("Tap to auth dataset 1");
         callback.assertUiHiddenEvent(username);
-        sUiBot.assertNoDatasets();
+        mUiBot.assertNoDatasets();
 
         // Check the results.
         mActivity.assertAutoFilled();
@@ -2192,7 +2508,6 @@
                 new CannedDataset.Builder()
                         .setField(ID_USERNAME, "dude")
                         .setField(ID_PASSWORD, "sweet")
-                        .setPresentation(createPresentation("Dataset"))
                         .build());
 
         // Configure the service behavior
@@ -2226,23 +2541,19 @@
 
         // Authenticate
         callback.assertUiShownEvent(username);
-        sUiBot.assertDatasets("Tap to auth dataset", "What, me auth?");
+        mUiBot.assertDatasets("Tap to auth dataset", "What, me auth?");
 
         final String chosenOne = selectAuth ? "Tap to auth dataset" : "What, me auth?";
-        sUiBot.selectDataset(chosenOne);
+        mUiBot.selectDataset(chosenOne);
         callback.assertUiHiddenEvent(username);
-        sUiBot.assertNoDatasets();
+        mUiBot.assertNoDatasets();
 
         // Check the results.
         mActivity.assertAutoFilled();
     }
 
     @Test
-    public void testDatasetAuthFiltering() throws Exception {
-        // TODO: current API requires these fields...
-        final RemoteViews bogusPresentation = createPresentation("Whatever man, I'm not used...");
-        final String bogusValue = "Y U REQUIRE IT?";
-
+    public void testDatasetAuthNoFiltering() throws Exception {
         // Set service.
         enableService();
         final MyAutofillCallback callback = mActivity.registerCallback();
@@ -2251,7 +2562,6 @@
         final CannedDataset unlockedDataset = new CannedDataset.Builder()
                 .setField(ID_USERNAME, "dude")
                 .setField(ID_PASSWORD, "sweet")
-                .setPresentation(bogusPresentation)
                 .build();
         final IntentSender authentication = AuthenticationActivity.createSender(mContext, 1,
                 unlockedDataset);
@@ -2259,8 +2569,8 @@
         // Configure the service behavior
         sReplier.addResponse(new CannedFillResponse.Builder()
                 .addDataset(new CannedDataset.Builder()
-                        .setField(ID_USERNAME, bogusValue)
-                        .setField(ID_PASSWORD, bogusValue)
+                        .setField(ID_USERNAME, UNUSED_AUTOFILL_VALUE)
+                        .setField(ID_PASSWORD, UNUSED_AUTOFILL_VALUE)
                         .setPresentation(createPresentation("Tap to auth dataset"))
                         .setAuthentication(authentication)
                         .build())
@@ -2278,22 +2588,177 @@
 
         // Make sure it's showing initially...
         callback.assertUiShownEvent(username);
-        sUiBot.assertDatasets("Tap to auth dataset");
+        mUiBot.assertDatasets("Tap to auth dataset");
 
         // ..then type something to hide it.
         runShellCommand("input keyevent KEYCODE_A");
         callback.assertUiHiddenEvent(username);
-        sUiBot.assertNoDatasets();
+        mUiBot.assertNoDatasets();
 
         // Now delete the char and assert it's shown again...
         runShellCommand("input keyevent KEYCODE_DEL");
         callback.assertUiShownEvent(username);
-        sUiBot.assertDatasets("Tap to auth dataset");
+        mUiBot.assertDatasets("Tap to auth dataset");
 
         // ...and select it this time
-        sUiBot.selectDataset("Tap to auth dataset");
+        mUiBot.selectDataset("Tap to auth dataset");
         callback.assertUiHiddenEvent(username);
-        sUiBot.assertNoDatasets();
+        mUiBot.assertNoDatasets();
+
+        // Check the results.
+        mActivity.assertAutoFilled();
+    }
+
+    @Test
+    public void testDatasetAuthFilteringUsingAutofillValue() throws Exception {
+        // Set service.
+        enableService();
+        final MyAutofillCallback callback = mActivity.registerCallback();
+
+        // Create the authentication intents
+        final CannedDataset unlockedDataset = new CannedDataset.Builder()
+                .setField(ID_USERNAME, "dude")
+                .setField(ID_PASSWORD, "sweet")
+                .build();
+        final IntentSender authentication = AuthenticationActivity.createSender(mContext, 1,
+                unlockedDataset);
+
+        // Configure the service behavior
+        sReplier.addResponse(new CannedFillResponse.Builder()
+                .addDataset(new CannedDataset.Builder()
+                        .setField(ID_USERNAME, "dude")
+                        .setField(ID_PASSWORD, "sweet")
+                        .setPresentation(createPresentation("DS1"))
+                        .setAuthentication(authentication)
+                        .build())
+                .addDataset(new CannedDataset.Builder()
+                        .setField(ID_USERNAME, "DUDE,THE")
+                        .setField(ID_PASSWORD, "SWEET")
+                        .setPresentation(createPresentation("DS2"))
+                        .setAuthentication(authentication)
+                        .build())
+                .addDataset(new CannedDataset.Builder()
+                        .setField(ID_USERNAME, "ZzBottom")
+                        .setField(ID_PASSWORD, "top")
+                        .setPresentation(createPresentation("DS3"))
+                        .setAuthentication(authentication)
+                        .build())
+                .build());
+
+        // Set expectation for the activity
+        mActivity.expectAutoFill("dude", "sweet");
+
+        // Trigger auto-fill.
+        mActivity.onUsername(View::requestFocus);
+
+        // Wait for onFill() before proceeding.
+        sReplier.getNextFillRequest();
+        final View username = mActivity.getUsername();
+
+        // Make sure it's showing initially...
+        callback.assertUiShownEvent(username);
+        mUiBot.assertDatasets("DS1", "DS2", "DS3");
+
+        // ...then type something to hide them.
+        runShellCommand("input keyevent KEYCODE_A");
+        callback.assertUiHiddenEvent(username);
+        mUiBot.assertNoDatasets();
+
+        // Now delete the char and assert they're shown again...
+        runShellCommand("input keyevent KEYCODE_DEL");
+        callback.assertUiShownEvent(username);
+        mUiBot.assertDatasets("DS1", "DS2", "DS3");
+
+        // ...then filter for 2
+        runShellCommand("input keyevent KEYCODE_D");
+        mUiBot.assertDatasets("DS1", "DS2");
+
+        // ...up to 1
+        runShellCommand("input keyevent KEYCODE_U");
+        mUiBot.assertDatasets("DS1", "DS2");
+        runShellCommand("input keyevent KEYCODE_D");
+        mUiBot.assertDatasets("DS1", "DS2");
+        runShellCommand("input keyevent KEYCODE_E");
+        mUiBot.assertDatasets("DS1", "DS2");
+        runShellCommand("input keyevent KEYCODE_COMMA");
+        mUiBot.assertDatasets("DS2");
+
+        // Now delete the char and assert 2 are shown again...
+        runShellCommand("input keyevent KEYCODE_DEL");
+        final UiObject2 picker = mUiBot.assertDatasets("DS1", "DS2");
+
+        // ...and select it this time
+        mUiBot.selectDataset(picker, "DS1");
+        callback.assertUiHiddenEvent(username);
+        mUiBot.assertNoDatasets();
+
+        // Check the results.
+        mActivity.assertAutoFilled();
+    }
+
+    @Test
+    public void testDatasetAuthFilteringUsingRegex() throws Exception {
+        // Set service.
+        enableService();
+        final MyAutofillCallback callback = mActivity.registerCallback();
+
+        // Create the authentication intents
+        final CannedDataset unlockedDataset = new CannedDataset.Builder()
+                .setField(ID_USERNAME, "dude")
+                .setField(ID_PASSWORD, "sweet")
+                .build();
+        final IntentSender authentication = AuthenticationActivity.createSender(mContext, 1,
+                unlockedDataset);
+
+        // Configure the service behavior
+
+        final Pattern min2Chars = Pattern.compile(".{2,}");
+        sReplier.addResponse(new CannedFillResponse.Builder()
+                .addDataset(new CannedDataset.Builder()
+                        .setField(ID_USERNAME, UNUSED_AUTOFILL_VALUE, min2Chars)
+                        .setField(ID_PASSWORD, UNUSED_AUTOFILL_VALUE)
+                        .setPresentation(createPresentation("Tap to auth dataset"))
+                        .setAuthentication(authentication)
+                        .build())
+                .build());
+
+        // Set expectation for the activity
+        mActivity.expectAutoFill("dude", "sweet");
+
+        // Trigger auto-fill.
+        mActivity.onUsername(View::requestFocus);
+
+        // Wait for onFill() before proceeding.
+        sReplier.getNextFillRequest();
+        final View username = mActivity.getUsername();
+
+        // Make sure it's showing initially...
+        callback.assertUiShownEvent(username);
+        mUiBot.assertDatasets("Tap to auth dataset");
+
+        // ...then type something to hide it.
+        runShellCommand("input keyevent KEYCODE_A");
+        callback.assertUiHiddenEvent(username);
+        mUiBot.assertNoDatasets();
+
+        // ...now type something again to show it, as the input will have 2 chars.
+        runShellCommand("input keyevent KEYCODE_A");
+        callback.assertUiShownEvent(username);
+        mUiBot.assertDatasets("Tap to auth dataset");
+
+        // Delete the char and assert it's not shown again...
+        runShellCommand("input keyevent KEYCODE_DEL");
+        callback.assertUiHiddenEvent(username);
+        mUiBot.assertNoDatasets();
+
+        // ...then type something again to show it, as the input will have 2 chars.
+        runShellCommand("input keyevent KEYCODE_A");
+        callback.assertUiShownEvent(username);
+
+        // ...and select it this time
+        mUiBot.selectDataset("Tap to auth dataset");
+        callback.assertUiHiddenEvent(username);
+        mUiBot.assertNoDatasets();
 
         // Check the results.
         mActivity.assertAutoFilled();
@@ -2310,10 +2775,6 @@
     }
 
     private void datasetAuthMixedFilteringTest(boolean selectAuth) throws Exception {
-        // TODO: current API requires these fields...
-        final RemoteViews bogusPresentation = createPresentation("Whatever man, I'm not used...");
-        final String bogusValue = "Y U REQUIRE IT?";
-
         // Set service.
         enableService();
         final MyAutofillCallback callback = mActivity.registerCallback();
@@ -2322,7 +2783,6 @@
         final CannedDataset unlockedDataset = new CannedDataset.Builder()
                 .setField(ID_USERNAME, "DUDE")
                 .setField(ID_PASSWORD, "SWEET")
-                .setPresentation(bogusPresentation)
                 .build();
         final IntentSender authentication = AuthenticationActivity.createSender(mContext, 1,
                 unlockedDataset);
@@ -2330,8 +2790,8 @@
         // Configure the service behavior
         sReplier.addResponse(new CannedFillResponse.Builder()
                 .addDataset(new CannedDataset.Builder()
-                        .setField(ID_USERNAME, bogusValue)
-                        .setField(ID_PASSWORD, bogusValue)
+                        .setField(ID_USERNAME, UNUSED_AUTOFILL_VALUE)
+                        .setField(ID_PASSWORD, UNUSED_AUTOFILL_VALUE)
                         .setPresentation(createPresentation("Tap to auth dataset"))
                         .setAuthentication(authentication)
                         .build())
@@ -2358,37 +2818,110 @@
 
         // Make sure it's showing initially...
         callback.assertUiShownEvent(username);
-        sUiBot.assertDatasets("Tap to auth dataset", "What, me auth?");
+        mUiBot.assertDatasets("Tap to auth dataset", "What, me auth?");
 
         // Filter the auth dataset.
         runShellCommand("input keyevent KEYCODE_D");
-        sUiBot.assertDatasets("What, me auth?");
+        mUiBot.assertDatasets("What, me auth?");
 
         // Filter all.
         runShellCommand("input keyevent KEYCODE_W");
         callback.assertUiHiddenEvent(username);
-        sUiBot.assertNoDatasets();
+        mUiBot.assertNoDatasets();
 
         // Now delete the char and assert the non-auth is shown again.
         runShellCommand("input keyevent KEYCODE_DEL");
         callback.assertUiShownEvent(username);
-        sUiBot.assertDatasets("What, me auth?");
+        mUiBot.assertDatasets("What, me auth?");
 
         // Delete again and assert all dataset are shown.
         runShellCommand("input keyevent KEYCODE_DEL");
-        sUiBot.assertDatasets("Tap to auth dataset", "What, me auth?");
+        mUiBot.assertDatasets("Tap to auth dataset", "What, me auth?");
 
         // ...and select it this time
         final String chosenOne = selectAuth ? "Tap to auth dataset" : "What, me auth?";
-        sUiBot.selectDataset(chosenOne);
+        mUiBot.selectDataset(chosenOne);
         callback.assertUiHiddenEvent(username);
-        sUiBot.assertNoDatasets();
+        mUiBot.assertNoDatasets();
 
         // Check the results.
         mActivity.assertAutoFilled();
     }
 
     @Test
+    public void testDatasetAuthClientStateSetOnIntentOnly() throws Exception {
+        fillDatasetAuthWithClientState(ClientStateLocation.INTENT_ONLY);
+    }
+
+    @Test
+    public void testDatasetAuthClientStateSetOnFillResponseOnly() throws Exception {
+        fillDatasetAuthWithClientState(ClientStateLocation.FILL_RESPONSE_ONLY);
+    }
+
+    @Test
+    public void testDatasetAuthClientStateSetOnIntentAndFillResponse() throws Exception {
+        fillDatasetAuthWithClientState(ClientStateLocation.BOTH);
+    }
+
+    private void fillDatasetAuthWithClientState(ClientStateLocation where) throws Exception {
+        // Set service.
+        enableService();
+
+        // Prepare the authenticated response
+        final CannedDataset dataset = new CannedDataset.Builder()
+                .setField(ID_USERNAME, "dude")
+                .setField(ID_PASSWORD, "sweet")
+                .build();
+        final IntentSender authentication = where == ClientStateLocation.FILL_RESPONSE_ONLY
+                ? AuthenticationActivity.createSender(mContext, 1,
+                        dataset)
+                : AuthenticationActivity.createSender(mContext, 1,
+                        dataset, newClientState("CSI", "FromIntent"));
+
+        // Configure the service behavior
+        sReplier.addResponse(new CannedFillResponse.Builder()
+                .setRequiredSavableIds(SAVE_DATA_TYPE_PASSWORD, ID_USERNAME, ID_PASSWORD)
+                .setExtras(newClientState("CSI", "FromResponse"))
+                .addDataset(new CannedDataset.Builder()
+                        .setField(ID_USERNAME, UNUSED_AUTOFILL_VALUE)
+                        .setField(ID_PASSWORD, UNUSED_AUTOFILL_VALUE)
+                        .setPresentation(createPresentation("Tap to auth dataset"))
+                        .setAuthentication(authentication)
+                        .build())
+                .build());
+
+        // Set expectation for the activity
+        mActivity.expectAutoFill("dude", "sweet");
+
+        // Trigger auto-fill.
+        mActivity.onUsername(View::requestFocus);
+        sReplier.getNextFillRequest();
+
+        // Tap authentication request.
+        mUiBot.selectDataset("Tap to auth dataset");
+
+        // Check the results.
+        mActivity.assertAutoFilled();
+
+        // Now trigger save.
+        mActivity.onUsername((v) -> v.setText("malkovich"));
+        mActivity.onPassword((v) -> v.setText("malkovich"));
+        final String expectedMessage = getWelcomeMessage("malkovich");
+        final String actualMessage = mActivity.tapLogin();
+        assertWithMessage("Wrong welcome msg").that(actualMessage).isEqualTo(expectedMessage);
+        mUiBot.saveForAutofill(true, SAVE_DATA_TYPE_PASSWORD);
+
+        // Assert client state on authentication activity.
+        assertClientState("auth activity", AuthenticationActivity.getData(), "CSI", "FromResponse");
+
+        // Assert client state on save request.
+        final SaveRequest saveRequest = sReplier.getNextSaveRequest();
+        final String expectedValue = where == ClientStateLocation.FILL_RESPONSE_ONLY
+                ? "FromResponse" : "FromIntent";
+        assertClientState("on save", saveRequest.data, "CSI", expectedValue);
+    }
+
+    @Test
     public void testDisableSelf() throws Exception {
         enableService();
 
@@ -2440,7 +2973,7 @@
         }, intentFilter);
 
         // Trigger the negative button.
-        sUiBot.saveForAutofill(SaveInfo.NEGATIVE_BUTTON_STYLE_REJECT,
+        mUiBot.saveForAutofill(SaveInfo.NEGATIVE_BUTTON_STYLE_REJECT,
                 false, SAVE_DATA_TYPE_PASSWORD);
 
         // Wait for the custom action.
@@ -2489,7 +3022,7 @@
         }, intentFilter);
 
         // Trigger the negative button.
-        sUiBot.saveForAutofill(SaveInfo.NEGATIVE_BUTTON_STYLE_CANCEL,
+        mUiBot.saveForAutofill(SaveInfo.NEGATIVE_BUTTON_STYLE_CANCEL,
                 false, SAVE_DATA_TYPE_PASSWORD);
 
         // Wait for the custom action.
@@ -2531,7 +3064,7 @@
         // Trigger auto-fill.
         mActivity.onUsername(View::requestFocus);
 
-        sUiBot.assertNoDatasets();
+        mUiBot.assertNoDatasets();
 
         final FillRequest fillRequest = sReplier.getNextFillRequest();
 
@@ -2576,13 +3109,13 @@
         mActivity.expectAutoFill("dude", "sweet");
 
         // Explicitly uses the contextual menu to test that functionality.
-        sUiBot.getAutofillMenuOption(ID_USERNAME).click();
+        mUiBot.getAutofillMenuOption(ID_USERNAME).click();
 
         final FillRequest fillRequest = sReplier.getNextFillRequest();
-        assertThat(fillRequest.flags).isEqualTo(FLAG_MANUAL_REQUEST);
+        assertHasFlags(fillRequest.flags, FLAG_MANUAL_REQUEST);
 
         // Should have been automatically filled.
-        sUiBot.selectDataset("The Dude");
+        mUiBot.selectDataset("The Dude");
 
         // Check the results.
         mActivity.assertAutoFilled();
@@ -2626,11 +3159,11 @@
         mActivity.forceAutofillOnUsername();
 
         final FillRequest fillRequest = sReplier.getNextFillRequest();
-        assertThat(fillRequest.flags).isEqualTo(FLAG_MANUAL_REQUEST);
+        assertHasFlags(fillRequest.flags, FLAG_MANUAL_REQUEST);
 
         // Auto-fill it.
-        final UiObject2 picker = sUiBot.assertDatasets("The Dude", "Jenny");
-        sUiBot.selectDataset(picker, pickFirst ? "The Dude" : "Jenny");
+        final UiObject2 picker = mUiBot.assertDatasets("The Dude", "Jenny");
+        mUiBot.selectDataset(picker, pickFirst ? "The Dude" : "Jenny");
 
         // Check the results.
         mActivity.assertAutoFilled();
@@ -2657,14 +3190,14 @@
         mActivity.forceAutofillOnUsername();
 
         final FillRequest fillRequest = sReplier.getNextFillRequest();
-        assertThat(fillRequest.flags).isEqualTo(FLAG_MANUAL_REQUEST);
+        assertHasFlags(fillRequest.flags, FLAG_MANUAL_REQUEST);
         // Username value should be available because it triggered the manual request...
         assertValue(fillRequest.structure, ID_USERNAME, "dud");
         // ... but password didn't
         assertTextIsSanitized(fillRequest.structure, ID_PASSWORD);
 
         // Selects the dataset.
-        sUiBot.selectDataset("The Dude");
+        mUiBot.selectDataset("The Dude");
 
         // Check the results.
         mActivity.assertAutoFilled();
@@ -2696,7 +3229,7 @@
         assertTextIsSanitized(fillRequest1.structure, ID_PASSWORD);
 
         // Select it.
-        sUiBot.selectDataset("The Dude");
+        mUiBot.selectDataset("The Dude");
 
         // Check the results.
         mActivity.assertAutoFilled();
@@ -2719,12 +3252,12 @@
 
         // Assert request.
         final FillRequest fillRequest2 = sReplier.getNextFillRequest();
-        assertThat(fillRequest2.flags).isEqualTo(FLAG_MANUAL_REQUEST);
+        assertHasFlags(fillRequest2.flags, FLAG_MANUAL_REQUEST);
         assertValue(fillRequest2.structure, ID_USERNAME, "dude");
         assertTextIsSanitized(fillRequest2.structure, ID_PASSWORD);
 
         // Select it.
-        sUiBot.selectDataset("THE DUDE");
+        mUiBot.selectDataset("THE DUDE");
 
         // Check the results.
         mActivity.assertAutoFilled();
@@ -2751,12 +3284,12 @@
 
         // Assert request.
         final FillRequest fillRequest1 = sReplier.getNextFillRequest();
-        assertThat(fillRequest1.flags).isEqualTo(FLAG_MANUAL_REQUEST);
+        assertHasFlags(fillRequest1.flags, FLAG_MANUAL_REQUEST);
         assertValue(fillRequest1.structure, ID_USERNAME, "");
         assertTextIsSanitized(fillRequest1.structure, ID_PASSWORD);
 
         // Select it.
-        sUiBot.selectDataset("The Dude");
+        mUiBot.selectDataset("The Dude");
 
         // Check the results.
         mActivity.assertAutoFilled();
@@ -2779,12 +3312,12 @@
 
         // Assert request.
         final FillRequest fillRequest2 = sReplier.getNextFillRequest();
-        assertThat(fillRequest2.flags).isEqualTo(FLAG_MANUAL_REQUEST);
+        assertHasFlags(fillRequest2.flags, FLAG_MANUAL_REQUEST);
         assertValue(fillRequest2.structure, ID_USERNAME, "dude");
         assertTextIsSanitized(fillRequest2.structure, ID_PASSWORD);
 
         // Select it.
-        sUiBot.selectDataset("THE DUDE");
+        mUiBot.selectDataset("THE DUDE");
 
         // Check the results.
         mActivity.assertAutoFilled();
@@ -2811,7 +3344,7 @@
                 mActivity.onUsername(View::requestFocus);
 
                 // Sanity check.
-                sUiBot.assertNoDatasets();
+                mUiBot.assertNoDatasets();
 
                 // Wait for onFill() before proceeding, otherwise the fields might be changed before
                 // the session started
@@ -2829,7 +3362,7 @@
                 mActivity.tapSave();
 
                 // Assert the snack bar is shown and tap "Save".
-                sUiBot.saveForAutofill(true, SAVE_DATA_TYPE_PASSWORD);
+                mUiBot.saveForAutofill(true, SAVE_DATA_TYPE_PASSWORD);
 
                 final SaveRequest saveRequest = sReplier.getNextSaveRequest();
 
@@ -2874,7 +3407,7 @@
                 sReplier.getNextFillRequest();
 
                 // Auto-fill it.
-                sUiBot.selectDataset("The Dude");
+                mUiBot.selectDataset("The Dude");
 
                 // Check the results.
                 mActivity.assertAutoFilled();
@@ -2925,421 +3458,19 @@
         sReplier.getNextFillRequest();
 
         // Click on the custom button
-        sUiBot.selectByText("Poke");
+        mUiBot.selectByText("Poke");
 
         // Make sure the click worked
-        sUiBot.selectByText("foo");
+        mUiBot.selectByText("foo");
 
         // Go back to the filled app.
-        sUiBot.pressBack();
+        mUiBot.pressBack();
 
         // The session should be gone
         assertNoDanglingSessions();
     }
 
     @Test
-    public void checkFillSelectionAfterSelectingDatasetAuthentication() throws Exception {
-        enableService();
-
-        // Set up FillResponse with dataset authentication
-        Bundle clientState = new Bundle();
-        clientState.putCharSequence("clientStateKey", "clientStateValue");
-
-        // Prepare the authenticated response
-        final IntentSender authentication = AuthenticationActivity.createSender(mContext, 1,
-                new CannedDataset.Builder()
-                        .setField(ID_USERNAME, "dude")
-                        .setField(ID_PASSWORD, "sweet")
-                        .setPresentation(createPresentation("Dataset"))
-                        .build());
-
-        sReplier.addResponse(new CannedFillResponse.Builder().addDataset(
-                new CannedDataset.Builder()
-                        .setField(ID_USERNAME, "username")
-                        .setId("name")
-                        .setPresentation(createPresentation("authentication"))
-                        .setAuthentication(authentication)
-                        .build())
-                .setExtras(clientState).build());
-        mActivity.expectAutoFill("dude", "sweet");
-
-        // Trigger autofill.
-        mActivity.onUsername(View::requestFocus);
-
-        // Authenticate
-        sUiBot.selectDataset("authentication");
-        sReplier.getNextFillRequest();
-        mActivity.assertAutoFilled();
-
-        // Verify fill selection
-        FillEventHistory selection = InstrumentedAutoFillService.peekInstance()
-                .getFillEventHistory();
-        assertThat(selection.getClientState().getCharSequence("clientStateKey")).isEqualTo(
-                "clientStateValue");
-
-        assertThat(selection.getEvents().size()).isEqualTo(1);
-        FillEventHistory.Event event = selection.getEvents().get(0);
-        assertThat(event.getType()).isEqualTo(TYPE_DATASET_AUTHENTICATION_SELECTED);
-        assertThat(event.getDatasetId()).isEqualTo("name");
-    }
-
-    @Test
-    public void checkFillSelectionAfterSelectingAuthentication() throws Exception {
-        enableService();
-
-        // Set up FillResponse with response wide authentication
-        Bundle clientState = new Bundle();
-        clientState.putCharSequence("clientStateKey", "clientStateValue");
-
-        // Prepare the authenticated response
-        final IntentSender authentication = AuthenticationActivity.createSender(mContext, 1,
-                new CannedFillResponse.Builder().addDataset(
-                        new CannedDataset.Builder()
-                                .setField(ID_USERNAME, "username")
-                                .setId("name")
-                                .setPresentation(createPresentation("dataset"))
-                                .build())
-                        .setExtras(clientState).build());
-
-        sReplier.addResponse(new CannedFillResponse.Builder().setExtras(clientState)
-                .setPresentation(createPresentation("authentication"))
-                .setAuthentication(authentication, ID_USERNAME)
-                .build());
-
-        // Trigger autofill.
-        mActivity.onUsername(View::requestFocus);
-
-        // Authenticate
-        sUiBot.selectDataset("authentication");
-        sReplier.getNextFillRequest();
-        sUiBot.assertDatasets("dataset");
-
-        // Verify fill selection
-        FillEventHistory selection = InstrumentedAutoFillService.peekInstance()
-                .getFillEventHistory();
-        assertThat(selection.getClientState().getCharSequence("clientStateKey")).isEqualTo(
-                "clientStateValue");
-
-        assertThat(selection.getEvents().size()).isEqualTo(1);
-        FillEventHistory.Event event = selection.getEvents().get(0);
-        assertThat(event.getType()).isEqualTo(TYPE_AUTHENTICATION_SELECTED);
-        assertThat(event.getDatasetId()).isNull();
-    }
-
-    @Test
-    public void checkFillSelectionAfterSelectingTwoDatasets() throws Exception {
-        enableService();
-
-        // Set up first partition with an anonymous dataset
-        sReplier.addResponse(new CannedFillResponse.Builder().addDataset(
-                new CannedDataset.Builder()
-                        .setField(ID_USERNAME, "username")
-                        .setPresentation(createPresentation("dataset1"))
-                        .build())
-                .build());
-        mActivity.expectAutoFill("username");
-
-        // Trigger autofill on username
-        mActivity.onUsername(View::requestFocus);
-        waitUntilConnected();
-        sUiBot.selectDataset("dataset1");
-        sReplier.getNextFillRequest();
-        mActivity.assertAutoFilled();
-
-        {
-            // Verify fill selection
-            FillEventHistory selection = InstrumentedAutoFillService.peekInstance()
-                    .getFillEventHistory();
-            assertThat(selection.getClientState()).isNull();
-
-            assertThat(selection.getEvents().size()).isEqualTo(1);
-            FillEventHistory.Event event = selection.getEvents().get(0);
-            assertThat(event.getType()).isEqualTo(TYPE_DATASET_SELECTED);
-            assertThat(event.getDatasetId()).isNull();
-        }
-
-        // Set up second partition with a named dataset
-        Bundle clientState = new Bundle();
-        clientState.putCharSequence("clientStateKey", "clientStateValue");
-
-        sReplier.addResponse(new CannedFillResponse.Builder()
-                .addDataset(
-                        new CannedDataset.Builder()
-                                .setField(ID_PASSWORD, "password2")
-                                .setPresentation(createPresentation("dataset2"))
-                                .setId("name2")
-                                .build())
-                .addDataset(
-                        new CannedDataset.Builder()
-                                .setField(ID_PASSWORD, "password3")
-                                .setPresentation(createPresentation("dataset3"))
-                                .setId("name3")
-                                .build())
-                .setExtras(clientState)
-                .setRequiredSavableIds(SAVE_DATA_TYPE_GENERIC, ID_PASSWORD).build());
-        mActivity.expectPasswordAutoFill("password3");
-
-        // Trigger autofill on password
-        mActivity.onPassword(View::requestFocus);
-        sUiBot.selectDataset("dataset3");
-        sReplier.getNextFillRequest();
-        mActivity.assertAutoFilled();
-
-        {
-            // Verify fill selection
-            FillEventHistory selection = InstrumentedAutoFillService.peekInstance()
-                    .getFillEventHistory();
-            assertThat(selection.getClientState().getCharSequence("clientStateKey")).isEqualTo(
-                    "clientStateValue");
-
-            assertThat(selection.getEvents().size()).isEqualTo(1);
-            FillEventHistory.Event event = selection.getEvents().get(0);
-            assertThat(event.getType()).isEqualTo(TYPE_DATASET_SELECTED);
-            assertThat(event.getDatasetId()).isEqualTo("name3");
-        }
-
-        mActivity.onPassword((v) -> v.setText("new password"));
-        mActivity.syncRunOnUiThread(() -> mActivity.finish());
-        waitUntilDisconnected();
-
-        {
-            // Verify fill selection
-            FillEventHistory selection = InstrumentedAutoFillService.peekInstance()
-                    .getFillEventHistory();
-            assertThat(selection.getClientState().getCharSequence("clientStateKey")).isEqualTo(
-                    "clientStateValue");
-
-            assertThat(selection.getEvents().size()).isEqualTo(2);
-            FillEventHistory.Event event1 = selection.getEvents().get(0);
-            assertThat(event1.getType()).isEqualTo(TYPE_DATASET_SELECTED);
-            assertThat(event1.getDatasetId()).isEqualTo("name3");
-
-            FillEventHistory.Event event2 = selection.getEvents().get(1);
-            assertThat(event2.getType()).isEqualTo(TYPE_SAVE_SHOWN);
-            assertThat(event2.getDatasetId()).isNull();
-        }
-    }
-
-    @Test
-    public void checkFillSelectionIsResetAfterReturningNull() throws Exception {
-        enableService();
-
-        // First reset
-        sReplier.addResponse(new CannedFillResponse.Builder().addDataset(
-                new CannedDataset.Builder()
-                        .setField(ID_USERNAME, "username")
-                        .setPresentation(createPresentation("dataset1"))
-                        .build())
-                .build());
-        mActivity.expectAutoFill("username");
-
-        mActivity.onUsername(View::requestFocus);
-        waitUntilConnected();
-        sReplier.getNextFillRequest();
-        sUiBot.selectDataset("dataset1");
-        mActivity.assertAutoFilled();
-
-        {
-            // Verify fill selection
-            FillEventHistory selection = InstrumentedAutoFillService.peekInstance()
-                    .getFillEventHistory();
-            assertThat(selection.getClientState()).isNull();
-
-            assertThat(selection.getEvents().size()).isEqualTo(1);
-            FillEventHistory.Event event = selection.getEvents().get(0);
-            assertThat(event.getType()).isEqualTo(TYPE_DATASET_SELECTED);
-            assertThat(event.getDatasetId()).isNull();
-        }
-
-        // Second request
-        sReplier.addResponse(NO_RESPONSE);
-        mActivity.onPassword(View::requestFocus);
-        sReplier.getNextFillRequest();
-        sUiBot.assertNoDatasets();
-        waitUntilDisconnected();
-
-        {
-            // Verify fill selection
-            FillEventHistory selection = InstrumentedAutoFillService.peekInstance()
-                    .getFillEventHistory();
-            assertThat(selection).isNull();
-        }
-    }
-
-    @Test
-    public void checkFillSelectionIsResetAfterReturningError() throws Exception {
-        enableService();
-
-        // First reset
-        sReplier.addResponse(new CannedFillResponse.Builder().addDataset(
-                new CannedDataset.Builder()
-                        .setField(ID_USERNAME, "username")
-                        .setPresentation(createPresentation("dataset1"))
-                        .build())
-                .build());
-        mActivity.expectAutoFill("username");
-
-        mActivity.onUsername(View::requestFocus);
-        waitUntilConnected();
-        sReplier.getNextFillRequest();
-        sUiBot.selectDataset("dataset1");
-        mActivity.assertAutoFilled();
-
-        {
-            // Verify fill selection
-            FillEventHistory selection = InstrumentedAutoFillService.peekInstance()
-                    .getFillEventHistory();
-            assertThat(selection.getClientState()).isNull();
-
-            assertThat(selection.getEvents().size()).isEqualTo(1);
-            FillEventHistory.Event event = selection.getEvents().get(0);
-            assertThat(event.getType()).isEqualTo(TYPE_DATASET_SELECTED);
-            assertThat(event.getDatasetId()).isNull();
-        }
-
-        // Second request
-        sReplier.addResponse(new CannedFillResponse.Builder().returnFailure("D'OH!").build());
-        mActivity.onPassword(View::requestFocus);
-        sReplier.getNextFillRequest();
-        sUiBot.assertNoDatasets();
-        waitUntilDisconnected();
-
-        {
-            // Verify fill selection
-            FillEventHistory selection = InstrumentedAutoFillService.peekInstance()
-                    .getFillEventHistory();
-            assertThat(selection).isNull();
-        }
-    }
-
-    @Test
-    public void checkFillSelectionIsResetAfterTimeout() throws Exception {
-        enableService();
-
-        // First reset
-        sReplier.addResponse(new CannedFillResponse.Builder().addDataset(
-                new CannedDataset.Builder()
-                        .setField(ID_USERNAME, "username")
-                        .setPresentation(createPresentation("dataset1"))
-                        .build())
-                .build());
-        mActivity.expectAutoFill("username");
-
-        mActivity.onUsername(View::requestFocus);
-        waitUntilConnected();
-        sReplier.getNextFillRequest();
-        sUiBot.selectDataset("dataset1");
-        mActivity.assertAutoFilled();
-
-        {
-            // Verify fill selection
-            FillEventHistory selection = InstrumentedAutoFillService.peekInstance()
-                    .getFillEventHistory();
-            assertThat(selection.getClientState()).isNull();
-
-            assertThat(selection.getEvents().size()).isEqualTo(1);
-            FillEventHistory.Event event = selection.getEvents().get(0);
-            assertThat(event.getType()).isEqualTo(TYPE_DATASET_SELECTED);
-            assertThat(event.getDatasetId()).isNull();
-        }
-
-        // Second request
-        sReplier.addResponse(DO_NOT_REPLY_RESPONSE);
-        mActivity.onPassword(View::requestFocus);
-        sReplier.getNextFillRequest();
-        waitUntilDisconnected();
-
-        {
-            // Verify fill selection
-            FillEventHistory selection = InstrumentedAutoFillService.peekInstance()
-                    .getFillEventHistory();
-            assertThat(selection).isNull();
-        }
-    }
-
-    private Bundle getBundle(String key, String value) {
-        final Bundle bundle = new Bundle();
-        bundle.putString(key, value);
-        return bundle;
-    }
-
-    /**
-     * Tests the following scenario:
-     *
-     * <ol>
-     *    <li>Activity A is launched.
-     *    <li>Activity A triggers autofill.
-     *    <li>Activity B is launched.
-     *    <li>Activity B triggers autofill.
-     *    <li>User goes back to Activity A.
-     *    <li>User triggers save on Activity A - at this point, service should have stats of
-     *        activity B, and stats for activity A should have beeen discarded.
-     * </ol>
-     */
-    @Test
-    public void checkFillSelectionFromPreviousSessionIsDiscarded() throws Exception {
-        enableService();
-
-        // Launch activity A
-        sReplier.addResponse(new CannedFillResponse.Builder()
-                .setExtras(getBundle("activity", "A"))
-                .setRequiredSavableIds(SAVE_DATA_TYPE_PASSWORD, ID_USERNAME, ID_PASSWORD)
-                .build());
-
-        // Trigger autofill on activity A
-        mActivity.onUsername(View::requestFocus);
-        waitUntilConnected();
-        sReplier.getNextFillRequest();
-
-        // Verify fill selection for Activity A
-        FillEventHistory selectionA = InstrumentedAutoFillService.peekInstance()
-                .getFillEventHistory();
-        assertThat(selectionA.getClientState().getString("activity")).isEqualTo("A");
-        assertThat(selectionA.getEvents()).isNull();
-
-        // Launch activity B
-        mContext.startActivity(new Intent(mContext, CheckoutActivity.class));
-
-        // Trigger autofill on activity B
-        sReplier.addResponse(new CannedFillResponse.Builder()
-                .setExtras(getBundle("activity", "B"))
-                .addDataset(new CannedDataset.Builder()
-                        .setField(ID_CC_NUMBER, "4815162342")
-                        .setPresentation(createPresentation("datasetB"))
-                        .build())
-                .build());
-        sUiBot.focusByRelativeId(ID_CC_NUMBER);
-        sReplier.getNextFillRequest();
-
-        // Verify fill selection for Activity B
-        final FillEventHistory selectionB = InstrumentedAutoFillService.peekInstance()
-                .getFillEventHistory();
-        assertThat(selectionB.getClientState().getString("activity")).isEqualTo("B");
-        assertThat(selectionB.getEvents()).isNull();
-
-        // Now switch back to A...
-        sUiBot.pressBack(); // dismiss keyboard
-        sUiBot.pressBack(); // dismiss task
-        sUiBot.assertShownByRelativeId(ID_USERNAME);
-        // ...and trigger save
-        // Set credentials...
-        mActivity.onUsername((v) -> v.setText("malkovich"));
-        mActivity.onPassword((v) -> v.setText("malkovich"));
-        final String expectedMessage = getWelcomeMessage("malkovich");
-        final String actualMessage = mActivity.tapLogin();
-        assertWithMessage("Wrong welcome msg").that(actualMessage).isEqualTo(expectedMessage);
-        sUiBot.saveForAutofill(true, SAVE_DATA_TYPE_PASSWORD);
-        sReplier.getNextSaveRequest();
-
-        // Finally, make sure history is right
-        final FillEventHistory finalSelection = InstrumentedAutoFillService.peekInstance()
-                .getFillEventHistory();
-        assertThat(finalSelection.getClientState().getString("activity")).isEqualTo("B");
-        assertThat(finalSelection.getEvents()).isNull();
-
-    }
-
-    @Test
     public void testIsServiceEnabled() throws Exception {
         disableService();
         final AutofillManager afm = mActivity.getAutofillManager();
@@ -3353,6 +3484,19 @@
     }
 
     @Test
+    public void testGetAutofillServiceComponentName() throws Exception {
+        final AutofillManager afm = mActivity.getAutofillManager();
+
+        enableService();
+        final ComponentName componentName = afm.getAutofillServiceComponentName();
+        assertThat(componentName.getPackageName()).isEqualTo(SERVICE_PACKAGE);
+        assertThat(componentName.getClassName()).endsWith(SERVICE_CLASS);
+
+        disableService();
+        assertThat(afm.getAutofillServiceComponentName()).isNull();
+    }
+
+    @Test
     public void testSetupComplete() throws Exception {
         enableService();
 
@@ -3385,13 +3529,52 @@
         // Trigger auto-fill.
         mActivity.onUsername(View::requestFocus);
         sReplier.getNextFillRequest();
-        sUiBot.assertDatasets("The Dude");
+        mUiBot.assertDatasets("The Dude");
 
         // Now disable service by setting another service
         Helper.enableAutofillService(mContext, NoOpAutofillService.SERVICE_NAME);
 
         // ...and make sure popup's gone
-        sUiBot.assertNoDatasets();
+        mUiBot.assertNoDatasets();
+    }
+
+    @Test
+    public void testServiceIsDisabledWhenNewServiceInfoIsInvalid() throws Exception {
+        serviceIsDisabledWhenNewServiceIsInvalid(BadAutofillService.SERVICE_NAME);
+    }
+
+    @Test
+    public void testServiceIsDisabledWhenNewServiceNameIsInvalid() throws Exception {
+        serviceIsDisabledWhenNewServiceIsInvalid("Y_U_NO_VALID");
+    }
+
+    private void serviceIsDisabledWhenNewServiceIsInvalid(String serviceName) throws Exception {
+        // Set service.
+        enableService();
+
+        // Set expectations.
+        sReplier.addResponse(new CannedDataset.Builder()
+                .setField(ID_USERNAME, "dude")
+                .setField(ID_PASSWORD, "sweet")
+                .setPresentation(createPresentation("The Dude"))
+                .build());
+        mActivity.expectAutoFill("dude", "sweet");
+
+        // Trigger autofill.
+        mActivity.onUsername(View::requestFocus);
+        sReplier.getNextFillRequest();
+        mUiBot.assertDatasets("The Dude");
+
+        // Now disable service by setting another service...
+        Helper.enableAutofillService(mContext, serviceName);
+
+        // ...and make sure popup's gone
+        mUiBot.assertNoDatasets();
+
+        // Then try to trigger autofill again...
+        mActivity.onPassword(View::requestFocus);
+        //...it should not work!
+        mUiBot.assertNoDatasets();
     }
 
     @Test
@@ -3412,7 +3595,7 @@
         sReplier.getNextFillRequest();
 
         // Auto-fill it.
-        sUiBot.selectDataset("The Dude");
+        mUiBot.selectDataset("The Dude");
 
         // Check the results.
         mActivity.assertAutoFilled();
@@ -3465,7 +3648,7 @@
         // Make sure all datasets are shown.
         // TODO: improve assertDatasets() so it supports scrolling, and assert all of them are
         // shown
-        sUiBot.assertDatasets("DS-1", "DS-2", "DS-3");
+        mUiBot.assertDatasets("DS-1", "DS-2", "DS-3");
 
         // TODO: once it supports scrolling, selects the last dataset and asserts it's filled.
     }
@@ -3477,7 +3660,7 @@
 
         // Set expectations.
         final OneTimeCancellationSignalListener listener =
-                new OneTimeCancellationSignalListener(Helper.FILL_TIMEOUT_MS + 2000);
+                new OneTimeCancellationSignalListener(Timeouts.FILL_TIMEOUT.ms() + 2000);
         sReplier.addResponse(DO_NOT_REPLY_RESPONSE);
 
         // Trigger auto-fill.
@@ -3491,4 +3674,27 @@
         waitUntilDisconnected();
         listener.assertOnCancelCalled();
     }
+
+    @Test
+    public void testNewTextAttributes() throws Exception {
+        enableService();
+        sReplier.addResponse(NO_RESPONSE);
+        mActivity.onUsername(View::requestFocus);
+
+        final FillRequest request = sReplier.getNextFillRequest();
+        final ViewNode username = findNodeByResourceId(request.structure, ID_USERNAME);
+        assertThat(username.getMinTextEms()).isEqualTo(2);
+        assertThat(username.getMaxTextEms()).isEqualTo(5);
+        assertThat(username.getMaxTextLength()).isEqualTo(25);
+
+        final ViewNode container = findNodeByResourceId(request.structure, ID_USERNAME_CONTAINER);
+        assertThat(container.getMinTextEms()).isEqualTo(-1);
+        assertThat(container.getMaxTextEms()).isEqualTo(-1);
+        assertThat(container.getMaxTextLength()).isEqualTo(-1);
+
+        final ViewNode password = findNodeByResourceId(request.structure, ID_PASSWORD);
+        assertThat(password.getMinTextEms()).isEqualTo(-1);
+        assertThat(password.getMaxTextEms()).isEqualTo(-1);
+        assertThat(password.getMaxTextLength()).isEqualTo(-1);
+    }
 }
diff --git a/tests/autofillservice/src/android/autofillservice/cts/LoginWithStringsActivity.java b/tests/autofillservice/src/android/autofillservice/cts/LoginWithStringsActivity.java
new file mode 100644
index 0000000..90c3e93
--- /dev/null
+++ b/tests/autofillservice/src/android/autofillservice/cts/LoginWithStringsActivity.java
@@ -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.
+ */
+package android.autofillservice.cts;
+
+/**
+ * Same as {@link LoginActivity}, but with the texts for some fields set from resources.
+ */
+public class LoginWithStringsActivity extends LoginActivity {
+
+    @Override
+    protected int getContentView() {
+        return R.layout.login_with_strings_activity;
+    }
+}
diff --git a/tests/autofillservice/src/android/autofillservice/cts/LoginWithStringsActivityTest.java b/tests/autofillservice/src/android/autofillservice/cts/LoginWithStringsActivityTest.java
new file mode 100644
index 0000000..73ab7d9
--- /dev/null
+++ b/tests/autofillservice/src/android/autofillservice/cts/LoginWithStringsActivityTest.java
@@ -0,0 +1,139 @@
+/*
+ * 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_PASSWORD_LABEL;
+import static android.autofillservice.cts.Helper.ID_USERNAME;
+import static android.autofillservice.cts.Helper.ID_USERNAME_LABEL;
+import static android.autofillservice.cts.Helper.assertTextAndValue;
+import static android.autofillservice.cts.Helper.assertTextFromResouces;
+import static android.autofillservice.cts.Helper.assertTextIsSanitized;
+import static android.autofillservice.cts.Helper.findNodeByResourceId;
+import static android.autofillservice.cts.InstrumentedAutoFillService.waitUntilConnected;
+import static android.autofillservice.cts.LoginActivity.AUTHENTICATION_MESSAGE;
+import static android.autofillservice.cts.LoginActivity.getWelcomeMessage;
+import static android.service.autofill.SaveInfo.SAVE_DATA_TYPE_PASSWORD;
+
+import static com.google.common.truth.Truth.assertThat;
+import static com.google.common.truth.Truth.assertWithMessage;
+
+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.os.Bundle;
+import android.view.View;
+
+import org.junit.Before;
+import org.junit.Rule;
+import org.junit.Test;
+
+public class LoginWithStringsActivityTest extends AutoFillServiceTestCase {
+
+    @Rule
+    public final AutofillActivityTestRule<LoginWithStringsActivity> mActivityRule =
+            new AutofillActivityTestRule<LoginWithStringsActivity>(LoginWithStringsActivity.class);
+
+    private LoginWithStringsActivity mActivity;
+
+    @Before
+    public void setActivity() {
+        mActivity = mActivityRule.getActivity();
+    }
+
+    @Test
+    public void testAutoFillOneDatasetAndSave() throws Exception {
+        // Set service.
+        enableService();
+
+        // Set expectations.
+        final Bundle extras = new Bundle();
+        extras.putString("numbers", "4815162342");
+
+        sReplier.addResponse(new CannedFillResponse.Builder()
+                .addDataset(new CannedDataset.Builder()
+                        .setId("I'm the alpha and the omega")
+                        .setField(ID_USERNAME, "dude")
+                        .setField(ID_PASSWORD, "sweet")
+                        .setPresentation(createPresentation("The Dude"))
+                        .build())
+                .setRequiredSavableIds(SAVE_DATA_TYPE_PASSWORD, ID_USERNAME, ID_PASSWORD)
+                .setExtras(extras)
+                .build());
+        mActivity.expectAutoFill("dude", "sweet");
+
+        // Trigger auto-fill.
+        mActivity.onUsername(View::requestFocus);
+        waitUntilConnected();
+
+        final FillRequest fillRequest = sReplier.getNextFillRequest();
+
+        // Make sure input was sanitized.
+        assertTextIsSanitized(fillRequest.structure, ID_USERNAME);
+        assertTextIsSanitized(fillRequest.structure, ID_PASSWORD);
+
+        // Make sure labels were not sanitized
+        assertTextFromResouces(fillRequest.structure, ID_USERNAME_LABEL, "Username", false,
+                "username_string");
+        assertTextFromResouces(fillRequest.structure, ID_PASSWORD_LABEL, "Password", false,
+                "password_string");
+
+        // Auto-fill it.
+        mUiBot.selectDataset("The Dude");
+
+        // Check the results.
+        mActivity.assertAutoFilled();
+
+        // Try to login, it will fail.
+        final String loginMessage = mActivity.tapLogin();
+
+        assertWithMessage("Wrong login msg").that(loginMessage).isEqualTo(AUTHENTICATION_MESSAGE);
+
+        // Set right password...
+        mActivity.onPassword((v) -> v.setText("dude"));
+
+        // ... and try again
+        final String expectedMessage = getWelcomeMessage("dude");
+        final String actualMessage = mActivity.tapLogin();
+        assertWithMessage("Wrong welcome msg").that(actualMessage).isEqualTo(expectedMessage);
+
+        // Assert the snack bar is shown and tap "Save".
+        mUiBot.saveForAutofill(true, SAVE_DATA_TYPE_PASSWORD);
+
+        final SaveRequest saveRequest = sReplier.getNextSaveRequest();
+
+        assertThat(saveRequest.datasetIds).containsExactly("I'm the alpha and the omega");
+
+        // Assert value of expected fields - should not be sanitized.
+        final ViewNode username = findNodeByResourceId(saveRequest.structure, ID_USERNAME);
+        assertTextAndValue(username, "dude");
+        final ViewNode password = findNodeByResourceId(saveRequest.structure, ID_USERNAME);
+        assertTextAndValue(password, "dude");
+
+        // Make sure labels were not sanitized
+        assertTextFromResouces(saveRequest.structure, ID_USERNAME_LABEL, "Username", false,
+                "username_string");
+        assertTextFromResouces(saveRequest.structure, ID_PASSWORD_LABEL, "Password", false,
+                "password_string");
+
+        // 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");
+    }
+}
diff --git a/tests/autofillservice/src/android/autofillservice/cts/MultipleExceptionsCatcher.java b/tests/autofillservice/src/android/autofillservice/cts/MultipleExceptionsCatcher.java
deleted file mode 100644
index 52c3bcc..0000000
--- a/tests/autofillservice/src/android/autofillservice/cts/MultipleExceptionsCatcher.java
+++ /dev/null
@@ -1,91 +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.autofillservice.cts;
-
-import android.support.annotation.NonNull;
-import android.support.annotation.Nullable;
-import android.util.Log;
-
-import java.io.IOException;
-import java.io.PrintWriter;
-import java.io.StringWriter;
-import java.util.ArrayList;
-import java.util.List;
-
-/**
- * Helper used to catch multiple exceptions that might have happened in a test case.
- */
-// TODO: move to common CTS code (and add test cases to it)
-public final class MultipleExceptionsCatcher {
-
-    private static final String TAG = "MultipleExceptionsCatcher";
-
-    private final List<Throwable> mThrowables = new ArrayList<>();
-
-    /**
-     * Runs {@code r} postponing any thrown exception to {@link #throwIfAny()}.
-     */
-    public MultipleExceptionsCatcher run(@NonNull Runnable r) {
-        try {
-            r.run();
-        } catch (Throwable t) {
-            mThrowables.add(t);
-        }
-        return this;
-    }
-
-    /**
-     * Adds an exception - if it's not {@code null} to the exceptions thrown by
-     * {@link #throwIfAny()}.
-     */
-    public MultipleExceptionsCatcher add(@Nullable Throwable t) {
-        if (t != null) {
-            mThrowables.add(t);
-        }
-        return this;
-    }
-
-    /**
-     * Throws one exception merging all exceptions thrown or added so far, if any.
-     */
-    public void throwIfAny() throws Throwable {
-        if (mThrowables.isEmpty()) return;
-
-        final int numberExceptions = mThrowables.size();
-        if (numberExceptions == 1) {
-            throw mThrowables.get(0);
-        }
-
-        String msg = "D'OH!";
-        try {
-            try (StringWriter sw = new StringWriter(); PrintWriter pw = new PrintWriter(sw)) {
-                sw.write("Caught " + numberExceptions + " exceptions\n");
-                for (int i = 0; i < numberExceptions; i++) {
-                    sw.write("\n---- Begin of exception #" + (i + 1) + " ----\n");
-                    final Throwable exception = mThrowables.get(i);
-                    exception.printStackTrace(pw);
-                    sw.write("---- End of exception #" + (i + 1) + " ----\n\n");
-                }
-                msg = sw.toString();
-            }
-        } catch (IOException e) {
-            // ignore close() errors - should not happen...
-            Log.e(TAG, "Exception closing StringWriter: " + e);
-        }
-        throw new AssertionError(msg);
-    }
-}
diff --git a/tests/autofillservice/src/android/autofillservice/cts/MultipleFragmentLoginTest.java b/tests/autofillservice/src/android/autofillservice/cts/MultipleFragmentLoginTest.java
index 3e30b9b..8da4add 100644
--- a/tests/autofillservice/src/android/autofillservice/cts/MultipleFragmentLoginTest.java
+++ b/tests/autofillservice/src/android/autofillservice/cts/MultipleFragmentLoginTest.java
@@ -18,8 +18,6 @@
 
 import static android.autofillservice.cts.CannedFillResponse.NO_RESPONSE;
 import static android.autofillservice.cts.FragmentContainerActivity.FRAGMENT_TAG;
-import static android.autofillservice.cts.Helper.FILL_TIMEOUT_MS;
-import static android.autofillservice.cts.Helper.eventually;
 import static android.autofillservice.cts.Helper.findNodeByResourceId;
 import static android.service.autofill.SaveInfo.SAVE_DATA_TYPE_GENERIC;
 
@@ -70,18 +68,12 @@
                 new InstrumentedAutoFillService.FillRequest[1];
 
         // Trigger autofill
-        eventually(() -> {
-            mActivity.syncRunOnUiThread(() -> {
-                mEditText2.requestFocus();
-                mEditText1.requestFocus();
-            });
+        mActivity.syncRunOnUiThread(() -> {
+            mEditText2.requestFocus();
+            mEditText1.requestFocus();
+        });
 
-            try {
-                fillRequest[0] = sReplier.getNextFillRequest();
-            } catch (InterruptedException e) {
-                throw new RuntimeException(e);
-            }
-        }, (int) (FILL_TIMEOUT_MS * 2));
+        fillRequest[0] = sReplier.getNextFillRequest();
 
         assertThat(fillRequest[0].data).isNull();
 
@@ -94,8 +86,8 @@
         assertThat(findNodeByResourceId(structure, "editText5")).isNull();
 
         // Wait until autofill has been applied
-        sUiBot.selectDataset("dataset1");
-        sUiBot.assertShownByText("editText1-autofilled");
+        mUiBot.selectDataset("dataset1");
+        mUiBot.assertShownByText("editText1-autofilled");
 
         // Manually fill view
         mActivity.syncRunOnUiThread(() -> mEditText2.setText("editText2-manually-filled"));
@@ -153,16 +145,16 @@
         assertThat(findNodeByResourceId(structure2, "editText5")).isNotNull();
 
         // Wait until autofill has been applied
-        sUiBot.selectDataset("dataset2");
-        sUiBot.assertShownByText("editText3-autofilled");
-        sUiBot.assertShownByText("editText4-autofilled");
+        mUiBot.selectDataset("dataset2");
+        mUiBot.assertShownByText("editText3-autofilled");
+        mUiBot.assertShownByText("editText4-autofilled");
 
         // Manually fill view
         mActivity.syncRunOnUiThread(() -> editText5.setText("editText5-manually-filled"));
 
         // Finish activity and save data
         mActivity.finish();
-        sUiBot.saveForAutofill(true, SAVE_DATA_TYPE_GENERIC);
+        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();
@@ -229,7 +221,7 @@
 
         // Check UI is shown, but don't select it.
         sReplier.getNextFillRequest();
-        sUiBot.assertDatasets("dataset1");
+        mUiBot.assertDatasets("dataset1");
 
         // Switch fragments
         sReplier.addResponse(NO_RESPONSE);
@@ -239,7 +231,7 @@
                         FRAGMENT_TAG).commitNow());
         // Make sure UI is gone.
         sReplier.getNextFillRequest();
-        sUiBot.assertNoDatasets();
+        mUiBot.assertNoDatasets();
     }
 
     // TODO: add similar tests for fragment with virtual view
diff --git a/tests/autofillservice/src/android/autofillservice/cts/MultipleTimesRadioGroupListener.java b/tests/autofillservice/src/android/autofillservice/cts/MultipleTimesRadioGroupListener.java
index b264a46..5af2762 100644
--- a/tests/autofillservice/src/android/autofillservice/cts/MultipleTimesRadioGroupListener.java
+++ b/tests/autofillservice/src/android/autofillservice/cts/MultipleTimesRadioGroupListener.java
@@ -16,7 +16,7 @@
 
 package android.autofillservice.cts;
 
-import static android.autofillservice.cts.Helper.FILL_TIMEOUT_MS;
+import static android.autofillservice.cts.Timeouts.FILL_TIMEOUT;
 
 import static com.google.common.truth.Truth.assertWithMessage;
 
@@ -26,7 +26,7 @@
 import java.util.concurrent.TimeUnit;
 
 /**
- * Custom {@link RadioGroup.OnCheckedChangeListener} used to assert an
+ * Custom {@link android.widget.RadioGroup.OnCheckedChangeListener} used to assert an
  * {@link RadioGroup} was auto-filled properly.
  */
 final class MultipleTimesRadioGroupListener implements RadioGroup.OnCheckedChangeListener {
@@ -49,8 +49,8 @@
     }
 
     void assertAutoFilled() throws Exception {
-        final boolean set = mLatch.await(FILL_TIMEOUT_MS, TimeUnit.MILLISECONDS);
-        assertWithMessage("Timeout (%s ms) on RadioGroup %s", FILL_TIMEOUT_MS, mName)
+        final boolean set = mLatch.await(FILL_TIMEOUT.ms(), TimeUnit.MILLISECONDS);
+        assertWithMessage("Timeout (%s ms) on RadioGroup %s", FILL_TIMEOUT.ms(), mName)
             .that(set).isTrue();
         final int actual = mRadioGroup.getAutofillValue().getListValue();
         assertWithMessage("Wrong auto-fill value on RadioGroup %s", mName)
diff --git a/tests/autofillservice/src/android/autofillservice/cts/MultipleTimesTextWatcher.java b/tests/autofillservice/src/android/autofillservice/cts/MultipleTimesTextWatcher.java
index c928f31..a510639 100644
--- a/tests/autofillservice/src/android/autofillservice/cts/MultipleTimesTextWatcher.java
+++ b/tests/autofillservice/src/android/autofillservice/cts/MultipleTimesTextWatcher.java
@@ -16,7 +16,7 @@
 
 package android.autofillservice.cts;
 
-import static android.autofillservice.cts.Helper.FILL_TIMEOUT_MS;
+import static android.autofillservice.cts.Timeouts.FILL_TIMEOUT;
 
 import static com.google.common.truth.Truth.assertWithMessage;
 
@@ -62,8 +62,8 @@
     }
 
     void assertAutoFilled() throws Exception {
-        final boolean set = mLatch.await(FILL_TIMEOUT_MS, TimeUnit.MILLISECONDS);
-        assertWithMessage("Timeout (%s ms) on EditText %s", FILL_TIMEOUT_MS, mName)
+        final boolean set = mLatch.await(FILL_TIMEOUT.ms(), TimeUnit.MILLISECONDS);
+        assertWithMessage("Timeout (%s ms) on EditText %s", FILL_TIMEOUT.ms(), mName)
                 .that(set).isTrue();
         final String actual = mEditText.getText().toString();
         assertWithMessage("Wrong auto-fill value on EditText %s", mName)
diff --git a/tests/autofillservice/src/android/autofillservice/cts/MultipleTimesTimeListener.java b/tests/autofillservice/src/android/autofillservice/cts/MultipleTimesTimeListener.java
index 1aed119..2519aec 100644
--- a/tests/autofillservice/src/android/autofillservice/cts/MultipleTimesTimeListener.java
+++ b/tests/autofillservice/src/android/autofillservice/cts/MultipleTimesTimeListener.java
@@ -16,7 +16,7 @@
 
 package android.autofillservice.cts;
 
-import static android.autofillservice.cts.Helper.FILL_TIMEOUT_MS;
+import static android.autofillservice.cts.Timeouts.FILL_TIMEOUT;
 
 import static com.google.common.truth.Truth.assertWithMessage;
 
@@ -50,8 +50,8 @@
     }
 
     void assertAutoFilled() throws Exception {
-        final boolean set = latch.await(FILL_TIMEOUT_MS, TimeUnit.MILLISECONDS);
-        assertWithMessage("Timeout (%s ms) on TimePicker %s", FILL_TIMEOUT_MS, name)
+        final boolean set = latch.await(FILL_TIMEOUT.ms(), TimeUnit.MILLISECONDS);
+        assertWithMessage("Timeout (%s ms) on TimePicker %s", FILL_TIMEOUT.ms(), name)
                 .that(set).isTrue();
         assertWithMessage("Wrong hour on TimePicker %s", name)
                 .that(timePicker.getHour()).isEqualTo(expectedHour);
diff --git a/tests/autofillservice/src/android/autofillservice/cts/MyAutofillCallback.java b/tests/autofillservice/src/android/autofillservice/cts/MyAutofillCallback.java
index 1ba8755..3d6acc9 100644
--- a/tests/autofillservice/src/android/autofillservice/cts/MyAutofillCallback.java
+++ b/tests/autofillservice/src/android/autofillservice/cts/MyAutofillCallback.java
@@ -16,10 +16,11 @@
 
 package android.autofillservice.cts;
 
-import static android.autofillservice.cts.Helper.CONNECTION_TIMEOUT_MS;
+import static android.autofillservice.cts.Timeouts.CONNECTION_TIMEOUT;
 
 import static com.google.common.truth.Truth.assertWithMessage;
 
+import android.util.Log;
 import android.view.View;
 import android.view.autofill.AutofillManager.AutofillCallback;
 
@@ -32,15 +33,18 @@
  */
 final class MyAutofillCallback extends AutofillCallback {
 
+    private static final String TAG = "MyAutofillCallback";
     private final BlockingQueue<MyEvent> mEvents = new LinkedBlockingQueue<>();
 
     @Override
     public void onAutofillEvent(View view, int event) {
+        Log.v(TAG, "onAutofillEvent: view=" + view + ", event=" + event);
         mEvents.offer(new MyEvent(view, event));
     }
 
     @Override
     public void onAutofillEvent(View view, int childId, int event) {
+        Log.v(TAG, "onAutofillEvent: view=" + view + ", child=" + childId + ", event=" + event);
         mEvents.offer(new MyEvent(view, childId, event));
     }
 
@@ -48,14 +52,25 @@
      * Gets the next available event or fail if it times out.
      */
     MyEvent getEvent() throws InterruptedException {
-        final MyEvent event = mEvents.poll(CONNECTION_TIMEOUT_MS, TimeUnit.MILLISECONDS);
+        final MyEvent event = mEvents.poll(CONNECTION_TIMEOUT.ms(), TimeUnit.MILLISECONDS);
         if (event == null) {
-            throw new RetryableException("no event in %d ms", CONNECTION_TIMEOUT_MS);
+            throw new RetryableException(CONNECTION_TIMEOUT, "no event");
         }
         return event;
     }
 
     /**
+     * Assert no more events were received.
+     */
+    void assertNotCalled() throws InterruptedException {
+        final MyEvent event = mEvents.poll(CONNECTION_TIMEOUT.ms(), TimeUnit.MILLISECONDS);
+        if (event != null) {
+            // Not retryable.
+            throw new IllegalStateException("should not have received " + event);
+        }
+    }
+
+    /**
      * Used to assert there is no event left behind.
      */
     void assertNumberUnhandledEvents(int expected) {
diff --git a/tests/autofillservice/src/android/autofillservice/cts/MyWebView.java b/tests/autofillservice/src/android/autofillservice/cts/MyWebView.java
new file mode 100644
index 0000000..9cddac6
--- /dev/null
+++ b/tests/autofillservice/src/android/autofillservice/cts/MyWebView.java
@@ -0,0 +1,107 @@
+/*
+ * 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.autofillservice.cts;
+
+import static android.autofillservice.cts.Timeouts.FILL_TIMEOUT;
+
+import static com.google.common.truth.Truth.assertWithMessage;
+
+import android.content.Context;
+import android.util.AttributeSet;
+import android.util.SparseArray;
+import android.view.autofill.AutofillValue;
+import android.webkit.WebView;
+
+import java.util.concurrent.CountDownLatch;
+import java.util.concurrent.TimeUnit;
+
+/**
+ * Custom {@link WebView} used to assert contents were autofilled.
+ */
+public class MyWebView extends WebView {
+
+    private FillExpectation mExpectation;
+
+    public MyWebView(Context context, AttributeSet attrs) {
+        super(context, attrs);
+    }
+
+    public void expectAutofill(String username, String password) {
+        mExpectation = new FillExpectation(username, password);
+    }
+
+    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);
+    }
+
+    @Override
+    public void autofill(SparseArray<AutofillValue> values) {
+        super.autofill(values);
+
+        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();
+        }
+    }
+
+    private class FillExpectation {
+        private final CountDownLatch mLatch = 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;
+        }
+    }
+}
diff --git a/tests/autofillservice/src/android/autofillservice/cts/NoOpAutofillService.java b/tests/autofillservice/src/android/autofillservice/cts/NoOpAutofillService.java
index 4255b6d..a75ded8 100644
--- a/tests/autofillservice/src/android/autofillservice/cts/NoOpAutofillService.java
+++ b/tests/autofillservice/src/android/autofillservice/cts/NoOpAutofillService.java
@@ -21,21 +21,26 @@
 import android.service.autofill.FillRequest;
 import android.service.autofill.SaveCallback;
 import android.service.autofill.SaveRequest;
+import android.util.Log;
 
 /**
  * {@link AutofillService} implementation that does not do anything...
  */
 public class NoOpAutofillService extends AutofillService {
 
+    private static final String TAG = "NoOpAutofillService";
+
     static final String SERVICE_NAME = NoOpAutofillService.class.getPackage().getName()
             + "/." + NoOpAutofillService.class.getSimpleName();
 
     @Override
     public void onFillRequest(FillRequest request, CancellationSignal cancellationSignal,
             FillCallback callback) {
+        Log.d(TAG, "onFillRequest()");
     }
 
     @Override
     public void onSaveRequest(SaveRequest request, SaveCallback callback) {
+        Log.d(TAG, "onFillResponse()");
     }
 }
diff --git a/tests/autofillservice/src/android/autofillservice/cts/OneTimeCompoundButtonListener.java b/tests/autofillservice/src/android/autofillservice/cts/OneTimeCompoundButtonListener.java
index 071dec6..4d7af94 100644
--- a/tests/autofillservice/src/android/autofillservice/cts/OneTimeCompoundButtonListener.java
+++ b/tests/autofillservice/src/android/autofillservice/cts/OneTimeCompoundButtonListener.java
@@ -16,7 +16,7 @@
 
 package android.autofillservice.cts;
 
-import static android.autofillservice.cts.Helper.FILL_TIMEOUT_MS;
+import static android.autofillservice.cts.Timeouts.FILL_TIMEOUT;
 
 import static com.google.common.truth.Truth.assertWithMessage;
 
@@ -48,8 +48,8 @@
     }
 
     void assertAutoFilled() throws Exception {
-        final boolean set = latch.await(FILL_TIMEOUT_MS, TimeUnit.MILLISECONDS);
-        assertWithMessage("Timeout (%s ms) on CompoundButton %s", FILL_TIMEOUT_MS, name)
+        final boolean set = latch.await(FILL_TIMEOUT.ms(), TimeUnit.MILLISECONDS);
+        assertWithMessage("Timeout (%s ms) on CompoundButton %s", FILL_TIMEOUT.ms(), name)
             .that(set).isTrue();
         final boolean actual = button.isChecked();
         assertWithMessage("Wrong auto-fill value on CompoundButton %s", name)
diff --git a/tests/autofillservice/src/android/autofillservice/cts/OneTimeDateListener.java b/tests/autofillservice/src/android/autofillservice/cts/OneTimeDateListener.java
index ef28a23..407861d 100644
--- a/tests/autofillservice/src/android/autofillservice/cts/OneTimeDateListener.java
+++ b/tests/autofillservice/src/android/autofillservice/cts/OneTimeDateListener.java
@@ -16,7 +16,7 @@
 
 package android.autofillservice.cts;
 
-import static android.autofillservice.cts.Helper.FILL_TIMEOUT_MS;
+import static android.autofillservice.cts.Timeouts.FILL_TIMEOUT;
 
 import static com.google.common.truth.Truth.assertWithMessage;
 
@@ -51,8 +51,8 @@
     }
 
     void assertAutoFilled() throws Exception {
-        final boolean set = latch.await(FILL_TIMEOUT_MS, TimeUnit.MILLISECONDS);
-        assertWithMessage("Timeout (%s ms) on DatePicker %s", FILL_TIMEOUT_MS, name)
+        final boolean set = latch.await(FILL_TIMEOUT.ms(), TimeUnit.MILLISECONDS);
+        assertWithMessage("Timeout (%s ms) on DatePicker %s", FILL_TIMEOUT.ms(), name)
             .that(set).isTrue();
         assertWithMessage("Wrong year on DatePicker %s", name)
             .that(datePicker.getYear()).isEqualTo(expectedYear);
diff --git a/tests/autofillservice/src/android/autofillservice/cts/OneTimeRadioGroupListener.java b/tests/autofillservice/src/android/autofillservice/cts/OneTimeRadioGroupListener.java
index 1903cb9..73ed648 100644
--- a/tests/autofillservice/src/android/autofillservice/cts/OneTimeRadioGroupListener.java
+++ b/tests/autofillservice/src/android/autofillservice/cts/OneTimeRadioGroupListener.java
@@ -16,7 +16,7 @@
 
 package android.autofillservice.cts;
 
-import static android.autofillservice.cts.Helper.FILL_TIMEOUT_MS;
+import static android.autofillservice.cts.Timeouts.FILL_TIMEOUT;
 
 import static com.google.common.truth.Truth.assertWithMessage;
 
@@ -47,8 +47,8 @@
     }
 
     void assertAutoFilled() throws Exception {
-        final boolean set = latch.await(FILL_TIMEOUT_MS, TimeUnit.MILLISECONDS);
-        assertWithMessage("Timeout (%s ms) on RadioGroup %s", FILL_TIMEOUT_MS, name)
+        final boolean set = latch.await(FILL_TIMEOUT.ms(), TimeUnit.MILLISECONDS);
+        assertWithMessage("Timeout (%s ms) on RadioGroup %s", FILL_TIMEOUT.ms(), name)
             .that(set).isTrue();
         final int actual = radioGroup.getCheckedRadioButtonId();
         assertWithMessage("Wrong auto-fill value on RadioGroup %s", name)
diff --git a/tests/autofillservice/src/android/autofillservice/cts/OneTimeSettingsListener.java b/tests/autofillservice/src/android/autofillservice/cts/OneTimeSettingsListener.java
deleted file mode 100644
index e723357..0000000
--- a/tests/autofillservice/src/android/autofillservice/cts/OneTimeSettingsListener.java
+++ /dev/null
@@ -1,51 +0,0 @@
-package android.autofillservice.cts;
-
-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.provider.Settings;
-
-import java.util.concurrent.CountDownLatch;
-import java.util.concurrent.TimeUnit;
-
-/**
- * Helper used to block tests until a secure settings value has been updated.
- */
-final class OneTimeSettingsListener extends ContentObserver {
-    private final CountDownLatch mLatch = new CountDownLatch(1);
-    private final ContentResolver mResolver;
-    private final String mKey;
-
-    public OneTimeSettingsListener(Context context, String key) {
-        super(new Handler(Looper.getMainLooper()));
-        mKey = key;
-        mResolver = context.getContentResolver();
-        mResolver.registerContentObserver(Settings.Secure.getUriFor(key), false, this);
-    }
-
-    @Override
-    public void onChange(boolean selfChange, Uri uri) {
-        mResolver.unregisterContentObserver(this);
-        mLatch.countDown();
-    }
-
-    /**
-     * Blocks for a few seconds until it's called.
-     *
-     * @throws IllegalStateException if it's not called.
-     */
-    void assertCalled() {
-        try {
-            final boolean updated = mLatch.await(5, TimeUnit.SECONDS);
-            if (!updated) {
-                throw new IllegalStateException("Settings " + mKey + " not called in 5s");
-            }
-        } catch (InterruptedException e) {
-            Thread.currentThread().interrupt();
-            throw new IllegalStateException("Interrupted", e);
-        }
-    }
-}
\ No newline at end of file
diff --git a/tests/autofillservice/src/android/autofillservice/cts/OneTimeSpinnerListener.java b/tests/autofillservice/src/android/autofillservice/cts/OneTimeSpinnerListener.java
index 6bc8279..5fb5973 100644
--- a/tests/autofillservice/src/android/autofillservice/cts/OneTimeSpinnerListener.java
+++ b/tests/autofillservice/src/android/autofillservice/cts/OneTimeSpinnerListener.java
@@ -16,7 +16,7 @@
 
 package android.autofillservice.cts;
 
-import static android.autofillservice.cts.Helper.FILL_TIMEOUT_MS;
+import static android.autofillservice.cts.Timeouts.FILL_TIMEOUT;
 
 import static com.google.common.truth.Truth.assertWithMessage;
 
@@ -44,8 +44,8 @@
     }
 
     void assertAutoFilled() throws Exception {
-        final boolean set = latch.await(FILL_TIMEOUT_MS, TimeUnit.MILLISECONDS);
-        assertWithMessage("Timeout (%s ms) on Spinner %s", FILL_TIMEOUT_MS, name)
+        final boolean set = latch.await(FILL_TIMEOUT.ms(), TimeUnit.MILLISECONDS);
+        assertWithMessage("Timeout (%s ms) on Spinner %s", FILL_TIMEOUT.ms(), name)
             .that(set).isTrue();
         final int actual = spinner.getSelectedItemPosition();
         assertWithMessage("Wrong auto-fill value on Spinner %s", name)
diff --git a/tests/autofillservice/src/android/autofillservice/cts/OptionalSaveActivityTest.java b/tests/autofillservice/src/android/autofillservice/cts/OptionalSaveActivityTest.java
index c82fa42..fb169d0 100644
--- a/tests/autofillservice/src/android/autofillservice/cts/OptionalSaveActivityTest.java
+++ b/tests/autofillservice/src/android/autofillservice/cts/OptionalSaveActivityTest.java
@@ -32,7 +32,6 @@
 import android.support.annotation.NonNull;
 import android.support.annotation.Nullable;
 
-import org.junit.After;
 import org.junit.Before;
 import org.junit.Rule;
 import org.junit.Test;
@@ -64,11 +63,6 @@
         mActivity = mActivityRule.getActivity();
     }
 
-    @After
-    public void finishWelcomeActivity() {
-        WelcomeActivity.finishIt();
-    }
-
     /**
      * Creates a standard builder common to all tests.
      */
@@ -122,7 +116,7 @@
         mActivity.syncRunOnUiThread(() -> mActivity.mAddress1.requestFocus());
 
         // Sanity check.
-        sUiBot.assertNoDatasets();
+        mUiBot.assertNoDatasets();
 
         // Wait for onFill() before proceeding, otherwise the fields might be changed before
         // the session started.
@@ -135,7 +129,7 @@
         mActivity.save();
 
         // Assert the snack bar is shown and tap "Save".
-        sUiBot.saveForAutofill(true, SAVE_DATA_TYPE_ADDRESS);
+        mUiBot.saveForAutofill(true, SAVE_DATA_TYPE_ADDRESS);
 
         final SaveRequest saveRequest = sReplier.getNextSaveRequest();
         assertWithMessage("onSave() not called").that(saveRequest).isNotNull();
@@ -182,7 +176,7 @@
         mActivity.syncRunOnUiThread(() -> mActivity.mAddress1.requestFocus());
 
         // Sanity check.
-        sUiBot.assertNoDatasets();
+        mUiBot.assertNoDatasets();
 
         // Wait for onFill() before proceeding, otherwise the fields might be changed before
         // the session started.
@@ -195,7 +189,7 @@
         mActivity.save();
 
         // Assert the snack bar is shown and tap "Save".
-        sUiBot.assertSaveNotShowing(SAVE_DATA_TYPE_ADDRESS);
+        mUiBot.assertSaveNotShowing(SAVE_DATA_TYPE_ADDRESS);
 
         // Once saved, the session should be finsihed.
         assertNoDanglingSessions();
@@ -320,7 +314,7 @@
         sReplier.getNextFillRequest();
 
         // Auto-fill it.
-        sUiBot.selectDataset("Da Dataset");
+        mUiBot.selectDataset("Da Dataset");
 
         // Check the results.
         mActivity.assertAutoFilled();
@@ -332,7 +326,7 @@
         mActivity.save();
 
         // Assert the snack bar is shown and tap "Save".
-        sUiBot.saveForAutofill(true, SAVE_DATA_TYPE_ADDRESS);
+        mUiBot.saveForAutofill(true, SAVE_DATA_TYPE_ADDRESS);
 
         final SaveRequest saveRequest = sReplier.getNextSaveRequest();
         assertWithMessage("onSave() not called").that(saveRequest).isNotNull();
@@ -419,7 +413,7 @@
         sReplier.getNextFillRequest();
 
         // Auto-fill it.
-        sUiBot.selectDataset("Da Dataset");
+        mUiBot.selectDataset("Da Dataset");
 
         // Check the results.
         mActivity.assertAutoFilled();
@@ -431,7 +425,7 @@
         mActivity.save();
 
         // Assert the snack bar is not shown.
-        sUiBot.assertSaveNotShowing(SAVE_DATA_TYPE_ADDRESS);
+        mUiBot.assertSaveNotShowing(SAVE_DATA_TYPE_ADDRESS);
 
         // Once saved, the session should be finsihed.
         assertNoDanglingSessions();
@@ -585,16 +579,16 @@
 
         // Make sure the snack bar is not shown.
         if (expectSaveUi) {
-            sUiBot.assertSaveShowing(SAVE_DATA_TYPE_ADDRESS);
+            mUiBot.assertSaveShowing(SAVE_DATA_TYPE_ADDRESS);
         } else {
-            sUiBot.assertSaveNotShowing(SAVE_DATA_TYPE_ADDRESS);
+            mUiBot.assertSaveNotShowing(SAVE_DATA_TYPE_ADDRESS);
         }
 
         // ...then tap save.
         mActivity.save();
 
         // Assert the snack bar is not shown.
-        sUiBot.assertSaveNotShowing(SAVE_DATA_TYPE_ADDRESS);
+        mUiBot.assertSaveNotShowing(SAVE_DATA_TYPE_ADDRESS);
     }
 
     @Test
@@ -621,7 +615,7 @@
         mActivity.syncRunOnUiThread(() -> mActivity.mAddress1.requestFocus());
         sReplier.getNextFillRequest();
 
-        sUiBot.selectDataset("SF");
+        mUiBot.selectDataset("SF");
         mActivity.assertAutoFilled();
 
         // Clear the field.
@@ -631,7 +625,7 @@
         mActivity.save();
 
         // ...and make sure the snack bar is not shown.
-        sUiBot.assertSaveNotShowing(SAVE_DATA_TYPE_ADDRESS);
+        mUiBot.assertSaveNotShowing(SAVE_DATA_TYPE_ADDRESS);
     }
 
     @Test
@@ -658,7 +652,7 @@
         mActivity.syncRunOnUiThread(() -> mActivity.mAddress1.requestFocus());
         sReplier.getNextFillRequest();
 
-        sUiBot.selectDataset("SF");
+        mUiBot.selectDataset("SF");
         mActivity.assertAutoFilled();
 
         // Clear the field.
@@ -668,7 +662,7 @@
         mActivity.save();
 
         // ...and make sure the snack bar is shown.
-        sUiBot.saveForAutofill(true, SAVE_DATA_TYPE_ADDRESS);
+        mUiBot.saveForAutofill(true, SAVE_DATA_TYPE_ADDRESS);
 
         // Finally, assert values.
         final SaveRequest saveRequest = sReplier.getNextSaveRequest();
diff --git a/tests/autofillservice/src/android/autofillservice/cts/OutOfProcessLoginActivity.java b/tests/autofillservice/src/android/autofillservice/cts/OutOfProcessLoginActivity.java
index 83d1ce2..d37cca6 100644
--- a/tests/autofillservice/src/android/autofillservice/cts/OutOfProcessLoginActivity.java
+++ b/tests/autofillservice/src/android/autofillservice/cts/OutOfProcessLoginActivity.java
@@ -45,6 +45,19 @@
     }
 
     @Override
+    protected void onStart() {
+        Log.i(LOG_TAG, "onStart()");
+        super.onStart();
+        try {
+            if (!getStartedMarker(this).createNewFile()) {
+                Log.e(LOG_TAG, "cannot write started file");
+            }
+        } catch (IOException e) {
+            Log.e(LOG_TAG, "cannot write started file");
+        }
+    }
+
+    @Override
     protected void onStop() {
         Log.i(LOG_TAG, "onStop()");
         super.onStop();
@@ -71,4 +84,14 @@
     @NonNull public static File getStoppedMarker(@NonNull Context context) {
         return new File(context.getFilesDir(), "stopped");
     }
+
+    /**
+     * Get the file that signals that the activity has entered {@link Activity#onStart()}.
+     *
+     * @param context Context of the app
+     * @return The marker file that is written onStart()
+     */
+    @NonNull public static File getStartedMarker(@NonNull Context context) {
+        return new File(context.getFilesDir(), "started");
+    }
 }
diff --git a/tests/autofillservice/src/android/autofillservice/cts/PartitionedActivityTest.java b/tests/autofillservice/src/android/autofillservice/cts/PartitionedActivityTest.java
index d458940..1882ed8 100644
--- a/tests/autofillservice/src/android/autofillservice/cts/PartitionedActivityTest.java
+++ b/tests/autofillservice/src/android/autofillservice/cts/PartitionedActivityTest.java
@@ -23,6 +23,8 @@
 import static android.autofillservice.cts.GridActivity.ID_L3C2;
 import static android.autofillservice.cts.GridActivity.ID_L4C1;
 import static android.autofillservice.cts.GridActivity.ID_L4C2;
+import static android.autofillservice.cts.Helper.UNUSED_AUTOFILL_VALUE;
+import static android.autofillservice.cts.Helper.assertHasFlags;
 import static android.autofillservice.cts.Helper.assertTextIsSanitized;
 import static android.autofillservice.cts.Helper.assertValue;
 import static android.autofillservice.cts.Helper.getContext;
@@ -46,12 +48,13 @@
 import android.content.IntentSender;
 import android.os.Bundle;
 import android.service.autofill.FillResponse;
-import android.widget.RemoteViews;
 
 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.
  */
@@ -68,6 +71,30 @@
         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));
+    }
+
     @Test
     public void testAutofillTwoPartitionsSkipFirst() throws Exception {
         // Set service.
@@ -83,7 +110,7 @@
         sReplier.addResponse(response1);
 
         // Trigger auto-fill on 1st partition.
-        mActivity.focusCell(1, 1);
+        focusCell(1, 1);
         final FillRequest fillRequest1 = sReplier.getNextFillRequest();
         assertThat(fillRequest1.flags).isEqualTo(0);
         final ViewNode p1l1c1 = assertTextIsSanitized(fillRequest1.structure, ID_L1C1);
@@ -92,9 +119,9 @@
         assertWithMessage("Focus on p1l1c2").that(p1l1c2.isFocused()).isFalse();
 
         // Make sure UI is shown, but don't tap it.
-        sUiBot.assertDatasets("l1c1");
-        mActivity.focusCell(1, 2);
-        sUiBot.assertDatasets("l1c2");
+        mUiBot.assertDatasets("l1c1");
+        focusCell(1, 2);
+        mUiBot.assertDatasets("l1c2");
 
         // Now tap a field in a different partition
         final CannedFillResponse response2 = new CannedFillResponse.Builder()
@@ -106,7 +133,7 @@
         sReplier.addResponse(response2);
 
         // Trigger auto-fill on 2nd partition.
-        mActivity.focusCell(2, 1);
+        focusCell(2, 1);
         final FillRequest fillRequest2 = sReplier.getNextFillRequest();
         assertThat(fillRequest2.flags).isEqualTo(0);
         final ViewNode p2l1c1 = assertTextIsSanitized(fillRequest2.structure, ID_L1C1);
@@ -118,16 +145,16 @@
         assertWithMessage("Focus on p2l2c1").that(p2l2c1.isFocused()).isTrue();
         assertWithMessage("Focus on p2l2c2").that(p2l2c2.isFocused()).isFalse();
         // Make sure UI is shown, but don't tap it.
-        sUiBot.assertDatasets("l2c1");
-        mActivity.focusCell(2, 2);
-        sUiBot.assertDatasets("l2c2");
+        mUiBot.assertDatasets("l2c1");
+        focusCell(2, 2);
+        mUiBot.assertDatasets("l2c2");
 
         // Now fill them
         final FillExpectation expectation1 = mActivity.expectAutofill()
               .onCell(1, 1, "l1c1")
               .onCell(1, 2, "l1c2");
-        mActivity.focusCell(1, 1);
-        sUiBot.selectDataset("l1c1");
+        focusCell(1, 1);
+        mUiBot.selectDataset("l1c1");
         expectation1.assertAutoFilled();
 
         // Change previous values to make sure they are not filled again
@@ -137,8 +164,8 @@
         final FillExpectation expectation2 = mActivity.expectAutofill()
                 .onCell(2, 1, "l2c1")
                 .onCell(2, 2, "l2c2");
-        mActivity.focusCell(2, 2);
-        sUiBot.selectDataset("l2c2");
+        focusCell(2, 2);
+        mUiBot.selectDataset("l2c2");
         expectation2.assertAutoFilled();
 
         // Make sure previous partition didn't change
@@ -166,7 +193,7 @@
                 .onCell(1, 2, "l1c2");
 
         // Trigger auto-fill.
-        mActivity.focusCell(1, 1);
+        focusCell(1, 1);
         final FillRequest fillRequest1 = sReplier.getNextFillRequest();
         assertThat(fillRequest1.flags).isEqualTo(0);
 
@@ -174,7 +201,7 @@
         assertTextIsSanitized(fillRequest1.structure, ID_L1C2);
 
         // Auto-fill it.
-        sUiBot.selectDataset("Partition 1");
+        mUiBot.selectDataset("Partition 1");
 
         // Check the results.
         expectation1.assertAutoFilled();
@@ -194,7 +221,7 @@
                 .onCell(2, 2, "l2c2");
 
         // Trigger auto-fill.
-        mActivity.focusCell(2, 1);
+        focusCell(2, 1);
         final FillRequest fillRequest2 = sReplier.getNextFillRequest();
         assertThat(fillRequest2.flags).isEqualTo(0);
 
@@ -204,7 +231,7 @@
         assertTextIsSanitized(fillRequest2.structure, ID_L2C2);
 
         // Auto-fill it.
-        sUiBot.selectDataset("Partition 2");
+        mUiBot.selectDataset("Partition 2");
 
         // Check the results.
         expectation2.assertAutoFilled();
@@ -253,7 +280,7 @@
         assertTextIsSanitized(fillRequest1.structure, ID_L1C2);
 
         // Auto-fill it.
-        sUiBot.selectDataset("Partition 1");
+        mUiBot.selectDataset("Partition 1");
 
         // Check the results.
         expectation1.assertAutoFilled();
@@ -287,7 +314,7 @@
         assertTextIsSanitized(fillRequest2.structure, ID_L2C2);
 
         // Auto-fill it.
-        sUiBot.selectDataset("Partition 2");
+        mUiBot.selectDataset("Partition 2");
 
         // Check the results.
         expectation2.assertAutoFilled();
@@ -323,7 +350,7 @@
         assertTextIsSanitized(fillRequest3.structure, ID_L3C2);
 
         // Auto-fill it.
-        sUiBot.selectDataset("Partition 3");
+        mUiBot.selectDataset("Partition 3");
 
         // Check the results.
         expectation3.assertAutoFilled();
@@ -361,7 +388,7 @@
         assertTextIsSanitized(fillRequest4.structure, ID_L4C2);
 
         // Auto-fill it.
-        sUiBot.selectDataset("Partition 4");
+        mUiBot.selectDataset("Partition 4");
 
         // Check the results.
         expectation4.assertAutoFilled();
@@ -387,7 +414,7 @@
                 .onCell(1, 2, "l1c2");
 
         // Trigger auto-fill.
-        mActivity.focusCell(1, 1);
+        focusCell(1, 1);
         final FillRequest fillRequest1 = sReplier.getNextFillRequest();
         assertThat(fillRequest1.flags).isEqualTo(0);
 
@@ -395,7 +422,7 @@
         assertTextIsSanitized(fillRequest1.structure, ID_L1C2);
 
         // Auto-fill it.
-        sUiBot.selectDataset("Partition 1");
+        mUiBot.selectDataset("Partition 1");
 
         // Check the results.
         expectation1.assertAutoFilled();
@@ -420,7 +447,7 @@
         // Trigger auto-fill.
         mActivity.forceAutofill(2, 1);
         final FillRequest fillRequest2 = sReplier.getNextFillRequest();
-        assertThat(fillRequest2.flags).isEqualTo(FLAG_MANUAL_REQUEST);
+        assertHasFlags(fillRequest2.flags, FLAG_MANUAL_REQUEST);
 
         assertValue(fillRequest2.structure, ID_L1C1, "l1c1");
         assertValue(fillRequest2.structure, ID_L1C2, "l1c2");
@@ -428,7 +455,7 @@
         assertTextIsSanitized(fillRequest2.structure, ID_L2C2);
 
         // Auto-fill it.
-        sUiBot.selectDataset("Partition 2");
+        mUiBot.selectDataset("Partition 2");
 
         // Check the results.
         expectation2.assertAutoFilled();
@@ -448,7 +475,7 @@
                 .onCell(3, 2, "l3c2");
 
         // Trigger auto-fill.
-        mActivity.focusCell(3, 1);
+        focusCell(3, 1);
         final FillRequest fillRequest3 = sReplier.getNextFillRequest();
         assertThat(fillRequest3.flags).isEqualTo(0);
 
@@ -460,7 +487,7 @@
         assertTextIsSanitized(fillRequest3.structure, ID_L3C2);
 
         // Auto-fill it.
-        sUiBot.selectDataset("Partition 3");
+        mUiBot.selectDataset("Partition 3");
 
         // Check the results.
         expectation3.assertAutoFilled();
@@ -484,7 +511,7 @@
         // Trigger auto-fill.
         mActivity.forceAutofill(4, 1);
         final FillRequest fillRequest4 = sReplier.getNextFillRequest();
-        assertThat(fillRequest4.flags).isEqualTo(FLAG_MANUAL_REQUEST);
+        assertHasFlags(fillRequest4.flags, FLAG_MANUAL_REQUEST);
 
         assertValue(fillRequest4.structure, ID_L1C1, "l1c1");
         assertValue(fillRequest4.structure, ID_L1C2, "l1c2");
@@ -496,7 +523,7 @@
         assertTextIsSanitized(fillRequest4.structure, ID_L4C2);
 
         // Auto-fill it.
-        sUiBot.selectDataset("Partition 4");
+        mUiBot.selectDataset("Partition 4");
 
         // Check the results.
         expectation4.assertAutoFilled();
@@ -521,11 +548,11 @@
         sReplier.addResponse(response1);
 
         // Trigger auto-fill on 1st partition.
-        mActivity.focusCell(1, 1);
+        focusCell(1, 1);
         final FillRequest fillRequest1 = sReplier.getNextFillRequest();
         assertThat(fillRequest1.flags).isEqualTo(0);
         assertThat(fillRequest1.data).isNull();
-        sUiBot.assertDatasets("l1c1");
+        mUiBot.assertDatasets("l1c1");
 
         // Prepare 2nd partition; it replaces 'number' and adds 'numbers2'
         extras.clear();
@@ -542,7 +569,7 @@
         sReplier.addResponse(response2);
 
         // Trigger auto-fill on 2nd partition
-        mActivity.focusCell(2, 1);
+        focusCell(2, 1);
         final FillRequest fillRequest2 = sReplier.getNextFillRequest();
         assertThat(fillRequest2.flags).isEqualTo(0);
         assertWithMessage("null bundle on request 2").that(fillRequest2.data).isNotNull();
@@ -561,7 +588,7 @@
         sReplier.addResponse(response3);
 
         // Trigger auto-fill on 3rd partition
-        mActivity.focusCell(3, 1);
+        focusCell(3, 1);
         final FillRequest fillRequest3 = sReplier.getNextFillRequest();
         assertThat(fillRequest3.flags).isEqualTo(0);
         assertWithMessage("null bundle on request 3").that(fillRequest2.data).isNotNull();
@@ -586,7 +613,7 @@
         sReplier.addResponse(response4);
 
         // Trigger auto-fill on 4th partition
-        mActivity.focusCell(4, 1);
+        focusCell(4, 1);
         final FillRequest fillRequest4 = sReplier.getNextFillRequest();
         assertThat(fillRequest4.flags).isEqualTo(0);
         assertWithMessage("non-null bundle on request 4").that(fillRequest4.data).isNull();
@@ -595,7 +622,7 @@
         mActivity.setText(1, 1, "L1C1");
         mActivity.save();
 
-        sUiBot.saveForAutofill(true, SAVE_DATA_TYPE_PASSWORD);
+        mUiBot.saveForAutofill(true, SAVE_DATA_TYPE_PASSWORD);
         final SaveRequest saveRequest = sReplier.getNextSaveRequest();
 
         assertWithMessage("wrong number of extras on save request bundle")
@@ -616,7 +643,7 @@
                         .build())
                 .build();
         sReplier.addResponse(response1);
-        mActivity.focusCell(1, 1);
+        focusCell(1, 1);
         sReplier.getNextFillRequest();
 
         // Trigger 2nd partition.
@@ -628,14 +655,14 @@
                 .setRequiredSavableIds(SAVE_DATA_TYPE_PASSWORD, ID_L2C1)
                 .build();
         sReplier.addResponse(response2);
-        mActivity.focusCell(2, 1);
+        focusCell(2, 1);
         sReplier.getNextFillRequest();
 
         // Trigger save
         mActivity.setText(2, 1, "L2C1");
         mActivity.save();
 
-        sUiBot.saveForAutofill(true, SAVE_DATA_TYPE_PASSWORD);
+        mUiBot.saveForAutofill(true, SAVE_DATA_TYPE_PASSWORD);
         final SaveRequest saveRequest = sReplier.getNextSaveRequest();
         assertValue(saveRequest.structure, ID_L2C1, "L2C1");
     }
@@ -653,7 +680,7 @@
                         .build())
                 .build();
         sReplier.addResponse(response1);
-        mActivity.focusCell(1, 1);
+        focusCell(1, 1);
         sReplier.getNextFillRequest();
 
         // Trigger 2nd partition.
@@ -665,14 +692,14 @@
                 .setRequiredSavableIds(SAVE_DATA_TYPE_PASSWORD, ID_L1C1)
                 .build();
         sReplier.addResponse(response2);
-        mActivity.focusCell(2, 1);
+        focusCell(2, 1);
         sReplier.getNextFillRequest();
 
         // Trigger save
         mActivity.setText(1, 1, "L1C1");
         mActivity.save();
 
-        sUiBot.saveForAutofill(true, SAVE_DATA_TYPE_PASSWORD);
+        mUiBot.saveForAutofill(true, SAVE_DATA_TYPE_PASSWORD);
         final SaveRequest saveRequest = sReplier.getNextSaveRequest();
         assertValue(saveRequest.structure, ID_L1C1, "L1C1");
     }
@@ -691,7 +718,7 @@
                 .setRequiredSavableIds(SAVE_DATA_TYPE_PASSWORD, ID_L1C1)
                 .build();
         sReplier.addResponse(response1);
-        mActivity.focusCell(1, 1);
+        focusCell(1, 1);
         sReplier.getNextFillRequest();
 
         // Trigger 2nd partition.
@@ -704,7 +731,7 @@
                         ID_L2C1)
                 .build();
         sReplier.addResponse(response2);
-        mActivity.focusCell(2, 1);
+        focusCell(2, 1);
         sReplier.getNextFillRequest();
 
         // Trigger save
@@ -712,7 +739,7 @@
         mActivity.setText(2, 1, "L2C1");
         mActivity.save();
 
-        sUiBot.saveForAutofill(true, SAVE_DATA_TYPE_PASSWORD, SAVE_DATA_TYPE_CREDIT_CARD);
+        mUiBot.saveForAutofill(true, SAVE_DATA_TYPE_PASSWORD, SAVE_DATA_TYPE_CREDIT_CARD);
         final SaveRequest saveRequest = sReplier.getNextSaveRequest();
         assertValue(saveRequest.structure, ID_L1C1, "L1C1");
         assertValue(saveRequest.structure, ID_L2C1, "L2C1");
@@ -732,7 +759,7 @@
                 .setRequiredSavableIds(SAVE_DATA_TYPE_PASSWORD, ID_L1C1)
                 .build();
         sReplier.addResponse(response1);
-        mActivity.focusCell(1, 1);
+        focusCell(1, 1);
         sReplier.getNextFillRequest();
 
         // Trigger 2nd partition.
@@ -745,7 +772,7 @@
                         ID_L2C1)
                 .build();
         sReplier.addResponse(response2);
-        mActivity.focusCell(2, 1);
+        focusCell(2, 1);
         sReplier.getNextFillRequest();
 
         // Trigger 3rd partition.
@@ -758,7 +785,7 @@
                         | SAVE_DATA_TYPE_USERNAME, ID_L3C1)
                 .build();
         sReplier.addResponse(response3);
-        mActivity.focusCell(3, 1);
+        focusCell(3, 1);
         sReplier.getNextFillRequest();
 
         // Trigger save
@@ -767,7 +794,7 @@
         mActivity.setText(3, 1, "L3C1");
         mActivity.save();
 
-        sUiBot.saveForAutofill(true, SAVE_DATA_TYPE_PASSWORD, SAVE_DATA_TYPE_CREDIT_CARD,
+        mUiBot.saveForAutofill(true, SAVE_DATA_TYPE_PASSWORD, SAVE_DATA_TYPE_CREDIT_CARD,
                 SAVE_DATA_TYPE_USERNAME);
         final SaveRequest saveRequest = sReplier.getNextSaveRequest();
         assertValue(saveRequest.structure, ID_L1C1, "L1C1");
@@ -789,7 +816,7 @@
                 .setRequiredSavableIds(SAVE_DATA_TYPE_PASSWORD, ID_L1C1)
                 .build();
         sReplier.addResponse(response1);
-        mActivity.focusCell(1, 1);
+        focusCell(1, 1);
         sReplier.getNextFillRequest();
 
         // Trigger 2nd partition.
@@ -801,7 +828,7 @@
                 .setRequiredSavableIds(SAVE_DATA_TYPE_PASSWORD | SAVE_DATA_TYPE_GENERIC, ID_L2C1)
                 .build();
         sReplier.addResponse(response2);
-        mActivity.focusCell(2, 1);
+        focusCell(2, 1);
         sReplier.getNextFillRequest();
 
         // Trigger 3rd partition.
@@ -815,7 +842,7 @@
                         ID_L3C1)
                 .build();
         sReplier.addResponse(response3);
-        mActivity.focusCell(3, 1);
+        focusCell(3, 1);
         sReplier.getNextFillRequest();
 
 
@@ -826,7 +853,7 @@
         mActivity.save();
 
         // Make sure GENERIC type is not shown on snackbar
-        sUiBot.saveForAutofill(true, SAVE_DATA_TYPE_PASSWORD, SAVE_DATA_TYPE_USERNAME);
+        mUiBot.saveForAutofill(true, SAVE_DATA_TYPE_PASSWORD, SAVE_DATA_TYPE_USERNAME);
         final SaveRequest saveRequest = sReplier.getNextSaveRequest();
         assertValue(saveRequest.structure, ID_L1C1, "L1C1");
         assertValue(saveRequest.structure, ID_L2C1, "L2C1");
@@ -847,7 +874,7 @@
                 .setRequiredSavableIds(SAVE_DATA_TYPE_PASSWORD, ID_L1C1)
                 .build();
         sReplier.addResponse(response1);
-        mActivity.focusCell(1, 1);
+        focusCell(1, 1);
         sReplier.getNextFillRequest();
 
         // Trigger 2nd partition.
@@ -860,7 +887,7 @@
                         ID_L2C1)
                 .build();
         sReplier.addResponse(response2);
-        mActivity.focusCell(2, 1);
+        focusCell(2, 1);
         sReplier.getNextFillRequest();
 
         // Trigger 3rd partition.
@@ -873,7 +900,7 @@
                         | SAVE_DATA_TYPE_USERNAME, ID_L3C1)
                 .build();
         sReplier.addResponse(response3);
-        mActivity.focusCell(3, 1);
+        focusCell(3, 1);
         sReplier.getNextFillRequest();
 
         // Trigger 4th partition.
@@ -886,7 +913,7 @@
                         | SAVE_DATA_TYPE_USERNAME | SAVE_DATA_TYPE_ADDRESS, ID_L4C1)
                 .build();
         sReplier.addResponse(response4);
-        mActivity.focusCell(4, 1);
+        focusCell(4, 1);
         sReplier.getNextFillRequest();
 
 
@@ -897,7 +924,7 @@
         mActivity.setText(4, 1, "L4C1");
         mActivity.save();
 
-        sUiBot.saveForAutofill(true, SAVE_DATA_TYPE_GENERIC);
+        mUiBot.saveForAutofill(true, SAVE_DATA_TYPE_GENERIC);
         final SaveRequest saveRequest = sReplier.getNextSaveRequest();
         assertValue(saveRequest.structure, ID_L1C1, "L1C1");
         assertValue(saveRequest.structure, ID_L2C1, "L2C1");
@@ -921,7 +948,7 @@
         sReplier.addResponse(response1);
 
         // Trigger auto-fill on 1st partition.
-        mActivity.focusCell(1, 1);
+        focusCell(1, 1);
         final FillRequest fillRequest1 = sReplier.getNextFillRequest();
         assertThat(fillRequest1.flags).isEqualTo(0);
         final ViewNode p1l1c1 = assertTextIsSanitized(fillRequest1.structure, ID_L1C1);
@@ -930,15 +957,15 @@
         assertWithMessage("Focus on p1l1c2").that(p1l1c2.isFocused()).isFalse();
 
         // Make sure UI is shown on 1st partition
-        sUiBot.assertDatasets("l1c1");
-        mActivity.focusCell(1, 2);
-        sUiBot.assertDatasets("l1c2");
+        mUiBot.assertDatasets("l1c1");
+        focusCell(1, 2);
+        mUiBot.assertDatasets("l1c2");
 
         // Make sure UI is not shown on ignored partition
-        mActivity.focusCell(2, 1);
-        sUiBot.assertNoDatasets();
-        mActivity.focusCell(2, 2);
-        sUiBot.assertNoDatasets();
+        focusCell(2, 1);
+        mUiBot.assertNoDatasets();
+        focusCellNoWindowChange(2, 2);
+        mUiBot.assertNoDatasets();
     }
 
     /**
@@ -971,7 +998,7 @@
                 .onCell(1, 2, "l1c2");
 
         // Trigger partition.
-        mActivity.focusCell(1, 1);
+        focusCell(1, 1);
         sReplier.getNextFillRequest();
 
 
@@ -995,7 +1022,7 @@
                 .onCell(2, 2, "L2C2");
 
         // Trigger partition.
-        mActivity.focusCell(2, 1);
+        focusCell(2, 1);
         sReplier.getNextFillRequest();
 
         /**
@@ -1020,7 +1047,7 @@
                 .onCell(3, 2, "L3C2");
 
         // Trigger partition.
-        mActivity.focusCell(3, 1);
+        focusCell(3, 1);
         sReplier.getNextFillRequest();
 
         /**
@@ -1043,49 +1070,49 @@
                 .onCell(4, 1, "l4c1");
 
         // Trigger partition.
-        mActivity.focusCell(4, 1);
+        focusCell(4, 1);
         sReplier.getNextFillRequest();
 
         /*
          *  Now move focus around to make sure the proper values are displayed each time.
          */
-        mActivity.focusCell(1, 1);
-        sUiBot.assertDatasets("P1D1", "P1D2");
-        mActivity.focusCell(1, 2);
-        sUiBot.assertDatasets("P1D1");
+        focusCell(1, 1);
+        mUiBot.assertDatasets("P1D1", "P1D2");
+        focusCell(1, 2);
+        mUiBot.assertDatasets("P1D1");
 
-        mActivity.focusCell(2, 1);
-        sUiBot.assertDatasets("P2D1");
-        mActivity.focusCell(2, 2);
-        sUiBot.assertDatasets("P2D1", "P2D2");
+        focusCell(2, 1);
+        mUiBot.assertDatasets("P2D1");
+        focusCell(2, 2);
+        mUiBot.assertDatasets("P2D1", "P2D2");
 
-        mActivity.focusCell(4, 1);
-        sUiBot.assertDatasets("P4D1", "P4D2");
-        mActivity.focusCell(4, 2);
-        sUiBot.assertDatasets("P4D2");
+        focusCell(4, 1);
+        mUiBot.assertDatasets("P4D1", "P4D2");
+        focusCell(4, 2);
+        mUiBot.assertDatasets("P4D2");
 
-        mActivity.focusCell(3, 2);
-        sUiBot.assertDatasets("P3D1", "P3D2");
-        mActivity.focusCell(3, 1);
-        sUiBot.assertDatasets("P3D1", "P3D2");
+        focusCell(3, 2);
+        mUiBot.assertDatasets("P3D1", "P3D2");
+        focusCell(3, 1);
+        mUiBot.assertDatasets("P3D1", "P3D2");
 
         /*
          *  Finally, autofill and check results.
          */
-        mActivity.focusCell(4, 1);
-        sUiBot.selectDataset("P4D1");
+        focusCell(4, 1);
+        mUiBot.selectDataset("P4D1");
         expectation4.assertAutoFilled();
 
-        mActivity.focusCell(1, 1);
-        sUiBot.selectDataset("P1D1");
+        focusCell(1, 1);
+        mUiBot.selectDataset("P1D1");
         expectation1.assertAutoFilled();
 
-        mActivity.focusCell(3, 1);
-        sUiBot.selectDataset("P3D2");
+        focusCell(3, 1);
+        mUiBot.selectDataset("P3D2");
         expectation3.assertAutoFilled();
 
-        mActivity.focusCell(2, 2);
-        sUiBot.selectDataset("P2D2");
+        focusCell(2, 2);
+        mUiBot.selectDataset("P2D2");
         expectation2.assertAutoFilled();
     }
 
@@ -1141,14 +1168,13 @@
         sReplier.addResponse(response1);
 
         // Trigger partition.
-        mActivity.focusCell(1, 1);
+        focusCell(1, 1);
         sReplier.getNextFillRequest();
 
         // Asserts proper datasets are shown on each field defined so far.
-        mActivity.focusCell(1, 1);
-        sUiBot.assertDatasets("P1D1", "P1D2");
-        mActivity.focusCell(1, 2);
-        sUiBot.assertDatasets("P1D1");
+        mUiBot.assertDatasets("P1D1", "P1D2");
+        focusCell(1, 2);
+        mUiBot.assertDatasets("P1D1");
 
         /**
          * 2nd partition.
@@ -1169,18 +1195,18 @@
         sReplier.addResponse(response2);
 
         // Trigger partition.
-        mActivity.focusCell(2, 1);
+        focusCell(2, 1);
         sReplier.getNextFillRequest();
 
         // Asserts proper datasets are shown on each field defined so far.
-        mActivity.focusCell(1, 1);
-        sUiBot.assertDatasets("P2D1"); // changed
-        mActivity.focusCell(1, 2);
-        sUiBot.assertDatasets("P1D1");
-        mActivity.focusCell(2, 1);
-        sUiBot.assertDatasets("P2D1");
-        mActivity.focusCell(2, 2);
-        sUiBot.assertDatasets("P2D1", "P2D2");
+        focusCell(1, 1);
+        mUiBot.assertDatasets("P2D1"); // changed
+        focusCell(1, 2);
+        mUiBot.assertDatasets("P1D1");
+        focusCell(2, 1);
+        mUiBot.assertDatasets("P2D1");
+        focusCell(2, 2);
+        mUiBot.assertDatasets("P2D1", "P2D2");
 
         /**
          * 3rd partition.
@@ -1203,22 +1229,22 @@
         sReplier.addResponse(response3);
 
         // Trigger partition.
-        mActivity.focusCell(3, 1);
+        focusCell(3, 1);
         sReplier.getNextFillRequest();
 
         // Asserts proper datasets are shown on each field defined so far.
-        mActivity.focusCell(1, 1);
-        sUiBot.assertDatasets("P2D1");
-        mActivity.focusCell(1, 2);
-        sUiBot.assertDatasets("P3D1"); // changed
-        mActivity.focusCell(2, 1);
-        sUiBot.assertDatasets("P2D1");
-        mActivity.focusCell(2, 2);
-        sUiBot.assertDatasets("P3D2"); // changed
-        mActivity.focusCell(3, 2);
-        sUiBot.assertDatasets("P3D1", "P3D2");
-        mActivity.focusCell(3, 1);
-        sUiBot.assertDatasets("P3D1", "P3D2");
+        focusCell(1, 1);
+        mUiBot.assertDatasets("P2D1");
+        focusCell(1, 2);
+        mUiBot.assertDatasets("P3D1"); // changed
+        focusCell(2, 1);
+        mUiBot.assertDatasets("P2D1");
+        focusCell(2, 2);
+        mUiBot.assertDatasets("P3D2"); // changed
+        focusCell(3, 2);
+        mUiBot.assertDatasets("P3D1", "P3D2");
+        focusCell(3, 1);
+        mUiBot.assertDatasets("P3D1", "P3D2");
 
         /**
          * 4th partition.
@@ -1251,26 +1277,26 @@
         sReplier.addResponse(response4);
 
         // Trigger partition.
-        mActivity.focusCell(4, 1);
+        focusCell(4, 1);
         sReplier.getNextFillRequest();
 
         // Asserts proper datasets are shown on each field defined so far.
-        mActivity.focusCell(1, 1);
-        sUiBot.assertDatasets("P4D1", "P4D2");
-        mActivity.focusCell(1, 2);
-        sUiBot.assertDatasets("P4D1", "P4D2");
-        mActivity.focusCell(2, 1);
-        sUiBot.assertDatasets("P4D1", "P4D2");
-        mActivity.focusCell(2, 2);
-        sUiBot.assertDatasets("P4D1", "P4D2");
-        mActivity.focusCell(3, 2);
-        sUiBot.assertDatasets("P4D1", "P4D2");
-        mActivity.focusCell(3, 1);
-        sUiBot.assertDatasets("P4D1", "P4D2");
-        mActivity.focusCell(4, 1);
-        sUiBot.assertDatasets("P4D1", "P4D2");
-        mActivity.focusCell(4, 2);
-        sUiBot.assertDatasets("P4D2");
+        focusCell(1, 1);
+        mUiBot.assertDatasets("P4D1", "P4D2");
+        focusCell(1, 2);
+        mUiBot.assertDatasets("P4D1", "P4D2");
+        focusCell(2, 1);
+        mUiBot.assertDatasets("P4D1", "P4D2");
+        focusCell(2, 2);
+        mUiBot.assertDatasets("P4D1", "P4D2");
+        focusCell(3, 2);
+        mUiBot.assertDatasets("P4D1", "P4D2");
+        focusCell(3, 1);
+        mUiBot.assertDatasets("P4D1", "P4D2");
+        focusCell(4, 1);
+        mUiBot.assertDatasets("P4D1", "P4D2");
+        focusCell(4, 2);
+        mUiBot.assertDatasets("P4D2");
 
         /*
          * Finally, autofill and check results.
@@ -1300,8 +1326,8 @@
             chosenOne = "P4D2";
         }
 
-        mActivity.focusCell(4, 1);
-        sUiBot.selectDataset(chosenOne);
+        focusCell(4, 1);
+        mUiBot.selectDataset(chosenOne);
         expectation.assertAutoFilled();
     }
 
@@ -1310,10 +1336,6 @@
         // Set service.
         enableService();
 
-        // TODO: current API requires these fields...
-        final RemoteViews bogusPresentation = createPresentation("Whatever man, I'm not used...");
-        final String bogusValue = "Y U REQUIRE IT?";
-
         /**
          * 1st partition.
          */
@@ -1322,19 +1344,18 @@
                 new CannedDataset.Builder()
                         .setField(ID_L1C1, "l1c1")
                         .setField(ID_L1C2, "l1c2")
-                        .setPresentation(bogusPresentation)
                         .build());
         final IntentSender auth12 = AuthenticationActivity.createSender(getContext(), 12);
         final CannedFillResponse response1 = new CannedFillResponse.Builder()
                 .addDataset(new CannedDataset.Builder()
                         .setAuthentication(auth11)
-                        .setField(ID_L1C1, bogusValue)
-                        .setField(ID_L1C2, bogusValue)
+                        .setField(ID_L1C1, UNUSED_AUTOFILL_VALUE)
+                        .setField(ID_L1C2, UNUSED_AUTOFILL_VALUE)
                         .setPresentation(createPresentation("P1D1"))
                         .build())
                 .addDataset(new CannedDataset.Builder()
                         .setAuthentication(auth12)
-                        .setField(ID_L1C1, bogusValue)
+                        .setField(ID_L1C1, UNUSED_AUTOFILL_VALUE)
                         .setPresentation(createPresentation("P1D2"))
                         .build())
                 .build();
@@ -1344,16 +1365,16 @@
                 .onCell(1, 2, "l1c2");
 
         // Trigger partition.
-        mActivity.focusCell(1, 1);
+        focusCell(1, 1);
         sReplier.getNextFillRequest();
 
         // Focus around different fields in the partition.
-        sUiBot.assertDatasets("P1D1", "P1D2");
-        mActivity.focusCell(1, 2);
-        sUiBot.assertDatasets("P1D1");
+        mUiBot.assertDatasets("P1D1", "P1D2");
+        focusCell(1, 2);
+        mUiBot.assertDatasets("P1D1");
 
         // Autofill it...
-        sUiBot.selectDataset("P1D1");
+        mUiBot.selectDataset("P1D1");
         // ... and assert result
         expectation1.assertAutoFilled();
 
@@ -1365,19 +1386,18 @@
         final IntentSender auth22 = AuthenticationActivity.createSender(getContext(), 22,
                 new CannedDataset.Builder()
                     .setField(ID_L2C2, "L2C2")
-                    .setPresentation(bogusPresentation)
                     .build());
         final CannedFillResponse response2 = new CannedFillResponse.Builder()
                 .addDataset(new CannedDataset.Builder()
                         .setAuthentication(auth21)
                         .setPresentation(createPresentation("P2D1"))
-                        .setField(ID_L2C1, bogusValue)
-                        .setField(ID_L2C2, bogusValue)
+                        .setField(ID_L2C1, UNUSED_AUTOFILL_VALUE)
+                        .setField(ID_L2C2, UNUSED_AUTOFILL_VALUE)
                         .build())
                 .addDataset(new CannedDataset.Builder()
                         .setAuthentication(auth22)
                         .setPresentation(createPresentation("P2D2"))
-                        .setField(ID_L2C2, bogusValue)
+                        .setField(ID_L2C2, UNUSED_AUTOFILL_VALUE)
                         .build())
                 .build();
         sReplier.addResponse(response2);
@@ -1385,16 +1405,16 @@
                 .onCell(2, 2, "L2C2");
 
         // Trigger partition.
-        mActivity.focusCell(2, 1);
+        focusCell(2, 1);
         sReplier.getNextFillRequest();
 
         // Focus around different fields in the partition.
-        sUiBot.assertDatasets("P2D1");
-        mActivity.focusCell(2, 2);
-        sUiBot.assertDatasets("P2D1", "P2D2");
+        mUiBot.assertDatasets("P2D1");
+        focusCell(2, 2);
+        mUiBot.assertDatasets("P2D1", "P2D2");
 
         // Autofill it...
-        sUiBot.selectDataset("P2D2");
+        mUiBot.selectDataset("P2D2");
         // ... and assert result
         expectation2.assertAutoFilled();
 
@@ -1406,21 +1426,20 @@
                 new CannedDataset.Builder()
                         .setField(ID_L3C1, "l3c1")
                         .setField(ID_L3C2, "l3c2")
-                        .setPresentation(bogusPresentation)
                         .build());
         final IntentSender auth32 = AuthenticationActivity.createSender(getContext(), 32);
         final CannedFillResponse response3 = new CannedFillResponse.Builder()
                 .addDataset(new CannedDataset.Builder()
                         .setAuthentication(auth31)
                         .setPresentation(createPresentation("P3D1"))
-                        .setField(ID_L3C1, bogusValue)
-                        .setField(ID_L3C2, bogusValue)
+                        .setField(ID_L3C1, UNUSED_AUTOFILL_VALUE)
+                        .setField(ID_L3C2, UNUSED_AUTOFILL_VALUE)
                         .build())
                 .addDataset(new CannedDataset.Builder()
                         .setAuthentication(auth32)
                         .setPresentation(createPresentation("P3D2"))
-                        .setField(ID_L3C1, bogusValue)
-                        .setField(ID_L3C2, bogusValue)
+                        .setField(ID_L3C1, UNUSED_AUTOFILL_VALUE)
+                        .setField(ID_L3C2, UNUSED_AUTOFILL_VALUE)
                         .build())
                 .build();
         sReplier.addResponse(response3);
@@ -1429,16 +1448,16 @@
                 .onCell(3, 2, "l3c2");
 
         // Trigger partition.
-        mActivity.focusCell(3, 2);
+        focusCell(3, 2);
         sReplier.getNextFillRequest();
 
         // Focus around different fields in the partition.
-        sUiBot.assertDatasets("P3D1", "P3D2");
-        mActivity.focusCell(3, 1);
-        sUiBot.assertDatasets("P3D1", "P3D2");
+        mUiBot.assertDatasets("P3D1", "P3D2");
+        focusCell(3, 1);
+        mUiBot.assertDatasets("P3D1", "P3D2");
 
         // Autofill it...
-        sUiBot.selectDataset("P3D1");
+        mUiBot.selectDataset("P3D1");
         // ... and assert result
         expectation3.assertAutoFilled();
 
@@ -1451,19 +1470,18 @@
                 new CannedDataset.Builder()
                     .setField(ID_L4C1, "L4C1")
                     .setField(ID_L4C2, "L4C2")
-                    .setPresentation(bogusPresentation)
                     .build());
         final CannedFillResponse response4 = new CannedFillResponse.Builder()
                 .addDataset(new CannedDataset.Builder()
                         .setAuthentication(auth41)
                         .setPresentation(createPresentation("P4D1"))
-                        .setField(ID_L4C1, bogusValue)
+                        .setField(ID_L4C1, UNUSED_AUTOFILL_VALUE)
                         .build())
                 .addDataset(new CannedDataset.Builder()
                         .setAuthentication(auth42)
                         .setPresentation(createPresentation("P4D2"))
-                        .setField(ID_L4C1, bogusValue)
-                        .setField(ID_L4C2, bogusValue)
+                        .setField(ID_L4C1, UNUSED_AUTOFILL_VALUE)
+                        .setField(ID_L4C2, UNUSED_AUTOFILL_VALUE)
                         .build())
                 .build();
         sReplier.addResponse(response4);
@@ -1472,16 +1490,16 @@
                 .onCell(4, 2, "L4C2");
 
         // Trigger partition.
-        mActivity.focusCell(4, 1);
+        focusCell(4, 1);
         sReplier.getNextFillRequest();
 
         // Focus around different fields in the partition.
-        sUiBot.assertDatasets("P4D1", "P4D2");
-        mActivity.focusCell(4, 2);
-        sUiBot.assertDatasets("P4D2");
+        mUiBot.assertDatasets("P4D1", "P4D2");
+        focusCell(4, 2);
+        mUiBot.assertDatasets("P4D2");
 
         // Autofill it...
-        sUiBot.selectDataset("P4D2");
+        mUiBot.selectDataset("P4D2");
         // ... and assert result
         expectation4.assertAutoFilled();
     }
@@ -1496,10 +1514,6 @@
         // Set service.
         enableService();
 
-        // TODO: current API requires these fields...
-        final RemoteViews bogusPresentation = createPresentation("Whatever man, I'm not used...");
-        final String bogusValue = "Y U REQUIRE IT?";
-
         /**
          * 1st partition.
          */
@@ -1508,19 +1522,18 @@
                 new CannedDataset.Builder()
                         .setField(ID_L1C1, "l1c1")
                         .setField(ID_L1C2, "l1c2")
-                        .setPresentation(bogusPresentation)
                         .build());
         final IntentSender auth12 = AuthenticationActivity.createSender(getContext(), 12);
         final CannedFillResponse response1 = new CannedFillResponse.Builder()
                 .addDataset(new CannedDataset.Builder()
                         .setAuthentication(auth11)
-                        .setField(ID_L1C1, bogusValue)
-                        .setField(ID_L1C2, bogusValue)
+                        .setField(ID_L1C1, UNUSED_AUTOFILL_VALUE)
+                        .setField(ID_L1C2, UNUSED_AUTOFILL_VALUE)
                         .setPresentation(createPresentation("P1D1"))
                         .build())
                 .addDataset(new CannedDataset.Builder()
                         .setAuthentication(auth12)
-                        .setField(ID_L1C1, bogusValue)
+                        .setField(ID_L1C1, UNUSED_AUTOFILL_VALUE)
                         .setPresentation(createPresentation("P1D2"))
                         .build())
                 .build();
@@ -1530,7 +1543,7 @@
                 .onCell(1, 2, "l1c2");
 
         // Trigger partition.
-        mActivity.focusCell(1, 1);
+        focusCell(1, 1);
         sReplier.getNextFillRequest();
 
         /**
@@ -1541,19 +1554,18 @@
         final IntentSender auth22 = AuthenticationActivity.createSender(getContext(), 22,
                 new CannedDataset.Builder()
                     .setField(ID_L2C2, "L2C2")
-                    .setPresentation(bogusPresentation)
                     .build());
         final CannedFillResponse response2 = new CannedFillResponse.Builder()
                 .addDataset(new CannedDataset.Builder()
                         .setAuthentication(auth21)
                         .setPresentation(createPresentation("P2D1"))
-                        .setField(ID_L2C1, bogusValue)
-                        .setField(ID_L2C2, bogusValue)
+                        .setField(ID_L2C1, UNUSED_AUTOFILL_VALUE)
+                        .setField(ID_L2C2, UNUSED_AUTOFILL_VALUE)
                         .build())
                 .addDataset(new CannedDataset.Builder()
                         .setAuthentication(auth22)
                         .setPresentation(createPresentation("P2D2"))
-                        .setField(ID_L2C2, bogusValue)
+                        .setField(ID_L2C2, UNUSED_AUTOFILL_VALUE)
                         .build())
                 .build();
         sReplier.addResponse(response2);
@@ -1561,7 +1573,7 @@
                 .onCell(2, 2, "L2C2");
 
         // Trigger partition.
-        mActivity.focusCell(2, 1);
+        focusCell(2, 1);
         sReplier.getNextFillRequest();
 
         /**
@@ -1572,21 +1584,20 @@
                 new CannedDataset.Builder()
                         .setField(ID_L3C1, "l3c1")
                         .setField(ID_L3C2, "l3c2")
-                        .setPresentation(bogusPresentation)
                         .build());
         final IntentSender auth32 = AuthenticationActivity.createSender(getContext(), 32);
         final CannedFillResponse response3 = new CannedFillResponse.Builder()
                 .addDataset(new CannedDataset.Builder()
                         .setAuthentication(auth31)
                         .setPresentation(createPresentation("P3D1"))
-                        .setField(ID_L3C1, bogusValue)
-                        .setField(ID_L3C2, bogusValue)
+                        .setField(ID_L3C1, UNUSED_AUTOFILL_VALUE)
+                        .setField(ID_L3C2, UNUSED_AUTOFILL_VALUE)
                         .build())
                 .addDataset(new CannedDataset.Builder()
                         .setAuthentication(auth32)
                         .setPresentation(createPresentation("P3D2"))
-                        .setField(ID_L3C1, bogusValue)
-                        .setField(ID_L3C2, bogusValue)
+                        .setField(ID_L3C1, UNUSED_AUTOFILL_VALUE)
+                        .setField(ID_L3C2, UNUSED_AUTOFILL_VALUE)
                         .build())
                 .build();
         sReplier.addResponse(response3);
@@ -1595,7 +1606,7 @@
                 .onCell(3, 2, "l3c2");
 
         // Trigger partition.
-        mActivity.focusCell(3, 2);
+        focusCell(3, 2);
         sReplier.getNextFillRequest();
 
         /**
@@ -1607,19 +1618,18 @@
                 new CannedDataset.Builder()
                     .setField(ID_L4C1, "L4C1")
                     .setField(ID_L4C2, "L4C2")
-                    .setPresentation(bogusPresentation)
                     .build());
         final CannedFillResponse response4 = new CannedFillResponse.Builder()
                 .addDataset(new CannedDataset.Builder()
                         .setAuthentication(auth41)
                         .setPresentation(createPresentation("P4D1"))
-                        .setField(ID_L4C1, bogusValue)
+                        .setField(ID_L4C1, UNUSED_AUTOFILL_VALUE)
                         .build())
                 .addDataset(new CannedDataset.Builder()
                         .setAuthentication(auth42)
                         .setPresentation(createPresentation("P4D2"))
-                        .setField(ID_L4C1, bogusValue)
-                        .setField(ID_L4C2, bogusValue)
+                        .setField(ID_L4C1, UNUSED_AUTOFILL_VALUE)
+                        .setField(ID_L4C2, UNUSED_AUTOFILL_VALUE)
                         .build())
                 .build();
         sReplier.addResponse(response4);
@@ -1627,49 +1637,49 @@
                 .onCell(4, 1, "L4C1")
                 .onCell(4, 2, "L4C2");
 
-        mActivity.focusCell(4, 1);
+        focusCell(4, 1);
         sReplier.getNextFillRequest();
 
         /*
          *  Now move focus around to make sure the proper values are displayed each time.
          */
-        mActivity.focusCell(1, 1);
-        sUiBot.assertDatasets("P1D1", "P1D2");
-        mActivity.focusCell(1, 2);
-        sUiBot.assertDatasets("P1D1");
+        focusCell(1, 1);
+        mUiBot.assertDatasets("P1D1", "P1D2");
+        focusCell(1, 2);
+        mUiBot.assertDatasets("P1D1");
 
-        mActivity.focusCell(2, 1);
-        sUiBot.assertDatasets("P2D1");
-        mActivity.focusCell(2, 2);
-        sUiBot.assertDatasets("P2D1", "P2D2");
+        focusCell(2, 1);
+        mUiBot.assertDatasets("P2D1");
+        focusCell(2, 2);
+        mUiBot.assertDatasets("P2D1", "P2D2");
 
-        mActivity.focusCell(4, 1);
-        sUiBot.assertDatasets("P4D1", "P4D2");
-        mActivity.focusCell(4, 2);
-        sUiBot.assertDatasets("P4D2");
+        focusCell(4, 1);
+        mUiBot.assertDatasets("P4D1", "P4D2");
+        focusCell(4, 2);
+        mUiBot.assertDatasets("P4D2");
 
-        mActivity.focusCell(3, 2);
-        sUiBot.assertDatasets("P3D1", "P3D2");
-        mActivity.focusCell(3, 1);
-        sUiBot.assertDatasets("P3D1", "P3D2");
+        focusCell(3, 2);
+        mUiBot.assertDatasets("P3D1", "P3D2");
+        focusCell(3, 1);
+        mUiBot.assertDatasets("P3D1", "P3D2");
 
         /*
          *  Finally, autofill and check results.
          */
-        mActivity.focusCell(4, 1);
-        sUiBot.selectDataset("P4D2");
+        focusCell(4, 1);
+        mUiBot.selectDataset("P4D2");
         expectation4.assertAutoFilled();
 
-        mActivity.focusCell(1, 1);
-        sUiBot.selectDataset("P1D1");
+        focusCell(1, 1);
+        mUiBot.selectDataset("P1D1");
         expectation1.assertAutoFilled();
 
-        mActivity.focusCell(3, 1);
-        sUiBot.selectDataset("P3D1");
+        focusCell(3, 1);
+        mUiBot.selectDataset("P3D1");
         expectation3.assertAutoFilled();
 
-        mActivity.focusCell(2, 2);
-        sUiBot.selectDataset("P2D2");
+        focusCell(2, 2);
+        mUiBot.selectDataset("P2D2");
         expectation2.assertAutoFilled();
     }
 
@@ -1683,10 +1693,6 @@
         // Set service.
         enableService();
 
-        // TODO: current API requires these fields...
-        final RemoteViews bogusPresentation = createPresentation("Whatever man, I'm not used...");
-        final String bogusValue = "Y U REQUIRE IT?";
-
         /**
          * 1st partition.
          */
@@ -1700,7 +1706,7 @@
                         .build())
                 .addDataset(new CannedDataset.Builder()
                         .setAuthentication(auth12)
-                        .setField(ID_L1C1, bogusValue)
+                        .setField(ID_L1C1, UNUSED_AUTOFILL_VALUE)
                         .setPresentation(createPresentation("P1D2"))
                         .build())
                 .build();
@@ -1710,7 +1716,7 @@
                 .onCell(1, 2, "l1c2");
 
         // Trigger partition.
-        mActivity.focusCell(1, 1);
+        focusCell(1, 1);
         sReplier.getNextFillRequest();
 
         /**
@@ -1720,7 +1726,6 @@
         final IntentSender auth22 = AuthenticationActivity.createSender(getContext(), 22,
                 new CannedDataset.Builder()
                     .setField(ID_L2C2, "L2C2")
-                    .setPresentation(bogusPresentation)
                     .build());
         final CannedFillResponse response2 = new CannedFillResponse.Builder()
                 .addDataset(new CannedDataset.Builder()
@@ -1731,7 +1736,7 @@
                 .addDataset(new CannedDataset.Builder()
                         .setAuthentication(auth22)
                         .setPresentation(createPresentation("P2D2"))
-                        .setField(ID_L2C2, bogusValue)
+                        .setField(ID_L2C2, UNUSED_AUTOFILL_VALUE)
                         .build())
                 .build();
         sReplier.addResponse(response2);
@@ -1739,7 +1744,7 @@
                 .onCell(2, 2, "L2C2");
 
         // Trigger partition.
-        mActivity.focusCell(2, 1);
+        focusCell(2, 1);
         sReplier.getNextFillRequest();
 
         /**
@@ -1750,14 +1755,13 @@
                 new CannedDataset.Builder()
                         .setField(ID_L3C1, "l3c1")
                         .setField(ID_L3C2, "l3c2")
-                        .setPresentation(bogusPresentation)
                         .build());
         final CannedFillResponse response3 = new CannedFillResponse.Builder()
                 .addDataset(new CannedDataset.Builder()
                         .setAuthentication(auth31)
                         .setPresentation(createPresentation("P3D1"))
-                        .setField(ID_L3C1, bogusValue)
-                        .setField(ID_L3C2, bogusValue)
+                        .setField(ID_L3C1, UNUSED_AUTOFILL_VALUE)
+                        .setField(ID_L3C2, UNUSED_AUTOFILL_VALUE)
                         .build())
                 .addDataset(new CannedDataset.Builder()
                         .setPresentation(createPresentation("P3D2"))
@@ -1771,7 +1775,7 @@
                 .onCell(3, 2, "l3c2");
 
         // Trigger partition.
-        mActivity.focusCell(3, 2);
+        focusCell(3, 2);
         sReplier.getNextFillRequest();
 
         /**
@@ -1783,7 +1787,7 @@
                 .addDataset(new CannedDataset.Builder()
                         .setAuthentication(auth41)
                         .setPresentation(createPresentation("P4D1"))
-                        .setField(ID_L4C1, bogusValue)
+                        .setField(ID_L4C1, UNUSED_AUTOFILL_VALUE)
                         .build())
                 .addDataset(new CannedDataset.Builder()
                         .setPresentation(createPresentation("P4D2"))
@@ -1796,49 +1800,49 @@
                 .onCell(4, 1, "L4C1")
                 .onCell(4, 2, "L4C2");
 
-        mActivity.focusCell(4, 1);
+        focusCell(4, 1);
         sReplier.getNextFillRequest();
 
         /*
          *  Now move focus around to make sure the proper values are displayed each time.
          */
-        mActivity.focusCell(1, 1);
-        sUiBot.assertDatasets("P1D1", "P1D2");
-        mActivity.focusCell(1, 2);
-        sUiBot.assertDatasets("P1D1");
+        focusCell(1, 1);
+        mUiBot.assertDatasets("P1D1", "P1D2");
+        focusCell(1, 2);
+        mUiBot.assertDatasets("P1D1");
 
-        mActivity.focusCell(2, 1);
-        sUiBot.assertDatasets("P2D1");
-        mActivity.focusCell(2, 2);
-        sUiBot.assertDatasets("P2D1", "P2D2");
+        focusCell(2, 1);
+        mUiBot.assertDatasets("P2D1");
+        focusCell(2, 2);
+        mUiBot.assertDatasets("P2D1", "P2D2");
 
-        mActivity.focusCell(4, 1);
-        sUiBot.assertDatasets("P4D1", "P4D2");
-        mActivity.focusCell(4, 2);
-        sUiBot.assertDatasets("P4D2");
+        focusCell(4, 1);
+        mUiBot.assertDatasets("P4D1", "P4D2");
+        focusCell(4, 2);
+        mUiBot.assertDatasets("P4D2");
 
-        mActivity.focusCell(3, 2);
-        sUiBot.assertDatasets("P3D1", "P3D2");
-        mActivity.focusCell(3, 1);
-        sUiBot.assertDatasets("P3D1", "P3D2");
+        focusCell(3, 2);
+        mUiBot.assertDatasets("P3D1", "P3D2");
+        focusCell(3, 1);
+        mUiBot.assertDatasets("P3D1", "P3D2");
 
         /*
          *  Finally, autofill and check results.
          */
-        mActivity.focusCell(4, 1);
-        sUiBot.selectDataset("P4D2");
+        focusCell(4, 1);
+        mUiBot.selectDataset("P4D2");
         expectation4.assertAutoFilled();
 
-        mActivity.focusCell(1, 1);
-        sUiBot.selectDataset("P1D1");
+        focusCell(1, 1);
+        mUiBot.selectDataset("P1D1");
         expectation1.assertAutoFilled();
 
-        mActivity.focusCell(3, 1);
-        sUiBot.selectDataset("P3D1");
+        focusCell(3, 1);
+        mUiBot.selectDataset("P3D1");
         expectation3.assertAutoFilled();
 
-        mActivity.focusCell(2, 2);
-        sUiBot.selectDataset("P2D2");
+        focusCell(2, 2);
+        mUiBot.selectDataset("P2D2");
         expectation2.assertAutoFilled();
     }
 
@@ -1876,10 +1880,6 @@
         // Set service.
         enableService();
 
-        // TODO: current API requires these fields...
-        final RemoteViews bogusPresentation = createPresentation("Whatever man, I'm not used...");
-        final String bogusValue = "Y U REQUIRE IT?";
-
         /**
          * 1st partition.
          */
@@ -1893,20 +1893,19 @@
                         .build())
                 .addDataset(new CannedDataset.Builder()
                         .setAuthentication(auth12)
-                        .setField(ID_L1C1, bogusValue)
+                        .setField(ID_L1C1, UNUSED_AUTOFILL_VALUE)
                         .setPresentation(createPresentation("P1D2"))
                         .build())
                 .build();
         sReplier.addResponse(response1);
         // Trigger partition.
-        mActivity.focusCell(1, 1);
+        focusCell(1, 1);
         sReplier.getNextFillRequest();
 
         // Asserts proper datasets are shown on each field defined so far.
-        mActivity.focusCell(1, 1);
-        sUiBot.assertDatasets("P1D1", "P1D2");
-        mActivity.focusCell(1, 2);
-        sUiBot.assertDatasets("P1D1");
+        mUiBot.assertDatasets("P1D1", "P1D2");
+        focusCell(1, 2);
+        mUiBot.assertDatasets("P1D1");
 
         /**
          * 2nd partition.
@@ -1917,15 +1916,14 @@
                     .setField(ID_L1C1, "2l1c1") // from previous partition
                     .setField(ID_L2C1, "2l2c1")
                     .setField(ID_L2C2, "2l2c2")
-                    .setPresentation(bogusPresentation)
                     .build());
         final CannedFillResponse response2 = new CannedFillResponse.Builder()
                 .addDataset(new CannedDataset.Builder()
                         .setAuthentication(auth21)
                         .setPresentation(createPresentation("P2D1"))
-                        .setField(ID_L1C1, bogusValue) // from previous partition
-                        .setField(ID_L2C1, bogusValue)
-                        .setField(ID_L2C2, bogusValue)
+                        .setField(ID_L1C1, UNUSED_AUTOFILL_VALUE) // from previous partition
+                        .setField(ID_L2C1, UNUSED_AUTOFILL_VALUE)
+                        .setField(ID_L2C2, UNUSED_AUTOFILL_VALUE)
                         .build())
                 .addDataset(new CannedDataset.Builder()
                         .setPresentation(createPresentation("P2D2"))
@@ -1935,18 +1933,18 @@
         sReplier.addResponse(response2);
 
         // Trigger partition.
-        mActivity.focusCell(2, 1);
+        focusCell(2, 1);
         sReplier.getNextFillRequest();
 
         // Asserts proper datasets are shown on each field defined so far.
-        mActivity.focusCell(1, 1);
-        sUiBot.assertDatasets("P2D1"); // changed
-        mActivity.focusCell(1, 2);
-        sUiBot.assertDatasets("P1D1");
-        mActivity.focusCell(2, 1);
-        sUiBot.assertDatasets("P2D1");
-        mActivity.focusCell(2, 2);
-        sUiBot.assertDatasets("P2D1", "P2D2");
+        focusCell(1, 1);
+        mUiBot.assertDatasets("P2D1"); // changed
+        focusCell(1, 2);
+        mUiBot.assertDatasets("P1D1");
+        focusCell(2, 1);
+        mUiBot.assertDatasets("P2D1");
+        focusCell(2, 2);
+        mUiBot.assertDatasets("P2D1", "P2D2");
 
         /**
          * 3rd partition.
@@ -1957,44 +1955,43 @@
                         .setField(ID_L1C2, "3l1c2") // from previous partition
                         .setField(ID_L3C1, "3l3c1")
                         .setField(ID_L3C2, "3l3c2")
-                        .setPresentation(bogusPresentation)
                         .build());
         final IntentSender auth32 = AuthenticationActivity.createSender(getContext(), 32);
         final CannedFillResponse response3 = new CannedFillResponse.Builder()
                 .addDataset(new CannedDataset.Builder()
                         .setAuthentication(auth31)
                         .setPresentation(createPresentation("P3D1"))
-                        .setField(ID_L1C2, bogusValue) // from previous partition
-                        .setField(ID_L3C1, bogusValue)
-                        .setField(ID_L3C2, bogusValue)
+                        .setField(ID_L1C2, UNUSED_AUTOFILL_VALUE) // from previous partition
+                        .setField(ID_L3C1, UNUSED_AUTOFILL_VALUE)
+                        .setField(ID_L3C2, UNUSED_AUTOFILL_VALUE)
                         .build())
                 .addDataset(new CannedDataset.Builder()
                         .setAuthentication(auth32)
                         .setPresentation(createPresentation("P3D2"))
-                        .setField(ID_L2C2, bogusValue) // from previous partition
-                        .setField(ID_L3C1, bogusValue)
-                        .setField(ID_L3C2, bogusValue)
+                        .setField(ID_L2C2, UNUSED_AUTOFILL_VALUE) // from previous partition
+                        .setField(ID_L3C1, UNUSED_AUTOFILL_VALUE)
+                        .setField(ID_L3C2, UNUSED_AUTOFILL_VALUE)
                         .build())
                 .build();
         sReplier.addResponse(response3);
 
         // Trigger partition.
-        mActivity.focusCell(3, 1);
+        focusCell(3, 1);
         sReplier.getNextFillRequest();
 
         // Asserts proper datasets are shown on each field defined so far.
-        mActivity.focusCell(1, 1);
-        sUiBot.assertDatasets("P2D1");
-        mActivity.focusCell(1, 2);
-        sUiBot.assertDatasets("P3D1"); // changed
-        mActivity.focusCell(2, 1);
-        sUiBot.assertDatasets("P2D1");
-        mActivity.focusCell(2, 2);
-        sUiBot.assertDatasets("P3D2"); // changed
-        mActivity.focusCell(3, 2);
-        sUiBot.assertDatasets("P3D1", "P3D2");
-        mActivity.focusCell(3, 1);
-        sUiBot.assertDatasets("P3D1", "P3D2");
+        focusCell(1, 1);
+        mUiBot.assertDatasets("P2D1");
+        focusCell(1, 2);
+        mUiBot.assertDatasets("P3D1"); // changed
+        focusCell(2, 1);
+        mUiBot.assertDatasets("P2D1");
+        focusCell(2, 2);
+        mUiBot.assertDatasets("P3D2"); // changed
+        focusCell(3, 2);
+        mUiBot.assertDatasets("P3D1", "P3D2");
+        focusCell(3, 1);
+        mUiBot.assertDatasets("P3D1", "P3D2");
 
         /**
          * 4th partition.
@@ -2009,7 +2006,6 @@
                         .setField(ID_L3C1, "4l3c1") // from previous partition
                         .setField(ID_L3C2, "4l3c2") // from previous partition
                         .setField(ID_L4C1, "4l4c1")
-                        .setPresentation(bogusPresentation)
                         .build());
         final IntentSender auth42 = AuthenticationActivity.createSender(getContext(), 42,
                 new CannedDataset.Builder()
@@ -2022,57 +2018,56 @@
                         .setField(ID_L1C1, "4L1C1") // from previous partition
                         .setField(ID_L4C1, "4L4C1")
                         .setField(ID_L4C2, "4L4C2")
-                        .setPresentation(bogusPresentation)
                         .build());
         final CannedFillResponse response4 = new CannedFillResponse.Builder()
                 .addDataset(new CannedDataset.Builder()
                         .setAuthentication(auth41)
                         .setPresentation(createPresentation("P4D1"))
-                        .setField(ID_L1C1, bogusValue) // from previous partition
-                        .setField(ID_L1C2, bogusValue) // from previous partition
-                        .setField(ID_L2C1, bogusValue) // from previous partition
-                        .setField(ID_L2C2, bogusValue) // from previous partition
-                        .setField(ID_L3C1, bogusValue) // from previous partition
-                        .setField(ID_L3C2, bogusValue) // from previous partition
-                        .setField(ID_L4C1, bogusValue)
+                        .setField(ID_L1C1, UNUSED_AUTOFILL_VALUE) // from previous partition
+                        .setField(ID_L1C2, UNUSED_AUTOFILL_VALUE) // from previous partition
+                        .setField(ID_L2C1, UNUSED_AUTOFILL_VALUE) // from previous partition
+                        .setField(ID_L2C2, UNUSED_AUTOFILL_VALUE) // from previous partition
+                        .setField(ID_L3C1, UNUSED_AUTOFILL_VALUE) // from previous partition
+                        .setField(ID_L3C2, UNUSED_AUTOFILL_VALUE) // from previous partition
+                        .setField(ID_L4C1, UNUSED_AUTOFILL_VALUE)
                         .build())
                 .addDataset(new CannedDataset.Builder()
                         .setAuthentication(auth42)
                         .setPresentation(createPresentation("P4D2"))
-                        .setField(ID_L1C1, bogusValue) // from previous partition
-                        .setField(ID_L1C2, bogusValue) // from previous partition
-                        .setField(ID_L2C1, bogusValue) // from previous partition
-                        .setField(ID_L2C2, bogusValue) // from previous partition
-                        .setField(ID_L3C1, bogusValue) // from previous partition
-                        .setField(ID_L3C2, bogusValue) // from previous partition
-                        .setField(ID_L1C1, bogusValue) // from previous partition
-                        .setField(ID_L4C1, bogusValue)
-                        .setField(ID_L4C2, bogusValue)
+                        .setField(ID_L1C1, UNUSED_AUTOFILL_VALUE) // from previous partition
+                        .setField(ID_L1C2, UNUSED_AUTOFILL_VALUE) // from previous partition
+                        .setField(ID_L2C1, UNUSED_AUTOFILL_VALUE) // from previous partition
+                        .setField(ID_L2C2, UNUSED_AUTOFILL_VALUE) // from previous partition
+                        .setField(ID_L3C1, UNUSED_AUTOFILL_VALUE) // from previous partition
+                        .setField(ID_L3C2, UNUSED_AUTOFILL_VALUE) // from previous partition
+                        .setField(ID_L1C1, UNUSED_AUTOFILL_VALUE) // from previous partition
+                        .setField(ID_L4C1, UNUSED_AUTOFILL_VALUE)
+                        .setField(ID_L4C2, UNUSED_AUTOFILL_VALUE)
                         .build())
                 .build();
         sReplier.addResponse(response4);
 
         // Trigger partition.
-        mActivity.focusCell(4, 1);
+        focusCell(4, 1);
         sReplier.getNextFillRequest();
 
         // Asserts proper datasets are shown on each field defined so far.
-        mActivity.focusCell(1, 1);
-        sUiBot.assertDatasets("P4D1", "P4D2");
-        mActivity.focusCell(1, 2);
-        sUiBot.assertDatasets("P4D1", "P4D2");
-        mActivity.focusCell(2, 1);
-        sUiBot.assertDatasets("P4D1", "P4D2");
-        mActivity.focusCell(2, 2);
-        sUiBot.assertDatasets("P4D1", "P4D2");
-        mActivity.focusCell(3, 2);
-        sUiBot.assertDatasets("P4D1", "P4D2");
-        mActivity.focusCell(3, 1);
-        sUiBot.assertDatasets("P4D1", "P4D2");
-        mActivity.focusCell(4, 1);
-        sUiBot.assertDatasets("P4D1", "P4D2");
-        mActivity.focusCell(4, 2);
-        sUiBot.assertDatasets("P4D2");
+        focusCell(1, 1);
+        mUiBot.assertDatasets("P4D1", "P4D2");
+        focusCell(1, 2);
+        mUiBot.assertDatasets("P4D1", "P4D2");
+        focusCell(2, 1);
+        mUiBot.assertDatasets("P4D1", "P4D2");
+        focusCell(2, 2);
+        mUiBot.assertDatasets("P4D1", "P4D2");
+        focusCell(3, 2);
+        mUiBot.assertDatasets("P4D1", "P4D2");
+        focusCell(3, 1);
+        mUiBot.assertDatasets("P4D1", "P4D2");
+        focusCell(4, 1);
+        mUiBot.assertDatasets("P4D1", "P4D2");
+        focusCell(4, 2);
+        mUiBot.assertDatasets("P4D2");
 
         /*
          * Finally, autofill and check results.
@@ -2102,9 +2097,9 @@
             chosenOne = "P4D2";
         }
 
-          mActivity.focusCell(4, 1);
-          sUiBot.selectDataset(chosenOne);
-          expectation.assertAutoFilled();
+        focusCell(4, 1);
+        mUiBot.selectDataset(chosenOne);
+        expectation.assertAutoFilled();
     }
 
     @Test
@@ -2129,10 +2124,10 @@
         final FillExpectation expectation1 = mActivity.expectAutofill()
                 .onCell(1, 1, "l1c1")
                 .onCell(1, 2, "l1c2");
-        mActivity.focusCell(1, 1);
+        focusCell(1, 1);
         sReplier.getNextFillRequest();
 
-        sUiBot.assertDatasets("Auth 1");
+        mUiBot.assertDatasets("Auth 1");
 
         // Prepare 2nd partition.
         final IntentSender auth2 = AuthenticationActivity.createSender(getContext(), 2,
@@ -2151,10 +2146,10 @@
         final FillExpectation expectation2 = mActivity.expectAutofill()
                 .onCell(2, 1, "l2c1")
                 .onCell(2, 2, "l2c2");
-        mActivity.focusCell(2, 1);
+        focusCell(2, 1);
         sReplier.getNextFillRequest();
 
-        sUiBot.assertDatasets("Auth 2");
+        mUiBot.assertDatasets("Auth 2");
 
         // Prepare 3rd partition.
         final IntentSender auth3 = AuthenticationActivity.createSender(getContext(), 3,
@@ -2173,10 +2168,10 @@
         final FillExpectation expectation3 = mActivity.expectAutofill()
                 .onCell(3, 1, "l3c1")
                 .onCell(3, 2, "l3c2");
-        mActivity.focusCell(3, 1);
+        focusCell(3, 1);
         sReplier.getNextFillRequest();
 
-        sUiBot.assertDatasets("Auth 3");
+        mUiBot.assertDatasets("Auth 3");
 
         // Prepare 4th partition.
         final IntentSender auth4 = AuthenticationActivity.createSender(getContext(), 4,
@@ -2195,52 +2190,52 @@
         final FillExpectation expectation4 = mActivity.expectAutofill()
                 .onCell(4, 1, "l4c1")
                 .onCell(4, 2, "l4c2");
-        mActivity.focusCell(4, 1);
+        focusCell(4, 1);
         sReplier.getNextFillRequest();
 
-        sUiBot.assertDatasets("Auth 4");
+        mUiBot.assertDatasets("Auth 4");
 
         // Now play around the focus to make sure they still display the right values.
 
-        mActivity.focusCell(1, 2);
-        sUiBot.assertDatasets("Auth 1");
-        mActivity.focusCell(1, 1);
-        sUiBot.assertDatasets("Auth 1");
+        focusCell(1, 2);
+        mUiBot.assertDatasets("Auth 1");
+        focusCell(1, 1);
+        mUiBot.assertDatasets("Auth 1");
 
-        mActivity.focusCell(3, 1);
-        sUiBot.assertDatasets("Auth 3");
-        mActivity.focusCell(3, 2);
-        sUiBot.assertDatasets("Auth 3");
+        focusCell(3, 1);
+        mUiBot.assertDatasets("Auth 3");
+        focusCell(3, 2);
+        mUiBot.assertDatasets("Auth 3");
 
-        mActivity.focusCell(2, 1);
-        sUiBot.assertDatasets("Auth 2");
-        mActivity.focusCell(4, 2);
-        sUiBot.assertDatasets("Auth 4");
+        focusCell(2, 1);
+        mUiBot.assertDatasets("Auth 2");
+        focusCell(4, 2);
+        mUiBot.assertDatasets("Auth 4");
 
-        mActivity.focusCell(2, 2);
-        sUiBot.assertDatasets("Auth 2");
-        mActivity.focusCell(4, 1);
-        sUiBot.assertDatasets("Auth 4");
+        focusCell(2, 2);
+        mUiBot.assertDatasets("Auth 2");
+        focusCell(4, 1);
+        mUiBot.assertDatasets("Auth 4");
 
         // Finally, autofill and check them.
-        mActivity.focusCell(2, 1);
-        sUiBot.selectDataset("Auth 2");
-        sUiBot.selectDataset("Partition 2");
+        focusCell(2, 1);
+        mUiBot.selectDataset("Auth 2");
+        mUiBot.selectDataset("Partition 2");
         expectation2.assertAutoFilled();
 
-        mActivity.focusCell(4, 1);
-        sUiBot.selectDataset("Auth 4");
-        sUiBot.selectDataset("Partition 4");
+        focusCell(4, 1);
+        mUiBot.selectDataset("Auth 4");
+        mUiBot.selectDataset("Partition 4");
         expectation4.assertAutoFilled();
 
-        mActivity.focusCell(3, 1);
-        sUiBot.selectDataset("Auth 3");
-        sUiBot.selectDataset("Partition 3");
+        focusCell(3, 1);
+        mUiBot.selectDataset("Auth 3");
+        mUiBot.selectDataset("Partition 3");
         expectation3.assertAutoFilled();
 
-        mActivity.focusCell(1, 1);
-        sUiBot.selectDataset("Auth 1");
-        sUiBot.selectDataset("Partition 1");
+        focusCell(1, 1);
+        mUiBot.selectDataset("Auth 1");
+        mUiBot.selectDataset("Partition 1");
         expectation1.assertAutoFilled();
     }
 
@@ -2262,13 +2257,13 @@
             sReplier.addResponse(response1);
 
             // Trigger autofill.
-            mActivity.focusCell(1, 1);
+            focusCell(1, 1);
             sReplier.getNextFillRequest();
 
             // Make sure UI is shown, but don't tap it.
-            sUiBot.assertDatasets("l1c1");
-            mActivity.focusCell(1, 2);
-            sUiBot.assertDatasets("l1c2");
+            mUiBot.assertDatasets("l1c1");
+            focusCell(1, 2);
+            mUiBot.assertDatasets("l1c2");
 
             // Prepare 2nd partition.
             final CannedFillResponse response2 = new CannedFillResponse.Builder()
@@ -2279,16 +2274,16 @@
             sReplier.addResponse(response2);
 
             // Trigger autofill on 2nd partition.
-            mActivity.focusCell(2, 1);
+            focusCell(2, 1);
 
             // Make sure it was ignored.
-            sUiBot.assertNoDatasets();
+            mUiBot.assertNoDatasets();
 
             // Make sure 1st partition is still working.
-            mActivity.focusCell(1, 2);
-            sUiBot.assertDatasets("l1c2");
-            mActivity.focusCell(1, 1);
-            sUiBot.assertDatasets("l1c1");
+            focusCell(1, 2);
+            mUiBot.assertDatasets("l1c2");
+            focusCell(1, 1);
+            mUiBot.assertDatasets("l1c1");
 
             // Prepare 3rd partition.
             final CannedFillResponse response3 = new CannedFillResponse.Builder()
@@ -2298,21 +2293,21 @@
                     .build();
             sReplier.addResponse(response3);
             // Trigger autofill on 3rd partition.
-            mActivity.focusCell(3, 2);
+            focusCell(3, 2);
 
             // Make sure it was ignored.
-            sUiBot.assertNoDatasets();
+            mUiBot.assertNoDatasets();
 
             // Make sure 1st partition is still working...
-            mActivity.focusCell(1, 2);
-            sUiBot.assertDatasets("l1c2");
-            mActivity.focusCell(1, 1);
-            sUiBot.assertDatasets("l1c1");
+            focusCell(1, 2);
+            mUiBot.assertDatasets("l1c2");
+            focusCell(1, 1);
+            mUiBot.assertDatasets("l1c1");
 
             //...and can be autofilled.
             final FillExpectation expectation = mActivity.expectAutofill()
                     .onCell(1, 1, "l1c1");
-            sUiBot.selectDataset("l1c1");
+            mUiBot.selectDataset("l1c1");
             expectation.assertAutoFilled();
         } finally {
             setMaxPartitions(maxBefore);
diff --git a/tests/autofillservice/src/android/autofillservice/cts/PreFilledLoginActivityTest.java b/tests/autofillservice/src/android/autofillservice/cts/PreFilledLoginActivityTest.java
index 75c742f..0f571ef 100644
--- a/tests/autofillservice/src/android/autofillservice/cts/PreFilledLoginActivityTest.java
+++ b/tests/autofillservice/src/android/autofillservice/cts/PreFilledLoginActivityTest.java
@@ -20,6 +20,7 @@
 import static android.autofillservice.cts.Helper.ID_USERNAME;
 import static android.autofillservice.cts.Helper.ID_USERNAME_LABEL;
 import static android.autofillservice.cts.Helper.assertTextAndValue;
+import static android.autofillservice.cts.Helper.assertTextFromResouces;
 import static android.autofillservice.cts.Helper.assertTextIsSanitized;
 import static android.autofillservice.cts.Helper.assertTextOnly;
 import static android.autofillservice.cts.Helper.findNodeByResourceId;
@@ -28,7 +29,6 @@
 import android.autofillservice.cts.InstrumentedAutoFillService.FillRequest;
 import android.autofillservice.cts.InstrumentedAutoFillService.SaveRequest;
 
-import org.junit.After;
 import org.junit.Before;
 import org.junit.Rule;
 import org.junit.Test;
@@ -49,11 +49,6 @@
         mActivity = mActivityRule.getActivity();
     }
 
-    @After
-    public void finishWelcomeActivity() {
-        WelcomeActivity.finishIt();
-    }
-
     @Test
     public void testSanitization() throws Exception {
         // Set service.
@@ -78,8 +73,8 @@
         assertTextIsSanitized(fillRequest.structure, ID_USERNAME_LABEL);
 
         // ...password label should be ok because it was set from other resource id
-        assertTextOnly(findNodeByResourceId(fillRequest.structure, ID_PASSWORD_LABEL),
-                "DA PASSWORD");
+        assertTextFromResouces(fillRequest.structure, ID_PASSWORD_LABEL, "DA PASSWORD", false,
+                "new_password_label");
 
         // ...username and password should be ok because they were set in the SML
         assertTextAndValue(findNodeByResourceId(fillRequest.structure, ID_USERNAME),
@@ -92,13 +87,13 @@
         mActivity.tapLogin();
 
         // Assert the snack bar is shown and tap "Save".
-        sUiBot.saveForAutofill(true, SAVE_DATA_TYPE_PASSWORD);
+        mUiBot.saveForAutofill(true, SAVE_DATA_TYPE_PASSWORD);
         final SaveRequest saveRequest = sReplier.getNextSaveRequest();
 
         // Assert sanitization on save: everything should be available!
         assertTextOnly(findNodeByResourceId(saveRequest.structure, ID_USERNAME_LABEL), "DA USER");
-        assertTextOnly(findNodeByResourceId(saveRequest.structure, ID_PASSWORD_LABEL),
-                "DA PASSWORD");
+        assertTextFromResouces(saveRequest.structure, ID_PASSWORD_LABEL, "DA PASSWORD", false,
+                "new_password_label");
         assertTextAndValue(findNodeByResourceId(saveRequest.structure, ID_USERNAME), "malkovich");
         assertTextAndValue(findNodeByResourceId(saveRequest.structure, ID_PASSWORD), "malkovich");
     }
diff --git a/tests/autofillservice/src/android/autofillservice/cts/PreSimpleSaveActivity.java b/tests/autofillservice/src/android/autofillservice/cts/PreSimpleSaveActivity.java
index 5cdcf9b..1cb5942 100644
--- a/tests/autofillservice/src/android/autofillservice/cts/PreSimpleSaveActivity.java
+++ b/tests/autofillservice/src/android/autofillservice/cts/PreSimpleSaveActivity.java
@@ -29,10 +29,20 @@
     static final String ID_PRE_LABEL = "preLabel";
     static final String ID_PRE_INPUT = "preInput";
 
+    private static PreSimpleSaveActivity sInstance;
+
     TextView mPreLabel;
     EditText mPreInput;
     Button mSubmit;
 
+    public static PreSimpleSaveActivity getInstance() {
+        return sInstance;
+    }
+
+    public PreSimpleSaveActivity() {
+        sInstance = this;
+    }
+
     @Override
     protected void onCreate(Bundle savedInstanceState) {
         super.onCreate(savedInstanceState);
@@ -48,4 +58,22 @@
             startActivity(new Intent(this, SimpleSaveActivity.class));
         });
     }
+
+    FillExpectation expectAutoFill(String input) {
+        final FillExpectation expectation = new FillExpectation(input);
+        mPreInput.addTextChangedListener(expectation.mInputWatcher);
+        return expectation;
+    }
+
+    final class FillExpectation {
+        private final OneTimeTextWatcher mInputWatcher;
+
+        private FillExpectation(String input) {
+            mInputWatcher = new OneTimeTextWatcher("input", mPreInput, input);
+        }
+
+        void assertAutoFilled() throws Exception {
+            mInputWatcher.assertAutoFilled();
+        }
+    }
 }
diff --git a/tests/autofillservice/src/android/autofillservice/cts/PreSimpleSaveActivityTest.java b/tests/autofillservice/src/android/autofillservice/cts/PreSimpleSaveActivityTest.java
index b193ddf..504f8ed 100644
--- a/tests/autofillservice/src/android/autofillservice/cts/PreSimpleSaveActivityTest.java
+++ b/tests/autofillservice/src/android/autofillservice/cts/PreSimpleSaveActivityTest.java
@@ -15,6 +15,7 @@
  */
 package android.autofillservice.cts;
 
+import static android.autofillservice.cts.Helper.ID_STATIC_TEXT;
 import static android.autofillservice.cts.Helper.assertTextAndValue;
 import static android.autofillservice.cts.Helper.findNodeByResourceId;
 import static android.autofillservice.cts.LoginActivity.ID_USERNAME_CONTAINER;
@@ -24,13 +25,23 @@
 import static android.service.autofill.SaveInfo.SAVE_DATA_TYPE_EMAIL_ADDRESS;
 import static android.service.autofill.SaveInfo.SAVE_DATA_TYPE_PASSWORD;
 
+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;
+import android.service.autofill.Validator;
+import android.support.test.uiautomator.By;
 import android.support.test.uiautomator.UiObject2;
 import android.view.View;
+import android.widget.RemoteViews;
 
 import org.junit.Rule;
 
+import java.util.regex.Pattern;
+
 public class PreSimpleSaveActivityTest extends CustomDescriptionWithLinkTestCase {
 
     @Rule
@@ -72,27 +83,28 @@
             mActivity.mSubmit.performClick();
         });
         // Make sure post-save activity is shown...
-        sUiBot.assertShownByRelativeId(ID_INPUT);
+        mUiBot.assertShownByRelativeId(ID_INPUT);
 
         // Tap the link.
         final UiObject2 saveUi = assertSaveUiWithLinkIsShown(SAVE_DATA_TYPE_PASSWORD);
         tapSaveUiLink(saveUi);
 
         // Make sure new activity is shown...
-        WelcomeActivity.assertShowingDefaultMessage(sUiBot);
-        sUiBot.assertSaveNotShowing(SAVE_DATA_TYPE_PASSWORD);
+        WelcomeActivity.assertShowingDefaultMessage(mUiBot);
+        mUiBot.assertSaveNotShowing(SAVE_DATA_TYPE_PASSWORD);
 
         // .. then do something to return to previous activity...
         switch (type) {
             case ROTATE_THEN_TAP_BACK_BUTTON:
-                sUiBot.setScreenOrientation(UiBot.LANDSCAPE);
+                mUiBot.setScreenOrientation(UiBot.LANDSCAPE);
+                WelcomeActivity.assertShowingDefaultMessage(mUiBot);
                 // not breaking on purpose
             case TAP_BACK_BUTTON:
-                sUiBot.pressBack();
+                mUiBot.pressBack();
                 break;
             case FINISH_ACTIVITY:
                 // ..then finishes it.
-                WelcomeActivity.finishIt();
+                WelcomeActivity.finishIt(mUiBot);
                 break;
             default:
                 throw new IllegalArgumentException("invalid type: " + type);
@@ -100,7 +112,7 @@
 
         // ... and tap save.
         final UiObject2 newSaveUi = assertSaveUiWithLinkIsShown(SAVE_DATA_TYPE_PASSWORD);
-        sUiBot.saveForAutofill(newSaveUi, true);
+        mUiBot.saveForAutofill(newSaveUi, true);
 
         final SaveRequest saveRequest = sReplier.getNextSaveRequest();
         assertTextAndValue(findNodeByResourceId(saveRequest.structure, ID_PRE_INPUT), "108");
@@ -130,32 +142,32 @@
             mActivity.mSubmit.performClick();
         });
         // Make sure post-save activity is shown...
-        sUiBot.assertShownByRelativeId(ID_INPUT);
+        mUiBot.assertShownByRelativeId(ID_INPUT);
 
         // Tap the link.
         final UiObject2 saveUi = assertSaveUiWithLinkIsShown(SAVE_DATA_TYPE_PASSWORD);
         tapSaveUiLink(saveUi);
 
         // Make sure new activity is shown...
-        WelcomeActivity.assertShowingDefaultMessage(sUiBot);
-        sUiBot.assertSaveNotShowing(SAVE_DATA_TYPE_PASSWORD);
+        WelcomeActivity.assertShowingDefaultMessage(mUiBot);
+        mUiBot.assertSaveNotShowing(SAVE_DATA_TYPE_PASSWORD);
 
         // Tap back to restore the Save UI...
-        sUiBot.pressBack();
+        mUiBot.pressBack();
 
         // ...but don't tap it...
-        final UiObject2 saveUi2 = sUiBot.assertSaveShowing(SAVE_DATA_TYPE_PASSWORD);
+        final UiObject2 saveUi2 = mUiBot.assertSaveShowing(SAVE_DATA_TYPE_PASSWORD);
 
         // ...instead, do something to dismiss it:
         switch (action) {
             case TOUCH_OUTSIDE:
-                sUiBot.assertShownByRelativeId(ID_LABEL).longClick();
+                mUiBot.assertShownByRelativeId(ID_LABEL).longClick();
                 break;
             case TAP_NO_ON_SAVE_UI:
-                sUiBot.saveForAutofill(saveUi2, false);
+                mUiBot.saveForAutofill(saveUi2, false);
                 break;
             case TAP_YES_ON_SAVE_UI:
-                sUiBot.saveForAutofill(true, SAVE_DATA_TYPE_PASSWORD);
+                mUiBot.saveForAutofill(true, SAVE_DATA_TYPE_PASSWORD);
 
                 final SaveRequest saveRequest = sReplier.getNextSaveRequest();
                 assertTextAndValue(findNodeByResourceId(saveRequest.structure, ID_PRE_INPUT),
@@ -165,7 +177,7 @@
             default:
                 throw new IllegalArgumentException("invalid action: " + action);
         }
-        sUiBot.assertSaveNotShowing(SAVE_DATA_TYPE_PASSWORD);
+        mUiBot.assertSaveNotShowing(SAVE_DATA_TYPE_PASSWORD);
 
         // Make sure previous session was finished.
         Helper.assertNoDanglingSessions();
@@ -192,10 +204,10 @@
             newActivty.mCommit.performClick();
         });
         // Make sure post-save activity is shown...
-        sUiBot.assertShownByRelativeId(ID_INPUT);
+        mUiBot.assertShownByRelativeId(ID_INPUT);
 
         // Save it...
-        sUiBot.saveForAutofill(true, SAVE_DATA_TYPE_EMAIL_ADDRESS);
+        mUiBot.saveForAutofill(true, SAVE_DATA_TYPE_EMAIL_ADDRESS);
 
         // ... and assert results
         final SaveRequest saveRequest = sReplier.getNextSaveRequest();
@@ -226,39 +238,39 @@
             mActivity.mSubmit.performClick();
         });
         // Make sure post-save activity is shown...
-        sUiBot.assertShownByRelativeId(ID_INPUT);
+        mUiBot.assertShownByRelativeId(ID_INPUT);
 
         // Tap the link.
         final UiObject2 saveUi = assertSaveUiWithLinkIsShown(SAVE_DATA_TYPE_PASSWORD);
         tapSaveUiLink(saveUi);
 
         // Make sure linked activity is shown...
-        WelcomeActivity.assertShowingDefaultMessage(sUiBot);
-        sUiBot.assertSaveNotShowing(SAVE_DATA_TYPE_PASSWORD);
+        WelcomeActivity.assertShowingDefaultMessage(mUiBot);
+        mUiBot.assertSaveNotShowing(SAVE_DATA_TYPE_PASSWORD);
 
         switch (type) {
             case TAP_RECENTS:
-                sUiBot.switchAppsUsingRecents();
+                mUiBot.switchAppsUsingRecents();
                 // Make sure right activity is showing.
-                sUiBot.assertShownByRelativeId(ID_INPUT);
+                mUiBot.assertShownByRelativeId(ID_INPUT);
                 break;
             case LAUNCH_PREVIOUS_ACTIVITY:
                 startActivity(PreSimpleSaveActivity.class);
-                sUiBot.assertShownByRelativeId(ID_INPUT);
+                mUiBot.assertShownByRelativeId(ID_INPUT);
                 break;
             case LAUNCH_NEW_ACTIVITY:
                 // Launch a 3rd activity...
                 startActivity(LoginActivity.class);
-                sUiBot.assertShownByRelativeId(ID_USERNAME_CONTAINER);
+                mUiBot.assertShownByRelativeId(ID_USERNAME_CONTAINER);
                 // ...then go back
-                sUiBot.pressBack();
-                sUiBot.assertShownByRelativeId(ID_INPUT);
+                mUiBot.pressBack();
+                mUiBot.assertShownByRelativeId(ID_INPUT);
                 break;
             default:
                 throw new IllegalArgumentException("invalid type: " + type);
         }
 
-        sUiBot.assertSaveNotShowing(SAVE_DATA_TYPE_PASSWORD);
+        mUiBot.assertSaveNotShowing(SAVE_DATA_TYPE_PASSWORD);
     }
 
     @Override
@@ -294,14 +306,14 @@
         tapSaveUiLink(saveUi);
 
         // Make sure new activity is shown...
-        WelcomeActivity.assertShowingDefaultMessage(sUiBot);
+        WelcomeActivity.assertShowingDefaultMessage(mUiBot);
 
         // Save UI should be showing as well, since Trampoline finished.
-        sUiBot.assertSaveShowing(SAVE_DATA_TYPE_PASSWORD);
+        mUiBot.assertSaveShowing(SAVE_DATA_TYPE_PASSWORD);
 
         // Go back and make sure it's showing the right activity.
-        sUiBot.pressBack();
-        sUiBot.assertShownByRelativeId(ID_INPUT);
+        mUiBot.pressBack();
+        mUiBot.assertShownByRelativeId(ID_INPUT);
 
         // Now triggers a new session in the new activity (SaveActivity) and do business as usual...
         sReplier.addResponse(new CannedFillResponse.Builder()
@@ -321,13 +333,67 @@
             newActivty.mCommit.performClick();
         });
         // Make sure post-save activity is shown...
-        sUiBot.assertShownByRelativeId(ID_INPUT);
+        mUiBot.assertShownByRelativeId(ID_INPUT);
 
         // Save it...
-        sUiBot.saveForAutofill(true, SAVE_DATA_TYPE_EMAIL_ADDRESS);
+        mUiBot.saveForAutofill(true, SAVE_DATA_TYPE_EMAIL_ADDRESS);
 
         // ... and assert results
         final SaveRequest saveRequest1 = sReplier.getNextSaveRequest();
         assertTextAndValue(findNodeByResourceId(saveRequest1.structure, ID_INPUT), "42");
     }
+
+    @Override
+    protected void tapLinkAfterUpdateAppliedTest(boolean updateLinkView) throws Exception {
+        startActivity(false);
+        // Set service.
+        enableService();
+
+        final CustomDescription.Builder customDescription =
+                newCustomDescriptionBuilder(WelcomeActivity.class);
+        final RemoteViews update = newTemplate();
+        if (updateLinkView) {
+            update.setCharSequence(R.id.link, "setText", "TAP ME IF YOU CAN");
+        } else {
+            update.setCharSequence(R.id.static_text, "setText", "ME!");
+        }
+        Validator validCondition = new RegexValidator(mActivity.mPreInput.getAutofillId(),
+                Pattern.compile(".*"));
+        customDescription.batchUpdate(validCondition,
+                new BatchUpdates.Builder().updateTemplate(update).build());
+
+        // Set expectations.
+        sReplier.addResponse(new CannedFillResponse.Builder()
+                .setCustomDescription(customDescription.build())
+                .setRequiredSavableIds(SAVE_DATA_TYPE_PASSWORD, ID_PRE_INPUT)
+                .build());
+
+        // Trigger autofill.
+        mActivity.syncRunOnUiThread(() -> mActivity.mPreInput.requestFocus());
+        sReplier.getNextFillRequest();
+        Helper.assertHasSessions(mPackageName);
+
+        // Trigger save.
+        mActivity.syncRunOnUiThread(() -> {
+            mActivity.mPreInput.setText("108");
+            mActivity.mSubmit.performClick();
+        });
+        // Make sure post-save activity is shown...
+        mUiBot.assertShownByRelativeId(ID_INPUT);
+
+        // Tap the link.
+        final UiObject2 saveUi;
+        if (updateLinkView) {
+            saveUi = assertSaveUiWithLinkIsShown(SAVE_DATA_TYPE_PASSWORD, "TAP ME IF YOU CAN");
+        } else {
+            saveUi = assertSaveUiWithLinkIsShown(SAVE_DATA_TYPE_PASSWORD);
+            final UiObject2 changed = saveUi.findObject(By.res(mPackageName, ID_STATIC_TEXT));
+            assertThat(changed.getText()).isEqualTo("ME!");
+        }
+        tapSaveUiLink(saveUi);
+
+        // Make sure new activity is shown...
+        WelcomeActivity.assertShowingDefaultMessage(mUiBot);
+        mUiBot.assertSaveNotShowing(SAVE_DATA_TYPE_PASSWORD);
+    }
 }
diff --git a/tests/autofillservice/src/android/autofillservice/cts/RetryRule.java b/tests/autofillservice/src/android/autofillservice/cts/RetryRule.java
index be740b1..2de94f8 100644
--- a/tests/autofillservice/src/android/autofillservice/cts/RetryRule.java
+++ b/tests/autofillservice/src/android/autofillservice/cts/RetryRule.java
@@ -45,17 +45,24 @@
 
             @Override
             public void evaluate() throws Throwable {
+                final String name = description.getDisplayName();
                 Throwable caught = null;
                 for (int i = 1; i <= mMaxAttempts; i++) {
                     try {
                         base.evaluate();
                         return;
-                    } catch (RetryableException | StaleObjectException e) {
+                    } catch (RetryableException e) {
+                        final Timeout timeout = e.getTimeout();
+                        if (timeout != null) {
+                            timeout.increase();
+                        }
                         caught = e;
-                        Log.w(TAG,
-                                description.getDisplayName() + ": attempt " + i + " failed: " + e);
+                    } catch (StaleObjectException e) {
+                        caught = e;
                     }
+                    Log.w(TAG, name + ": attempt " + i + " failed: " + caught);
                 }
+                Log.e(TAG, name + ": giving up after " + mMaxAttempts + " attempts");
                 throw caught;
             }
         };
diff --git a/tests/autofillservice/src/android/autofillservice/cts/RetryRuleTest.java b/tests/autofillservice/src/android/autofillservice/cts/RetryRuleTest.java
index 3b733f6..473ec4e 100644
--- a/tests/autofillservice/src/android/autofillservice/cts/RetryRuleTest.java
+++ b/tests/autofillservice/src/android/autofillservice/cts/RetryRuleTest.java
@@ -68,6 +68,17 @@
     }
 
     @Test
+    public void testPassOnRetryableExceptionWithTimeout() throws Throwable {
+        final Timeout timeout = new Timeout("YOUR TIME IS GONE", 1, 2, 10);
+        final RetryableException exception = new RetryableException(timeout, "Y U NO?");
+        final RetryRule rule = new RetryRule(2);
+        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 {
diff --git a/tests/autofillservice/src/android/autofillservice/cts/RetryableException.java b/tests/autofillservice/src/android/autofillservice/cts/RetryableException.java
index 7ca7d62..55b4d5c 100644
--- a/tests/autofillservice/src/android/autofillservice/cts/RetryableException.java
+++ b/tests/autofillservice/src/android/autofillservice/cts/RetryableException.java
@@ -16,20 +16,52 @@
 
 package android.autofillservice.cts;
 
+import android.support.annotation.Nullable;
+
 /**
  * Exception that cause the {@link RetryRule} to re-try a test.
  */
 public class RetryableException extends RuntimeException {
 
+    @Nullable
+    private final Timeout mTimeout;
+
     public RetryableException(String msg) {
-        super(msg);
+        this((Timeout) null, msg);
     }
 
     public RetryableException(String format, Object...args) {
-        super(String.format(format, args));
+        this((Timeout) null, String.format(format, args));
     }
 
     public RetryableException(Throwable cause, String format, Object...args) {
+        this((Timeout) null, String.format(format, args), cause);
+    }
+
+    public RetryableException(@Nullable Timeout timeout, String msg) {
+        super(msg);
+        this.mTimeout = timeout;
+    }
+
+    public RetryableException(@Nullable Timeout timeout, String format, Object...args) {
+        super(String.format(format, args));
+        this.mTimeout = timeout;
+    }
+
+    public RetryableException(@Nullable Timeout timeout, Throwable cause, String format,
+            Object...args) {
         super(String.format(format, args), cause);
+        this.mTimeout = timeout;
+    }
+
+    @Nullable
+    public Timeout getTimeout() {
+        return mTimeout;
+    }
+
+    @Override
+    public String getMessage() {
+        final String superMessage = super.getMessage();
+        return mTimeout == null ? superMessage : superMessage + " (timeout=" + mTimeout + ")";
     }
 }
diff --git a/tests/autofillservice/src/android/autofillservice/cts/SafeCleanerRule.java b/tests/autofillservice/src/android/autofillservice/cts/SafeCleanerRule.java
new file mode 100644
index 0000000..bceb11b
--- /dev/null
+++ b/tests/autofillservice/src/android/autofillservice/cts/SafeCleanerRule.java
@@ -0,0 +1,142 @@
+/*
+ * 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.autofillservice.cts;
+
+import android.support.annotation.NonNull;
+import android.util.Log;
+
+import org.junit.rules.TestRule;
+import org.junit.runner.Description;
+import org.junit.runners.model.Statement;
+
+import java.io.IOException;
+import java.io.PrintWriter;
+import java.io.StringWriter;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.concurrent.Callable;
+
+/**
+ * Rule used to safely run clean up code after a test is finished, so that exceptions thrown by
+ * the cleanup code don't hide exception thrown by the test body
+ */
+// TODO: move to common CTS code
+public final class SafeCleanerRule implements TestRule {
+
+    private static final String TAG = "SafeCleanerRule";
+
+    private final List<Runnable> mCleaners = new ArrayList<>();
+    private final List<Callable<List<Throwable>>> mExtraThrowables = new ArrayList<>();
+    private final List<Throwable> mThrowables = new ArrayList<>();
+
+    /**
+     * Runs {@code cleaner} after the test is finished, catching any {@link Throwable} thrown by it.
+     */
+    public SafeCleanerRule run(@NonNull Runnable cleaner) {
+        mCleaners.add(cleaner);
+        return this;
+    }
+
+    /**
+     * Adds exceptions directly.
+     *
+     * <p>Typically used when exceptions were caught asychronously during the test execution.
+     */
+    public SafeCleanerRule add(@NonNull Callable<List<Throwable>> exceptions) {
+        mExtraThrowables.add(exceptions);
+        return this;
+    }
+
+    @Override
+    public Statement apply(Statement base, Description description) {
+        return new Statement() {
+            @Override
+            public void evaluate() throws Throwable {
+                // First run the test
+                try {
+                    base.evaluate();
+                } catch (Throwable t) {
+                    Log.w(TAG, "Adding exception from main test");
+                    mThrowables.add(t);
+                }
+
+                // Then the cleanup runners
+                for (Runnable runner : mCleaners) {
+                    try {
+                        runner.run();
+                    } catch (Throwable t) {
+                        Log.w(TAG, "Adding exception from cleaner");
+                        mThrowables.add(t);
+                    }
+                }
+
+                // And finally add the extra exceptions
+                for (Callable<List<Throwable>> extraThrowablesCallable : mExtraThrowables) {
+                    final List<Throwable> extraThrowables = extraThrowablesCallable.call();
+                    if (extraThrowables != null) {
+                        Log.w(TAG, "Adding " + extraThrowables.size() + " extra exceptions");
+                        mThrowables.addAll(extraThrowables);
+                    }
+                }
+
+                // Finally, throw up!
+                if (mThrowables.isEmpty()) return;
+
+                final int numberExceptions = mThrowables.size();
+                if (numberExceptions == 1) {
+                    throw mThrowables.get(0);
+                }
+                throw new MultipleExceptions(mThrowables);
+            }
+        };
+    }
+
+    private static String toMesssage(List<Throwable> throwables) {
+        String msg = "D'OH!";
+        try {
+            try (StringWriter sw = new StringWriter(); PrintWriter pw = new PrintWriter(sw)) {
+                sw.write("Caught " + throwables.size() + " exceptions\n");
+                for (int i = 0; i < throwables.size(); i++) {
+                    sw.write("\n---- Begin of exception #" + (i + 1) + " ----\n");
+                    final Throwable exception = throwables.get(i);
+                    exception.printStackTrace(pw);
+                    sw.write("---- End of exception #" + (i + 1) + " ----\n\n");
+                }
+                msg = sw.toString();
+            }
+        } catch (IOException e) {
+            // ignore close() errors - should not happen...
+            Log.e(TAG, "Exception closing StringWriter: " + e);
+        }
+        return msg;
+    }
+
+    // VisibleForTesting
+    static class MultipleExceptions extends AssertionError {
+        private final List<Throwable> mThrowables;
+
+        private MultipleExceptions(List<Throwable> throwables) {
+            super(toMesssage(throwables));
+
+            this.mThrowables = throwables;
+        }
+
+        List<Throwable> getThrowables() {
+            return mThrowables;
+        }
+    }
+}
diff --git a/tests/autofillservice/src/android/autofillservice/cts/SafeCleanerRuleTest.java b/tests/autofillservice/src/android/autofillservice/cts/SafeCleanerRuleTest.java
new file mode 100644
index 0000000..7fc021e
--- /dev/null
+++ b/tests/autofillservice/src/android/autofillservice/cts/SafeCleanerRuleTest.java
@@ -0,0 +1,148 @@
+/*
+ * 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.autofillservice.cts;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import static org.mockito.Mockito.verify;
+import static org.testng.Assert.expectThrows;
+
+import com.google.common.collect.ImmutableList;
+
+import org.junit.Test;
+import org.junit.runner.Description;
+import org.junit.runner.RunWith;
+import org.junit.runners.model.Statement;
+import org.mockito.Mock;
+import org.mockito.junit.MockitoJUnitRunner;
+
+import java.util.List;
+import java.util.concurrent.Callable;
+
+@RunWith(MockitoJUnitRunner.class)
+public class SafeCleanerRuleTest {
+
+    private static class FailureStatement extends Statement {
+        private final Throwable mThrowable;
+
+        FailureStatement(Throwable t) {
+            mThrowable = t;
+        }
+
+        @Override
+        public void evaluate() throws Throwable {
+            throw mThrowable;
+        }
+    }
+
+    private final Description mDescription = Description.createSuiteDescription("Whatever");
+    private final RuntimeException mRuntimeException = new RuntimeException("D'OH!");
+
+    // Use mocks for objects that don't throw any exception.
+    @Mock private Runnable mGoodGuyRunner1;
+    @Mock private Runnable mGoodGuyRunner2;
+    @Mock private Callable<List<Throwable>> mGoodGuyExtraExceptions1;
+    @Mock private Callable<List<Throwable>> mGoodGuyExtraExceptions2;
+    @Mock private Statement mGoodGuyStatement;
+
+    @Test
+    public void testEmptyRule_testPass() throws Throwable {
+        final SafeCleanerRule rule = new SafeCleanerRule();
+        rule.apply(mGoodGuyStatement, mDescription).evaluate();
+    }
+
+    @Test
+    public void testEmptyRule_testFails() throws Throwable {
+        final SafeCleanerRule rule = new SafeCleanerRule();
+        final Throwable actualException = expectThrows(RuntimeException.class,
+                () -> rule.apply(new FailureStatement(mRuntimeException), mDescription).evaluate());
+        assertThat(actualException).isSameAs(mRuntimeException);
+    }
+
+    @Test
+    public void testOnlyTestFails() throws Throwable {
+        final SafeCleanerRule rule = new SafeCleanerRule()
+                .run(mGoodGuyRunner1)
+                .add(mGoodGuyExtraExceptions1);
+        final Throwable actualException = expectThrows(RuntimeException.class,
+                () -> rule.apply(new FailureStatement(mRuntimeException), mDescription).evaluate());
+        assertThat(actualException).isSameAs(mRuntimeException);
+        verify(mGoodGuyRunner1).run();
+        verify(mGoodGuyExtraExceptions1).call();
+    }
+
+    @Test
+    public void testTestPass_oneRunnerFails() throws Throwable {
+        final SafeCleanerRule rule = new SafeCleanerRule()
+                .run(mGoodGuyRunner1)
+                .run(() -> { throw mRuntimeException; })
+                .run(mGoodGuyRunner2)
+                .add(mGoodGuyExtraExceptions1);
+        final Throwable actualException = expectThrows(RuntimeException.class,
+                () -> rule.apply(mGoodGuyStatement, mDescription).evaluate());
+        assertThat(actualException).isSameAs(mRuntimeException);
+        verify(mGoodGuyRunner1).run();
+        verify(mGoodGuyRunner2).run();
+        verify(mGoodGuyExtraExceptions1).call();
+    }
+
+    @Test
+    public void testTestPass_oneExtraExceptionThrown() throws Throwable {
+        final SafeCleanerRule rule = new SafeCleanerRule()
+                .run(mGoodGuyRunner1)
+                .add(() -> { return ImmutableList.of(mRuntimeException); })
+                .add(mGoodGuyExtraExceptions1)
+                .run(mGoodGuyRunner2);
+        final Throwable actualException = expectThrows(RuntimeException.class,
+                () -> rule.apply(mGoodGuyStatement, mDescription).evaluate());
+        assertThat(actualException).isSameAs(mRuntimeException);
+        verify(mGoodGuyRunner1).run();
+        verify(mGoodGuyRunner2).run();
+        verify(mGoodGuyExtraExceptions1).call();
+    }
+
+    @Test
+    public void testThrowTheKitchenSinkAKAEverybodyThrows() throws Throwable {
+        final Exception extra1 = new Exception("1");
+        final Exception extra2 = new Exception("2");
+        final Exception extra3 = new Exception("3");
+        final Error error1 = new Error("one");
+        final Error error2 = new Error("two");
+        final RuntimeException testException  = new RuntimeException("TEST, Y U NO PASS?");
+        final SafeCleanerRule rule = new SafeCleanerRule()
+                .run(mGoodGuyRunner1)
+                .add(mGoodGuyExtraExceptions1)
+                .add(() -> { return ImmutableList.of(extra1, extra2); })
+                .run(() -> {
+                    throw error1;
+                })
+                .run(mGoodGuyRunner2)
+                .add(() -> { return ImmutableList.of(extra3); })
+                .add(mGoodGuyExtraExceptions2)
+                .run(() -> { throw error2; });
+
+        final SafeCleanerRule.MultipleExceptions actualException = expectThrows(
+                SafeCleanerRule.MultipleExceptions.class,
+                () -> rule.apply(new FailureStatement(testException), mDescription).evaluate());
+        assertThat(actualException.getThrowables())
+                .containsExactly(testException, error1, error2, extra1, extra2, extra3)
+                .inOrder();
+        verify(mGoodGuyRunner1).run();
+        verify(mGoodGuyRunner2).run();
+        verify(mGoodGuyExtraExceptions1).call();
+    }
+}
diff --git a/tests/autofillservice/src/android/autofillservice/cts/SaveInfoTest.java b/tests/autofillservice/src/android/autofillservice/cts/SaveInfoTest.java
index 702e1b1..d34eda1 100644
--- a/tests/autofillservice/src/android/autofillservice/cts/SaveInfoTest.java
+++ b/tests/autofillservice/src/android/autofillservice/cts/SaveInfoTest.java
@@ -16,8 +16,11 @@
 
 package android.autofillservice.cts;
 
+import static org.mockito.Mockito.mock;
 import static org.testng.Assert.assertThrows;
 
+import android.service.autofill.InternalSanitizer;
+import android.service.autofill.Sanitizer;
 import android.service.autofill.SaveInfo;
 import android.support.test.runner.AndroidJUnit4;
 import android.view.autofill.AutofillId;
@@ -28,6 +31,9 @@
 @RunWith(AndroidJUnit4.class)
 public class SaveInfoTest {
 
+    private  final AutofillId mId = new AutofillId(42);
+    private final InternalSanitizer mSanitizer = mock(InternalSanitizer.class);
+
     @Test
     public void testRequiredIdsBuilder_null() {
         assertThrows(IllegalArgumentException.class,
@@ -56,14 +62,14 @@
     @Test
     public void testSetOptionalIds_null() {
         final SaveInfo.Builder builder = new SaveInfo.Builder(SaveInfo.SAVE_DATA_TYPE_GENERIC,
-                new AutofillId[] { new AutofillId(42) });
+                new AutofillId[] { mId });
         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[] { new AutofillId(42) });
+                new AutofillId[] { mId });
         assertThrows(IllegalArgumentException.class,
                 () -> builder.setOptionalIds(new AutofillId[] {}));
     }
@@ -71,8 +77,38 @@
     @Test
     public void testSetOptional_nullEntry() {
         final SaveInfo.Builder builder = new SaveInfo.Builder(SaveInfo.SAVE_DATA_TYPE_GENERIC,
-                new AutofillId[] { new AutofillId(42) });
+                new AutofillId[] { mId });
         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 });
+        // Null sanitizer
+        assertThrows(IllegalArgumentException.class,
+                () -> builder.addSanitizer(null, mId));
+        // Invalid sanitizer class
+        assertThrows(IllegalArgumentException.class,
+                () -> builder.addSanitizer(mock(Sanitizer.class), mId));
+        // Null ids
+        assertThrows(IllegalArgumentException.class,
+                () -> builder.addSanitizer(mSanitizer, (AutofillId[]) null));
+        // Empty ids
+        assertThrows(IllegalArgumentException.class,
+                () -> builder.addSanitizer(mSanitizer, new AutofillId[] {}));
+        // Repeated ids
+        assertThrows(IllegalArgumentException.class,
+                () -> builder.addSanitizer(mSanitizer, new AutofillId[] {mId, mId}));
+    }
+
+    @Test
+    public void testAddSanitizer_sameIdOnDifferentCalls() {
+        final SaveInfo.Builder builder = new SaveInfo.Builder(SaveInfo.SAVE_DATA_TYPE_GENERIC,
+                new AutofillId[] { mId });
+        builder.addSanitizer(mSanitizer, mId);
+        assertThrows(IllegalArgumentException.class, () -> builder.addSanitizer(mSanitizer, mId));
+    }
+
 }
diff --git a/tests/autofillservice/src/android/autofillservice/cts/SessionLifecycleTest.java b/tests/autofillservice/src/android/autofillservice/cts/SessionLifecycleTest.java
index bd26426..0df508b 100644
--- a/tests/autofillservice/src/android/autofillservice/cts/SessionLifecycleTest.java
+++ b/tests/autofillservice/src/android/autofillservice/cts/SessionLifecycleTest.java
@@ -19,16 +19,15 @@
 import static android.autofillservice.cts.Helper.ID_LOGIN;
 import static android.autofillservice.cts.Helper.ID_PASSWORD;
 import static android.autofillservice.cts.Helper.ID_USERNAME;
-import static android.autofillservice.cts.Helper.assertNoDanglingSessions;
 import static android.autofillservice.cts.Helper.assertTextAndValue;
-import static android.autofillservice.cts.Helper.eventually;
 import static android.autofillservice.cts.Helper.findNodeByResourceId;
 import static android.autofillservice.cts.Helper.getContext;
 import static android.autofillservice.cts.Helper.getOutOfProcessPid;
-import static android.autofillservice.cts.Helper.runShellCommand;
+import static android.autofillservice.cts.OutOfProcessLoginActivity.getStartedMarker;
 import static android.autofillservice.cts.OutOfProcessLoginActivity.getStoppedMarker;
 import static android.autofillservice.cts.UiBot.LANDSCAPE;
 import static android.autofillservice.cts.UiBot.PORTRAIT;
+import static android.autofillservice.cts.common.ShellHelper.runShellCommand;
 import static android.service.autofill.SaveInfo.SAVE_DATA_TYPE_PASSWORD;
 
 import static com.google.common.truth.Truth.assertThat;
@@ -46,6 +45,8 @@
 import org.junit.Before;
 import org.junit.Test;
 
+import java.util.concurrent.Callable;
+
 /**
  * Test the lifecycle of a autofill session
  */
@@ -56,6 +57,27 @@
     private static final String BUTTON_FULL_ID = "android.autofillservice.cts:id/button";
     private static final String CANCEL_FULL_ID = "android.autofillservice.cts:id/cancel";
 
+    /**
+     * Delay for activity start/stop.
+     * TODO figure out a better way to wait without using sleep().
+     */
+    private static final long WAIT_ACTIVITY_MS = 1000;
+
+    private static final Timeout SESSION_LIFECYCLE_TIMEOUT = new Timeout(
+            "SESSION_LIFECYCLE_TIMEOUT", 500, 2F, 5000);
+
+    /**
+     * Runs an {@code assertion}, retrying until {@code timeout} is reached.
+     */
+    private static void eventually(String description, Callable<Boolean> assertion)
+            throws Exception {
+        SESSION_LIFECYCLE_TIMEOUT.run(description, assertion);
+    }
+
+    public SessionLifecycleTest() {
+        super(new UiBot(SESSION_LIFECYCLE_TIMEOUT));
+    }
+
     @Before
     public void cleanUpState() {
         Helper.preTestCleanup();
@@ -65,8 +87,8 @@
      * Prevents the screen to rotate by itself
      */
     @Before
-    public void disableAutoRotation() {
-        Helper.disableAutoRotation(sUiBot);
+    public void disableAutoRotation() throws Exception {
+        Helper.disableAutoRotation(mUiBot);
     }
 
     /**
@@ -79,14 +101,30 @@
 
     private void killOfProcessLoginActivityProcess() throws Exception {
         // Waiting for activity to stop (stop marker appears)
-        eventually(() -> assertThat(getStoppedMarker(getContext()).exists()).isTrue());
+        eventually("getStoppedMarker()", () -> {
+            return getStoppedMarker(getContext()).exists();
+        });
 
         // onStop might not be finished, hence wait more
-        SystemClock.sleep(1000);
+        SystemClock.sleep(WAIT_ACTIVITY_MS);
 
         // Kill activity that is in the background
         runShellCommand("kill -9 %d",
-                getOutOfProcessPid("android.autofillservice.cts.outside"));
+                getOutOfProcessPid("android.autofillservice.cts.outside",
+                        SESSION_LIFECYCLE_TIMEOUT));
+    }
+
+    private void startAndWaitExternalActivity() throws Exception {
+        Intent outOfProcessAcvitityStartIntent = new Intent(getContext(),
+                OutOfProcessLoginActivity.class);
+        getStartedMarker(getContext()).delete();
+        getContext().startActivity(outOfProcessAcvitityStartIntent);
+        eventually("getStartedMarker()", () -> {
+            return getStartedMarker(getContext()).exists();
+        });
+        getStartedMarker(getContext()).delete();
+        // Even if we wait the activity started, UiObject still fails. Have to wait a little bit.
+        SystemClock.sleep(WAIT_ACTIVITY_MS);
     }
 
     @Test
@@ -95,9 +133,7 @@
         enableService();
 
         // Start activity that is autofilled in a separate process so it can be killed
-        Intent outOfProcessAcvitityStartIntent = new Intent(getContext(),
-                OutOfProcessLoginActivity.class);
-        getContext().startActivity(outOfProcessAcvitityStartIntent);
+        startAndWaitExternalActivity();
 
         // Set expectations.
         final Bundle extras = new Bundle();
@@ -123,62 +159,66 @@
         sReplier.addResponse(response);
 
         // Trigger autofill on username
-        sUiBot.selectById(USERNAME_FULL_ID);
+        mUiBot.selectById(USERNAME_FULL_ID);
 
         // Wait for fill request to be processed
         sReplier.getNextFillRequest();
 
         // Wait until authentication is shown
-        sUiBot.assertDatasets("authenticate");
+        mUiBot.assertDatasets("authenticate");
 
         // Change orientation which triggers a destroy -> create in the app as the activity
         // cannot deal with such situations
-        sUiBot.setScreenOrientation(LANDSCAPE);
+        mUiBot.setScreenOrientation(LANDSCAPE);
+
+        // Wait context and Views being recreated in rotation
+        mUiBot.assertShownById(USERNAME_FULL_ID);
 
         // Delete stopped marker
         getStoppedMarker(getContext()).delete();
 
         // Authenticate
-        sUiBot.selectDataset("authenticate");
+        mUiBot.selectDataset("authenticate");
 
         // Kill activity that is in the background
         killOfProcessLoginActivityProcess();
 
         // Change orientation which triggers a destroy -> create in the app as the activity
         // cannot deal with such situations
-        sUiBot.setScreenOrientation(PORTRAIT);
+        mUiBot.setScreenOrientation(PORTRAIT);
 
         // Approve authentication
-        sUiBot.selectById(BUTTON_FULL_ID);
+        mUiBot.selectById(BUTTON_FULL_ID);
 
         // Wait for dataset to be shown
-        sUiBot.assertDatasets("dataset");
+        mUiBot.assertDatasets("dataset");
 
         // Change orientation which triggers a destroy -> create in the app as the activity
         // cannot deal with such situations
-        sUiBot.setScreenOrientation(LANDSCAPE);
+        mUiBot.setScreenOrientation(LANDSCAPE);
 
         // Select dataset
-        sUiBot.selectDataset("dataset");
+        mUiBot.selectDataset("dataset");
 
         // Check the results.
-        eventually(() -> assertThat(sUiBot.getTextById(USERNAME_FULL_ID)).isEqualTo(
-                "autofilled username"));
+        eventually("getTextById(" + USERNAME_FULL_ID + ")", () -> {
+            return mUiBot.getTextById(USERNAME_FULL_ID).equals("autofilled username");
+        });
 
         // Set password
-        sUiBot.setTextById(PASSWORD_FULL_ID, "new password");
+        mUiBot.setTextById(PASSWORD_FULL_ID, "new password");
 
         // Login
-        sUiBot.selectById(LOGIN_FULL_ID);
+        mUiBot.selectById(LOGIN_FULL_ID);
 
         // Wait for save UI to be shown
-        sUiBot.assertShownById("android:id/autofill_save_yes");
+        mUiBot.assertShownById("android:id/autofill_save_yes");
 
         // Change orientation to make sure save UI can handle this
-        sUiBot.setScreenOrientation(PORTRAIT);
+        mUiBot.setScreenOrientation(PORTRAIT);
 
         // Tap "Save".
-        sUiBot.selectById("android:id/autofill_save_yes");
+        mUiBot.selectById("android:id/autofill_save_yes");
 
         // Get save request
         InstrumentedAutoFillService.SaveRequest saveRequest = sReplier.getNextSaveRequest();
@@ -197,7 +237,9 @@
         final String extraValue = saveRequest.data.getString("numbers");
         assertWithMessage("extras not passed on save").that(extraValue).isEqualTo("4815162342");
 
-        eventually(() -> assertNoDanglingSessions());
+        eventually("assert dangling sessions", () -> {
+            return Helper.listSessions().isEmpty();
+        });
     }
 
     @Test
@@ -206,9 +248,7 @@
         enableService();
 
         // Start activity that is autofilled in a separate process so it can be killed
-        Intent outOfProcessAcvitityStartIntent = new Intent(getContext(),
-                OutOfProcessLoginActivity.class);
-        getContext().startActivity(outOfProcessAcvitityStartIntent);
+        startAndWaitExternalActivity();
 
         // Create the authentication intent (launching a full screen activity)
         IntentSender authentication = PendingIntent.getActivity(getContext(), 0,
@@ -222,28 +262,28 @@
         sReplier.addResponse(response);
 
         // Trigger autofill on username
-        sUiBot.selectById(USERNAME_FULL_ID);
+        mUiBot.selectById(USERNAME_FULL_ID);
 
         // Wait for fill request to be processed
         sReplier.getNextFillRequest();
 
         // Wait until authentication is shown
-        sUiBot.assertDatasets("authenticate");
+        mUiBot.assertDatasets("authenticate");
 
         // Delete stopped marker
         getStoppedMarker(getContext()).delete();
 
         // Authenticate
-        sUiBot.selectDataset("authenticate");
+        mUiBot.selectDataset("authenticate");
 
         // Kill activity that is in the background
         killOfProcessLoginActivityProcess();
 
         // Cancel authentication activity
-        sUiBot.pressBack();
+        mUiBot.pressBack();
 
         // Authentication should still be shown
-        sUiBot.assertDatasets("authenticate");
+        mUiBot.assertDatasets("authenticate");
     }
 
     @Test
@@ -252,25 +292,23 @@
         enableService();
 
         // Start activity that is autofilled in a separate process so it can be killed
-        Intent outOfProcessAcvitityStartIntent = new Intent(getContext(),
-                OutOfProcessLoginActivity.class);
-        getContext().startActivity(outOfProcessAcvitityStartIntent);
+        startAndWaitExternalActivity();
 
         CannedFillResponse response = new CannedFillResponse.Builder()
                 .addDataset(new CannedFillResponse.CannedDataset.Builder(
                         createPresentation("dataset"))
-                        .setField(ID_USERNAME, "filled").build())
+                                .setField(ID_USERNAME, "filled").build())
                 .build();
         sReplier.addResponse(response);
 
         // Trigger autofill on username
-        sUiBot.selectById(USERNAME_FULL_ID);
+        mUiBot.selectById(USERNAME_FULL_ID);
 
         // Wait for fill request to be processed
         sReplier.getNextFillRequest();
 
         // Wait until dataset is shown
-        sUiBot.assertDatasets("dataset");
+        mUiBot.assertDatasets("dataset");
 
         // Delete stopped marker
         getStoppedMarker(getContext()).delete();
@@ -285,10 +323,10 @@
         killOfProcessLoginActivityProcess();
 
         // Cancel activity on top
-        sUiBot.pressBack();
+        mUiBot.pressBack();
 
         // Dataset should still be shown
-        sUiBot.assertDatasets("dataset");
+        mUiBot.assertDatasets("dataset");
     }
 
     @Test
@@ -297,26 +335,24 @@
         enableService();
 
         // Start activity that is autofilled in a separate process so it can be killed
-        Intent outOfProcessAcvitityStartIntent = new Intent(getContext(),
-                OutOfProcessLoginActivity.class);
-        getContext().startActivity(outOfProcessAcvitityStartIntent);
+        startAndWaitExternalActivity();
 
         // Prepare response for first activity
         CannedFillResponse response = new CannedFillResponse.Builder()
                 .addDataset(new CannedFillResponse.CannedDataset.Builder(
                         createPresentation("dataset1"))
-                        .setField(ID_USERNAME, "filled").build())
+                                .setField(ID_USERNAME, "filled").build())
                 .build();
         sReplier.addResponse(response);
 
         // Trigger autofill on username
-        sUiBot.selectById(USERNAME_FULL_ID);
+        mUiBot.selectById(USERNAME_FULL_ID);
 
         // Wait for fill request to be processed
         sReplier.getNextFillRequest();
 
         // Wait until dataset1 is shown
-        sUiBot.assertDatasets("dataset1");
+        mUiBot.assertDatasets("dataset1");
 
         // Delete stopped marker
         getStoppedMarker(getContext()).delete();
@@ -325,7 +361,7 @@
         response = new CannedFillResponse.Builder()
                 .addDataset(new CannedFillResponse.CannedDataset.Builder(
                         createPresentation("dataset2"))
-                        .setField(ID_USERNAME, "filled").build())
+                                .setField(ID_USERNAME, "filled").build())
                 .build();
         sReplier.addResponse(response);
 
@@ -338,18 +374,18 @@
         killOfProcessLoginActivityProcess();
 
         // Trigger autofill on username in nested activity
-        sUiBot.selectById(USERNAME_FULL_ID);
+        mUiBot.selectById(USERNAME_FULL_ID);
 
         // Wait for fill request to be processed
         sReplier.getNextFillRequest();
 
         // Wait until dataset in nested activity is shown
-        sUiBot.assertDatasets("dataset2");
+        mUiBot.assertDatasets("dataset2");
 
         // Tap "Cancel".
-        sUiBot.selectById(CANCEL_FULL_ID);
+        mUiBot.selectById(CANCEL_FULL_ID);
 
         // Dataset should still be shown
-        sUiBot.assertDatasets("dataset1");
+        mUiBot.assertDatasets("dataset1");
     }
 }
diff --git a/tests/autofillservice/src/android/autofillservice/cts/SimpleSaveActivity.java b/tests/autofillservice/src/android/autofillservice/cts/SimpleSaveActivity.java
index 0866b2d..cbb8523 100644
--- a/tests/autofillservice/src/android/autofillservice/cts/SimpleSaveActivity.java
+++ b/tests/autofillservice/src/android/autofillservice/cts/SimpleSaveActivity.java
@@ -16,6 +16,8 @@
 package android.autofillservice.cts;
 
 import android.os.Bundle;
+import android.util.Log;
+import android.view.autofill.AutofillManager;
 import android.widget.Button;
 import android.widget.EditText;
 import android.widget.TextView;
@@ -25,19 +27,24 @@
  */
 public class SimpleSaveActivity extends AbstractAutoFillActivity {
 
+    private static final String TAG = "SimpleSaveActivity";
+
     static final String ID_LABEL = "label";
     static final String ID_INPUT = "input";
     static final String ID_PASSWORD = "password";
     static final String ID_COMMIT = "commit";
     static final String TEXT_LABEL = "Label:";
 
+    private static SimpleSaveActivity sInstance;
+
     TextView mLabel;
     EditText mInput;
     EditText mPassword;
     Button mCancel;
     Button mCommit;
 
-    private static SimpleSaveActivity sInstance;
+    private boolean mAutoCommit = true;
+    private boolean mClearFieldsOnSubmit = false;
 
     public static SimpleSaveActivity getInstance() {
         return sInstance;
@@ -60,7 +67,46 @@
         mCommit = findViewById(R.id.commit);
 
         mCancel.setOnClickListener((v) -> getAutofillManager().cancel());
-        mCommit.setOnClickListener((v) -> getAutofillManager().commit());
+        mCommit.setOnClickListener((v) -> onCommit());
+    }
+
+    private void onCommit() {
+        if (mClearFieldsOnSubmit) {
+            resetFields();
+        }
+        if (mAutoCommit) {
+            Log.d(TAG, "onCommit(): calling AFM.commit()");
+            getAutofillManager().commit();
+        } else {
+            Log.d(TAG, "onCommit(): NOT calling AFM.commit()");
+        }
+    }
+
+    private void resetFields() {
+        Log.d(TAG, "resetFields()");
+        mInput.setText("");
+        mPassword.setText("");
+    }
+
+    /**
+     * Defines whether the activity should automatically call {@link AutofillManager#commit()} when
+     * the commit button is tapped.
+     */
+    void setAutoCommit(boolean flag) {
+        mAutoCommit = flag;
+    }
+
+    /**
+     * Defines whether the activity should automatically clear its fields when submit is clicked.
+     */
+    void setClearFieldsOnSubmit(boolean flag) {
+        mClearFieldsOnSubmit = flag;
+    }
+
+    FillExpectation expectAutoFill(String input) {
+        final FillExpectation expectation = new FillExpectation(input, null);
+        mInput.addTextChangedListener(expectation.mInputWatcher);
+        return expectation;
     }
 
     FillExpectation expectAutoFill(String input, String password) {
@@ -76,12 +122,16 @@
 
         private FillExpectation(String input, String password) {
             mInputWatcher = new OneTimeTextWatcher("input", mInput, input);
-            mPasswordWatcher = new OneTimeTextWatcher("password", mPassword, password);
+            mPasswordWatcher = password == null
+                    ? null
+                    : new OneTimeTextWatcher("password", mPassword, password);
         }
 
         void assertAutoFilled() throws Exception {
             mInputWatcher.assertAutoFilled();
-            mPasswordWatcher.assertAutoFilled();
+            if (mPasswordWatcher != null) {
+                mPasswordWatcher.assertAutoFilled();
+            }
         }
     }
 }
diff --git a/tests/autofillservice/src/android/autofillservice/cts/SimpleSaveActivityTest.java b/tests/autofillservice/src/android/autofillservice/cts/SimpleSaveActivityTest.java
index 9fb16a2..f10b394 100644
--- a/tests/autofillservice/src/android/autofillservice/cts/SimpleSaveActivityTest.java
+++ b/tests/autofillservice/src/android/autofillservice/cts/SimpleSaveActivityTest.java
@@ -15,7 +15,10 @@
  */
 package android.autofillservice.cts;
 
+import static android.autofillservice.cts.AntiTrimmerTextWatcher.TRIMMER_PATTERN;
+import static android.autofillservice.cts.Helper.ID_STATIC_TEXT;
 import static android.autofillservice.cts.Helper.assertTextAndValue;
+import static android.autofillservice.cts.Helper.assertTextValue;
 import static android.autofillservice.cts.Helper.findNodeByResourceId;
 import static android.autofillservice.cts.LoginActivity.ID_USERNAME_CONTAINER;
 import static android.autofillservice.cts.SimpleSaveActivity.ID_COMMIT;
@@ -26,18 +29,30 @@
 import static android.service.autofill.SaveInfo.SAVE_DATA_TYPE_GENERIC;
 import static android.service.autofill.SaveInfo.SAVE_DATA_TYPE_PASSWORD;
 
+import static com.google.common.truth.Truth.assertThat;
 import static com.google.common.truth.Truth.assertWithMessage;
 
 import android.autofillservice.cts.CannedFillResponse.CannedDataset;
 import android.autofillservice.cts.InstrumentedAutoFillService.SaveRequest;
 import android.autofillservice.cts.SimpleSaveActivity.FillExpectation;
 import android.content.Intent;
+import android.service.autofill.BatchUpdates;
+import android.service.autofill.CustomDescription;
+import android.service.autofill.RegexValidator;
+import android.service.autofill.SaveInfo;
+import android.service.autofill.TextValueSanitizer;
+import android.service.autofill.Validator;
+import android.support.test.uiautomator.By;
 import android.support.test.uiautomator.UiObject2;
 import android.view.View;
+import android.view.autofill.AutofillId;
+import android.widget.RemoteViews;
 
 import org.junit.Rule;
 import org.junit.Test;
 
+import java.util.regex.Pattern;
+
 public class SimpleSaveActivityTest extends CustomDescriptionWithLinkTestCase {
 
     @Rule
@@ -89,7 +104,7 @@
 
         // Select dataset.
         final FillExpectation autofillExpecation = mActivity.expectAutoFill("id", "pass");
-        sUiBot.selectDataset("YO");
+        mUiBot.selectDataset("YO");
         autofillExpecation.assertAutoFilled();
 
         mActivity.syncRunOnUiThread(() -> {
@@ -97,10 +112,10 @@
             mActivity.mPassword.setText("PASS");
             mActivity.mCommit.performClick();
         });
-        final UiObject2 saveUi = sUiBot.assertSaveShowing(SAVE_DATA_TYPE_GENERIC);
+        final UiObject2 saveUi = mUiBot.assertSaveShowing(SAVE_DATA_TYPE_GENERIC);
 
         // Save it...
-        sUiBot.saveForAutofill(saveUi, true);
+        mUiBot.saveForAutofill(saveUi, true);
 
         // ... and assert results
         final SaveRequest saveRequest = sReplier.getNextSaveRequest();
@@ -130,15 +145,15 @@
                 .build());
 
         // Trigger autofill.
-        sUiBot.assertShownByRelativeId(ID_INPUT).click();
+        mUiBot.assertShownByRelativeId(ID_INPUT).click();
         sReplier.getNextFillRequest();
 
         // Select dataset...
-        sUiBot.selectDataset("YO");
+        mUiBot.selectDataset("YO");
 
         // ...and assert autofilled values.
-        final UiObject2 input = sUiBot.assertShownByRelativeId(ID_INPUT);
-        final UiObject2 password = sUiBot.assertShownByRelativeId(ID_PASSWORD);
+        final UiObject2 input = mUiBot.assertShownByRelativeId(ID_INPUT);
+        final UiObject2 password = mUiBot.assertShownByRelativeId(ID_PASSWORD);
 
         assertWithMessage("wrong value for 'input'").that(input.getText()).isEqualTo("id");
         // TODO: password field is shown as **** ; ideally we should assert it's a password
@@ -151,8 +166,8 @@
         // Trigger save...
         input.setText("ID");
         password.setText("PASS");
-        sUiBot.assertShownByRelativeId(ID_COMMIT).click();
-        sUiBot.saveForAutofill(true, SAVE_DATA_TYPE_GENERIC);
+        mUiBot.assertShownByRelativeId(ID_COMMIT).click();
+        mUiBot.saveForAutofill(true, SAVE_DATA_TYPE_GENERIC);
 
         // ... and assert results
         final SaveRequest saveRequest = sReplier.getNextSaveRequest();
@@ -167,11 +182,12 @@
 
     @Test
     public void testSave_afterRotation() throws Exception {
-        sUiBot.setScreenOrientation(UiBot.PORTRAIT);
+        mUiBot.setScreenOrientation(UiBot.PORTRAIT);
         try {
             saveTest(true);
         } finally {
-            sUiBot.setScreenOrientation(UiBot.PORTRAIT);
+            mUiBot.setScreenOrientation(UiBot.PORTRAIT);
+            cleanUpAfterScreenOrientationIsBackToPortrait();
         }
     }
 
@@ -196,15 +212,18 @@
             mActivity.mInput.setText("108");
             mActivity.mCommit.performClick();
         });
-        UiObject2 saveUi = sUiBot.assertSaveShowing(SAVE_DATA_TYPE_GENERIC);
+        UiObject2 saveUi = mUiBot.assertSaveShowing(SAVE_DATA_TYPE_GENERIC);
 
         if (rotate) {
-            sUiBot.setScreenOrientation(UiBot.LANDSCAPE);
-            saveUi = sUiBot.assertSaveShowing(SAVE_DATA_TYPE_GENERIC);
+            // After the device rotates, the input field get focus and generate a new session.
+            sReplier.addResponse(CannedFillResponse.NO_RESPONSE);
+
+            mUiBot.setScreenOrientation(UiBot.LANDSCAPE);
+            saveUi = mUiBot.assertSaveShowing(SAVE_DATA_TYPE_GENERIC);
         }
 
         // Save it...
-        sUiBot.saveForAutofill(saveUi, true);
+        mUiBot.saveForAutofill(saveUi, true);
 
         // ... and assert results
         final SaveRequest saveRequest = sReplier.getNextSaveRequest();
@@ -212,6 +231,37 @@
     }
 
     @Test
+    public void testSave_launchIntent() throws Exception {
+        startActivity();
+
+        // Set service.
+        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());
+
+        // Trigger autofill.
+        mActivity.syncRunOnUiThread(() -> mActivity.mInput.requestFocus());
+        sReplier.getNextFillRequest();
+        Helper.assertHasSessions(mPackageName);
+
+        // Trigger save.
+        mActivity.syncRunOnUiThread(() -> {
+            mActivity.mInput.setText("108");
+            mActivity.mCommit.performClick();
+        });
+
+        // Save it...
+        mUiBot.saveForAutofill(true, SAVE_DATA_TYPE_GENERIC);
+        sReplier.getNextSaveRequest();
+        // ... and assert activity was launched
+        WelcomeActivity.assertShowing(mUiBot, "Saved by the bell");
+    }
+
+    @Test
     public void testSaveThenStartNewSessionRightAway() throws Exception {
         startActivity();
 
@@ -236,7 +286,7 @@
         });
 
         // Make sure Save UI for 1st session was canceled....
-        sUiBot.assertSaveNotShowing(SAVE_DATA_TYPE_GENERIC);
+        mUiBot.assertSaveNotShowing(SAVE_DATA_TYPE_GENERIC);
 
         //... and 2nd session canceled as well.
         Helper.assertNoDanglingSessions();
@@ -270,7 +320,7 @@
         });
 
         // Assert it's not showing.
-        sUiBot.assertSaveNotShowing(SAVE_DATA_TYPE_GENERIC);
+        mUiBot.assertSaveNotShowing(SAVE_DATA_TYPE_GENERIC);
     }
 
     @Test
@@ -304,7 +354,7 @@
 
         // Then launches the main activity.
         startActivity(true);
-        sUiBot.assertShownByRelativeId(ID_INPUT);
+        mUiBot.assertShownByRelativeId(ID_INPUT);
 
         // And finally test it..
         dismissSaveTest(DismissType.RECENTS_BUTTON);
@@ -329,31 +379,31 @@
             mActivity.mInput.setText("108");
             mActivity.mCommit.performClick();
         });
-        sUiBot.assertSaveShowing(SAVE_DATA_TYPE_GENERIC);
+        mUiBot.assertSaveShowing(SAVE_DATA_TYPE_GENERIC);
 
         // Then make sure it goes away when user doesn't want it..
         switch (dismissType) {
             case BACK_BUTTON:
-                sUiBot.pressBack();
+                mUiBot.pressBack();
                 break;
             case HOME_BUTTON:
-                sUiBot.pressHome();
+                mUiBot.pressHome();
                 break;
             case TOUCH_OUTSIDE:
-                sUiBot.assertShownByText(TEXT_LABEL).click();
+                mUiBot.assertShownByText(TEXT_LABEL).click();
                 break;
             case FOCUS_OUTSIDE:
                 mActivity.syncRunOnUiThread(() -> mActivity.mLabel.requestFocus());
-                sUiBot.assertShownByText(TEXT_LABEL).click();
+                mUiBot.assertShownByText(TEXT_LABEL).click();
                 break;
             case RECENTS_BUTTON:
-                sUiBot.switchAppsUsingRecents();
-                WelcomeActivity.assertShowingDefaultMessage(sUiBot);
+                mUiBot.switchAppsUsingRecents();
+                WelcomeActivity.assertShowingDefaultMessage(mUiBot);
                 break;
             default:
                 throw new IllegalArgumentException("invalid dismiss type: " + dismissType);
         }
-        sUiBot.assertSaveNotShowing(SAVE_DATA_TYPE_GENERIC);
+        mUiBot.assertSaveNotShowing(SAVE_DATA_TYPE_GENERIC);
     }
 
     @Test
@@ -372,25 +422,25 @@
                 .build());
 
         // Trigger autofill.
-        sUiBot.assertShownByRelativeId(ID_INPUT).click();
+        mUiBot.assertShownByRelativeId(ID_INPUT).click();
         sReplier.getNextFillRequest();
-        sUiBot.assertDatasets("YO");
+        mUiBot.assertDatasets("YO");
         callback.assertUiShownEvent(mActivity.mInput);
 
         // Go home, you are drunk!
-        sUiBot.pressHome();
-        sUiBot.assertNoDatasets();
+        mUiBot.pressHome();
+        mUiBot.assertNoDatasets();
         callback.assertUiHiddenEvent(mActivity.mInput);
 
         // Switch back to the activity.
         restartActivity();
-        sUiBot.assertShownByText(TEXT_LABEL, Helper.ACTIVITY_RESURRECTION_MS);
-        final UiObject2 datasetPicker = sUiBot.assertDatasets("YO");
+        mUiBot.assertShownByText(TEXT_LABEL, Timeouts.ACTIVITY_RESURRECTION);
+        final UiObject2 datasetPicker = mUiBot.assertDatasets("YO");
         callback.assertUiShownEvent(mActivity.mInput);
 
         // Now autofill it.
         final FillExpectation autofillExpecation = mActivity.expectAutoFill("id", "pass");
-        sUiBot.selectDataset(datasetPicker, "YO");
+        mUiBot.selectDataset(datasetPicker, "YO");
         autofillExpecation.assertAutoFilled();
     }
 
@@ -407,18 +457,18 @@
         // Trigger autofill.
         mActivity.syncRunOnUiThread(() -> mActivity.mInput.requestFocus());
         sReplier.getNextFillRequest();
-        sUiBot.assertNoDatasets();
+        mUiBot.assertNoDatasets();
 
         // Trigger save, but don't tap it.
         mActivity.syncRunOnUiThread(() -> {
             mActivity.mInput.setText("108");
             mActivity.mCommit.performClick();
         });
-        sUiBot.assertSaveShowing(SAVE_DATA_TYPE_GENERIC);
+        mUiBot.assertSaveShowing(SAVE_DATA_TYPE_GENERIC);
 
         // Go home, you are drunk!
-        sUiBot.pressHome();
-        sUiBot.assertSaveNotShowing(SAVE_DATA_TYPE_GENERIC);
+        mUiBot.pressHome();
+        mUiBot.assertSaveNotShowing(SAVE_DATA_TYPE_GENERIC);
         Helper.assertNoDanglingSessions();
 
         // Prepare the response for the next session, which will be automatically triggered
@@ -434,15 +484,15 @@
 
         // Switch back to the activity.
         restartActivity();
-        sUiBot.assertShownByText(TEXT_LABEL, Helper.ACTIVITY_RESURRECTION_MS);
-        sUiBot.assertSaveNotShowing(SAVE_DATA_TYPE_GENERIC);
+        mUiBot.assertShownByText(TEXT_LABEL, Timeouts.ACTIVITY_RESURRECTION);
+        mUiBot.assertSaveNotShowing(SAVE_DATA_TYPE_GENERIC);
         sReplier.getNextFillRequest();
-        sUiBot.assertNoDatasets();
+        mUiBot.assertNoDatasets();
 
         // Trigger and select UI.
         mActivity.syncRunOnUiThread(() -> mActivity.mPassword.requestFocus());
         final FillExpectation autofillExpecation = mActivity.expectAutoFill("id", "pass");
-        sUiBot.selectDataset("YO");
+        mUiBot.selectDataset("YO");
 
         // Assert it.
         autofillExpecation.assertAutoFilled();
@@ -453,7 +503,7 @@
         intent.setFlags(
                 Intent.FLAG_ACTIVITY_RETAIN_IN_RECENTS | Intent.FLAG_ACTIVITY_NEW_DOCUMENT);
         mContext.startActivity(intent);
-        WelcomeActivity.assertShowingDefaultMessage(sUiBot);
+        WelcomeActivity.assertShowingDefaultMessage(mUiBot);
     }
 
     @Override
@@ -485,34 +535,44 @@
         tapSaveUiLink(saveUi);
 
         // Make sure new activity is shown...
-        WelcomeActivity.assertShowingDefaultMessage(sUiBot);
-        sUiBot.assertSaveNotShowing(SAVE_DATA_TYPE_GENERIC);
+        WelcomeActivity.assertShowingDefaultMessage(mUiBot);
+        mUiBot.assertSaveNotShowing(SAVE_DATA_TYPE_GENERIC);
 
         // .. then do something to return to previous activity...
         switch (type) {
             case ROTATE_THEN_TAP_BACK_BUTTON:
-                sUiBot.setScreenOrientation(UiBot.LANDSCAPE);
+                // After the device rotates, the input field get focus and generate a new session.
+                sReplier.addResponse(CannedFillResponse.NO_RESPONSE);
+
+                mUiBot.setScreenOrientation(UiBot.LANDSCAPE);
+                WelcomeActivity.assertShowingDefaultMessage(mUiBot);
                 // not breaking on purpose
             case TAP_BACK_BUTTON:
                 // ..then go back and save it.
-                sUiBot.pressBack();
+                mUiBot.pressBack();
                 break;
             case FINISH_ACTIVITY:
                 // ..then finishes it.
-                WelcomeActivity.finishIt();
+                WelcomeActivity.finishIt(mUiBot);
                 break;
             default:
                 throw new IllegalArgumentException("invalid type: " + type);
         }
         // Make sure previous activity is back...
-        sUiBot.assertShownByRelativeId(ID_INPUT);
+        mUiBot.assertShownByRelativeId(ID_INPUT);
 
         // ... and tap save.
         final UiObject2 newSaveUi = assertSaveUiWithLinkIsShown(SAVE_DATA_TYPE_GENERIC);
-        sUiBot.saveForAutofill(newSaveUi, true);
+        mUiBot.saveForAutofill(newSaveUi, true);
 
         final SaveRequest saveRequest = sReplier.getNextSaveRequest();
         assertTextAndValue(findNodeByResourceId(saveRequest.structure, ID_INPUT), "108");
+
+    }
+
+    @Override
+    protected void cleanUpAfterScreenOrientationIsBackToPortrait() throws Exception {
+        sReplier.getNextFillRequest();
     }
 
     @Override
@@ -544,27 +604,27 @@
         tapSaveUiLink(saveUi);
 
         // Make sure new activity is shown.
-        WelcomeActivity.assertShowingDefaultMessage(sUiBot);
-        sUiBot.assertSaveNotShowing(SAVE_DATA_TYPE_GENERIC);
+        WelcomeActivity.assertShowingDefaultMessage(mUiBot);
+        mUiBot.assertSaveNotShowing(SAVE_DATA_TYPE_GENERIC);
 
         // Tap back to restore the Save UI...
-        sUiBot.pressBack();
+        mUiBot.pressBack();
         // Make sure previous activity is back...
-        sUiBot.assertShownByRelativeId(ID_LABEL);
+        mUiBot.assertShownByRelativeId(ID_LABEL);
 
         // ...but don't tap it...
-        final UiObject2 saveUi2 = sUiBot.assertSaveShowing(SAVE_DATA_TYPE_GENERIC);
+        final UiObject2 saveUi2 = mUiBot.assertSaveShowing(SAVE_DATA_TYPE_GENERIC);
 
         // ...instead, do something to dismiss it:
         switch (action) {
             case TOUCH_OUTSIDE:
-                sUiBot.assertShownByRelativeId(ID_LABEL).longClick();
+                mUiBot.assertShownByRelativeId(ID_LABEL).longClick();
                 break;
             case TAP_NO_ON_SAVE_UI:
-                sUiBot.saveForAutofill(saveUi2, false);
+                mUiBot.saveForAutofill(saveUi2, false);
                 break;
             case TAP_YES_ON_SAVE_UI:
-                sUiBot.saveForAutofill(true, SAVE_DATA_TYPE_GENERIC);
+                mUiBot.saveForAutofill(true, SAVE_DATA_TYPE_GENERIC);
                 final SaveRequest saveRequest = sReplier.getNextSaveRequest();
                 assertTextAndValue(findNodeByResourceId(saveRequest.structure, ID_INPUT), "108");
                 Helper.assertNoDanglingSessions();
@@ -572,7 +632,7 @@
             default:
                 throw new IllegalArgumentException("invalid action: " + action);
         }
-        sUiBot.assertSaveNotShowing(SAVE_DATA_TYPE_GENERIC);
+        mUiBot.assertSaveNotShowing(SAVE_DATA_TYPE_GENERIC);
 
         // Make sure previous session was finished.
         Helper.assertNoDanglingSessions();
@@ -599,7 +659,7 @@
         });
 
         // Save it...
-        sUiBot.saveForAutofill(true, SAVE_DATA_TYPE_GENERIC);
+        mUiBot.saveForAutofill(true, SAVE_DATA_TYPE_GENERIC);
 
         // ... and assert results
         final SaveRequest saveRequest = sReplier.getNextSaveRequest();
@@ -634,12 +694,12 @@
         // Tap the link.
         tapSaveUiLink(saveUi);
         // Make sure new activity is shown...
-        WelcomeActivity.assertShowingDefaultMessage(sUiBot);
-        sUiBot.assertSaveNotShowing(SAVE_DATA_TYPE_GENERIC);
+        WelcomeActivity.assertShowingDefaultMessage(mUiBot);
+        mUiBot.assertSaveNotShowing(SAVE_DATA_TYPE_GENERIC);
 
         switch (type) {
             case TAP_RECENTS:
-                sUiBot.switchAppsUsingRecents();
+                mUiBot.switchAppsUsingRecents();
                 break;
             case LAUNCH_PREVIOUS_ACTIVITY:
                 startActivity(SimpleSaveActivity.class);
@@ -647,17 +707,75 @@
             case LAUNCH_NEW_ACTIVITY:
                 // Launch a 3rd activity...
                 startActivity(LoginActivity.class);
-                sUiBot.assertShownByRelativeId(ID_USERNAME_CONTAINER);
+                mUiBot.assertShownByRelativeId(ID_USERNAME_CONTAINER);
                 // ...then go back
-                sUiBot.pressBack();
+                mUiBot.pressBack();
                 break;
             default:
                 throw new IllegalArgumentException("invalid type: " + type);
         }
         // Make sure right activity is showing
-        sUiBot.assertShownByRelativeId(ID_INPUT);
+        mUiBot.assertShownByRelativeId(ID_INPUT, Timeouts.ACTIVITY_RESURRECTION);
 
-        sUiBot.assertSaveNotShowing(SAVE_DATA_TYPE_GENERIC);
+        mUiBot.assertSaveNotShowing(SAVE_DATA_TYPE_GENERIC);
+    }
+
+    @Test
+    public void testSelectedDatasetsAreSentOnSaveRequest() throws Exception {
+        startActivity();
+
+        // Set service.
+        enableService();
+
+        // Set expectations.
+        sReplier.addResponse(new CannedFillResponse.Builder()
+                .setRequiredSavableIds(SAVE_DATA_TYPE_GENERIC, ID_INPUT, ID_PASSWORD)
+                // Added on reversed order on purpose
+                .addDataset(new CannedDataset.Builder()
+                        .setId("D2")
+                        .setField(ID_INPUT, "id again")
+                        .setField(ID_PASSWORD, "pass")
+                        .setPresentation(createPresentation("D2"))
+                        .build())
+                .addDataset(new CannedDataset.Builder()
+                        .setId("D1")
+                        .setField(ID_INPUT, "id")
+                        .setPresentation(createPresentation("D1"))
+                        .build())
+                .build());
+
+        // Trigger autofill.
+        mActivity.syncRunOnUiThread(() -> mActivity.mInput.requestFocus());
+        sReplier.getNextFillRequest();
+
+        // Select 1st dataset.
+        final FillExpectation autofillExpecation1 = mActivity.expectAutoFill("id");
+        final UiObject2 picker1 = mUiBot.assertDatasets("D2", "D1");
+        mUiBot.selectDataset(picker1, "D1");
+        autofillExpecation1.assertAutoFilled();
+
+        // Select 2nd dataset.
+        mActivity.syncRunOnUiThread(() -> mActivity.mPassword.requestFocus());
+        final FillExpectation autofillExpecation2 = mActivity.expectAutoFill("id again", "pass");
+        final UiObject2 picker2 = mUiBot.assertDatasets("D2");
+        mUiBot.selectDataset(picker2, "D2");
+        autofillExpecation2.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();
+        assertTextAndValue(findNodeByResourceId(saveRequest.structure, ID_INPUT), "ID");
+        assertTextAndValue(findNodeByResourceId(saveRequest.structure, ID_PASSWORD), "PASS");
+        assertThat(saveRequest.datasetIds).containsExactly("D1", "D2").inOrder();
     }
 
     @Override
@@ -693,14 +811,14 @@
         tapSaveUiLink(saveUi);
 
         // Make sure new activity is shown...
-        WelcomeActivity.assertShowingDefaultMessage(sUiBot);
+        WelcomeActivity.assertShowingDefaultMessage(mUiBot);
 
         // Save UI should be showing as well, since Trampoline finished.
-        sUiBot.assertSaveShowing(SAVE_DATA_TYPE_GENERIC);
+        mUiBot.assertSaveShowing(SAVE_DATA_TYPE_GENERIC);
 
         // Go back and make sure it's showing the right activity.
-        sUiBot.pressBack();
-        sUiBot.assertShownByRelativeId(ID_LABEL);
+        mUiBot.pressBack();
+        mUiBot.assertShownByRelativeId(ID_LABEL);
 
         // Now start a new session.
         sReplier.addResponse(new CannedFillResponse.Builder()
@@ -712,9 +830,327 @@
             mActivity.mPassword.setText("42");
             mActivity.mCommit.performClick();
         });
-        sUiBot.saveForAutofill(true, SAVE_DATA_TYPE_PASSWORD);
+        mUiBot.saveForAutofill(true, SAVE_DATA_TYPE_PASSWORD);
         final SaveRequest saveRequest = sReplier.getNextSaveRequest();
         assertTextAndValue(findNodeByResourceId(saveRequest.structure, ID_INPUT), "108");
         assertTextAndValue(findNodeByResourceId(saveRequest.structure, ID_PASSWORD), "42");
     }
+
+    @Test
+    public void testSanitizeOnSaveWhenAppChangeValues() throws Exception {
+        startActivity();
+
+        // Set listeners that will change the saved value
+        new AntiTrimmerTextWatcher(mActivity.mInput);
+        new AntiTrimmerTextWatcher(mActivity.mPassword);
+
+        // Set service.
+        enableService();
+
+        // Set expectations.
+        final AutofillId inputId = mActivity.mInput.getAutofillId();
+        final AutofillId passwordId = mActivity.mPassword.getAutofillId();
+        sReplier.addResponse(new CannedFillResponse.Builder()
+                .setRequiredSavableIds(SAVE_DATA_TYPE_GENERIC, ID_INPUT)
+                .addSanitizer(new TextValueSanitizer(TRIMMER_PATTERN, "$1"), inputId, passwordId)
+                .build());
+
+        // Trigger autofill.
+        mActivity.syncRunOnUiThread(() -> mActivity.mInput.requestFocus());
+        sReplier.getNextFillRequest();
+        Helper.assertHasSessions(mPackageName);
+
+        // Trigger save.
+        mActivity.syncRunOnUiThread(() -> {
+            mActivity.mInput.setText("id");
+            mActivity.mPassword.setText("pass");
+            mActivity.mCommit.performClick();
+        });
+
+        // Save it...
+        mUiBot.saveForAutofill(true, SAVE_DATA_TYPE_GENERIC);
+
+        // ... and assert results
+        final SaveRequest saveRequest = sReplier.getNextSaveRequest();
+        assertTextValue(findNodeByResourceId(saveRequest.structure, ID_INPUT), "id");
+        assertTextValue(findNodeByResourceId(saveRequest.structure, ID_PASSWORD), "pass");
+    }
+
+    @Test
+    public void testSanitizeOnSaveNoChange() throws Exception {
+        startActivity();
+
+        // Set service.
+        enableService();
+
+        // Set expectations.
+        final AutofillId inputId = mActivity.mInput.getAutofillId();
+        final AutofillId passwordId = mActivity.mPassword.getAutofillId();
+        sReplier.addResponse(new CannedFillResponse.Builder()
+                .setRequiredSavableIds(SAVE_DATA_TYPE_GENERIC, ID_INPUT)
+                .setOptionalSavableIds(ID_PASSWORD)
+                .addSanitizer(new TextValueSanitizer(TRIMMER_PATTERN, "$1"), inputId, passwordId)
+                .build());
+
+        // Trigger autofill.
+        mActivity.syncRunOnUiThread(() -> mActivity.mInput.requestFocus());
+        sReplier.getNextFillRequest();
+        mUiBot.assertNoDatasets();
+
+        // Trigger save.
+        mActivity.syncRunOnUiThread(() -> {
+            mActivity.mInput.setText("#id#");
+            mActivity.mPassword.setText("#pass#");
+            mActivity.mCommit.performClick();
+        });
+
+        // Save it...
+        mUiBot.saveForAutofill(true, SAVE_DATA_TYPE_GENERIC);
+
+        // ... and assert results
+        final SaveRequest saveRequest = sReplier.getNextSaveRequest();
+        assertTextValue(findNodeByResourceId(saveRequest.structure, ID_INPUT), "id");
+        assertTextValue(findNodeByResourceId(saveRequest.structure, ID_PASSWORD), "pass");
+    }
+
+    @Test
+    public void testDontSaveWhenSanitizedValueForRequiredFieldDidntChange() throws Exception {
+        startActivity();
+
+        // Set listeners that will change the saved value
+        new AntiTrimmerTextWatcher(mActivity.mInput);
+        new AntiTrimmerTextWatcher(mActivity.mPassword);
+
+        // Set service.
+        enableService();
+
+        // Set expectations.
+        final AutofillId inputId = mActivity.mInput.getAutofillId();
+        final AutofillId passwordId = mActivity.mPassword.getAutofillId();
+        sReplier.addResponse(new CannedFillResponse.Builder()
+                .setRequiredSavableIds(SAVE_DATA_TYPE_GENERIC, ID_INPUT, ID_PASSWORD)
+                .addSanitizer(new TextValueSanitizer(TRIMMER_PATTERN, "$1"), inputId, passwordId)
+                .addDataset(new CannedDataset.Builder()
+                        .setField(ID_INPUT, "id")
+                        .setField(ID_PASSWORD, "pass")
+                        .setPresentation(createPresentation("YO"))
+                        .build())
+                .build());
+
+        // Trigger autofill.
+        mActivity.syncRunOnUiThread(() -> mActivity.mInput.requestFocus());
+        sReplier.getNextFillRequest();
+
+        mActivity.syncRunOnUiThread(() -> {
+            mActivity.mInput.setText("id");
+            mActivity.mPassword.setText("pass");
+            mActivity.mCommit.performClick();
+        });
+
+        mUiBot.assertSaveNotShowing(SAVE_DATA_TYPE_GENERIC);
+    }
+
+    @Test
+    public void testDontSaveWhenSanitizedValueForOptionalFieldDidntChange() throws Exception {
+        startActivity();
+
+        // Set service.
+        enableService();
+
+        // Set expectations.
+        final AutofillId passwordId = mActivity.mPassword.getAutofillId();
+        sReplier.addResponse(new CannedFillResponse.Builder()
+                .setRequiredSavableIds(SAVE_DATA_TYPE_GENERIC, ID_INPUT)
+                .setOptionalSavableIds(ID_PASSWORD)
+                .addSanitizer(new TextValueSanitizer(Pattern.compile("(pass) "), "$1"), passwordId)
+                .addDataset(new CannedDataset.Builder()
+                        .setField(ID_INPUT, "id")
+                        .setField(ID_PASSWORD, "pass")
+                        .setPresentation(createPresentation("YO"))
+                        .build())
+                .build());
+
+        // Trigger autofill.
+        mActivity.syncRunOnUiThread(() -> mActivity.mInput.requestFocus());
+
+        mActivity.syncRunOnUiThread(() -> {
+            mActivity.mInput.setText("id");
+            mActivity.mPassword.setText("#pass#");
+            mActivity.mCommit.performClick();
+        });
+
+        mUiBot.assertSaveNotShowing(SAVE_DATA_TYPE_GENERIC);
+    }
+
+    @Test
+    public void testDontSaveWhenRequiredFieldFailedSanitization() throws Exception {
+        startActivity();
+
+        // Set service.
+        enableService();
+
+        // Set expectations.
+        final AutofillId inputId = mActivity.mInput.getAutofillId();
+        final AutofillId passwordId = mActivity.mPassword.getAutofillId();
+        sReplier.addResponse(new CannedFillResponse.Builder()
+                .setRequiredSavableIds(SAVE_DATA_TYPE_GENERIC, ID_INPUT, ID_PASSWORD)
+                .addSanitizer(new TextValueSanitizer(Pattern.compile("dude"), "$1"),
+                        inputId, passwordId)
+                .addDataset(new CannedDataset.Builder()
+                        .setField(ID_INPUT, "#id#")
+                        .setField(ID_PASSWORD, "#pass#")
+                        .setPresentation(createPresentation("YO"))
+                        .build())
+                .build());
+
+        // Trigger autofill.
+        mActivity.syncRunOnUiThread(() -> mActivity.mInput.requestFocus());
+        sReplier.getNextFillRequest();
+
+        mActivity.syncRunOnUiThread(() -> {
+            mActivity.mInput.setText("id");
+            mActivity.mPassword.setText("pass");
+            mActivity.mCommit.performClick();
+        });
+
+        mUiBot.assertSaveNotShowing(SAVE_DATA_TYPE_GENERIC);
+    }
+
+    @Test
+    public void testDontSaveWhenOptionalFieldFailedSanitization() throws Exception {
+        startActivity();
+
+        // Set service.
+        enableService();
+
+        // Set expectations.
+        final AutofillId inputId = mActivity.mInput.getAutofillId();
+        final AutofillId passwordId = mActivity.mPassword.getAutofillId();
+        sReplier.addResponse(new CannedFillResponse.Builder()
+                .setRequiredSavableIds(SAVE_DATA_TYPE_GENERIC, ID_INPUT)
+                .setOptionalSavableIds(ID_PASSWORD)
+                .addSanitizer(new TextValueSanitizer(Pattern.compile("dude"), "$1"),
+                        inputId, passwordId)
+                .addDataset(new CannedDataset.Builder()
+                        .setField(ID_INPUT, "id")
+                        .setField(ID_PASSWORD, "#pass#")
+                        .setPresentation(createPresentation("YO"))
+                        .build())
+                .build());
+
+        // Trigger autofill.
+        mActivity.syncRunOnUiThread(() -> mActivity.mInput.requestFocus());
+        sReplier.getNextFillRequest();
+
+        mActivity.syncRunOnUiThread(() -> {
+            mActivity.mInput.setText("id");
+            mActivity.mPassword.setText("pass");
+            mActivity.mCommit.performClick();
+        });
+
+        mUiBot.assertSaveNotShowing(SAVE_DATA_TYPE_GENERIC);
+    }
+
+    @Test
+    public void testExplicitySaveButton() throws Exception {
+        explicitySaveButtonTest(false, 0);
+    }
+
+    @Test
+    public void testExplicitySaveButtonWhenAppClearFields() throws Exception {
+        explicitySaveButtonTest(true, 0);
+    }
+
+    @Test
+    public void testExplicitySaveButtonOnly() throws Exception {
+        explicitySaveButtonTest(false, SaveInfo.FLAG_DONT_SAVE_ON_FINISH);
+    }
+
+    /**
+     * Tests scenario where service explicitly indicates which button is used to save.
+     */
+    private void explicitySaveButtonTest(boolean clearFieldsOnSubmit, int flags) throws Exception {
+        startActivity();
+        mActivity.setAutoCommit(false);
+        mActivity.setClearFieldsOnSubmit(clearFieldsOnSubmit);
+
+        // Set service.
+        enableService();
+
+        // Set expectations.
+        sReplier.addResponse(new CannedFillResponse.Builder()
+                .setRequiredSavableIds(SAVE_DATA_TYPE_GENERIC, ID_INPUT)
+                .setSaveTriggerId(mActivity.mCommit.getAutofillId())
+                .setSaveInfoFlags(flags)
+                .build());
+
+        // Trigger autofill.
+        mActivity.syncRunOnUiThread(() -> mActivity.mInput.requestFocus());
+        sReplier.getNextFillRequest();
+        Helper.assertHasSessions(mPackageName);
+        // Trigger save.
+        mActivity.syncRunOnUiThread(() -> {
+            mActivity.mInput.setText("108");
+            mActivity.mCommit.performClick();
+        });
+        UiObject2 saveUi = mUiBot.assertSaveShowing(SAVE_DATA_TYPE_GENERIC);
+
+        // Save it...
+        mUiBot.saveForAutofill(saveUi, true);
+
+        // ... and assert results
+        final SaveRequest saveRequest = sReplier.getNextSaveRequest();
+        assertTextAndValue(findNodeByResourceId(saveRequest.structure, ID_INPUT), "108");
+    }
+
+    @Override
+    protected void tapLinkAfterUpdateAppliedTest(boolean updateLinkView) throws Exception {
+        startActivity();
+        // Set service.
+        enableService();
+
+        // Set expectations.
+        final CustomDescription.Builder customDescription =
+                newCustomDescriptionBuilder(WelcomeActivity.class);
+        final RemoteViews update = newTemplate();
+        if (updateLinkView) {
+            update.setCharSequence(R.id.link, "setText", "TAP ME IF YOU CAN");
+        } else {
+            update.setCharSequence(R.id.static_text, "setText", "ME!");
+        }
+        Validator validCondition = new RegexValidator(mActivity.mInput.getAutofillId(),
+                Pattern.compile(".*"));
+        customDescription.batchUpdate(validCondition,
+                new BatchUpdates.Builder().updateTemplate(update).build());
+
+        sReplier.addResponse(new CannedFillResponse.Builder()
+                .setCustomDescription(customDescription.build())
+                .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();
+        });
+        final UiObject2 saveUi;
+        if (updateLinkView) {
+            saveUi = assertSaveUiWithLinkIsShown(SAVE_DATA_TYPE_GENERIC, "TAP ME IF YOU CAN");
+        } else {
+            saveUi = assertSaveUiWithLinkIsShown(SAVE_DATA_TYPE_GENERIC);
+            final UiObject2 changed = saveUi.findObject(By.res(mPackageName, ID_STATIC_TEXT));
+            assertThat(changed.getText()).isEqualTo("ME!");
+        }
+
+        // Tap the link.
+        tapSaveUiLink(saveUi);
+
+        // Make sure new activity is shown...
+        WelcomeActivity.assertShowingDefaultMessage(mUiBot);
+        mUiBot.assertSaveNotShowing(SAVE_DATA_TYPE_GENERIC);
+    }
 }
diff --git a/tests/autofillservice/src/android/autofillservice/cts/TextValueSanitizerTest.java b/tests/autofillservice/src/android/autofillservice/cts/TextValueSanitizerTest.java
new file mode 100644
index 0000000..572e3b1
--- /dev/null
+++ b/tests/autofillservice/src/android/autofillservice/cts/TextValueSanitizerTest.java
@@ -0,0 +1,94 @@
+/*
+ * 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.autofillservice.cts;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import static org.testng.Assert.assertThrows;
+
+import android.service.autofill.TextValueSanitizer;
+import android.support.test.runner.AndroidJUnit4;
+import android.view.autofill.AutofillValue;
+
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+import java.util.regex.Pattern;
+
+@RunWith(AndroidJUnit4.class)
+public class TextValueSanitizerTest {
+
+    @Test
+    public void testConstructor_nullValues() {
+        assertThrows(NullPointerException.class,
+                () -> new TextValueSanitizer(Pattern.compile("42"), null));
+        assertThrows(NullPointerException.class,
+                () -> new TextValueSanitizer(null, "42"));
+    }
+
+    @Test
+    public void testSanitize_nullValue() {
+        final TextValueSanitizer sanitizer = new TextValueSanitizer(Pattern.compile("42"), "42");
+        assertThat(sanitizer.sanitize(null)).isNull();
+    }
+
+    @Test
+    public void testSanitize_nonTextValue() {
+        final TextValueSanitizer sanitizer = new TextValueSanitizer(Pattern.compile("42"), "42");
+        final AutofillValue value = AutofillValue.forToggle(true);
+        assertThat(sanitizer.sanitize(value)).isNull();
+    }
+
+    @Test
+    public void testSanitize_badRegex() {
+        final TextValueSanitizer sanitizer = new TextValueSanitizer(Pattern.compile(".*(\\d*).*"),
+                "$2"); // invalid group
+        final AutofillValue value = AutofillValue.forText("blah 42  blaH");
+        assertThat(sanitizer.sanitize(value)).isNull();
+    }
+
+    @Test
+    public void testSanitize_valueMismatch() {
+        final TextValueSanitizer sanitizer = new TextValueSanitizer(Pattern.compile("42"), "xxx");
+        final AutofillValue value = AutofillValue.forText("43");
+        assertThat(sanitizer.sanitize(value)).isNull();
+    }
+
+    @Test
+    public void testSanitize_simpleMatch() {
+        final TextValueSanitizer sanitizer = new TextValueSanitizer(Pattern.compile("42"),
+                "forty-two");
+        assertThat(sanitizer.sanitize(AutofillValue.forText("42")).getTextValue())
+            .isEqualTo("forty-two");
+    }
+
+    @Test
+    public void testSanitize_multipleMatches() {
+        final TextValueSanitizer sanitizer = new TextValueSanitizer(Pattern.compile(".*(\\d*).*"),
+                "Number");
+        assertThat(sanitizer.sanitize(AutofillValue.forText("blah 42  blaH")).getTextValue())
+            .isEqualTo("NumberNumber");
+    }
+
+    @Test
+    public void testSanitize_groupSubstitutionMatch() {
+        final TextValueSanitizer sanitizer =
+                new TextValueSanitizer(Pattern.compile("\\s*(\\d*)\\s*"), "$1");
+        assertThat(sanitizer.sanitize(AutofillValue.forText("  42 ")).getTextValue())
+                .isEqualTo("42");
+    }
+}
diff --git a/tests/autofillservice/src/android/autofillservice/cts/TimePickerTestCase.java b/tests/autofillservice/src/android/autofillservice/cts/TimePickerTestCase.java
index d4be2e6..4bd8cf0 100644
--- a/tests/autofillservice/src/android/autofillservice/cts/TimePickerTestCase.java
+++ b/tests/autofillservice/src/android/autofillservice/cts/TimePickerTestCase.java
@@ -31,7 +31,6 @@
 import android.autofillservice.cts.InstrumentedAutoFillService.SaveRequest;
 import android.icu.util.Calendar;
 
-import org.junit.After;
 import org.junit.Test;
 
 /**
@@ -42,11 +41,6 @@
 
     protected abstract T getTimePickerActivity();
 
-    @After
-    public void finishWelcomeActivity() {
-        WelcomeActivity.finishIt();
-    }
-
     @Test
     public void testAutoFillAndSave() throws Exception {
         final T activity = getTimePickerActivity();
@@ -78,7 +72,7 @@
         assertTextIsSanitized(fillRequest.structure, ID_TIME_PICKER);
         assertNumberOfChildren(fillRequest.structure, ID_TIME_PICKER, 0);
         // Auto-fill it.
-        sUiBot.selectDataset("Adventure Time");
+        mUiBot.selectDataset("Adventure Time");
 
         // Check the results.
         activity.assertAutoFilled();
@@ -87,7 +81,7 @@
         activity.setTime(10, 40);
         activity.tapOk();
 
-        sUiBot.saveForAutofill(true, SAVE_DATA_TYPE_GENERIC);
+        mUiBot.saveForAutofill(true, SAVE_DATA_TYPE_GENERIC);
         final SaveRequest saveRequest = sReplier.getNextSaveRequest();
         assertWithMessage("onSave() not called").that(saveRequest).isNotNull();
 
diff --git a/tests/autofillservice/src/android/autofillservice/cts/Timeout.java b/tests/autofillservice/src/android/autofillservice/cts/Timeout.java
new file mode 100644
index 0000000..e709dac
--- /dev/null
+++ b/tests/autofillservice/src/android/autofillservice/cts/Timeout.java
@@ -0,0 +1,181 @@
+/*
+ * 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.autofillservice.cts;
+
+import android.os.SystemClock;
+import android.support.annotation.NonNull;
+import android.text.TextUtils;
+import android.util.Log;
+
+import java.util.concurrent.Callable;
+
+/**
+ * A "smart" timeout that supports exponential backoff.
+ */
+//TODO: move to common CTS Code
+public final class Timeout {
+
+    private static final String TAG = "Timeout";
+    private static final boolean VERBOSE = true;
+
+    private final String mName;
+    private long mCurrentValue;
+    private final float mMultiplier;
+    private final long mMaxValue;
+
+    /**
+     * Default constructor.
+     *
+     * @param name name to be used for logging purposes.
+     * @param initialValue initial timeout value, in ms.
+     * @param multiplier multiplier for {@link #increase()}.
+     * @param maxValue max timeout value (in ms) set by {@link #increase()}.
+     *
+     * @throws IllegalArgumentException if {@code name} is {@code null} or empty,
+     * {@code initialValue}, {@code multiplir} or {@code maxValue} are less than {@code 1},
+     * or if {@code initialValue} is higher than {@code maxValue}
+     */
+    public Timeout(String name, long initialValue, float multiplier, long maxValue) {
+        if (initialValue < 1 || maxValue < 1 || initialValue > maxValue) {
+            throw new IllegalArgumentException(
+                    "invalid initial and/or max values: " + initialValue + " and " + maxValue);
+        }
+        if (multiplier <= 1) {
+            throw new IllegalArgumentException("multiplier must be higher than 1: " + multiplier);
+        }
+        if (TextUtils.isEmpty(name)) {
+            throw new IllegalArgumentException("no name");
+        }
+        mName = name;
+        mCurrentValue = initialValue;
+        mMultiplier = multiplier;
+        mMaxValue = maxValue;
+        Log.d(TAG, "Constructor: " + this + " at " + JUnitHelper.getCurrentTestName());
+    }
+
+    /**
+     * Gets the current timeout, in ms.
+     */
+    public long ms() {
+        return mCurrentValue;
+    }
+
+    /**
+     * Gets the max timeout, in ms.
+     */
+    public long getMaxValue() {
+        return mMaxValue;
+    }
+
+    /**
+     * @return the mMultiplier
+     */
+    public float getMultiplier() {
+        return mMultiplier;
+    }
+
+    /**
+     * Gets the user-friendly name of this timeout.
+     */
+    @NonNull
+    public String getName() {
+        return mName;
+    }
+
+    /**
+     * Increases the current value by the {@link #getMultiplier()}, up to {@link #getMaxValue()}.
+     *
+     * @return previous current value.
+     */
+    public long increase() {
+        final long oldValue = mCurrentValue;
+        mCurrentValue = Math.min(mMaxValue, (long) (mCurrentValue * mMultiplier));
+        if (oldValue != mCurrentValue) {
+            Log.w(TAG, mName + " increased from " + oldValue + "ms to " + mCurrentValue + "ms at "
+                    + JUnitHelper.getCurrentTestName());
+        }
+        return oldValue;
+    }
+
+    /**
+     * Runs a {@code job} many times before giving up, sleeping between failed attempts up to
+     * {@link #ms()}.
+     *
+     * @param description description of the job for logging purposes.
+     * @param job job to be run, must return {@code null} if it failed and should be retried.
+     * @throws RetryableException if all attempts failed.
+     * @throws IllegalArgumentException if {@code description} is {@code null} or empty, if
+     * {@code job} is {@code  null}, or if {@code maxAttempts} is less than 1.
+     * @throws Exception any other exception thrown by helper methods.
+     *
+     * @return job's result.
+     */
+    public <T> T run(String description, Callable<T> job) throws Exception {
+        return run(description, 100, job);
+    }
+
+    /**
+     * Runs a {@code job} many times before giving up, sleeping between failed attempts up to
+     * {@link #ms()}.
+     *
+     * @param description description of the job for logging purposes.
+     * @param job job to be run, must return {@code null} if it failed and should be retried.
+     * @param retryMs how long to sleep between failures.
+     * @throws RetryableException if all attempts failed.
+     * @throws IllegalArgumentException if {@code description} is {@code null} or empty, if
+     * {@code job} is {@code  null}, or if {@code maxAttempts} is less than 1.
+     * @throws Exception any other exception thrown by helper methods.
+     *
+     * @return job's result.
+     */
+    public <T> T run(String description, long retryMs, Callable<T> job) throws Exception {
+        if (TextUtils.isEmpty(description)) {
+            throw new IllegalArgumentException("no description");
+        }
+        if (job == null) {
+            throw new IllegalArgumentException("no job");
+        }
+        if (retryMs < 1) {
+            throw new IllegalArgumentException("need to sleep at least 1ms, right?");
+        }
+        long startTime = System.currentTimeMillis();
+        int attempt = 0;
+        while (System.currentTimeMillis() - startTime <= mCurrentValue) {
+            final T result = job.call();
+            if (result != null) {
+                // Good news, everyone: job succeeded on first attempt!
+                return result;
+            }
+            attempt++;
+            if (VERBOSE) {
+                Log.v(TAG, description + " failed at attempt #" + attempt + "; sleeping for "
+                        + retryMs + "ms before trying again");
+            }
+            SystemClock.sleep(retryMs);
+            retryMs *= mMultiplier;
+        }
+        Log.w(TAG, description + " failed after " + attempt + " attempts and "
+                + (System.currentTimeMillis() - startTime) + "ms: " + this);
+        throw new RetryableException(this, description);
+    }
+
+    @Override
+    public String toString() {
+        return mName + ": [current=" + mCurrentValue + "ms; multiplier=" + mMultiplier + "x; max="
+                + mMaxValue + "ms]";
+    }
+
+}
diff --git a/tests/autofillservice/src/android/autofillservice/cts/TimeoutTest.java b/tests/autofillservice/src/android/autofillservice/cts/TimeoutTest.java
new file mode 100644
index 0000000..69c69e4
--- /dev/null
+++ b/tests/autofillservice/src/android/autofillservice/cts/TimeoutTest.java
@@ -0,0 +1,123 @@
+/*
+ * 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.autofillservice.cts;
+
+import static android.autofillservice.cts.Helper.assertFloat;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import static org.mockito.Mockito.when;
+import static org.testng.Assert.assertThrows;
+import static org.testng.Assert.expectThrows;
+
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.Mock;
+import org.mockito.junit.MockitoJUnitRunner;
+
+import java.util.concurrent.Callable;
+
+@RunWith(MockitoJUnitRunner.class)
+public class TimeoutTest {
+
+    private static final String NAME = "TIME, Y U NO OUT?";
+    private static final String DESC = "something";
+
+    @Mock
+    private Callable<Object> mJob;
+
+    @Test
+    public void testInvalidConstructor() {
+        // Invalid name
+        assertThrows(IllegalArgumentException.class, ()-> new Timeout(null, 1, 2, 2));
+        assertThrows(IllegalArgumentException.class, ()-> new Timeout("", 1, 2, 2));
+        // Invalid initial value
+        assertThrows(IllegalArgumentException.class, ()-> new Timeout(NAME, -1, 2, 2));
+        assertThrows(IllegalArgumentException.class, ()-> new Timeout(NAME, 0, 2, 2));
+        // Invalid multiplier
+        assertThrows(IllegalArgumentException.class, ()-> new Timeout(NAME, 1, -1, 2));
+        assertThrows(IllegalArgumentException.class, ()-> new Timeout(NAME, 1, 0, 2));
+        assertThrows(IllegalArgumentException.class, ()-> new Timeout(NAME, 1, 1, 2));
+        // Invalid max value
+        assertThrows(IllegalArgumentException.class, ()-> new Timeout(NAME, 1, 2, -1));
+        assertThrows(IllegalArgumentException.class, ()-> new Timeout(NAME, 1, 2, 0));
+        // Max value cannot be less than initial
+        assertThrows(IllegalArgumentException.class, ()-> new Timeout(NAME, 2, 2, 1));
+    }
+
+    @Test
+    public void testGetters() {
+        final Timeout timeout = new Timeout(NAME, 1, 2, 5);
+        assertThat(timeout.ms()).isEqualTo(1);
+        assertFloat(timeout.getMultiplier(), 2);
+        assertThat(timeout.getMaxValue()).isEqualTo(5);
+        assertThat(timeout.getName()).isEqualTo(NAME);
+    }
+
+    @Test
+    public void testIncrease() {
+        final Timeout timeout = new Timeout(NAME, 1, 2, 5);
+        // Pre-maximum
+        assertThat(timeout.increase()).isEqualTo(1);
+        assertThat(timeout.ms()).isEqualTo(2);
+        assertThat(timeout.increase()).isEqualTo(2);
+        assertThat(timeout.ms()).isEqualTo(4);
+        // Post-maximum
+        assertThat(timeout.increase()).isEqualTo(4);
+        assertThat(timeout.ms()).isEqualTo(5);
+        assertThat(timeout.increase()).isEqualTo(5);
+        assertThat(timeout.ms()).isEqualTo(5);
+    }
+
+    @Test
+    public void testRun_invalidArgs() {
+        final Timeout timeout = new Timeout(NAME, 1, 2, 5);
+        // Invalid description
+        assertThrows(IllegalArgumentException.class, ()-> timeout.run(null, mJob));
+        assertThrows(IllegalArgumentException.class, ()-> timeout.run("", mJob));
+        // Invalid max attempts
+        assertThrows(IllegalArgumentException.class, ()-> timeout.run(DESC, -1, mJob));
+        assertThrows(IllegalArgumentException.class, ()-> timeout.run(DESC, 0, mJob));
+        // Invalid job
+        assertThrows(IllegalArgumentException.class, ()-> timeout.run(DESC, null));
+    }
+
+    @Test
+    public void testRun_successOnFirstAttempt() throws Exception {
+        final Timeout timeout = new Timeout(NAME, 100, 2, 500);
+        final Object result = new Object();
+        when(mJob.call()).thenReturn(result);
+        assertThat(timeout.run(DESC, 1, mJob)).isSameAs(result);
+    }
+
+    @Test
+    public void testRun_successOnSecondAttempt() throws Exception {
+        final Timeout timeout = new Timeout(NAME, 100, 2, 500);
+        final Object result = new Object();
+        when(mJob.call()).thenReturn((Object) null, result);
+        assertThat(timeout.run(DESC, 10, mJob)).isSameAs(result);
+    }
+
+    @Test
+    public void testRun_allAttemptsFailed() throws Exception {
+        final Timeout timeout = new Timeout(NAME, 100, 2, 500);
+        final RetryableException e = expectThrows(RetryableException.class,
+                () -> timeout.run(DESC, 10, mJob));
+        assertThat(e.getMessage()).contains(DESC);
+        assertThat(e.getTimeout()).isSameAs(timeout);
+    }
+}
diff --git a/tests/autofillservice/src/android/autofillservice/cts/Timeouts.java b/tests/autofillservice/src/android/autofillservice/cts/Timeouts.java
new file mode 100644
index 0000000..6111cf6
--- /dev/null
+++ b/tests/autofillservice/src/android/autofillservice/cts/Timeouts.java
@@ -0,0 +1,91 @@
+/*
+ * 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.autofillservice.cts;
+
+/**
+ * Timeouts for common tasks.
+ */
+final class Timeouts {
+
+    /**
+     * Timeout until framework binds / unbinds from service.
+     */
+    static final Timeout CONNECTION_TIMEOUT = new Timeout("CONNECTION_TIMEOUT", 1000, 2F, 2000);
+
+    /**
+     * Timeout until framework unbinds from a service.
+     */
+    static final Timeout IDLE_UNBIND_TIMEOUT = new Timeout("IDLE_UNBIND_TIMEOUT", 5000, 2F, 10000);
+
+    /**
+     * Timeout to get the expected number of fill events.
+     */
+    static final Timeout FILL_EVENTS_TIMEOUT = new Timeout("FILL_EVENTS_TIMEOUT", 1000, 2F, 10000);
+
+    /**
+     * Timeout for expected autofill requests.
+     */
+    static final Timeout FILL_TIMEOUT = new Timeout("FILL_TIMEOUT", 1000, 2F, 2000);
+
+    /**
+     * Timeout for expected save requests.
+     */
+    static final Timeout SAVE_TIMEOUT = new Timeout("SAVE_TIMEOUT", 1000, 2F, 5000);
+
+    /**
+     * Time to wait if a UI change is not expected
+     */
+    static final Timeout NOT_SHOWING_TIMEOUT = new Timeout("NOT_SHOWING_TIMEOUT", 100, 2F, 500);
+
+    /**
+     * Timeout for UI operations. Typically used by {@link UiBot}.
+     */
+    static final Timeout UI_TIMEOUT = new Timeout("UI_TIMEOUT", 500, 2F, 2000);
+
+    /**
+     * Timeout for showing the autofill dataset picker UI.
+     *
+     * <p>The value is usually higher than {@link #UI_TIMEOUT} because the performance of the
+     * dataset picker UI can be affect by external factors in some low-level devices.
+     *
+     * <p>Typically used by {@link UiBot}.
+     */
+    static final Timeout UI_DATASET_PICKER_TIMEOUT =
+            new Timeout("UI_DATASET_PICKER_TIMEOUT", 500, 2F, 4000);
+
+    /**
+     * Timeout (in milliseconds) for an activity to be brought out to top.
+     */
+    static final Timeout ACTIVITY_RESURRECTION =
+            new Timeout("ACTIVITY_RESURRECTION", 6000, 1.5F, 20000);
+
+    /**
+     * Timeout for changing the screen orientation.
+     */
+    static final Timeout UI_SCREEN_ORIENTATION_TIMEOUT =
+            new Timeout("UI_SCREEN_ORIENTATION_TIMEOUT", 5000, 2F, 10000);
+
+    /**
+     * Timeout for using Recents to swtich activities.
+     */
+    static final Timeout UI_RECENTS_SWITCH_TIMEOUT =
+            new Timeout("UI_RECENTS_SWITCH_TIMEOUT", 200, 2F, 1000);
+
+    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 5611499..d4f77ca 100644
--- a/tests/autofillservice/src/android/autofillservice/cts/UiBot.java
+++ b/tests/autofillservice/src/android/autofillservice/cts/UiBot.java
@@ -16,9 +16,12 @@
 
 package android.autofillservice.cts;
 
-import static android.autofillservice.cts.Helper.NOT_SHOWING_TIMEOUT_MS;
-import static android.autofillservice.cts.Helper.SAVE_TIMEOUT_MS;
-import static android.autofillservice.cts.Helper.UI_TIMEOUT_MS;
+import static android.autofillservice.cts.Timeouts.NOT_SHOWING_TIMEOUT;
+import static android.autofillservice.cts.Timeouts.SAVE_TIMEOUT;
+import static android.autofillservice.cts.Timeouts.UI_DATASET_PICKER_TIMEOUT;
+import static android.autofillservice.cts.Timeouts.UI_RECENTS_SWITCH_TIMEOUT;
+import static android.autofillservice.cts.Timeouts.UI_SCREEN_ORIENTATION_TIMEOUT;
+import static android.autofillservice.cts.Timeouts.UI_TIMEOUT;
 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;
@@ -33,32 +36,39 @@
 import android.app.UiAutomation;
 import android.content.Context;
 import android.content.res.Resources;
-import android.os.RemoteException;
 import android.os.SystemClock;
 import android.service.autofill.SaveInfo;
+import android.support.test.InstrumentationRegistry;
 import android.support.test.uiautomator.By;
 import android.support.test.uiautomator.BySelector;
 import android.support.test.uiautomator.UiDevice;
 import android.support.test.uiautomator.UiObject2;
+import android.support.test.uiautomator.Until;
 import android.text.Html;
 import android.util.Log;
+import android.view.accessibility.AccessibilityEvent;
 import android.view.accessibility.AccessibilityWindowInfo;
 
+import java.io.ByteArrayOutputStream;
 import java.io.IOException;
 import java.util.ArrayList;
 import java.util.Arrays;
 import java.util.List;
+import java.util.concurrent.TimeoutException;
 
 /**
  * Helper for UI-related needs.
  */
 final class UiBot {
 
+    private static final String TAG = "AutoFillCtsUiBot";
+
     private static final String RESOURCE_ID_DATASET_PICKER = "autofill_dataset_picker";
     private static final String RESOURCE_ID_SAVE_SNACKBAR = "autofill_save";
     private static final String RESOURCE_ID_SAVE_ICON = "autofill_save_icon";
     private static final String RESOURCE_ID_SAVE_TITLE = "autofill_save_title";
     private static final String RESOURCE_ID_CONTEXT_MENUITEM = "floating_toolbar_menu_item_text";
+    private static final String RESOURCE_ID_SAVE_BUTTON_NO = "autofill_save_no";
 
     private static final String RESOURCE_STRING_SAVE_TITLE = "autofill_save_title";
     private static final String RESOURCE_STRING_SAVE_TITLE_WITH_TYPE =
@@ -70,13 +80,21 @@
     private static final String RESOURCE_STRING_SAVE_TYPE_USERNAME = "autofill_save_type_username";
     private static final String RESOURCE_STRING_SAVE_TYPE_EMAIL_ADDRESS =
             "autofill_save_type_email_address";
+    private static final String RESOURCE_STRING_SAVE_BUTTON_NOT_NOW = "save_password_notnow";
+    private static final String RESOURCE_STRING_SAVE_BUTTON_NO_THANKS = "autofill_save_no";
+
     private static final String RESOURCE_STRING_AUTOFILL = "autofill";
     private static final String RESOURCE_STRING_DATASET_PICKER_ACCESSIBILITY_TITLE =
             "autofill_picker_accessibility_title";
     private static final String RESOURCE_STRING_SAVE_SNACKBAR_ACCESSIBILITY_TITLE =
             "autofill_save_accessibility_title";
 
-    private static final String TAG = "AutoFillCtsUiBot";
+    private static final BySelector DATASET_PICKER_SELECTOR = By.res("android",
+            RESOURCE_ID_DATASET_PICKER);
+    private static final BySelector SAVE_UI_SELECTOR = By.res("android", RESOURCE_ID_SAVE_SNACKBAR);
+
+    private static final boolean DONT_DUMP_ON_ERROR = false;
+    private static final boolean DUMP_ON_ERROR = true;
 
     /** Pass to {@link #setScreenOrientation(int)} to change the display to portrait mode */
     public static int PORTRAIT = 0;
@@ -84,13 +102,19 @@
     /** Pass to {@link #setScreenOrientation(int)} to change the display to landscape mode */
     public static int LANDSCAPE = 1;
 
-
     private final UiDevice mDevice;
     private final Context mContext;
     private final String mPackageName;
     private final UiAutomation mAutoman;
+    private final Timeout mDefaultTimeout;
 
-    UiBot(Instrumentation instrumentation) throws Exception {
+    UiBot() {
+        this(UI_TIMEOUT);
+    }
+
+    UiBot(Timeout defaultTimeout) {
+        mDefaultTimeout = defaultTimeout;
+        final Instrumentation instrumentation = InstrumentationRegistry.getInstrumentation();
         mDevice = UiDevice.getInstance(instrumentation);
         mContext = instrumentation.getContext();
         mPackageName = mContext.getPackageName();
@@ -100,16 +124,8 @@
     /**
      * Asserts the dataset chooser is not shown.
      */
-    void assertNoDatasets() {
-        final UiObject2 picker;
-        try {
-            picker = findDatasetPicker(NOT_SHOWING_TIMEOUT_MS);
-        } catch (Throwable t) {
-            // Use a more elegant check than catching the expection because it's not showing...
-            return;
-        }
-        throw new RetryableException(
-                "Should not be showing datasets, but got " + getChildrenAsText(picker));
+    void assertNoDatasets() throws Exception {
+        assertNotShowing("datasets", DATASET_PICKER_SELECTOR, NOT_SHOWING_TIMEOUT);
     }
 
     /**
@@ -117,10 +133,31 @@
      *
      * @return the dataset picker object.
      */
-    UiObject2 assertDatasets(String...names) {
-        final UiObject2 picker = findDatasetPicker();
+    UiObject2 assertDatasets(String...names) throws Exception {
+        final UiObject2 picker = findDatasetPicker(UI_DATASET_PICKER_TIMEOUT);
         assertWithMessage("wrong dataset names").that(getChildrenAsText(picker))
-                .containsExactlyElementsIn(Arrays.asList(names));
+                .containsExactlyElementsIn(Arrays.asList(names)).inOrder();
+        return picker;
+    }
+
+    /**
+     * Asserts the dataset chooser is shown and contains the given datasets, header, and footer.
+     *
+     * @return the dataset picker object.
+     */
+    UiObject2 assertDatasetsWithBorders(String header, String footer, String...names)
+            throws Exception {
+        final UiObject2 picker = findDatasetPicker(UI_DATASET_PICKER_TIMEOUT);
+        final List<String> expectedChild = new ArrayList<>();
+        if (header != null) {
+            expectedChild.add(header);
+        }
+        expectedChild.addAll(Arrays.asList(names));
+        if (footer != null) {
+            expectedChild.add(footer);
+        }
+        assertWithMessage("wrong elements on dataset picker").that(getChildrenAsText(picker))
+                .containsExactlyElementsIn(expectedChild).inOrder();
         return picker;
     }
 
@@ -146,8 +183,8 @@
     /**
      * Selects a dataset that should be visible in the floating UI.
      */
-    void selectDataset(String name) {
-        final UiObject2 picker = findDatasetPicker();
+    void selectDataset(String name) throws Exception {
+        final UiObject2 picker = findDatasetPicker(UI_DATASET_PICKER_TIMEOUT);
         selectDataset(picker, name);
     }
 
@@ -168,7 +205,7 @@
      * <p><b>NOTE:</b> when selecting an option in dataset picker is shown, prefer
      * {@link #selectDataset(String)}.
      */
-    void selectByText(String name) {
+    void selectByText(String name) throws Exception {
         Log.v(TAG, "selectByText(): " + name);
 
         final UiObject2 object = waitForObject(By.text(name));
@@ -181,12 +218,12 @@
      * <p><b>NOTE:</b> when asserting the dataset picker is shown, prefer
      * {@link #assertDatasets(String...)}.
      */
-    public UiObject2 assertShownByText(String text) {
-        return assertShownByText(text, UI_TIMEOUT_MS);
+    public UiObject2 assertShownByText(String text) throws Exception {
+        return assertShownByText(text, mDefaultTimeout);
     }
 
-    public UiObject2 assertShownByText(String text, int timeoutMs) {
-        final UiObject2 object = waitForObject(By.text(text), timeoutMs);
+    public UiObject2 assertShownByText(String text, Timeout timeout) throws Exception {
+        final UiObject2 object = waitForObject(By.text(text), timeout);
         assertWithMessage("No node with text '%s'", text).that(object).isNotNull();
         return object;
     }
@@ -195,7 +232,7 @@
      * Asserts a node with the given content description is shown.
      *
      */
-    public UiObject2 assertShownByContentDescription(String contentDescription) {
+    public UiObject2 assertShownByContentDescription(String contentDescription) throws Exception {
         final UiObject2 object = waitForObject(By.desc(contentDescription));
         assertWithMessage("No node with content description '%s'", contentDescription).that(object)
                 .isNotNull();
@@ -214,33 +251,68 @@
     /**
      * Selects a view by id.
      */
-    void selectById(String id) {
+    void selectById(String id) throws Exception {
         Log.v(TAG, "selectById(): " + id);
 
-        final UiObject2 view = waitForObject(By.res(id));
+        final UiObject2 view = waitForObject(By.res(id), mDefaultTimeout);
         view.click();
     }
 
     /**
      * Asserts the id is shown on the screen.
      */
-    void assertShownById(String id) {
+    void assertShownById(String id) throws Exception {
         assertThat(waitForObject(By.res(id))).isNotNull();
     }
 
     /**
      * Asserts the id is shown on the screen, using a resource id from the test package.
      */
-    UiObject2 assertShownByRelativeId(String id) {
-        final UiObject2 obj = waitForObject(By.res(mPackageName, id));
+    UiObject2 assertShownByRelativeId(String id) throws Exception {
+        return assertShownByRelativeId(id, mDefaultTimeout);
+    }
+
+    UiObject2 assertShownByRelativeId(String id, Timeout timeout) throws Exception {
+        final UiObject2 obj = waitForObject(By.res(mPackageName, id), timeout);
         assertThat(obj).isNotNull();
         return obj;
     }
+    /**
+     * Asserts the id is not shown on the screen anymore, using a resource id from the test package.
+     *
+     * <p><b>Note:</b> this method should only called AFTER the id was previously shown, otherwise
+     * it might pass without really asserting anything.
+     */
+    void assertGoneByRelativeId(String id, Timeout timeout) {
+        boolean gone = mDevice.wait(Until.gone(By.res(mPackageName, id)), timeout.ms());
+        if (!gone) {
+            final String message = "Object with id '" + id + "' should be gone after "
+                    + timeout + " ms";
+            dumpScreen(message);
+            throw new RetryableException(message);
+        }
+    }
+
+    /**
+     * Asserts that a {@code selector} is not showing after {@code timeout} milliseconds.
+     */
+    private void assertNotShowing(String description, BySelector selector, Timeout timeout)
+            throws Exception {
+        final UiObject2 object;
+        try {
+            object = waitForObject(null, selector, timeout, DONT_DUMP_ON_ERROR);
+        } catch (RetryableException t) {
+            // Not found as expected.
+            return;
+        }
+        throw new RetryableException(timeout, "Should not be showing %s, but got %s",
+                description, getChildrenAsText(object));
+    }
 
     /**
      * Gets the text set on a view.
      */
-    String getTextById(String id) {
+    String getTextById(String id) throws Exception {
         final UiObject2 obj = waitForObject(By.res(id));
         return obj.getText();
     }
@@ -248,14 +320,14 @@
     /**
      * Focus in the view with the given resource id.
      */
-    void focusByRelativeId(String id) {
+    void focusByRelativeId(String id) throws Exception {
         waitForObject(By.res(mPackageName, id)).click();
     }
 
     /**
      * Sets a new text on a view.
      */
-    void setTextById(String id, String newText) {
+    void setTextById(String id, String newText) throws Exception {
         UiObject2 view = waitForObject(By.res(id));
         view.setText(newText);
     }
@@ -263,14 +335,14 @@
     /**
      * Asserts the save snackbar is showing and returns it.
      */
-    UiObject2 assertSaveShowing(int type) {
-        return assertSaveShowing(SAVE_TIMEOUT_MS, type);
+    UiObject2 assertSaveShowing(int type) throws Exception {
+        return assertSaveShowing(SAVE_TIMEOUT, type);
     }
 
     /**
      * Asserts the save snackbar is showing and returns it.
      */
-    UiObject2 assertSaveShowing(long timeout, int type) {
+    UiObject2 assertSaveShowing(Timeout timeout, int type) throws Exception {
         return assertSaveShowing(null, timeout, type);
     }
 
@@ -293,11 +365,16 @@
     /**
      * Uses the Recents button to switch back to previous activity
      */
-    void switchAppsUsingRecents() throws RemoteException {
+    void switchAppsUsingRecents() throws Exception {
         Log.d(TAG, "switchAppsUsingRecents()");
 
         // Press once to show list of apps...
         mDevice.pressRecentApps();
+
+        // ...wait until apps are shown...
+        // TODO(b/37566627): figure out a way to wait for a specific UI instead.
+        SystemClock.sleep(UI_RECENTS_SWITCH_TIMEOUT.ms());
+
         // ...press again to go back to the activity.
         mDevice.pressRecentApps();
     }
@@ -305,15 +382,8 @@
     /**
      * Asserts the save snackbar is not showing and returns it.
      */
-    void assertSaveNotShowing(int type) {
-        try {
-            assertSaveShowing(NOT_SHOWING_TIMEOUT_MS, type);
-        } catch (Throwable t) {
-            // TODO: use a more elegant check than catching the expection because it's not showing
-            // (in which case it wouldn't need a type as parameter).
-            return;
-        }
-        throw new RetryableException("snack bar is showing");
+    void assertSaveNotShowing(int type) throws Exception {
+        assertNotShowing("save UI for type " + type, SAVE_UI_SELECTOR, NOT_SHOWING_TIMEOUT);
     }
 
     private String getSaveTypeString(int type) {
@@ -340,33 +410,33 @@
         return getString(typeResourceName);
     }
 
-    UiObject2 assertSaveShowing(String description, int... types) {
+    UiObject2 assertSaveShowing(String description, int... types) throws Exception {
         return assertSaveShowing(SaveInfo.NEGATIVE_BUTTON_STYLE_CANCEL, description,
-                SAVE_TIMEOUT_MS, types);
+                SAVE_TIMEOUT, types);
     }
 
-    UiObject2 assertSaveShowing(String description, long timeout, int... types) {
+    UiObject2 assertSaveShowing(String description, Timeout timeout, int... types)
+            throws Exception {
         return assertSaveShowing(SaveInfo.NEGATIVE_BUTTON_STYLE_CANCEL, description, timeout,
                 types);
     }
 
     UiObject2 assertSaveShowing(int negativeButtonStyle, String description,
-            int... types) {
-        return assertSaveShowing(negativeButtonStyle, description, SAVE_TIMEOUT_MS, types);
+            int... types) throws Exception {
+        return assertSaveShowing(negativeButtonStyle, description, SAVE_TIMEOUT, types);
     }
 
-    UiObject2 assertSaveShowing(int negativeButtonStyle, String description, long timeout,
-            int... types) {
-        final UiObject2 snackbar = waitForObject(By.res("android", RESOURCE_ID_SAVE_SNACKBAR),
-                timeout);
+    UiObject2 assertSaveShowing(int negativeButtonStyle, String description, Timeout timeout,
+            int... types) throws Exception {
+        final UiObject2 snackbar = waitForObject(SAVE_UI_SELECTOR, timeout);
 
         final UiObject2 titleView =
-                waitForObject(snackbar, By.res("android", RESOURCE_ID_SAVE_TITLE), UI_TIMEOUT_MS);
+                waitForObject(snackbar, By.res("android", RESOURCE_ID_SAVE_TITLE), timeout);
         assertWithMessage("save title (%s) is not shown", RESOURCE_ID_SAVE_TITLE).that(titleView)
                 .isNotNull();
 
         final UiObject2 iconView =
-                waitForObject(snackbar, By.res("android", RESOURCE_ID_SAVE_ICON), UI_TIMEOUT_MS);
+                waitForObject(snackbar, By.res("android", RESOURCE_ID_SAVE_ICON), timeout);
         assertWithMessage("save icon (%s) is not shown", RESOURCE_ID_SAVE_ICON).that(iconView)
                 .isNotNull();
 
@@ -403,11 +473,15 @@
             assertWithMessage("save subtitle(%s)", description).that(saveSubTitle).isNotNull();
         }
 
-        final String negativeButtonText = (negativeButtonStyle
-                == SaveInfo.NEGATIVE_BUTTON_STYLE_REJECT) ? "NOT NOW" : "NO THANKS";
-        UiObject2 negativeButton = snackbar.findObject(By.text(negativeButtonText));
-        assertWithMessage("negative button (%s)", negativeButtonText)
-                .that(negativeButton).isNotNull();
+        final String negativeButtonStringId =
+                (negativeButtonStyle == SaveInfo.NEGATIVE_BUTTON_STYLE_REJECT)
+                ? RESOURCE_STRING_SAVE_BUTTON_NOT_NOW
+                : RESOURCE_STRING_SAVE_BUTTON_NO_THANKS;
+        final String expectedNegativeButtonText = getString(negativeButtonStringId).toUpperCase();
+        final UiObject2 negativeButton = waitForObject(snackbar,
+                By.res("android", RESOURCE_ID_SAVE_BUTTON_NO), timeout);
+        assertWithMessage("wrong text on negative button")
+                .that(negativeButton.getText().toUpperCase()).isEqualTo(expectedNegativeButtonText);
 
         final String expectedAccessibilityTitle =
                 getString(RESOURCE_STRING_SAVE_SNACKBAR_ACCESSIBILITY_TITLE);
@@ -422,7 +496,7 @@
      * @param yesDoIt {@code true} for 'YES', {@code false} for 'NO THANKS'.
      * @param types expected types of save info.
      */
-    void saveForAutofill(boolean yesDoIt, int... types) {
+    void saveForAutofill(boolean yesDoIt, int... types) throws Exception {
         final UiObject2 saveSnackBar = assertSaveShowing(
                 SaveInfo.NEGATIVE_BUTTON_STYLE_CANCEL, null, types);
         saveForAutofill(saveSnackBar, yesDoIt);
@@ -434,7 +508,7 @@
      * @param yesDoIt {@code true} for 'YES', {@code false} for 'NO THANKS'.
      * @param types expected types of save info.
      */
-    void saveForAutofill(int negativeButtonStyle, boolean yesDoIt, int... types) {
+    void saveForAutofill(int negativeButtonStyle, boolean yesDoIt, int... types) throws Exception {
         final UiObject2 saveSnackBar = assertSaveShowing(negativeButtonStyle,null, types);
         saveForAutofill(saveSnackBar, yesDoIt);
     }
@@ -464,14 +538,14 @@
      *
      * @param id resource id of the field.
      */
-    UiObject2 getAutofillMenuOption(String id) {
+    UiObject2 getAutofillMenuOption(String id) throws Exception {
         final UiObject2 field = waitForObject(By.res(mPackageName, id));
         // TODO: figure out why obj.longClick() doesn't always work
         field.click(3000);
 
         final List<UiObject2> menuItems = waitForObjects(
-                By.res("android", RESOURCE_ID_CONTEXT_MENUITEM));
-        final String expectedText = getString(RESOURCE_STRING_AUTOFILL);
+                By.res("android", RESOURCE_ID_CONTEXT_MENUITEM), mDefaultTimeout);
+        final String expectedText = getAutofillContextualMenuTitle();
         final StringBuffer menuNames = new StringBuffer();
         for (UiObject2 menuItem : menuItems) {
             final String menuName = menuItem.getText();
@@ -483,6 +557,10 @@
         throw new RetryableException("no '%s' on '%s'", expectedText, menuNames);
     }
 
+    String getAutofillContextualMenuTitle() {
+        return getString(RESOURCE_STRING_AUTOFILL);
+    }
+
     /**
      * Gets a string from the Android resources.
      */
@@ -506,8 +584,8 @@
      *
      * @param selector {@link BySelector} that identifies the object.
      */
-    private UiObject2 waitForObject(BySelector selector) {
-        return waitForObject(selector, UI_TIMEOUT_MS);
+    private UiObject2 waitForObject(BySelector selector) throws Exception {
+        return waitForObject(selector, mDefaultTimeout);
     }
 
     /**
@@ -515,24 +593,30 @@
      *
      * @param parent where to find the object (or {@code null} to use device's root).
      * @param selector {@link BySelector} that identifies the object.
-     * @param timeout timeout in ms
+     * @param timeout timeout in ms.
+     * @param dumpOnError whether the window hierarchy should be dumped if the object is not found.
      */
-    private UiObject2 waitForObject(UiObject2 parent, BySelector selector, long timeout) {
+    private UiObject2 waitForObject(UiObject2 parent, BySelector selector, Timeout timeout,
+            boolean dumpOnError) throws Exception {
         // NOTE: mDevice.wait does not work for the save snackbar, so we need a polling approach.
-        final int maxTries = 5;
-        final long napTime = timeout / maxTries;
-        for (int i = 1; i <= maxTries; i++) {
-            final UiObject2 uiObject = parent != null
-                    ? parent.findObject(selector)
-                    : mDevice.findObject(selector);
-            if (uiObject != null) {
-                return uiObject;
-            }
-            SystemClock.sleep(napTime);
-        }
-        throw new RetryableException("Object with selector '%s' not found in %d ms",
-                selector, UI_TIMEOUT_MS);
+        try {
+            return timeout.run("waitForObject(" + selector + ")", () -> {
+                return parent != null
+                        ? parent.findObject(selector)
+                        : mDevice.findObject(selector);
 
+            });
+        } catch (RetryableException e) {
+            if (dumpOnError) {
+                dumpScreen("waitForObject() for " + selector + "failed");
+            }
+            throw e;
+        }
+    }
+
+    private UiObject2 waitForObject(UiObject2 parent, BySelector selector, Timeout timeout)
+            throws Exception {
+        return waitForObject(parent, selector, timeout, DUMP_ON_ERROR);
     }
 
     /**
@@ -541,17 +625,25 @@
      * @param selector {@link BySelector} that identifies the object.
      * @param timeout timeout in ms
      */
-    private UiObject2 waitForObject(BySelector selector, long timeout) {
+    private UiObject2 waitForObject(BySelector selector, Timeout timeout) throws Exception {
         return waitForObject(null, selector, timeout);
     }
 
     /**
-     * Waits for and returns a list of objects.
-     *
-     * @param selector {@link BySelector} that identifies the object.
+     * 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)
      */
-    private List<UiObject2> waitForObjects(BySelector selector) {
-        return waitForObjects(selector, UI_TIMEOUT_MS);
+    public void waitForWindowChange(Runnable runnable, long timeoutMillis) throws TimeoutException {
+        mAutoman.executeAndWaitForEvent(runnable, (AccessibilityEvent event) -> {
+            switch (event.getEventType()) {
+                case AccessibilityEvent.TYPE_WINDOWS_CHANGED:
+                case AccessibilityEvent.TYPE_WINDOW_STATE_CHANGED:
+                    return true;
+            }
+            return false;
+        }, timeoutMillis);
     }
 
     /**
@@ -560,28 +652,26 @@
      * @param selector {@link BySelector} that identifies the object.
      * @param timeout timeout in ms
      */
-    private List<UiObject2> waitForObjects(BySelector selector, long timeout) {
+    private List<UiObject2> waitForObjects(BySelector selector, Timeout timeout) throws Exception {
         // NOTE: mDevice.wait does not work for the save snackbar, so we need a polling approach.
-        final int maxTries = 5;
-        final long napTime = timeout / maxTries;
-        for (int i = 1; i <= maxTries; i++) {
-            final List<UiObject2> uiObjects = mDevice.findObjects(selector);
-            if (uiObjects != null && !uiObjects.isEmpty()) {
-                return uiObjects;
-            }
-            SystemClock.sleep(napTime);
+        try {
+            return timeout.run("waitForObject(" + selector + ")", () -> {
+                final List<UiObject2> uiObjects = mDevice.findObjects(selector);
+                if (uiObjects != null && !uiObjects.isEmpty()) {
+                    return uiObjects;
+                }
+                return null;
+
+            });
+
+        } catch (RetryableException e) {
+            dumpScreen("waitForObjects() for " + selector + "failed");
+            throw e;
         }
-        throw new RetryableException("Objects with selector '%s' not found in %d ms",
-                selector, UI_TIMEOUT_MS);
     }
 
-    private UiObject2 findDatasetPicker() {
-        return findDatasetPicker(UI_TIMEOUT_MS);
-    }
-
-    private UiObject2 findDatasetPicker(long timeout) {
-        final UiObject2 picker = waitForObject(By.res("android", RESOURCE_ID_DATASET_PICKER),
-                timeout);
+    private UiObject2 findDatasetPicker(Timeout timeout) throws Exception {
+        final UiObject2 picker = waitForObject(DATASET_PICKER_SELECTOR, timeout);
 
         final String expectedTitle = getString(RESOURCE_STRING_DATASET_PICKER_ACCESSIBILITY_TITLE);
         assertAccessibilityTitle(picker, expectedTitle);
@@ -611,27 +701,12 @@
      *
      * @throws RetryableException if value didn't change.
      */
-    public void setScreenOrientation(int orientation) {
+    public void setScreenOrientation(int orientation) throws Exception {
         mAutoman.setRotation(orientation);
 
-        long startTime = System.currentTimeMillis();
-
-        while (System.currentTimeMillis() - startTime <= Helper.UI_SCREEN_ORIENTATION_TIMEOUT_MS) {
-            final int actualValue = getScreenOrientation();
-            if (actualValue == orientation) {
-                return;
-            }
-            Log.w(TAG, "setScreenOrientation(): sleeping " + Helper.RETRY_MS
-                    + "ms until orientation is " + orientation
-                    + " (instead of " + actualValue + ")");
-            try {
-                Thread.sleep(Helper.RETRY_MS);
-            } catch (InterruptedException e) {
-                Thread.currentThread().interrupt();
-            }
-        }
-        throw new RetryableException("Screen orientation didn't change to %d in %d ms", orientation,
-                Helper.UI_SCREEN_ORIENTATION_TIMEOUT_MS);
+        UI_SCREEN_ORIENTATION_TIMEOUT.run("setScreenOrientation(" + orientation + ")", () -> {
+            return getScreenOrientation() == orientation ? Boolean.TRUE : null;
+        });
     }
 
     /**
@@ -646,7 +721,22 @@
     /**
      * Dumps the current view hierarchy int the output stream.
      */
-    public void dumpScreen() throws IOException {
-        mDevice.dumpWindowHierarchy(System.out);
+    public void dumpScreen(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);
+                }
+            }
+        } catch (IOException e) {
+            // Just ignore it...
+            Log.e(TAG, "exception dumping window hierarchy", e);
+            return;
+        }
     }
 }
diff --git a/tests/autofillservice/src/android/autofillservice/cts/UserDataTest.java b/tests/autofillservice/src/android/autofillservice/cts/UserDataTest.java
new file mode 100644
index 0000000..951f6e4
--- /dev/null
+++ b/tests/autofillservice/src/android/autofillservice/cts/UserDataTest.java
@@ -0,0 +1,132 @@
+/*
+ * 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.autofillservice.cts;
+
+import static android.provider.Settings.Secure.AUTOFILL_USER_DATA_MAX_FIELD_CLASSIFICATION_IDS_SIZE;
+import static android.provider.Settings.Secure.AUTOFILL_USER_DATA_MAX_USER_DATA_SIZE;
+import static android.provider.Settings.Secure.AUTOFILL_USER_DATA_MAX_VALUE_LENGTH;
+import static android.provider.Settings.Secure.AUTOFILL_USER_DATA_MIN_VALUE_LENGTH;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import static org.testng.Assert.assertThrows;
+
+import android.autofillservice.cts.common.SettingsStateChangerRule;
+import android.content.Context;
+import android.service.autofill.UserData;
+import android.support.test.InstrumentationRegistry;
+
+import com.google.common.base.Strings;
+
+import org.junit.Before;
+import org.junit.ClassRule;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.junit.MockitoJUnitRunner;
+
+@RunWith(MockitoJUnitRunner.class)
+public class UserDataTest {
+
+    private static final Context sContext = InstrumentationRegistry.getContext();
+
+    @ClassRule
+    public static final SettingsStateChangerRule sUserDataMaxFcSizeChanger =
+            new SettingsStateChangerRule(sContext,
+                    AUTOFILL_USER_DATA_MAX_FIELD_CLASSIFICATION_IDS_SIZE, "10");
+
+    @ClassRule
+    public static final SettingsStateChangerRule sUserDataMaxUserSizeChanger =
+            new SettingsStateChangerRule(sContext, AUTOFILL_USER_DATA_MAX_USER_DATA_SIZE, "10");
+
+    @ClassRule
+    public static final SettingsStateChangerRule sUserDataMinValueChanger =
+            new SettingsStateChangerRule(sContext, AUTOFILL_USER_DATA_MIN_VALUE_LENGTH, "5");
+
+    @ClassRule
+    public static final SettingsStateChangerRule sUserDataMaxValueChanger =
+            new SettingsStateChangerRule(sContext, AUTOFILL_USER_DATA_MAX_VALUE_LENGTH, "50");
+
+
+    private final String mShortValue = Strings.repeat("k", UserData.getMinValueLength() - 1);
+    private final String mLongValue = "LONG VALUE, Y U NO SHORTER"
+            + Strings.repeat("?", UserData.getMaxValueLength());
+    private final String mRemoteId = "id1";
+    private final String mRemoteId2 = "id2";
+    private final String mValue = mShortValue + "-1";
+    private final String mValue2 = mShortValue + "-2";
+
+    private UserData.Builder mBuilder;
+
+    @Before
+    public void setFixtures() {
+        mBuilder = new UserData.Builder(mRemoteId, mValue);
+    }
+
+    @Test
+    public void testBuilder_invalid() {
+        assertThrows(NullPointerException.class, () -> new UserData.Builder(mRemoteId, null));
+        assertThrows(IllegalArgumentException.class, () -> new UserData.Builder(mRemoteId, ""));
+        assertThrows(IllegalArgumentException.class,
+                () -> new UserData.Builder(mRemoteId, mShortValue));
+        assertThrows(IllegalArgumentException.class,
+                () -> new UserData.Builder(mRemoteId, mLongValue));
+        assertThrows(NullPointerException.class, () -> new UserData.Builder(null, mValue));
+        assertThrows(IllegalArgumentException.class, () -> new UserData.Builder("", mValue));
+    }
+
+    @Test
+    public void testAdd_invalid() {
+        assertThrows(NullPointerException.class, () -> mBuilder.add(mRemoteId, null));
+        assertThrows(IllegalArgumentException.class, () -> mBuilder.add(mRemoteId, ""));
+        assertThrows(IllegalArgumentException.class, () -> mBuilder.add(mRemoteId, mShortValue));
+        assertThrows(IllegalArgumentException.class, () -> mBuilder.add(mRemoteId, mLongValue));
+        assertThrows(NullPointerException.class, () -> mBuilder.add(null, mValue));
+        assertThrows(IllegalArgumentException.class, () -> mBuilder.add("", mValue));
+    }
+
+    @Test
+    public void testAdd_duplicatedId() {
+        assertThrows(IllegalStateException.class, () -> mBuilder.add(mRemoteId, mValue2));
+    }
+
+    @Test
+    public void testAdd_duplicatedValue() {
+        assertThrows(IllegalStateException.class, () -> mBuilder.add(mRemoteId2, mValue));
+    }
+
+    @Test
+    public void testAdd_maximumReached() {
+        // Must start from 1 because first is added on builder
+        for (int i = 1; i < UserData.getMaxFieldClassificationIdsSize() - 1; i++) {
+            mBuilder.add("ID" + i, mShortValue.toUpperCase() + i);
+        }
+        assertThrows(IllegalStateException.class, () -> mBuilder.add(mRemoteId, mValue));
+    }
+
+    @Test
+    public void testBuild_valid() {
+        assertThat(mBuilder.build()).isNotNull();
+    }
+
+    @Test
+    public void testNoMoreInteractionsAfterBuild() {
+        testBuild_valid();
+
+        assertThrows(IllegalStateException.class, () -> mBuilder.add(mRemoteId2, mValue));
+        assertThrows(IllegalStateException.class, () -> mBuilder.build());
+    }
+}
diff --git a/tests/autofillservice/src/android/autofillservice/cts/ValidatorTest.java b/tests/autofillservice/src/android/autofillservice/cts/ValidatorTest.java
index 21ebac2..194fba8 100644
--- a/tests/autofillservice/src/android/autofillservice/cts/ValidatorTest.java
+++ b/tests/autofillservice/src/android/autofillservice/cts/ValidatorTest.java
@@ -21,22 +21,24 @@
 import static android.autofillservice.cts.Helper.assertNoDanglingSessions;
 import static android.service.autofill.SaveInfo.SAVE_DATA_TYPE_GENERIC;
 
+import static com.google.common.truth.Truth.assertThat;
+
+import static org.mockito.Mockito.doReturn;
+import static org.mockito.Mockito.mock;
+
+import android.service.autofill.InternalValidator;
 import android.service.autofill.LuhnChecksumValidator;
-import android.service.autofill.RegexValidator;
-import android.service.autofill.Validator;
-import android.service.autofill.Validators;
-import android.support.annotation.NonNull;
+import android.service.autofill.ValueFinder;
 import android.view.View;
 import android.view.autofill.AutofillId;
 
-import org.junit.After;
 import org.junit.Before;
 import org.junit.Rule;
 import org.junit.Test;
 
-import java.util.function.BiFunction;
-import java.util.regex.Pattern;
-
+/**
+ * Simple integration test to verify that the UI is only shown if the validator passes.
+ */
 public class ValidatorTest extends AutoFillServiceTestCase {
     @Rule
     public final AutofillActivityTestRule<LoginActivity> mActivityRule =
@@ -49,29 +51,30 @@
         mActivity = mActivityRule.getActivity();
     }
 
-    @After
-    public void finishWelcomeActivity() {
-        WelcomeActivity.finishIt();
+    @Test
+    public void testShowUiWhenValidatorPass() throws Exception {
+        integrationTest(true);
     }
 
-    /**
-     * Base test
-     *
-     * @param validatorBuilder method to build a validator
-     * @param willSaveBeShown  Whether the save pop-up will be shown
-     */
-    private void testValidator(
-            @NonNull BiFunction<AutofillId, AutofillId, Validator> validatorBuilder,
-            boolean willSaveBeShown) throws Exception {
+    @Test
+    public void testDontShowUiWhenValidatorFails() throws Exception {
+        integrationTest(false);
+    }
+
+    private void integrationTest(boolean willSaveBeShown) throws Exception {
         enableService();
 
-        AutofillId usernameId = mActivity.getUsername().getAutofillId();
-        AutofillId passwordId = mActivity.getPassword().getAutofillId();
+        final AutofillId usernameId = mActivity.getUsername().getAutofillId();
+
+        final String username = willSaveBeShown ? "7992739871-3" : "4815162342-108";
+        final LuhnChecksumValidator validator = new LuhnChecksumValidator(usernameId);
+        // Sanity check to make sure the validator is properly configured
+        assertValidator(validator, usernameId, username, willSaveBeShown);
 
         // Set response
         sReplier.addResponse(new CannedFillResponse.Builder()
                 .setRequiredSavableIds(SAVE_DATA_TYPE_GENERIC, ID_USERNAME, ID_PASSWORD)
-                .setValidator(validatorBuilder.apply(usernameId, passwordId))
+                .setValidator(validator)
                 .build());
 
         // Trigger auto-fill
@@ -81,59 +84,24 @@
         sReplier.getNextFillRequest();
 
         // Trigger save.
-        mActivity.onUsername((v) -> v.setText("7992739871-3"));
-        mActivity.onPassword((v) -> v.setText("passwd"));
+        mActivity.onUsername((v) -> v.setText(username));
+        mActivity.onPassword((v) -> v.setText("pass"));
         mActivity.tapLogin();
 
         if (willSaveBeShown) {
-            sUiBot.saveForAutofill(true, SAVE_DATA_TYPE_GENERIC);
+            mUiBot.saveForAutofill(true, SAVE_DATA_TYPE_GENERIC);
             sReplier.getNextSaveRequest();
         } else {
-            sUiBot.assertSaveNotShowing(SAVE_DATA_TYPE_GENERIC);
+            mUiBot.assertSaveNotShowing(SAVE_DATA_TYPE_GENERIC);
         }
 
         assertNoDanglingSessions();
     }
 
-    @Test
-    public void checkForInvalidField() throws Exception {
-        testValidator((usernameId, passwordId) -> Validators.or(
-                new LuhnChecksumValidator(new AutofillId(-1)),
-                new RegexValidator(passwordId, Pattern.compile("pass.*"))), true);
-    }
-
-    @Test
-    public void checkBoth() throws Exception {
-        testValidator((usernameId, passwordId) -> Validators.and(
-                new LuhnChecksumValidator(usernameId),
-                new RegexValidator(passwordId, Pattern.compile("pass.*"))), true);
-    }
-
-    @Test
-    public void checkEither1() throws Exception {
-        testValidator((usernameId, passwordId) -> Validators.or(
-                new RegexValidator(usernameId, Pattern.compile("7.*")),
-                new RegexValidator(passwordId, Pattern.compile("pass.*"))), true);
-    }
-
-    @Test
-    public void checkEither2() throws Exception {
-        testValidator((usernameId, passwordId) -> Validators.or(
-                new RegexValidator(usernameId, Pattern.compile("invalid")),
-                new RegexValidator(passwordId, Pattern.compile("pass.*"))), true);
-    }
-
-    @Test
-    public void checkBothButFail() throws Exception {
-        testValidator((usernameId, passwordId) -> Validators.and(
-                new RegexValidator(usernameId, Pattern.compile("7.*")),
-                new RegexValidator(passwordId, Pattern.compile("invalid"))), false);
-    }
-
-    @Test
-    public void checkEitherButFail() throws Exception {
-        testValidator((usernameId, passwordId) -> Validators.or(
-                new RegexValidator(usernameId, Pattern.compile("invalid")),
-                new RegexValidator(passwordId, Pattern.compile("invalid"))), false);
+    private void assertValidator(InternalValidator validator, AutofillId id, String text,
+            boolean valid) {
+        final ValueFinder valueFinder = mock(ValueFinder.class);
+        doReturn(text).when(valueFinder).findByAutofillId(id);
+        assertThat(validator.isValid(valueFinder)).isEqualTo(valid);
     }
 }
diff --git a/tests/autofillservice/src/android/autofillservice/cts/ValidatorsTest.java b/tests/autofillservice/src/android/autofillservice/cts/ValidatorsTest.java
new file mode 100644
index 0000000..6c7979d
--- /dev/null
+++ b/tests/autofillservice/src/android/autofillservice/cts/ValidatorsTest.java
@@ -0,0 +1,146 @@
+/*
+ * 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.autofillservice.cts;
+
+import static android.service.autofill.Validators.and;
+import static android.service.autofill.Validators.not;
+import static android.service.autofill.Validators.or;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import static org.mockito.Mockito.doReturn;
+import static org.mockito.Mockito.never;
+import static org.mockito.Mockito.verify;
+import static org.testng.Assert.assertThrows;
+
+import android.service.autofill.InternalValidator;
+import android.service.autofill.Validator;
+import android.service.autofill.ValueFinder;
+
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.Mock;
+import org.mockito.junit.MockitoJUnitRunner;
+
+@RunWith(MockitoJUnitRunner.class)
+public class ValidatorsTest extends AutoFillServiceTestCase {
+
+    @Mock private Validator mInvalidValidator;
+    @Mock private ValueFinder mValueFinder;
+    @Mock private InternalValidator mValidValidator;
+    @Mock private InternalValidator mValidValidator2;
+
+    @Test
+    public void testAnd_null() {
+        assertThrows(NullPointerException.class, () -> and((Validator) null));
+        assertThrows(NullPointerException.class, () -> and(mValidValidator, null));
+        assertThrows(NullPointerException.class, () -> and(null, mValidValidator));
+    }
+
+    @Test
+    public void testAnd_invalid() {
+        assertThrows(IllegalArgumentException.class, () -> and(mInvalidValidator));
+        assertThrows(IllegalArgumentException.class, () -> and(mValidValidator, mInvalidValidator));
+        assertThrows(IllegalArgumentException.class, () -> and(mInvalidValidator, mValidValidator));
+    }
+
+    @Test
+    public void testAnd_firstFailed() {
+        doReturn(false).when(mValidValidator).isValid(mValueFinder);
+        assertThat(((InternalValidator) and(mValidValidator, mValidValidator2))
+                .isValid(mValueFinder)).isFalse();
+        verify(mValidValidator2, never()).isValid(mValueFinder);
+    }
+
+    @Test
+    public void testAnd_firstPassedSecondFailed() {
+        doReturn(true).when(mValidValidator).isValid(mValueFinder);
+        doReturn(false).when(mValidValidator2).isValid(mValueFinder);
+        assertThat(((InternalValidator) and(mValidValidator, mValidValidator2))
+                .isValid(mValueFinder)).isFalse();
+    }
+
+    @Test
+    public void testAnd_AllPassed() {
+        doReturn(true).when(mValidValidator).isValid(mValueFinder);
+        doReturn(true).when(mValidValidator2).isValid(mValueFinder);
+        assertThat(((InternalValidator) and(mValidValidator, mValidValidator2))
+                .isValid(mValueFinder)).isTrue();
+    }
+
+    @Test
+    public void testOr_null() {
+        assertThrows(NullPointerException.class, () -> or((Validator) null));
+        assertThrows(NullPointerException.class, () -> or(mValidValidator, null));
+        assertThrows(NullPointerException.class, () -> or(null, mValidValidator));
+    }
+
+    @Test
+    public void testOr_invalid() {
+        assertThrows(IllegalArgumentException.class, () -> or(mInvalidValidator));
+        assertThrows(IllegalArgumentException.class, () -> or(mValidValidator, mInvalidValidator));
+        assertThrows(IllegalArgumentException.class, () -> or(mInvalidValidator, mValidValidator));
+    }
+
+    @Test
+    public void testOr_AllFailed() {
+        doReturn(false).when(mValidValidator).isValid(mValueFinder);
+        doReturn(false).when(mValidValidator2).isValid(mValueFinder);
+        assertThat(((InternalValidator) or(mValidValidator, mValidValidator2))
+                .isValid(mValueFinder)).isFalse();
+    }
+
+    @Test
+    public void testOr_firstPassed() {
+        doReturn(true).when(mValidValidator).isValid(mValueFinder);
+        assertThat(((InternalValidator) or(mValidValidator, mValidValidator2))
+                .isValid(mValueFinder)).isTrue();
+        verify(mValidValidator2, never()).isValid(mValueFinder);
+    }
+
+    @Test
+    public void testOr_secondPassed() {
+        doReturn(false).when(mValidValidator).isValid(mValueFinder);
+        doReturn(true).when(mValidValidator2).isValid(mValueFinder);
+        assertThat(((InternalValidator) or(mValidValidator, mValidValidator2))
+                .isValid(mValueFinder)).isTrue();
+    }
+
+    @Test
+    public void testNot_null() {
+        assertThrows(IllegalArgumentException.class, () -> not(null));
+    }
+
+    @Test
+    public void testNot_invalidClass() {
+        assertThrows(IllegalArgumentException.class, () -> not(mInvalidValidator));
+    }
+
+    @Test
+    public void testNot_falseToTrue() {
+        doReturn(false).when(mValidValidator).isValid(mValueFinder);
+        final InternalValidator notValidator = (InternalValidator) not(mValidValidator);
+        assertThat(notValidator.isValid(mValueFinder)).isTrue();
+    }
+
+    @Test
+    public void testNot_trueToFalse() {
+        doReturn(true).when(mValidValidator).isValid(mValueFinder);
+        final InternalValidator notValidator = (InternalValidator) not(mValidValidator);
+        assertThat(notValidator.isValid(mValueFinder)).isFalse();
+    }
+}
diff --git a/tests/autofillservice/src/android/autofillservice/cts/VirtualContainerActivityTest.java b/tests/autofillservice/src/android/autofillservice/cts/VirtualContainerActivityTest.java
index 31eae24..75bd25e 100644
--- a/tests/autofillservice/src/android/autofillservice/cts/VirtualContainerActivityTest.java
+++ b/tests/autofillservice/src/android/autofillservice/cts/VirtualContainerActivityTest.java
@@ -42,13 +42,11 @@
 import android.support.test.uiautomator.UiObject2;
 import android.view.autofill.AutofillManager;
 
-import org.junit.After;
 import org.junit.Before;
 import org.junit.Rule;
 import org.junit.Test;
 
-import java.util.concurrent.CountDownLatch;
-import java.util.concurrent.TimeUnit;
+import java.util.concurrent.TimeoutException;
 
 /**
  * Test case for an activity containing virtual children.
@@ -66,11 +64,6 @@
         mActivity = mActivityRule.getActivity();
     }
 
-    @After
-    public void finishWelcomeActivity() {
-        WelcomeActivity.finishIt();
-    }
-
     @Test
     public void testAutofillSync() throws Exception {
         autofillTest(true);
@@ -82,6 +75,30 @@
     }
 
     /**
+     * Focus to username and expect window event
+     */
+    void focusToUsername() throws TimeoutException {
+        mUiBot.waitForWindowChange(() -> mActivity.mUsername.changeFocus(true),
+                Timeouts.UI_TIMEOUT.getMaxValue());
+    }
+
+    /**
+     * 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.
+        mActivityRule.runOnUiThread(() -> mActivity.mUsername.changeFocus(true));
+    }
+
+    /**
+     * Focus to password and expect window event
+     */
+    void focusToPassword() throws TimeoutException {
+        mUiBot.waitForWindowChange(() -> mActivity.mPassword.changeFocus(true),
+                Timeouts.UI_TIMEOUT.getMaxValue());
+    }
+
+    /**
      * Tests autofilling the virtual views, using the sync / async version of ViewStructure.addChild
      */
     private void autofillTest(boolean sync) throws Exception {
@@ -98,13 +115,13 @@
         mActivity.mCustomView.setSync(sync);
 
         // Trigger auto-fill.
-        mActivity.mUsername.changeFocus(true);
+        focusToUsername();
         assertDatasetShown(mActivity.mUsername, "The Dude");
 
         // Play around with focus to make sure picker is properly drawn.
-        mActivity.mPassword.changeFocus(true);
+        focusToPassword();
         assertDatasetShown(mActivity.mPassword, "The Dude");
-        mActivity.mUsername.changeFocus(true);
+        focusToUsername();
         assertDatasetShown(mActivity.mUsername, "The Dude");
 
         // Make sure input was sanitized.
@@ -145,7 +162,7 @@
         assertWithMessage("Password node is focused").that(password.isFocused()).isFalse();
 
         // Auto-fill it.
-        sUiBot.selectDataset("The Dude");
+        mUiBot.selectDataset("The Dude");
 
         // Check the results.
         mActivity.assertAutoFilled();
@@ -176,18 +193,18 @@
         mActivity.expectAutoFill("DUDE", "SWEET");
 
         // Trigger auto-fill.
-        mActivity.mUsername.changeFocus(true);
+        focusToUsername();
         sReplier.getNextFillRequest();
         assertDatasetShown(mActivity.mUsername, "The Dude", "THE DUDE");
 
         // Play around with focus to make sure picker is properly drawn.
-        mActivity.mPassword.changeFocus(true);
+        focusToPassword();
         assertDatasetShown(mActivity.mPassword, "The Dude", "THE DUDE");
-        mActivity.mUsername.changeFocus(true);
+        focusToUsername();
         assertDatasetShown(mActivity.mUsername, "The Dude", "THE DUDE");
 
         // Auto-fill it.
-        sUiBot.selectDataset("THE DUDE");
+        mUiBot.selectDataset("THE DUDE");
 
         // Check the results.
         mActivity.assertAutoFilled();
@@ -217,7 +234,7 @@
         sReplier.getNextFillRequest();
 
         // Select datatest.
-        sUiBot.selectDataset("The Dude");
+        mUiBot.selectDataset("The Dude");
 
         // Check the results.
         mActivity.assertAutoFilled();
@@ -267,8 +284,8 @@
         sReplier.getNextFillRequest();
 
         // Auto-fill it.
-        final UiObject2 picker = sUiBot.assertDatasets("The Dude", "Jenny");
-        sUiBot.selectDataset(picker, pickFirst? "The Dude" : "Jenny");
+        final UiObject2 picker = mUiBot.assertDatasets("The Dude", "Jenny");
+        mUiBot.selectDataset(picker, pickFirst ? "The Dude" : "Jenny");
 
         // Check the results.
         mActivity.assertAutoFilled();
@@ -293,43 +310,43 @@
         mActivity.expectAutoFill("dude", "sweet");
 
         // Trigger auto-fill.
-        mActivity.mUsername.changeFocus(true);
+        focusToUsername();
         sReplier.getNextFillRequest();
 
         callback.assertUiShownEvent(mActivity.mCustomView, mActivity.mUsername.text.id);
 
         // Change focus
-        mActivity.mPassword.changeFocus(true);
+        focusToPassword();
         callback.assertUiHiddenEvent(mActivity.mCustomView, mActivity.mUsername.text.id);
         callback.assertUiShownEvent(mActivity.mCustomView, mActivity.mPassword.text.id);
     }
 
     @Test
-    public void testAutofillCallbackDisabled() throws Exception {
+    public void testAutofillCallbackDisabled() throws Throwable {
         // Set service.
         disableService();
         final MyAutofillCallback callback = mActivity.registerCallback();
 
         // Trigger auto-fill.
-        mActivity.mUsername.changeFocus(true);
+        focusToUsernameExpectNoWindowEvent();
 
         // Assert callback was called
         callback.assertUiUnavailableEvent(mActivity.mCustomView, mActivity.mUsername.text.id);
     }
 
     @Test
-    public void testAutofillCallbackNoDatasets() throws Exception {
+    public void testAutofillCallbackNoDatasets() throws Throwable {
         callbackUnavailableTest(NO_RESPONSE);
     }
 
     @Test
-    public void testAutofillCallbackNoDatasetsButSaveInfo() throws Exception {
+    public void testAutofillCallbackNoDatasetsButSaveInfo() throws Throwable {
         callbackUnavailableTest(new CannedFillResponse.Builder()
                 .setRequiredSavableIds(SAVE_DATA_TYPE_PASSWORD, ID_USERNAME, ID_PASSWORD)
                 .build());
     }
 
-    private void callbackUnavailableTest(CannedFillResponse response) throws Exception {
+    private void callbackUnavailableTest(CannedFillResponse response) throws Throwable {
         // Set service.
         enableService();
         final MyAutofillCallback callback = mActivity.registerCallback();
@@ -338,11 +355,11 @@
         sReplier.addResponse(response);
 
         // Trigger auto-fill.
-        mActivity.mUsername.changeFocus(true);
+        focusToUsernameExpectNoWindowEvent();
         sReplier.getNextFillRequest();
 
         // Auto-fill it.
-        sUiBot.assertNoDatasets();
+        mUiBot.assertNoDatasets();
 
         // Assert callback was called
         callback.assertUiUnavailableEvent(mActivity.mCustomView, mActivity.mUsername.text.id);
@@ -365,34 +382,28 @@
         mActivity.expectAutoFill("dude", "sweet");
 
         // Trigger auto-fill.
-        mActivity.mUsername.changeFocus(true);
+        focusToUsername();
         sReplier.getNextFillRequest();
         assertDatasetShown(mActivity.mUsername, "The Dude");
 
-        sUiBot.pressBack();
+        mUiBot.pressBack();
 
-        sUiBot.assertSaveNotShowing(SAVE_DATA_TYPE_PASSWORD);
+        mUiBot.assertSaveNotShowing(SAVE_DATA_TYPE_PASSWORD);
     }
 
     @Test
-    public void testSaveDialogShownWhenAllVirtualViewsNotVisible() throws Exception {
+    public void testSaveDialogShownWhenAllVirtualViewsNotVisible() throws Throwable {
         // Set service.
         enableService();
 
         // Set expectations.
         sReplier.addResponse(new CannedFillResponse.Builder()
                 .setRequiredSavableIds(SAVE_DATA_TYPE_PASSWORD, ID_USERNAME, ID_PASSWORD)
-                .setFlags(SaveInfo.FLAG_SAVE_ON_ALL_VIEWS_INVISIBLE)
+                .setSaveInfoFlags(SaveInfo.FLAG_SAVE_ON_ALL_VIEWS_INVISIBLE)
                 .build());
 
-        final CountDownLatch latch = new CountDownLatch(1);
-
         // Trigger auto-fill.
-        mActivity.runOnUiThread(() -> {
-            mActivity.mUsername.changeFocus(true);
-            latch.countDown();
-        });
-        latch.await(Helper.UI_TIMEOUT_MS, TimeUnit.MILLISECONDS);
+        focusToUsernameExpectNoWindowEvent();
         sReplier.getNextFillRequest();
 
         // TODO: 63602573 Should be removed once this bug is fixed
@@ -409,7 +420,7 @@
         });
 
         // Make sure save is shown
-        sUiBot.assertSaveShowing(SAVE_DATA_TYPE_PASSWORD);
+        mUiBot.assertSaveShowing(SAVE_DATA_TYPE_PASSWORD);
     }
 
     @Test
@@ -427,7 +438,7 @@
                 .build());
 
         // Trigger auto-fill.
-        mActivity.mUsername.changeFocus(true);
+        focusToUsername();
         assertDatasetShown(mActivity.mUsername, "The Dude");
 
         // Make sure package name was sanitized.
@@ -439,8 +450,8 @@
     /**
      * Asserts the dataset picker is properly displayed in a give line.
      */
-    private void assertDatasetShown(Line line, String... expectedDatasets) {
-        final Rect pickerBounds = sUiBot.assertDatasets(expectedDatasets).getVisibleBounds();
+    private void assertDatasetShown(Line line, String... expectedDatasets) throws Exception {
+        final Rect pickerBounds = mUiBot.assertDatasets(expectedDatasets).getVisibleBounds();
         final Rect fieldBounds = line.getAbsCoordinates();
         assertWithMessage("vertical coordinates don't match; picker=%s, field=%s", pickerBounds,
                 fieldBounds).that(pickerBounds.top).isEqualTo(fieldBounds.bottom);
diff --git a/tests/autofillservice/src/android/autofillservice/cts/VirtualContainerView.java b/tests/autofillservice/src/android/autofillservice/cts/VirtualContainerView.java
index 8eecc29..92fc7a7 100644
--- a/tests/autofillservice/src/android/autofillservice/cts/VirtualContainerView.java
+++ b/tests/autofillservice/src/android/autofillservice/cts/VirtualContainerView.java
@@ -16,7 +16,7 @@
 
 package android.autofillservice.cts;
 
-import static android.autofillservice.cts.Helper.FILL_TIMEOUT_MS;
+import static android.autofillservice.cts.Timeouts.FILL_TIMEOUT;
 
 import static com.google.common.truth.Truth.assertWithMessage;
 
@@ -74,7 +74,7 @@
     private int mUnfocusedColor;
     private boolean mSync = true;
     private boolean mOverrideDispatchProvideAutofillStructure = false;
-    private ComponentName mFakedComponentName;
+    private ComponentName mFackedComponentName;
 
     public VirtualContainerView(Context context, AttributeSet attrs) {
         super(context, attrs);
@@ -195,15 +195,15 @@
         Log.d(TAG, "onProvideAutofillVirtualStructure(): flags = " + flags);
         super.onProvideAutofillVirtualStructure(structure, flags);
 
-        if (mFakedComponentName != null) {
-            Log.d(TAG, "Faking package name to " + mFakedComponentName);
+        if (mFackedComponentName != null) {
+            Log.d(TAG, "Faking package name to " + mFackedComponentName);
             try {
                 final AssistStructure assistStructure = Helper.getField(structure, "mAssist");
                 if (assistStructure != null) {
-                    Helper.setField(assistStructure, "mActivityComponent", mFakedComponentName);
+                    Helper.setField(assistStructure, "mActivityComponent", mFackedComponentName);
                 }
             } catch (Exception e) {
-                Log.e(TAG, "Could not fake package name to " + mFakedComponentName, e);
+                Log.e(TAG, "Could not fake package name to " + mFackedComponentName, e);
             }
         }
 
@@ -270,9 +270,10 @@
     }
 
     void fakePackageName(ComponentName name) {
-        mFakedComponentName = name;
+        mFackedComponentName = name;
     }
 
+
     void setOverrideDispatchProvideAutofillStructure(boolean flag) {
         mOverrideDispatchProvideAutofillStructure = flag;
     }
@@ -372,8 +373,8 @@
             }
 
             void assertAutoFilled() throws Exception {
-                final boolean set = latch.await(FILL_TIMEOUT_MS, TimeUnit.MILLISECONDS);
-                assertWithMessage("Timeout (%s ms) on Line %s", FILL_TIMEOUT_MS, label)
+                final boolean set = latch.await(FILL_TIMEOUT.ms(), TimeUnit.MILLISECONDS);
+                assertWithMessage("Timeout (%s ms) on Line %s", FILL_TIMEOUT.ms(), label)
                         .that(set).isTrue();
                 final String actual = text.text.toString();
                 assertWithMessage("Wrong auto-fill value on Line %s", label)
diff --git a/tests/autofillservice/src/android/autofillservice/cts/WebViewActivity.java b/tests/autofillservice/src/android/autofillservice/cts/WebViewActivity.java
index 9bc3df0..a7c880f 100644
--- a/tests/autofillservice/src/android/autofillservice/cts/WebViewActivity.java
+++ b/tests/autofillservice/src/android/autofillservice/cts/WebViewActivity.java
@@ -18,11 +18,13 @@
 import android.os.Bundle;
 import android.support.test.uiautomator.UiObject2;
 import android.util.Log;
+import android.view.View;
 import android.webkit.WebResourceRequest;
 import android.webkit.WebResourceResponse;
 import android.webkit.WebView;
 import android.webkit.WebViewClient;
 import android.widget.EditText;
+import android.widget.LinearLayout;
 
 import java.io.IOException;
 
@@ -33,7 +35,18 @@
     private static final String FAKE_URL = "https://" + FAKE_DOMAIN + ":666/login.html";
     static final String ID_WEBVIEW = "webview";
 
-    WebView mWebView;
+    static final String HTML_NAME_USERNAME = "username";
+    static final String HTML_NAME_PASSWORD = "password";
+
+    static final String ID_OUTSIDE1 = "outside1";
+    static final String ID_OUTSIDE2 = "outside2";
+
+    private MyWebView mWebView;
+
+    private LinearLayout mOutsideContainer1;
+    private LinearLayout mOutsideContainer2;
+    EditText mOutside1;
+    EditText mOutside2;
 
     @Override
     protected void onCreate(Bundle savedInstanceState) {
@@ -64,36 +77,50 @@
                 }
             }
         });
-        mWebView.loadUrl(FAKE_URL);
+
+        mOutsideContainer1 = findViewById(R.id.outsideContainer1);
+        mOutsideContainer2 = findViewById(R.id.outsideContainer2);
+        mOutside1 = findViewById(R.id.outside1);
+        mOutside2 = findViewById(R.id.outside2);
     }
 
-    public UiObject2 getUsernameLabel(UiBot uiBot) {
+    public MyWebView loadWebView() {
+        syncRunOnUiThread(() -> mWebView.loadUrl(FAKE_URL));
+        return mWebView;
+    }
+
+    public void loadOutsideViews() {
+        syncRunOnUiThread(() -> {
+            mOutsideContainer1.setVisibility(View.VISIBLE);
+            mOutsideContainer2.setVisibility(View.VISIBLE);
+        });
+    }
+
+    public UiObject2 getUsernameLabel(UiBot uiBot) throws Exception {
         return getLabel(uiBot, "Username: ");
     }
 
-    public UiObject2 getPasswordLabel(UiBot uiBot) {
+    public UiObject2 getPasswordLabel(UiBot uiBot) throws Exception {
         return getLabel(uiBot, "Password: ");
     }
 
-
-    public UiObject2 getUsernameInput(UiBot uiBot) {
+    public UiObject2 getUsernameInput(UiBot uiBot) throws Exception {
         return getInput(uiBot, "Username: ");
     }
 
-    public UiObject2 getPasswordInput(UiBot uiBot) {
+    public UiObject2 getPasswordInput(UiBot uiBot) throws Exception {
         return getInput(uiBot, "Password: ");
     }
 
-    public UiObject2 getLoginButton(UiBot uiBot) {
-        return uiBot.assertShownByContentDescription("Login");
+    public UiObject2 getLoginButton(UiBot uiBot) throws Exception {
+        return getLabel(uiBot, "Login");
     }
 
-    private UiObject2 getLabel(UiBot uiBot, String contentDescription) {
-        final UiObject2 label = uiBot.assertShownByContentDescription(contentDescription);
-        return label;
+    private UiObject2 getLabel(UiBot uiBot, String label) throws Exception {
+        return uiBot.assertShownByText(label);
     }
 
-    private UiObject2 getInput(UiBot uiBot, String contentDescription) {
+    private UiObject2 getInput(UiBot uiBot, String contentDescription) throws Exception {
         // First get the label..
         final UiObject2 label = getLabel(uiBot, contentDescription);
 
@@ -105,10 +132,12 @@
                 if (child.getClassName().equals(EditText.class.getName())) {
                     return child;
                 }
+                uiBot.dumpScreen("getInput() for " + child + "failed");
                 throw new IllegalStateException("Invalid class for " + child);
             }
             previous = child;
         }
+        uiBot.dumpScreen("getInput() for label " + label + "failed");
         throw new IllegalStateException("could not find username (label=" + label + ")");
     }
 }
diff --git a/tests/autofillservice/src/android/autofillservice/cts/WebViewActivityTest.java b/tests/autofillservice/src/android/autofillservice/cts/WebViewActivityTest.java
index 7eb73d2..c6a2d4c 100644
--- a/tests/autofillservice/src/android/autofillservice/cts/WebViewActivityTest.java
+++ b/tests/autofillservice/src/android/autofillservice/cts/WebViewActivityTest.java
@@ -15,7 +15,11 @@
  */
 package android.autofillservice.cts;
 
-import static android.autofillservice.cts.Helper.runShellCommand;
+import static android.autofillservice.cts.WebViewActivity.HTML_NAME_PASSWORD;
+import static android.autofillservice.cts.WebViewActivity.HTML_NAME_USERNAME;
+import static android.autofillservice.cts.WebViewActivity.ID_OUTSIDE1;
+import static android.autofillservice.cts.WebViewActivity.ID_OUTSIDE2;
+import static android.autofillservice.cts.common.ShellHelper.runShellCommand;
 import static android.service.autofill.SaveInfo.SAVE_DATA_TYPE_PASSWORD;
 
 import static com.google.common.truth.Truth.assertThat;
@@ -65,15 +69,18 @@
         // Set service.
         enableService();
 
+        // Load WebView
+        mActivity.loadWebView();
+
         // Set expectations.
         sReplier.addResponse(CannedFillResponse.NO_RESPONSE);
 
         // Trigger autofill.
-        mActivity.getUsernameInput(sUiBot).click();
+        mActivity.getUsernameInput(mUiBot).click();
         sReplier.getNextFillRequest();
 
         // Assert not shown.
-        sUiBot.assertNoDatasets();
+        mUiBot.assertNoDatasets();
     }
 
     @Test
@@ -81,57 +88,56 @@
         // Set service.
         enableService();
 
+        // Load WebView
+        final MyWebView myWebView = mActivity.loadWebView();
+
         // Set expectations.
+        myWebView.expectAutofill("dude", "sweet");
         final MyAutofillCallback callback = mActivity.registerCallback();
         sReplier.addResponse(new CannedDataset.Builder()
-                .setField("username", "dude")
-                .setField("password", "sweet")
+                .setField(HTML_NAME_USERNAME, "dude")
+                .setField(HTML_NAME_PASSWORD, "sweet")
                 .setPresentation(createPresentation("The Dude"))
                 .build());
 
         // Trigger autofill.
-        mActivity.getUsernameInput(sUiBot).click();
+        mActivity.getUsernameInput(mUiBot).click();
         final FillRequest fillRequest = sReplier.getNextFillRequest();
-        sUiBot.assertDatasets("The Dude");
+        mUiBot.assertDatasets("The Dude");
 
         // Change focus around.
-        final int usernameChildId = callback.assertUiShownEventForVirtualChild(mActivity.mWebView);
-        mActivity.getUsernameLabel(sUiBot).click();
-        callback.assertUiHiddenEvent(mActivity.mWebView, usernameChildId);
-        sUiBot.assertNoDatasets();
-        mActivity.getPasswordInput(sUiBot).click();
-        final int passwordChildId = callback.assertUiShownEventForVirtualChild(mActivity.mWebView);
-        final UiObject2 datasetPicker = sUiBot.assertDatasets("The Dude");
+        final int usernameChildId = callback.assertUiShownEventForVirtualChild(myWebView);
+        mActivity.getUsernameLabel(mUiBot).click();
+        callback.assertUiHiddenEvent(myWebView, usernameChildId);
+        mUiBot.assertNoDatasets();
+        mActivity.getPasswordInput(mUiBot).click();
+        final int passwordChildId = callback.assertUiShownEventForVirtualChild(myWebView);
+        final UiObject2 datasetPicker = mUiBot.assertDatasets("The Dude");
 
         // Now Autofill it.
-        sUiBot.selectDataset(datasetPicker, "The Dude");
-        sUiBot.assertNoDatasets();
-        callback.assertUiHiddenEvent(mActivity.mWebView, passwordChildId);
+        mUiBot.selectDataset(datasetPicker, "The Dude");
+        myWebView.assertAutofilled();
+        mUiBot.assertNoDatasets();
+        callback.assertUiHiddenEvent(myWebView, passwordChildId);
 
         // Make sure screen was autofilled.
-        assertThat(mActivity.getUsernameInput(sUiBot).getText()).isEqualTo("dude");
+        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(sUiBot).getText();
+        final String password = mActivity.getPasswordInput(mUiBot).getText();
         assertThat(password).isNotEqualTo("sweet");
         assertThat(password).hasLength(5);
 
         // Assert structure passed to service.
         try {
-            final ViewNode webViewNode = Helper.findWebViewNode(fillRequest.structure, "FORM AM I");
-            // TODO(b/66953802): class name should be android.webkit.WebView, and form name should
-            // be inside HtmlInfo, but Chromium 61 does not implement that.
-            if (webViewNode.getClassName().equals("android.webkit.WebView")) {
-                final HtmlInfo htmlInfo = Helper.assertHasHtmlTag(webViewNode, "form");
-                Helper.assertHasAttribute(htmlInfo, "name", "FORM AM I");
-            } else {
-                assertThat(webViewNode.getClassName()).isEqualTo("FORM AM I");
-                assertThat(webViewNode.getHtmlInfo()).isNull();
-            }
+            final ViewNode webViewNode =
+                    Helper.findWebViewNodeByFormName(fillRequest.structure, "FORM AM I");
+            assertThat(webViewNode.getClassName()).isEqualTo("android.webkit.WebView");
             assertThat(webViewNode.getWebDomain()).isEqualTo(WebViewActivity.FAKE_DOMAIN);
+            assertThat(webViewNode.getWebScheme()).isEqualTo("https");
 
             final ViewNode usernameNode =
-                    Helper.findNodeByHtmlName(fillRequest.structure, "username");
+                    Helper.findNodeByHtmlName(fillRequest.structure, HTML_NAME_USERNAME);
             Helper.assertTextIsSanitized(usernameNode);
             final HtmlInfo usernameHtmlInfo = Helper.assertHasHtmlTag(usernameNode, "input");
             Helper.assertHasAttribute(usernameHtmlInfo, "type", "text");
@@ -141,7 +147,7 @@
             assertThat(usernameNode.getHint()).isEqualTo("There's no place like a holder");
 
             final ViewNode passwordNode =
-                    Helper.findNodeByHtmlName(fillRequest.structure, "password");
+                    Helper.findNodeByHtmlName(fillRequest.structure, HTML_NAME_PASSWORD);
             Helper.assertTextIsSanitized(passwordNode);
             final HtmlInfo passwordHtmlInfo = Helper.assertHasHtmlTag(passwordNode, "input");
             Helper.assertHasAttribute(passwordHtmlInfo, "type", "password");
@@ -161,37 +167,43 @@
         // Set service.
         enableService();
 
+        // Load WebView
+        mActivity.loadWebView();
+
         // Set expectations.
         sReplier.addResponse(new CannedFillResponse.Builder()
-                .setRequiredSavableIds(SAVE_DATA_TYPE_PASSWORD, "username", "password")
+                .setRequiredSavableIds(SAVE_DATA_TYPE_PASSWORD,
+                        HTML_NAME_USERNAME, HTML_NAME_PASSWORD)
                 .build());
 
         // Trigger autofill.
-        mActivity.getUsernameInput(sUiBot).click();
+        mActivity.getUsernameInput(mUiBot).click();
         sReplier.getNextFillRequest();
 
         // Assert not shown.
-        sUiBot.assertNoDatasets();
+        mUiBot.assertNoDatasets();
 
         // Trigger save.
         if (INJECT_EVENTS) {
-            mActivity.getUsernameInput(sUiBot).click();
+            mActivity.getUsernameInput(mUiBot).click();
             runShellCommand("input keyevent KEYCODE_U");
-            mActivity.getPasswordInput(sUiBot).click();
+            mActivity.getPasswordInput(mUiBot).click();
             runShellCommand("input keyevent KEYCODE_P");
         } else {
-            mActivity.getUsernameInput(sUiBot).setText("DUDE");
-            mActivity.getPasswordInput(sUiBot).setText("SWEET");
+            mActivity.getUsernameInput(mUiBot).setText("DUDE");
+            mActivity.getPasswordInput(mUiBot).setText("SWEET");
         }
-        mActivity.getLoginButton(sUiBot).click();
+        mActivity.getLoginButton(mUiBot).click();
 
         // Assert save UI shown.
-        sUiBot.saveForAutofill(true, SAVE_DATA_TYPE_PASSWORD);
+        mUiBot.saveForAutofill(true, SAVE_DATA_TYPE_PASSWORD);
 
         // Assert results
         final SaveRequest saveRequest = sReplier.getNextSaveRequest();
-        final ViewNode usernameNode = Helper.findNodeByHtmlName(saveRequest.structure, "username");
-        final ViewNode passwordNode = Helper.findNodeByHtmlName(saveRequest.structure, "password");
+        final ViewNode usernameNode = Helper.findNodeByHtmlName(saveRequest.structure,
+                HTML_NAME_USERNAME);
+        final ViewNode passwordNode = Helper.findNodeByHtmlName(saveRequest.structure,
+                HTML_NAME_PASSWORD);
         if (INJECT_EVENTS) {
             Helper.assertTextAndValue(usernameNode, "u");
             Helper.assertTextAndValue(passwordNode, "p");
@@ -206,64 +218,74 @@
         // Set service.
         enableService();
 
+        // Load WebView
+        final MyWebView myWebView = mActivity.loadWebView();
+
         // Set expectations.
         final MyAutofillCallback callback = mActivity.registerCallback();
+        myWebView.expectAutofill("dude", "sweet");
         sReplier.addResponse(new CannedFillResponse.Builder()
-                .setRequiredSavableIds(SAVE_DATA_TYPE_PASSWORD, "username", "password")
+                .setRequiredSavableIds(SAVE_DATA_TYPE_PASSWORD,
+                        HTML_NAME_USERNAME, HTML_NAME_PASSWORD)
                 .addDataset(new CannedDataset.Builder()
-                        .setField("username", "dude")
-                        .setField("password", "sweet")
+                        .setField(HTML_NAME_USERNAME, "dude")
+                        .setField(HTML_NAME_PASSWORD, "sweet")
                         .setPresentation(createPresentation("The Dude"))
                         .build())
                 .build());
 
         // Trigger autofill.
-        mActivity.getUsernameInput(sUiBot).click();
+        mActivity.getUsernameInput(mUiBot).click();
         final FillRequest fillRequest = sReplier.getNextFillRequest();
-        sUiBot.assertDatasets("The Dude");
-        final int usernameChildId = callback.assertUiShownEventForVirtualChild(mActivity.mWebView);
+        mUiBot.assertDatasets("The Dude");
+        final int usernameChildId = callback.assertUiShownEventForVirtualChild(myWebView);
 
         // Assert structure passed to service.
-        final ViewNode usernameNode = Helper.findNodeByHtmlName(fillRequest.structure, "username");
+        final ViewNode usernameNode = Helper.findNodeByHtmlName(fillRequest.structure,
+                HTML_NAME_USERNAME);
         Helper.assertTextIsSanitized(usernameNode);
         assertThat(usernameNode.isFocused()).isTrue();
         assertThat(usernameNode.getAutofillHints()).asList().containsExactly("username");
-        final ViewNode passwordNode = Helper.findNodeByHtmlName(fillRequest.structure, "password");
+        final ViewNode passwordNode = Helper.findNodeByHtmlName(fillRequest.structure,
+                HTML_NAME_PASSWORD);
         Helper.assertTextIsSanitized(passwordNode);
         assertThat(passwordNode.getAutofillHints()).asList().containsExactly("current-password");
         assertThat(passwordNode.isFocused()).isFalse();
 
         // Autofill it.
-        sUiBot.selectDataset("The Dude");
-        callback.assertUiHiddenEvent(mActivity.mWebView, usernameChildId);
+        mUiBot.selectDataset("The Dude");
+        myWebView.assertAutofilled();
+        callback.assertUiHiddenEvent(myWebView, usernameChildId);
 
         // Make sure screen was autofilled.
-        assertThat(mActivity.getUsernameInput(sUiBot).getText()).isEqualTo("dude");
+        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(sUiBot).getText();
+        final String password = mActivity.getPasswordInput(mUiBot).getText();
         assertThat(password).isNotEqualTo("sweet");
         assertThat(password).hasLength(5);
 
         // Now trigger save.
         if (INJECT_EVENTS) {
-            mActivity.getUsernameInput(sUiBot).click();
+            mActivity.getUsernameInput(mUiBot).click();
             runShellCommand("input keyevent KEYCODE_U");
-            mActivity.getPasswordInput(sUiBot).click();
+            mActivity.getPasswordInput(mUiBot).click();
             runShellCommand("input keyevent KEYCODE_P");
         } else {
-            mActivity.getUsernameInput(sUiBot).setText("DUDE");
-            mActivity.getPasswordInput(sUiBot).setText("SWEET");
+            mActivity.getUsernameInput(mUiBot).setText("DUDE");
+            mActivity.getPasswordInput(mUiBot).setText("SWEET");
         }
-        mActivity.getLoginButton(sUiBot).click();
+        mActivity.getLoginButton(mUiBot).click();
 
         // Assert save UI shown.
-        sUiBot.saveForAutofill(true, SAVE_DATA_TYPE_PASSWORD);
+        mUiBot.saveForAutofill(true, SAVE_DATA_TYPE_PASSWORD);
 
         // Assert results
         final SaveRequest saveRequest = sReplier.getNextSaveRequest();
-        final ViewNode usernameNode2 = Helper.findNodeByHtmlName(saveRequest.structure, "username");
-        final ViewNode passwordNode2 = Helper.findNodeByHtmlName(saveRequest.structure, "password");
+        final ViewNode usernameNode2 = Helper.findNodeByHtmlName(saveRequest.structure,
+                HTML_NAME_USERNAME);
+        final ViewNode passwordNode2 = Helper.findNodeByHtmlName(saveRequest.structure,
+                HTML_NAME_PASSWORD);
         if (INJECT_EVENTS) {
             Helper.assertTextAndValue(usernameNode2, "dudeu");
             Helper.assertTextAndValue(passwordNode2, "sweetp");
@@ -272,4 +294,285 @@
             Helper.assertTextAndValue(passwordNode2, "SWEET");
         }
     }
+
+    @Test
+    public void testAutofillAndSave_withExternalViews_loadWebViewFirst() throws Exception {
+        // Set service.
+        enableService();
+
+        // Load views
+        final MyWebView myWebView = mActivity.loadWebView();
+        mActivity.loadOutsideViews();
+
+        // Set expectations.
+        myWebView.expectAutofill("dude", "sweet");
+        final OneTimeTextWatcher outside1Watcher = new OneTimeTextWatcher("outside1",
+                mActivity.mOutside1, "duder");
+        final OneTimeTextWatcher outside2Watcher = new OneTimeTextWatcher("outside2",
+                mActivity.mOutside2, "sweeter");
+        mActivity.mOutside1.addTextChangedListener(outside1Watcher);
+        mActivity.mOutside2.addTextChangedListener(outside2Watcher);
+
+        final MyAutofillCallback callback = mActivity.registerCallback();
+        sReplier.setIdMode(IdMode.HTML_NAME_OR_RESOURCE_ID);
+        sReplier.addResponse(new CannedFillResponse.Builder()
+                .setRequiredSavableIds(SAVE_DATA_TYPE_PASSWORD,
+                        HTML_NAME_USERNAME, HTML_NAME_PASSWORD, ID_OUTSIDE1, ID_OUTSIDE2)
+                .addDataset(new CannedDataset.Builder()
+                        .setField(HTML_NAME_USERNAME, "dude", createPresentation("USER"))
+                        .setField(HTML_NAME_PASSWORD, "sweet", createPresentation("PASS"))
+                        .setField(ID_OUTSIDE1, "duder", createPresentation("OUT1"))
+                        .setField(ID_OUTSIDE2, "sweeter", createPresentation("OUT2"))
+                        .build())
+                .build());
+
+        // Trigger autofill.
+        mActivity.getUsernameInput(mUiBot).click();
+        final FillRequest fillRequest = sReplier.getNextFillRequest();
+        mUiBot.assertDatasets("USER");
+        final int usernameChildId = callback.assertUiShownEventForVirtualChild(myWebView);
+
+        // Assert structure passed to service.
+        final ViewNode usernameFillNode = Helper.findNodeByHtmlName(fillRequest.structure,
+                HTML_NAME_USERNAME);
+        Helper.assertTextIsSanitized(usernameFillNode);
+        assertThat(usernameFillNode.isFocused()).isTrue();
+        assertThat(usernameFillNode.getAutofillHints()).asList().containsExactly("username");
+        final ViewNode passwordFillNode = Helper.findNodeByHtmlName(fillRequest.structure,
+                HTML_NAME_PASSWORD);
+        Helper.assertTextIsSanitized(passwordFillNode);
+        assertThat(passwordFillNode.getAutofillHints()).asList()
+                .containsExactly("current-password");
+        assertThat(passwordFillNode.isFocused()).isFalse();
+
+        final ViewNode outside1FillNode = Helper.findNodeByResourceId(fillRequest.structure,
+                ID_OUTSIDE1);
+        Helper.assertTextIsSanitized(outside1FillNode);
+        final ViewNode outside2FillNode = Helper.findNodeByResourceId(fillRequest.structure,
+                ID_OUTSIDE2);
+        Helper.assertTextIsSanitized(outside2FillNode);
+
+        // Move focus around to make sure UI is shown accordingly
+        mActivity.runOnUiThread(() -> mActivity.mOutside1.requestFocus());
+        callback.assertUiHiddenEvent(myWebView, usernameChildId);
+        mUiBot.assertDatasets("OUT1");
+        callback.assertUiShownEvent(mActivity.mOutside1);
+
+        mActivity.getPasswordInput(mUiBot).click();
+        callback.assertUiHiddenEvent(mActivity.mOutside1);
+        mUiBot.assertDatasets("PASS");
+        final int passwordChildId = callback.assertUiShownEventForVirtualChild(myWebView);
+
+        mActivity.runOnUiThread(() -> mActivity.mOutside2.requestFocus());
+        callback.assertUiHiddenEvent(myWebView, passwordChildId);
+        final UiObject2 datasetPicker = mUiBot.assertDatasets("OUT2");
+        callback.assertUiShownEvent(mActivity.mOutside2);
+
+        // Autofill it.
+        mUiBot.selectDataset(datasetPicker, "OUT2");
+        callback.assertUiHiddenEvent(mActivity.mOutside2);
+
+        myWebView.assertAutofilled();
+        outside1Watcher.assertAutoFilled();
+        outside2Watcher.assertAutoFilled();
+
+        // Now trigger save.
+        if (INJECT_EVENTS) {
+            mActivity.getUsernameInput(mUiBot).click();
+            runShellCommand("input keyevent KEYCODE_U");
+            mActivity.getPasswordInput(mUiBot).click();
+            runShellCommand("input keyevent KEYCODE_P");
+        } else {
+            mActivity.getUsernameInput(mUiBot).setText("DUDE");
+            mActivity.getPasswordInput(mUiBot).setText("SWEET");
+        }
+        mActivity.runOnUiThread(() -> {
+            mActivity.mOutside1.setText("DUDER");
+            mActivity.mOutside2.setText("SWEETER");
+        });
+
+        mActivity.getLoginButton(mUiBot).click();
+
+        // Assert save UI shown.
+        mUiBot.saveForAutofill(true, SAVE_DATA_TYPE_PASSWORD);
+
+        // Assert results
+        final SaveRequest saveRequest = sReplier.getNextSaveRequest();
+        final ViewNode usernameSaveNode = Helper.findNodeByHtmlName(saveRequest.structure,
+                HTML_NAME_USERNAME);
+        final ViewNode passwordSaveNode = Helper.findNodeByHtmlName(saveRequest.structure,
+                HTML_NAME_PASSWORD);
+        if (INJECT_EVENTS) {
+            Helper.assertTextAndValue(usernameSaveNode, "dudeu");
+            Helper.assertTextAndValue(passwordSaveNode, "sweetp");
+        } else {
+            Helper.assertTextAndValue(usernameSaveNode, "DUDE");
+            Helper.assertTextAndValue(passwordSaveNode, "SWEET");
+        }
+
+        final ViewNode outside1SaveNode = Helper.findNodeByResourceId(saveRequest.structure,
+                ID_OUTSIDE1);
+        Helper.assertTextAndValue(outside1SaveNode, "DUDER");
+        final ViewNode outside2SaveNode = Helper.findNodeByResourceId(saveRequest.structure,
+                ID_OUTSIDE2);
+        Helper.assertTextAndValue(outside2SaveNode, "SWEETER");
+    }
+
+
+    @Test
+    public void testAutofillAndSave_withExternalViews_loadExternalViewsFirst() throws Exception {
+        if (true) return; // TODO(b/69461853): re-enable once WebView fixes it
+
+        // Set service.
+        enableService();
+
+        // Load outside views
+        mActivity.loadOutsideViews();
+
+        // Set expectations.
+        final OneTimeTextWatcher outside1Watcher = new OneTimeTextWatcher("outside1",
+                mActivity.mOutside1, "duder");
+        final OneTimeTextWatcher outside2Watcher = new OneTimeTextWatcher("outside2",
+                mActivity.mOutside2, "sweeter");
+        mActivity.mOutside1.addTextChangedListener(outside1Watcher);
+        mActivity.mOutside2.addTextChangedListener(outside2Watcher);
+
+        final MyAutofillCallback callback = mActivity.registerCallback();
+        sReplier.setIdMode(IdMode.RESOURCE_ID);
+        sReplier.addResponse(new CannedFillResponse.Builder()
+                .addDataset(new CannedDataset.Builder()
+                        .setField(ID_OUTSIDE1, "duder", createPresentation("OUT1"))
+                        .setField(ID_OUTSIDE2, "sweeter", createPresentation("OUT2"))
+                        .build())
+                .build());
+
+        // Trigger autofill.
+        mActivity.runOnUiThread(() -> mActivity.mOutside1.requestFocus());
+        final FillRequest fillRequest1 = sReplier.getNextFillRequest();
+        mUiBot.assertDatasets("OUT1");
+        callback.assertUiShownEvent(mActivity.mOutside1);
+
+        // Move focus around to make sure UI is shown accordingly
+        mActivity.runOnUiThread(() -> mActivity.mOutside2.requestFocus());
+        callback.assertUiHiddenEvent(mActivity.mOutside1);
+        mUiBot.assertDatasets("OUT2");
+        callback.assertUiShownEvent(mActivity.mOutside2);
+
+        // Assert structure passed to service.
+        final ViewNode outside1FillNode = Helper.findNodeByResourceId(fillRequest1.structure,
+                ID_OUTSIDE1);
+        Helper.assertTextIsSanitized(outside1FillNode);
+        final ViewNode outside2FillNode = Helper.findNodeByResourceId(fillRequest1.structure,
+                ID_OUTSIDE2);
+        Helper.assertTextIsSanitized(outside2FillNode);
+
+        // Now load Webiew
+        final MyWebView myWebView = mActivity.loadWebView();
+
+        // Set expectations
+        myWebView.expectAutofill("dude", "sweet");
+        sReplier.setIdMode(IdMode.HTML_NAME_OR_RESOURCE_ID);
+        sReplier.addResponse(new CannedFillResponse.Builder()
+                .setRequiredSavableIds(SAVE_DATA_TYPE_PASSWORD,
+                        HTML_NAME_USERNAME, HTML_NAME_PASSWORD, ID_OUTSIDE1, ID_OUTSIDE2)
+                .addDataset(new CannedDataset.Builder()
+                        .setField(HTML_NAME_USERNAME, "dude", createPresentation("USER"))
+                        .setField(HTML_NAME_PASSWORD, "sweet", createPresentation("PASS"))
+                        .build())
+                .build());
+
+        // Trigger autofill.
+        mActivity.getUsernameInput(mUiBot).click();
+        final FillRequest fillRequest2 = sReplier.getNextFillRequest();
+        callback.assertUiHiddenEvent(mActivity.mOutside2);
+        mUiBot.assertDatasets("USER");
+        final int usernameChildId = callback.assertUiShownEventForVirtualChild(myWebView);
+
+        // Move focus around to make sure UI is shown accordingly
+        mActivity.runOnUiThread(() -> mActivity.mOutside1.requestFocus());
+        callback.assertUiHiddenEvent(myWebView, usernameChildId);
+        mUiBot.assertDatasets("OUT1");
+        callback.assertUiShownEvent(mActivity.mOutside1);
+
+        mActivity.runOnUiThread(() -> mActivity.mOutside2.requestFocus());
+        callback.assertUiHiddenEvent(mActivity.mOutside1);
+        mUiBot.assertDatasets("OUT2");
+        callback.assertUiShownEvent(mActivity.mOutside2);
+
+        mActivity.getPasswordInput(mUiBot).click();
+        callback.assertUiHiddenEvent(mActivity.mOutside2);
+        mUiBot.assertDatasets("PASS");
+        final int passwordChildId = callback.assertUiShownEventForVirtualChild(myWebView);
+
+        mActivity.runOnUiThread(() -> mActivity.mOutside2.requestFocus());
+        callback.assertUiHiddenEvent(myWebView, passwordChildId);
+        final UiObject2 datasetPicker = mUiBot.assertDatasets("OUT2");
+        callback.assertUiShownEvent(mActivity.mOutside2);
+
+        // Assert structure passed to service.
+        final ViewNode usernameFillNode = Helper.findNodeByHtmlName(fillRequest2.structure,
+                HTML_NAME_USERNAME);
+        Helper.assertTextIsSanitized(usernameFillNode);
+        assertThat(usernameFillNode.isFocused()).isTrue();
+        assertThat(usernameFillNode.getAutofillHints()).asList().containsExactly("username");
+        final ViewNode passwordFillNode = Helper.findNodeByHtmlName(fillRequest2.structure,
+                HTML_NAME_PASSWORD);
+        Helper.assertTextIsSanitized(passwordFillNode);
+        assertThat(passwordFillNode.getAutofillHints()).asList()
+                .containsExactly("current-password");
+        assertThat(passwordFillNode.isFocused()).isFalse();
+
+        // Autofill external views (2nd partition)
+        mUiBot.selectDataset(datasetPicker, "OUT2");
+        callback.assertUiHiddenEvent(mActivity.mOutside2);
+        outside1Watcher.assertAutoFilled();
+        outside2Watcher.assertAutoFilled();
+
+        // Autofill Webview (1st partition)
+        mActivity.getUsernameInput(mUiBot).click();
+        callback.assertUiShownEventForVirtualChild(myWebView);
+        mUiBot.selectDataset("USER");
+        myWebView.assertAutofilled();
+
+        // Now trigger save.
+        if (INJECT_EVENTS) {
+            mActivity.getUsernameInput(mUiBot).click();
+            runShellCommand("input keyevent KEYCODE_U");
+            mActivity.getPasswordInput(mUiBot).click();
+            runShellCommand("input keyevent KEYCODE_P");
+        } else {
+            mActivity.getUsernameInput(mUiBot).setText("DUDE");
+            mActivity.getPasswordInput(mUiBot).setText("SWEET");
+        }
+        mActivity.runOnUiThread(() -> {
+            mActivity.mOutside1.setText("DUDER");
+            mActivity.mOutside2.setText("SWEETER");
+        });
+
+        mActivity.getLoginButton(mUiBot).click();
+
+        // Assert save UI shown.
+        mUiBot.saveForAutofill(true, SAVE_DATA_TYPE_PASSWORD);
+
+        // Assert results
+        final SaveRequest saveRequest = sReplier.getNextSaveRequest();
+        final ViewNode usernameSaveNode = Helper.findNodeByHtmlName(saveRequest.structure,
+                HTML_NAME_USERNAME);
+        final ViewNode passwordSaveNode = Helper.findNodeByHtmlName(saveRequest.structure,
+                HTML_NAME_PASSWORD);
+        if (INJECT_EVENTS) {
+            Helper.assertTextAndValue(usernameSaveNode, "dudeu");
+            Helper.assertTextAndValue(passwordSaveNode, "sweetp");
+        } else {
+            Helper.assertTextAndValue(usernameSaveNode, "DUDE");
+            Helper.assertTextAndValue(passwordSaveNode, "SWEET");
+        }
+
+        final ViewNode outside1SaveNode = Helper.findNodeByResourceId(saveRequest.structure,
+                ID_OUTSIDE1);
+        Helper.assertTextAndValue(outside1SaveNode, "DUDER");
+        final ViewNode outside2SaveNode = Helper.findNodeByResourceId(saveRequest.structure,
+                ID_OUTSIDE2);
+        Helper.assertTextAndValue(outside2SaveNode, "SWEETER");
+    }
 }
diff --git a/tests/autofillservice/src/android/autofillservice/cts/WelcomeActivity.java b/tests/autofillservice/src/android/autofillservice/cts/WelcomeActivity.java
index 5d3baca..993951d 100644
--- a/tests/autofillservice/src/android/autofillservice/cts/WelcomeActivity.java
+++ b/tests/autofillservice/src/android/autofillservice/cts/WelcomeActivity.java
@@ -17,7 +17,10 @@
 
 import static com.google.common.truth.Truth.assertWithMessage;
 
+import android.app.PendingIntent;
+import android.content.Context;
 import android.content.Intent;
+import android.content.IntentSender;
 import android.os.Bundle;
 import android.support.annotation.Nullable;
 import android.support.test.uiautomator.UiObject2;
@@ -35,9 +38,12 @@
     private static final String TAG = "WelcomeActivity";
 
     static final String EXTRA_MESSAGE = "message";
-    static final String ID_OUTPUT = "output";
+    static final String ID_WELCOME = "welcome";
 
-    private TextView mOutput;
+    private static int sPendingIntentId;
+    private static PendingIntent sPendingIntent;
+
+    private TextView mWelcome;
 
     public WelcomeActivity() {
         sInstance = this;
@@ -49,22 +55,34 @@
 
         setContentView(R.layout.welcome_activity);
 
-        mOutput = (TextView) findViewById(R.id.output);
+        mWelcome = (TextView) findViewById(R.id.welcome);
 
         final Intent intent = getIntent();
         final String message = intent.getStringExtra(EXTRA_MESSAGE);
 
         if (!TextUtils.isEmpty(message)) {
-            mOutput.setText(message);
+            mWelcome.setText(message);
         }
 
-        Log.d(TAG, "Output: " + mOutput.getText());
+        Log.d(TAG, "Message: " + message);
     }
 
-    static void finishIt() {
+    @Override
+    protected void onDestroy() {
+        super.onDestroy();
+
+        Log.v(TAG, "Setting sInstance to null onDestroy()");
+        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);
+        }
+        if (sPendingIntent != null) {
+            sPendingIntent.cancel();
         }
     }
 
@@ -75,11 +93,25 @@
 
     // TODO: reuse in other places
     static void assertShowing(UiBot uiBot, @Nullable String expectedMessage) throws Exception {
-        final UiObject2 activity = uiBot.assertShownByRelativeId(ID_OUTPUT);
+        final UiObject2 activity = uiBot.assertShownByRelativeId(ID_WELCOME);
         if (expectedMessage == null) {
             expectedMessage = "Welcome to the jungle!";
         }
         assertWithMessage("wrong text on '%s'", activity).that(activity.getText())
                 .isEqualTo(expectedMessage);
     }
+
+    public static IntentSender createSender(Context context, String message) {
+        if (sPendingIntent != null) {
+            throw new IllegalArgumentException("Already have pending intent (id="
+                    + sPendingIntentId + "): " + sPendingIntent);
+        }
+        ++sPendingIntentId;
+        Log.v(TAG, "createSender: id=" + sPendingIntentId + " message=" + message);
+        final Intent intent = new Intent(context, WelcomeActivity.class)
+                .putExtra(EXTRA_MESSAGE, message)
+                .setFlags(Intent.FLAG_ACTIVITY_NO_HISTORY);
+        sPendingIntent = PendingIntent.getActivity(context, sPendingIntentId, intent, 0);
+        return sPendingIntent.getIntentSender();
+    }
 }
diff --git a/tests/autofillservice/src/android/autofillservice/cts/common/OneTimeSettingsListener.java b/tests/autofillservice/src/android/autofillservice/cts/common/OneTimeSettingsListener.java
new file mode 100644
index 0000000..cbdcf44
--- /dev/null
+++ b/tests/autofillservice/src/android/autofillservice/cts/common/OneTimeSettingsListener.java
@@ -0,0 +1,67 @@
+/*
+ * 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.autofillservice.cts.common;
+
+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.provider.Settings;
+
+import java.util.concurrent.CountDownLatch;
+import java.util.concurrent.TimeUnit;
+
+/**
+ * Helper used to block tests until a secure settings value has been updated.
+ */
+public final class OneTimeSettingsListener extends ContentObserver {
+    private final CountDownLatch mLatch = new CountDownLatch(1);
+    private final ContentResolver mResolver;
+    private final String mKey;
+
+    public OneTimeSettingsListener(Context context, String key) {
+        super(new Handler(Looper.getMainLooper()));
+        mKey = key;
+        mResolver = context.getContentResolver();
+        mResolver.registerContentObserver(Settings.Secure.getUriFor(key), false, this);
+    }
+
+    @Override
+    public void onChange(boolean selfChange, Uri uri) {
+        mResolver.unregisterContentObserver(this);
+        mLatch.countDown();
+    }
+
+    /**
+     * Blocks for a few seconds until it's called.
+     *
+     * @throws IllegalStateException if it's not called.
+     */
+    public void assertCalled() {
+        try {
+            final boolean updated = mLatch.await(5, TimeUnit.SECONDS);
+            if (!updated) {
+                throw new IllegalStateException("Settings " + mKey + " not called in 5s");
+            }
+        } catch (InterruptedException e) {
+            Thread.currentThread().interrupt();
+            throw new IllegalStateException("Interrupted", e);
+        }
+    }
+}
diff --git a/tests/autofillservice/src/android/autofillservice/cts/common/README.txt b/tests/autofillservice/src/android/autofillservice/cts/common/README.txt
new file mode 100644
index 0000000..97d3b48
--- /dev/null
+++ b/tests/autofillservice/src/android/autofillservice/cts/common/README.txt
@@ -0,0 +1,2 @@
+This package contains utilities that are not tied to Autofill and might eventually move to
+a common CTS package.
\ No newline at end of file
diff --git a/tests/autofillservice/src/android/autofillservice/cts/common/SettingsHelper.java b/tests/autofillservice/src/android/autofillservice/cts/common/SettingsHelper.java
new file mode 100644
index 0000000..374710f
--- /dev/null
+++ b/tests/autofillservice/src/android/autofillservice/cts/common/SettingsHelper.java
@@ -0,0 +1,94 @@
+/*
+ * 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.autofillservice.cts.common;
+
+import static android.autofillservice.cts.common.ShellHelper.runShellCommand;
+
+import static com.google.common.truth.Truth.assertWithMessage;
+
+import android.content.Context;
+import android.provider.Settings;
+import android.support.annotation.NonNull;
+import android.support.annotation.Nullable;
+
+/**
+ * Provides utilities to interact with the device's {@link Settings}.
+ */
+// TODO: make it more generic (it's hardcoded to 'secure' provider on current user
+public final class SettingsHelper {
+
+    /**
+     * Uses a Shell command to set the given preference.
+     */
+    public static void set(@NonNull String key, @Nullable String value) {
+        if (value == null) {
+            delete(key);
+            return;
+        }
+        runShellCommand("settings put secure %s %s default", key, value);
+    }
+
+    /**
+     * Uses a Shell command to set the given preference, and verifies it was correctly set.
+     */
+    public static void syncSet(@NonNull Context context, @NonNull String key,
+            @Nullable String value) {
+        if (value == null) {
+            syncDelete(context, key);
+            return;
+        }
+
+        final OneTimeSettingsListener observer = new OneTimeSettingsListener(context, key);
+        set(key, value);
+        observer.assertCalled();
+
+        final String newValue = get(key);
+        assertWithMessage("invalid value for '%s' settings", key).that(newValue).isEqualTo(value);
+    }
+
+    /**
+     * Uses a Shell command to delete the given preference.
+     */
+    public static void delete(@NonNull String key) {
+        runShellCommand("settings delete secure %s", key);
+    }
+
+    /**
+     * Uses a Shell command to delete the given preference, and verifies it was correctly deleted.
+     */
+    public static void syncDelete(@NonNull Context context, @NonNull String key) {
+
+        final OneTimeSettingsListener observer = new OneTimeSettingsListener(context, key);
+        delete(key);
+        observer.assertCalled();
+
+        final String newValue = get(key);
+        assertWithMessage("invalid value for '%s' settings", key).that(newValue).isEqualTo("null");
+    }
+
+    /**
+     * Gets the value of a given preference using Shell command.
+     */
+    @NonNull
+    public static String get(@NonNull String key) {
+        return runShellCommand("settings get secure %s", key);
+    }
+
+    private SettingsHelper() {
+        throw new UnsupportedOperationException("contain static methods only");
+    }
+}
diff --git a/tests/autofillservice/src/android/autofillservice/cts/common/SettingsStateChangerRule.java b/tests/autofillservice/src/android/autofillservice/cts/common/SettingsStateChangerRule.java
new file mode 100644
index 0000000..82b2ef5
--- /dev/null
+++ b/tests/autofillservice/src/android/autofillservice/cts/common/SettingsStateChangerRule.java
@@ -0,0 +1,42 @@
+/*
+ * 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.autofillservice.cts.common;
+
+import android.content.Context;
+import android.provider.Settings;
+import android.support.annotation.NonNull;
+import android.support.annotation.Nullable;
+
+/**
+ * JUnit rule used to set a {@link Settings} preference before the test is run.
+ *
+ * <p>It stores the current value before the test, changes it (if necessary), then restores it after
+ * the test (if necessary).
+ */
+public class SettingsStateChangerRule extends StateChangerRule<String> {
+
+    /**
+     * Default constructor.
+     *
+     * @param context context used to retrieve the {@link Settings} provider.
+     * @param key prefence key.
+     * @param value value to be set before the test is run.
+     */
+    public SettingsStateChangerRule(@NonNull Context context, @NonNull String key,
+            @Nullable String value) {
+        super(new SettingsStateManager(context, key), value);
+    }
+}
diff --git a/tests/autofillservice/src/android/autofillservice/cts/common/SettingsStateKeeperRule.java b/tests/autofillservice/src/android/autofillservice/cts/common/SettingsStateKeeperRule.java
new file mode 100644
index 0000000..2e8262c
--- /dev/null
+++ b/tests/autofillservice/src/android/autofillservice/cts/common/SettingsStateKeeperRule.java
@@ -0,0 +1,38 @@
+/*
+ * 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.autofillservice.cts.common;
+
+import android.content.Context;
+import android.provider.Settings;
+import android.support.annotation.NonNull;
+
+/**
+ * JUnit rule used to restore a {@link Settings} preference after the test is run.
+ *
+ * <p>It stores the current value before the test, and restores it after the test (if necessary).
+ */
+public class SettingsStateKeeperRule extends StateKeeperRule<String> {
+
+    /**
+     * Default constructor.
+     *
+     * @param context context used to retrieve the {@link Settings} provider.
+     * @param key prefence key.
+     */
+    public SettingsStateKeeperRule(@NonNull Context context, @NonNull String key) {
+        super(new SettingsStateManager(context, key));
+    }
+}
diff --git a/tests/autofillservice/src/android/autofillservice/cts/common/SettingsStateManager.java b/tests/autofillservice/src/android/autofillservice/cts/common/SettingsStateManager.java
new file mode 100644
index 0000000..057c3b0
--- /dev/null
+++ b/tests/autofillservice/src/android/autofillservice/cts/common/SettingsStateManager.java
@@ -0,0 +1,55 @@
+/*
+ * 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.autofillservice.cts.common;
+
+import android.content.Context;
+import android.provider.Settings;
+import android.support.annotation.NonNull;
+import android.support.annotation.Nullable;
+
+import com.google.common.base.Preconditions;
+
+/**
+ * Manages the state of a preference backed by {@link Settings}.
+ */
+public class SettingsStateManager implements StateManager<String> {
+
+    private final Context mContext;
+    private final String mKey;
+
+    /**
+     * Default constructor.
+     *
+     * @param context context used to retrieve the {@link Settings} provider.
+     * @param key prefence key.
+     */
+    public SettingsStateManager(@NonNull Context context, @NonNull String key) {
+        mContext = Preconditions.checkNotNull(context);
+        mKey = Preconditions.checkNotNull(key);
+    }
+
+    @Override
+    public void set(@Nullable String value) {
+        SettingsHelper.syncSet(mContext, mKey, value);
+    }
+
+    @Override
+    @Nullable
+    public String get() {
+        return SettingsHelper.get(mKey);
+    }
+}
diff --git a/tests/autofillservice/src/android/autofillservice/cts/common/ShellHelper.java b/tests/autofillservice/src/android/autofillservice/cts/common/ShellHelper.java
new file mode 100644
index 0000000..3e15af2
--- /dev/null
+++ b/tests/autofillservice/src/android/autofillservice/cts/common/ShellHelper.java
@@ -0,0 +1,52 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.autofillservice.cts.common;
+
+import android.support.annotation.NonNull;
+import android.support.test.InstrumentationRegistry;
+import android.text.TextUtils;
+import android.util.Log;
+
+import com.android.compatibility.common.util.SystemUtil;
+
+/**
+ * Provides Shell-based utilities such as running a command.
+ */
+public final class ShellHelper {
+
+    private static final String TAG = "ShellHelper";
+
+    /**
+     * Runs a Shell command, returning a trimmed response.
+     */
+    @NonNull
+    public static String runShellCommand(@NonNull String template, Object...args) {
+        final String command = String.format(template, args);
+        Log.d(TAG, "runShellCommand(): " + command);
+        try {
+            final String result = SystemUtil
+                    .runShellCommand(InstrumentationRegistry.getInstrumentation(), command);
+            return TextUtils.isEmpty(result) ? "" : result.trim();
+        } catch (Exception e) {
+            throw new RuntimeException("Command '" + command + "' failed: ", e);
+        }
+    }
+
+    private ShellHelper() {
+        throw new UnsupportedOperationException("contain static methods only");
+    }
+}
diff --git a/tests/autofillservice/src/android/autofillservice/cts/common/StateChangerRule.java b/tests/autofillservice/src/android/autofillservice/cts/common/StateChangerRule.java
new file mode 100644
index 0000000..8e94e2d
--- /dev/null
+++ b/tests/autofillservice/src/android/autofillservice/cts/common/StateChangerRule.java
@@ -0,0 +1,73 @@
+/*
+ * 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.autofillservice.cts.common;
+
+import android.support.annotation.NonNull;
+import android.support.annotation.Nullable;
+
+import com.google.common.base.Preconditions;
+
+import org.junit.rules.TestRule;
+import org.junit.runner.Description;
+import org.junit.runners.model.Statement;
+
+import java.util.Objects;
+
+/**
+ * JUnit rule used to prepare a state before the test is run.
+ *
+ * <p>It stores the current state before the test, changes it (if necessary), then restores it after
+ * the test (if necessary).
+ */
+public class StateChangerRule<T> implements TestRule {
+
+    private final StateManager<T> mStateManager;
+    private final T mValue;
+
+    /**
+     * Default constructor.
+     *
+     * @param stateManager abstraction used to mange the state.
+     * @param value value to be set before the test is run.
+     */
+    public StateChangerRule(@NonNull StateManager<T> stateManager, @Nullable T value) {
+        mStateManager = Preconditions.checkNotNull(stateManager);
+        mValue = value;
+    }
+
+    @Override
+    public Statement apply(Statement base, Description description) {
+        return new Statement() {
+
+            @Override
+            public void evaluate() throws Throwable {
+                final T previousValue = mStateManager.get();
+                if (!Objects.equals(previousValue, mValue)) {
+                    mStateManager.set(mValue);
+                }
+                try {
+                    base.evaluate();
+                } finally {
+                    final T currentValue = mStateManager.get();
+                    if (!Objects.equals(currentValue, previousValue)) {
+                        mStateManager.set(previousValue);
+                    }
+                }
+            }
+        };
+    }
+}
diff --git a/tests/autofillservice/src/android/autofillservice/cts/common/StateChangerRuleTest.java b/tests/autofillservice/src/android/autofillservice/cts/common/StateChangerRuleTest.java
new file mode 100644
index 0000000..9c2018e
--- /dev/null
+++ b/tests/autofillservice/src/android/autofillservice/cts/common/StateChangerRuleTest.java
@@ -0,0 +1,173 @@
+/*
+ * 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.autofillservice.cts.common;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import static org.mockito.ArgumentMatchers.anyString;
+import static org.mockito.Mockito.doThrow;
+import static org.mockito.Mockito.never;
+import static org.mockito.Mockito.times;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.verifyNoMoreInteractions;
+import static org.mockito.Mockito.when;
+import static org.testng.Assert.assertThrows;
+import static org.testng.Assert.expectThrows;
+
+import org.junit.Test;
+import org.junit.runner.Description;
+import org.junit.runner.RunWith;
+import org.junit.runners.model.Statement;
+import org.mockito.Mock;
+import org.mockito.junit.MockitoJUnitRunner;
+
+@RunWith(MockitoJUnitRunner.class)
+public class StateChangerRuleTest {
+
+    private final RuntimeException mRuntimeException = new RuntimeException("D'OH");
+    private final Description mDescription = Description.createSuiteDescription("Whatever");
+
+    @Mock
+    private StateManager<String> mStateManager;
+
+    @Mock
+    private Statement mStatement;
+
+    @Test
+    public void testInvalidConstructor() {
+        assertThrows(NullPointerException.class,
+                () -> new StateChangerRule<Object>(null, "value"));
+    }
+
+    @Test
+    public void testSetAndRestoreOnSuccess() throws Throwable {
+        final StateChangerRule<String> rule = new StateChangerRule<>(mStateManager,
+                "newValue");
+        when(mStateManager.get()).thenReturn("before", "changed");
+
+        rule.apply(mStatement, mDescription).evaluate();
+
+        verify(mStatement, times(1)).evaluate();
+        verify(mStateManager, times(2)).get(); // Needed because of verifyNoMoreInteractions()
+        verify(mStateManager, times(1)).set("newValue");
+        verify(mStateManager, times(1)).set("before");
+        verifyNoMoreInteractions(mStateManager); // Make sure set() was not called again
+    }
+
+    @Test
+    public void testDontSetIfSameValueOnSuccess() throws Throwable {
+        final StateChangerRule<String> rule = new StateChangerRule<>(mStateManager,
+                "sameValue");
+        when(mStateManager.get()).thenReturn("sameValue");
+
+        rule.apply(mStatement, mDescription).evaluate();
+
+        verify(mStatement, times(1)).evaluate();
+        verify(mStateManager, never()).set(anyString());
+    }
+
+    @Test
+    public void testSetButDontRestoreIfSameValueOnSuccess() throws Throwable {
+        final StateChangerRule<String> rule = new StateChangerRule<>(mStateManager,
+                "newValue");
+        when(mStateManager.get()).thenReturn("before", "before");
+
+        rule.apply(mStatement, mDescription).evaluate();
+
+        verify(mStatement, times(1)).evaluate();
+        verify(mStateManager, times(2)).get(); // Needed because of verifyNoMoreInteractions()
+        verify(mStateManager, times(1)).set("newValue");
+        verifyNoMoreInteractions(mStateManager); // Make sure set() was not called again
+    }
+
+    @Test
+    public void testDontSetButRestoreIfValueChangedOnSuccess() throws Throwable {
+        final StateChangerRule<String> rule = new StateChangerRule<>(mStateManager,
+                "sameValue");
+        when(mStateManager.get()).thenReturn("sameValue", "changed");
+
+        rule.apply(mStatement, mDescription).evaluate();
+
+        verify(mStateManager, times(2)).get(); // Needed because of verifyNoMoreInteractions()
+        verify(mStatement, times(1)).evaluate();
+        verify(mStateManager, times(1)).set("sameValue");
+        verifyNoMoreInteractions(mStateManager); // Make sure set() was not called again
+    }
+
+    @Test
+    public void testSetAndRestoreOnFailure() throws Throwable {
+        final StateChangerRule<String> rule = new StateChangerRule<>(mStateManager,
+                "newValue");
+        when(mStateManager.get()).thenReturn("before", "changed");
+        doThrow(mRuntimeException).when(mStatement).evaluate();
+
+        final RuntimeException actualException = expectThrows(RuntimeException.class,
+                () -> rule.apply(mStatement, mDescription).evaluate());
+        assertThat(actualException).isSameAs(mRuntimeException);
+
+        verify(mStateManager, times(2)).get(); // Needed because of verifyNoMoreInteractions()
+        verify(mStateManager, times(1)).set("newValue");
+        verify(mStateManager, times(1)).set("before");
+        verifyNoMoreInteractions(mStateManager); // Make sure set() was not called again
+    }
+
+    @Test
+    public void testDontSetIfSameValueOnFailure() throws Throwable {
+        final StateChangerRule<String> rule = new StateChangerRule<>(mStateManager,
+                "sameValue");
+        when(mStateManager.get()).thenReturn("sameValue");
+        doThrow(mRuntimeException).when(mStatement).evaluate();
+
+        final RuntimeException actualException = expectThrows(RuntimeException.class,
+                () -> rule.apply(mStatement, mDescription).evaluate());
+        assertThat(actualException).isSameAs(mRuntimeException);
+
+        verify(mStateManager, never()).set(anyString());
+    }
+
+    @Test
+    public void testSetButDontRestoreIfSameValueOnFailure() throws Throwable {
+        final StateChangerRule<String> rule = new StateChangerRule<>(mStateManager,
+                "newValue");
+        when(mStateManager.get()).thenReturn("before", "before");
+        doThrow(mRuntimeException).when(mStatement).evaluate();
+
+        final RuntimeException actualException = expectThrows(RuntimeException.class,
+                () -> rule.apply(mStatement, mDescription).evaluate());
+        assertThat(actualException).isSameAs(mRuntimeException);
+
+        verify(mStateManager, times(2)).get(); // Needed because of verifyNoMoreInteractions()
+        verify(mStateManager, times(1)).set("newValue");
+        verifyNoMoreInteractions(mStateManager); // Make sure set() was not called again
+    }
+
+    @Test
+    public void testDontSetButRestoreIfValueChangedOnFailure() throws Throwable {
+        final StateChangerRule<String> rule = new StateChangerRule<>(mStateManager,
+                "sameValue");
+        when(mStateManager.get()).thenReturn("sameValue", "changed");
+        doThrow(mRuntimeException).when(mStatement).evaluate();
+
+        final RuntimeException actualException = expectThrows(RuntimeException.class,
+                () -> rule.apply(mStatement, mDescription).evaluate());
+        assertThat(actualException).isSameAs(mRuntimeException);
+
+        verify(mStateManager, times(2)).get(); // Needed because of verifyNoMoreInteractions()
+        verify(mStateManager, times(1)).set("sameValue");
+        verifyNoMoreInteractions(mStateManager); // Make sure set() was not called again
+    }
+}
diff --git a/tests/autofillservice/src/android/autofillservice/cts/common/StateKeeperRule.java b/tests/autofillservice/src/android/autofillservice/cts/common/StateKeeperRule.java
new file mode 100644
index 0000000..4d5ced7
--- /dev/null
+++ b/tests/autofillservice/src/android/autofillservice/cts/common/StateKeeperRule.java
@@ -0,0 +1,65 @@
+/*
+ * 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.autofillservice.cts.common;
+
+import android.support.annotation.NonNull;
+
+import com.google.common.base.Preconditions;
+
+import org.junit.rules.TestRule;
+import org.junit.runner.Description;
+import org.junit.runners.model.Statement;
+
+import java.util.Objects;
+
+/**
+ * JUnit rule used to restore a state after the test is run.
+ *
+ * <p>It stores the current state before the test, and restores it after the test (if necessary).
+ */
+public class StateKeeperRule<T> implements TestRule {
+
+    private final StateManager<T> mStateManager;
+
+    /**
+     * Default constructor.
+     *
+     * @param stateManager abstraction used to mange the state.
+     */
+    public StateKeeperRule(@NonNull StateManager<T> stateManager) {
+        mStateManager = Preconditions.checkNotNull(stateManager);
+    }
+
+    @Override
+    public Statement apply(Statement base, Description description) {
+        return new Statement() {
+
+            @Override
+            public void evaluate() throws Throwable {
+                final T previousValue = mStateManager.get();
+                try {
+                    base.evaluate();
+                } finally {
+                    final T currentValue = mStateManager.get();
+                    if (!Objects.equals(previousValue, currentValue)) {
+                        mStateManager.set(previousValue);
+                    }
+                }
+            }
+        };
+    }
+}
diff --git a/tests/autofillservice/src/android/autofillservice/cts/common/StateKeeperRuleTest.java b/tests/autofillservice/src/android/autofillservice/cts/common/StateKeeperRuleTest.java
new file mode 100644
index 0000000..6424d3c
--- /dev/null
+++ b/tests/autofillservice/src/android/autofillservice/cts/common/StateKeeperRuleTest.java
@@ -0,0 +1,107 @@
+/*
+ * 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.autofillservice.cts.common;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import static org.mockito.ArgumentMatchers.anyString;
+import static org.mockito.Mockito.doThrow;
+import static org.mockito.Mockito.never;
+import static org.mockito.Mockito.times;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.verifyNoMoreInteractions;
+import static org.mockito.Mockito.when;
+import static org.testng.Assert.assertThrows;
+import static org.testng.Assert.expectThrows;
+
+import org.junit.Test;
+import org.junit.runner.Description;
+import org.junit.runner.RunWith;
+import org.junit.runners.model.Statement;
+import org.mockito.Mock;
+import org.mockito.junit.MockitoJUnitRunner;
+
+@RunWith(MockitoJUnitRunner.class)
+public class StateKeeperRuleTest {
+
+    private final RuntimeException mRuntimeException = new RuntimeException("D'OH");
+    private final Description mDescription = Description.createSuiteDescription("Whatever");
+
+    @Mock
+    private StateManager<String> mStateManager;
+
+    @Mock
+    private Statement mStatement;
+
+    @Test
+    public void testInvalidConstructor() {
+        assertThrows(NullPointerException.class, () -> new StateKeeperRule<Object>(null));
+    }
+
+    @Test
+    public void testRestoreOnSuccess() throws Throwable {
+        final StateKeeperRule<String> rule = new StateKeeperRule<>(mStateManager);
+        when(mStateManager.get()).thenReturn("before", "changed");
+
+        rule.apply(mStatement, mDescription).evaluate();
+
+        verify(mStatement, times(1)).evaluate();
+        verify(mStateManager, times(2)).get(); // Needed because of verifyNoMoreInteractions()
+        verify(mStateManager, times(1)).set("before");
+        verifyNoMoreInteractions(mStateManager); // Make sure set() was not called again
+    }
+
+    @Test
+    public void testRestoreOnFailure() throws Throwable {
+        final StateKeeperRule<String> rule = new StateKeeperRule<>(mStateManager);
+        when(mStateManager.get()).thenReturn("before", "changed");
+        doThrow(mRuntimeException).when(mStatement).evaluate();
+
+        final RuntimeException actualException = expectThrows(RuntimeException.class,
+                () -> rule.apply(mStatement, mDescription).evaluate());
+
+        assertThat(actualException).isSameAs(mRuntimeException);
+        verify(mStateManager, times(2)).get(); // Needed because of verifyNoMoreInteractions()
+        verify(mStateManager, times(1)).set("before");
+        verifyNoMoreInteractions(mStateManager); // Make sure set() was not called again
+    }
+
+    @Test
+    public void testDoNotRestoreWhenNotChanged() throws Throwable {
+        final StateKeeperRule<String> rule = new StateKeeperRule<>(mStateManager);
+        when(mStateManager.get()).thenReturn("not_changed");
+
+        rule.apply(mStatement, mDescription).evaluate();
+
+        verify(mStatement, times(1)).evaluate();
+        verify(mStateManager, never()).set(anyString());
+    }
+
+    @Test
+    public void testDoNotRestoreOnFailure() throws Throwable {
+        final StateKeeperRule<String> rule = new StateKeeperRule<>(mStateManager);
+        when(mStateManager.get()).thenReturn("not_changed");
+        doThrow(mRuntimeException).when(mStatement).evaluate();
+
+        final RuntimeException actualException = expectThrows(RuntimeException.class,
+                () -> rule.apply(mStatement, mDescription).evaluate());
+
+        assertThat(actualException).isSameAs(mRuntimeException);
+
+        verify(mStateManager, never()).set(anyString());
+    }
+}
diff --git a/tests/autofillservice/src/android/autofillservice/cts/common/StateManager.java b/tests/autofillservice/src/android/autofillservice/cts/common/StateManager.java
new file mode 100644
index 0000000..376a555
--- /dev/null
+++ b/tests/autofillservice/src/android/autofillservice/cts/common/StateManager.java
@@ -0,0 +1,34 @@
+/*
+ * 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.autofillservice.cts.common;
+
+import android.support.annotation.Nullable;
+
+/**
+ * Abstraction for a state that is managed somewhere, like Android Settings.
+ */
+public interface StateManager<T> {
+
+    /**
+     * Sets a new state.
+     */
+    void set(@Nullable T value);
+
+    /**
+     * Gets the current state.
+     */
+    @Nullable T get();
+}
diff --git a/tests/backup/Android.mk b/tests/backup/Android.mk
index bb1f8fc..8e9df3b 100644
--- a/tests/backup/Android.mk
+++ b/tests/backup/Android.mk
@@ -21,7 +21,13 @@
 # and when built explicitly put it in the data partition
 LOCAL_MODULE_PATH := $(TARGET_OUT_DATA_APPS)
 
-LOCAL_JAVA_LIBRARIES := android.test.runner telephony-common voip-common org.apache.http.legacy
+LOCAL_JAVA_LIBRARIES := \
+    android.test.runner \
+    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
 
diff --git a/tests/backup/AndroidManifest.xml b/tests/backup/AndroidManifest.xml
index 333045a..28745f3 100644
--- a/tests/backup/AndroidManifest.xml
+++ b/tests/backup/AndroidManifest.xml
@@ -20,6 +20,7 @@
 
     <application>
         <uses-library android:name="android.test.runner" />
+        <uses-library android:name="org.apache.http.legacy" />
     </application>
 
     <instrumentation android:name="android.support.test.runner.AndroidJUnitRunner"
diff --git a/tests/backup/AndroidTest.xml b/tests/backup/AndroidTest.xml
index 015f595..8e0bfa0 100644
--- a/tests/backup/AndroidTest.xml
+++ b/tests/backup/AndroidTest.xml
@@ -15,6 +15,7 @@
   ~ limitations under the License
   -->
 <configuration description="Config for CTS Backup test cases">
+    <option name="test-suite-tag" value="cts" />
     <option name="config-descriptor:metadata" key="component" value="backup" />
     <target_preparer class="com.android.tradefed.targetprep.suite.SuiteApkInstaller">
         <option name="cleanup-apks" value="true" />
diff --git a/tests/backup/app/fullbackup/AndroidManifest.xml b/tests/backup/app/fullbackup/AndroidManifest.xml
index 8161a95..138c774 100644
--- a/tests/backup/app/fullbackup/AndroidManifest.xml
+++ b/tests/backup/app/fullbackup/AndroidManifest.xml
@@ -23,6 +23,8 @@
         android:backupAgent="FullBackupBackupAgent"
         android:label="Android Backup CTS App"
         android:fullBackupOnly="true">
+        <uses-library android:name="android.test.runner" />
+
 
         <activity
             android:name=".MainActivity"
diff --git a/tests/backup/app/keyvalue/AndroidManifest.xml b/tests/backup/app/keyvalue/AndroidManifest.xml
index c99b4b4..3ed302d 100644
--- a/tests/backup/app/keyvalue/AndroidManifest.xml
+++ b/tests/backup/app/keyvalue/AndroidManifest.xml
@@ -22,6 +22,8 @@
         android:allowBackup="true"
         android:backupAgent="android.backup.app.KeyValueBackupAgent"
         android:label="Android Key Value Backup CTS App">
+        <uses-library android:name="android.test.runner" />
+
         <activity
             android:name="android.backup.app.MainActivity"
             android:label="Android Key Value Backup CTS App" >
diff --git a/tests/backup/src/android/backup/cts/BaseBackupCtsTest.java b/tests/backup/src/android/backup/cts/BaseBackupCtsTest.java
index bbd0d26..343f2d9 100644
--- a/tests/backup/src/android/backup/cts/BaseBackupCtsTest.java
+++ b/tests/backup/src/android/backup/cts/BaseBackupCtsTest.java
@@ -21,14 +21,14 @@
 import android.os.ParcelFileDescriptor;
 import android.test.InstrumentationTestCase;
 
+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;
-import java.util.UUID;
-import java.util.concurrent.TimeUnit;
 
 /**
  * Base class for backup instrumentation tests.
@@ -41,9 +41,14 @@
     private static final String LOCAL_TRANSPORT =
             "android/com.android.internal.backup.LocalTransport";
 
-    private static final int SMALL_LOGCAT_DELAY = 1000;
-
     private boolean isBackupSupported;
+    private LogcatInspector mLogcatInspector =
+            new LogcatInspector() {
+                @Override
+                protected InputStream executeShellCommand(String command) throws IOException {
+                    return executeStreamedShellCommand(getInstrumentation(), command);
+                }
+            };
 
     @Override
     protected void setUp() throws Exception {
@@ -73,55 +78,16 @@
         return output.contains("* " + LOCAL_TRANSPORT);
     }
 
-    /**
-     * Attempts to clear logcat.
-     *
-     * Clearing logcat is known to be unreliable, so this methods also output a unique separator
-     * that can be used to find this point in the log even if clearing failed.
-     * @return a unique separator string
-     * @throws Exception
-     */
-    protected String clearLogcat() throws Exception {
-        exec("logcat -c");
-        String uniqueString = ":::" + UUID.randomUUID().toString();
-        exec("log -t " + APP_LOG_TAG + " " + uniqueString);
-        return uniqueString;
+    /** See {@link LogcatInspector#mark(String)}. */
+    protected String markLogcat() throws Exception {
+        return mLogcatInspector.mark(APP_LOG_TAG);
     }
 
-    /**
-     * Wait for up to maxTimeoutInSeconds for the given strings to appear in the logcat in the given order.
-     * By passing the separator returned by {@link #clearLogcat} as the first string you can ensure that only
-     * logs emitted after that call to clearLogcat are found.
-     *
-     * @throws AssertionError if the strings are not found in the given time.
-     */
+    /** See {@link LogcatInspector#assertLogcatContainsInOrder(String, int, String...)}. */
     protected void waitForLogcat(int maxTimeoutInSeconds, String... logcatStrings)
-        throws Exception {
-        long timeout = System.currentTimeMillis() + TimeUnit.SECONDS.toMillis(maxTimeoutInSeconds);
-        int stringIndex = 0;
-        while (timeout >= System.currentTimeMillis()) {
-            FileInputStream fis = executeStreamedShellCommand(getInstrumentation(),
-                    "logcat -v brief -d " + APP_LOG_TAG + ":* *:S");
-            BufferedReader log = new BufferedReader(new InputStreamReader(fis));
-            String line;
-            stringIndex = 0;
-            while ((line = log.readLine()) != null) {
-                if (line.contains(logcatStrings[stringIndex])) {
-                    stringIndex++;
-                    if (stringIndex >= logcatStrings.length) {
-                        drainAndClose(log);
-                        return;
-                    }
-                }
-            }
-            closeQuietly(log);
-            // In case the key has not been found, wait for the log to update before
-            // performing the next search.
-            Thread.sleep(SMALL_LOGCAT_DELAY);
-        }
-        fail("Couldn't find " + logcatStrings[stringIndex] +
-            (stringIndex > 0 ? " after " + logcatStrings[stringIndex - 1] : "") +
-            " within " + maxTimeoutInSeconds + " seconds ");
+            throws Exception {
+        mLogcatInspector.assertLogcatContainsInOrder(
+                APP_LOG_TAG + ":* *:S", maxTimeoutInSeconds, logcatStrings);
     }
 
     protected void createTestFileOfSize(String packageName, int size) throws Exception {
@@ -145,8 +111,8 @@
         }
     }
 
-    private static FileInputStream executeStreamedShellCommand(Instrumentation instrumentation,
-                                                               String command) throws Exception {
+    private static FileInputStream executeStreamedShellCommand(
+            Instrumentation instrumentation, String command) throws IOException {
         final ParcelFileDescriptor pfd =
                 instrumentation.getUiAutomation().executeShellCommand(command);
         return new ParcelFileDescriptor.AutoCloseInputStream(pfd);
diff --git a/tests/backup/src/android/backup/cts/FullBackupLifecycleTest.java b/tests/backup/src/android/backup/cts/FullBackupLifecycleTest.java
index beefa01..9f81ecd 100644
--- a/tests/backup/src/android/backup/cts/FullBackupLifecycleTest.java
+++ b/tests/backup/src/android/backup/cts/FullBackupLifecycleTest.java
@@ -31,7 +31,7 @@
         if (!isBackupSupported()) {
             return;
         }
-        String backupSeparator = clearLogcat();
+        String backupSeparator = markLogcat();
 
         // Make sure there's something to backup
         createTestFileOfSize(BACKUP_APP_NAME, LOCAL_TRANSPORT_CONFORMING_FILE_SIZE);
@@ -45,7 +45,7 @@
             "Full backup requested",
             "onDestroy");
 
-        String restoreSeparator = clearLogcat();
+        String restoreSeparator = markLogcat();
 
         // Now request restore and wait for it to complete
         exec("bmgr restore " + BACKUP_APP_NAME);
diff --git a/tests/backup/src/android/backup/cts/FullBackupQuotaTest.java b/tests/backup/src/android/backup/cts/FullBackupQuotaTest.java
index 3924e87..56d489d 100644
--- a/tests/backup/src/android/backup/cts/FullBackupQuotaTest.java
+++ b/tests/backup/src/android/backup/cts/FullBackupQuotaTest.java
@@ -36,7 +36,7 @@
         if (!isBackupSupported()) {
             return;
         }
-        String separator = clearLogcat();
+        String separator = markLogcat();
         // Launch test app and create file exceeding limit for local transport
         createTestFileOfSize(BACKUP_APP_NAME, LOCAL_TRANSPORT_EXCEEDING_FILE_SIZE);
 
@@ -59,7 +59,7 @@
             Thread.sleep(3000);
         } catch (InterruptedException e) {}
 
-        String separator = clearLogcat();
+        String separator = markLogcat();
         exec("bmgr backupnow " + 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 d957bbc..0bb5243 100644
--- a/tests/backup/src/android/backup/cts/KeyValueLifecycleTest.java
+++ b/tests/backup/src/android/backup/cts/KeyValueLifecycleTest.java
@@ -31,7 +31,7 @@
         if (!isBackupSupported()) {
             return;
         }
-        String backupSeparator = clearLogcat();
+        String backupSeparator = markLogcat();
 
         // Make sure there's something to backup
         createTestFileOfSize(BACKUP_APP_NAME, LOCAL_TRANSPORT_CONFORMING_FILE_SIZE);
@@ -44,7 +44,7 @@
             "Backup requested",
             "onDestroy");
 
-        String restoreSeparator = clearLogcat();
+        String restoreSeparator = markLogcat();
 
         // Now request restore and wait for it to complete
         exec("bmgr restore " + BACKUP_APP_NAME);
diff --git a/tests/backup/src/android/backup/cts/KeyValueQuotaTest.java b/tests/backup/src/android/backup/cts/KeyValueQuotaTest.java
index c29f810..6fca9ad 100644
--- a/tests/backup/src/android/backup/cts/KeyValueQuotaTest.java
+++ b/tests/backup/src/android/backup/cts/KeyValueQuotaTest.java
@@ -36,7 +36,7 @@
         if (!isBackupSupported()) {
             return;
         }
-        String separator = clearLogcat();
+        String separator = markLogcat();
         // Launch test app and create file exceeding limit for local transport
         createTestFileOfSize(BACKUP_APP_NAME, LOCAL_TRANSPORT_EXCEEDING_FILE_SIZE);
 
@@ -50,7 +50,7 @@
         if (!isBackupSupported()) {
             return;
         }
-        String separator = clearLogcat();
+        String separator = markLogcat();
         exec("bmgr backupnow " + BACKUP_APP_NAME);
         waitForLogcat(TIMEOUT_SECONDS, separator,
             "quota is " + LOCAL_TRANSPORT_BACKUP_QUOTA);
diff --git a/tests/camera/Android.mk b/tests/camera/Android.mk
index c8ba9cd..4160f84 100644
--- a/tests/camera/Android.mk
+++ b/tests/camera/Android.mk
@@ -64,7 +64,7 @@
 
 LOCAL_SDK_VERSION := test_current
 
-LOCAL_JAVA_LIBRARIES := android.test.runner
+LOCAL_JAVA_LIBRARIES := android.test.runner android.test.base.stubs
 
 cts_runtime_hint := 120
 
diff --git a/tests/camera/AndroidTest.xml b/tests/camera/AndroidTest.xml
index c046a59..2a2cdc9 100644
--- a/tests/camera/AndroidTest.xml
+++ b/tests/camera/AndroidTest.xml
@@ -14,6 +14,7 @@
      limitations under the License.
 -->
 <configuration description="Config for CTS Camera test cases">
+    <option name="test-suite-tag" value="cts" />
     <option name="config-descriptor:metadata" key="component" value="camera" />
     <target_preparer class="com.android.tradefed.targetprep.suite.SuiteApkInstaller">
         <option name="cleanup-apks" value="true" />
diff --git a/tests/camera/api25test/AndroidTest.xml b/tests/camera/api25test/AndroidTest.xml
index a7b2ae7..45f5d28 100644
--- a/tests/camera/api25test/AndroidTest.xml
+++ b/tests/camera/api25test/AndroidTest.xml
@@ -14,6 +14,7 @@
      limitations under the License.
 -->
 <configuration description="Config for CTS Camera API 25 test cases">
+    <option name="test-suite-tag" value="cts" />
     <option name="config-descriptor:metadata" key="component" value="camera" />
     <option name="not-shardable" value="true" />
     <target_preparer class="com.android.tradefed.targetprep.suite.SuiteApkInstaller">
diff --git a/tests/camera/libctscamera2jni/Android.mk b/tests/camera/libctscamera2jni/Android.mk
index 649908d..2cd4f64 100644
--- a/tests/camera/libctscamera2jni/Android.mk
+++ b/tests/camera/libctscamera2jni/Android.mk
@@ -44,7 +44,7 @@
     libz \
 
 # NDK build, shared C++ runtime
-LOCAL_SDK_VERSION := 24
+LOCAL_SDK_VERSION := current
 LOCAL_NDK_STL_VARIANT := c++_shared
 
 include $(BUILD_SHARED_LIBRARY)
diff --git a/tests/camera/libctscamera2jni/native-camera-jni.cpp b/tests/camera/libctscamera2jni/native-camera-jni.cpp
index b2124ce..1a35a9f 100644
--- a/tests/camera/libctscamera2jni/native-camera-jni.cpp
+++ b/tests/camera/libctscamera2jni/native-camera-jni.cpp
@@ -18,9 +18,12 @@
 #define LOG_TAG "NativeCamera"
 #include <log/log.h>
 
+#include <chrono>
+#include <condition_variable>
 #include <string>
 #include <map>
 #include <mutex>
+#include <vector>
 #include <unistd.h>
 #include <assert.h>
 #include <jni.h>
@@ -238,6 +241,173 @@
     int mOnActive = 0;
 };
 
+class CaptureResultListener {
+  public:
+    ~CaptureResultListener() {
+        std::unique_lock<std::mutex> l(mMutex);
+        clearSavedRequestsLocked();
+    }
+
+    static void onCaptureStart(void* /*obj*/, ACameraCaptureSession* /*session*/,
+            const ACaptureRequest* /*request*/, int64_t /*timestamp*/) {
+        //Not used for now
+    }
+
+    static void onCaptureProgressed(void* /*obj*/, ACameraCaptureSession* /*session*/,
+            ACaptureRequest* /*request*/, const ACameraMetadata* /*result*/) {
+        //Not used for now
+    }
+
+    static void onCaptureCompleted(void* obj, ACameraCaptureSession* /*session*/,
+            ACaptureRequest* request, const ACameraMetadata* result) {
+        ALOGV("%s", __FUNCTION__);
+        if ((obj == nullptr) || (result == nullptr)) {
+            return;
+        }
+        CaptureResultListener* thiz = reinterpret_cast<CaptureResultListener*>(obj);
+        std::lock_guard<std::mutex> lock(thiz->mMutex);
+        ACameraMetadata_const_entry entry;
+        auto ret = ACameraMetadata_getConstEntry(result, ACAMERA_SYNC_FRAME_NUMBER, &entry);
+        if (ret != ACAMERA_OK) {
+            ALOGE("Error: Sync frame number missing from result!");
+            return;
+        }
+
+        if (thiz->mSaveCompletedRequests) {
+            thiz->mCompletedRequests.push_back(ACaptureRequest_copy(request));
+        }
+
+        thiz->mLastCompletedFrameNumber = entry.data.i64[0];
+        thiz->mResultCondition.notify_one();
+    }
+
+    static void onCaptureFailed(void* obj, ACameraCaptureSession* /*session*/,
+            ACaptureRequest* /*request*/, ACameraCaptureFailure* failure) {
+        ALOGV("%s", __FUNCTION__);
+        if ((obj == nullptr) || (failure == nullptr)) {
+            return;
+        }
+        CaptureResultListener* thiz = reinterpret_cast<CaptureResultListener*>(obj);
+        std::lock_guard<std::mutex> lock(thiz->mMutex);
+        thiz->mLastFailedFrameNumber = failure->frameNumber;
+        thiz->mResultCondition.notify_one();
+    }
+
+    static void onCaptureSequenceCompleted(void* obj, ACameraCaptureSession* /*session*/,
+            int sequenceId, int64_t frameNumber) {
+        ALOGV("%s", __FUNCTION__);
+        if (obj == nullptr) {
+            return;
+        }
+        CaptureResultListener* thiz = reinterpret_cast<CaptureResultListener*>(obj);
+        std::lock_guard<std::mutex> lock(thiz->mMutex);
+        thiz->mLastSequenceIdCompleted = sequenceId;
+        thiz->mLastSequenceFrameNumber = frameNumber;
+        thiz->mResultCondition.notify_one();
+    }
+
+    static void onCaptureSequenceAborted(void* /*obj*/, ACameraCaptureSession* /*session*/,
+            int /*sequenceId*/) {
+        //Not used for now
+    }
+
+    static void onCaptureBufferLost(void* obj, ACameraCaptureSession* /*session*/,
+            ACaptureRequest* /*request*/, ANativeWindow* /*window*/, int64_t frameNumber) {
+        ALOGV("%s", __FUNCTION__);
+        if (obj == nullptr) {
+            return;
+        }
+        CaptureResultListener* thiz = reinterpret_cast<CaptureResultListener*>(obj);
+        std::lock_guard<std::mutex> lock(thiz->mMutex);
+        thiz->mLastLostFrameNumber = frameNumber;
+        thiz->mResultCondition.notify_one();
+    }
+
+    int64_t getCaptureSequenceLastFrameNumber(int64_t sequenceId, uint32_t timeoutSec) {
+        int64_t ret = -1;
+        std::unique_lock<std::mutex> l(mMutex);
+
+        while (mLastSequenceIdCompleted != sequenceId) {
+            auto timeout = std::chrono::system_clock::now() +
+                           std::chrono::seconds(timeoutSec);
+            if (std::cv_status::timeout == mResultCondition.wait_until(l, timeout)) {
+                break;
+            }
+        }
+
+        if (mLastSequenceIdCompleted == sequenceId) {
+            ret = mLastSequenceFrameNumber;
+        }
+
+        return ret;
+    }
+
+    bool waitForFrameNumber(int64_t frameNumber, uint32_t timeoutSec) {
+        bool ret = false;
+        std::unique_lock<std::mutex> l(mMutex);
+
+        while ((mLastCompletedFrameNumber != frameNumber) &&
+                (mLastLostFrameNumber != frameNumber) &&
+                (mLastFailedFrameNumber != frameNumber)) {
+            auto timeout = std::chrono::system_clock::now() +
+                           std::chrono::seconds(timeoutSec);
+            if (std::cv_status::timeout == mResultCondition.wait_until(l, timeout)) {
+                break;
+            }
+        }
+
+        if ((mLastCompletedFrameNumber == frameNumber) ||
+                (mLastLostFrameNumber == frameNumber) ||
+                (mLastFailedFrameNumber == frameNumber)) {
+            ret = true;
+        }
+
+        return ret;
+    }
+
+    void setRequestSave(bool enable) {
+        std::unique_lock<std::mutex> l(mMutex);
+        if (!enable) {
+            clearSavedRequestsLocked();
+        }
+        mSaveCompletedRequests = enable;
+    }
+
+    // The lifecycle of returned ACaptureRequest* is still managed by CaptureResultListener
+    void getCompletedRequests(std::vector<ACaptureRequest*>* out) {
+        std::unique_lock<std::mutex> l(mMutex);
+        *out = mCompletedRequests;
+    }
+
+    void reset() {
+        std::lock_guard<std::mutex> lock(mMutex);
+        mLastSequenceIdCompleted = -1;
+        mLastSequenceFrameNumber = -1;
+        mLastCompletedFrameNumber = -1;
+        mLastLostFrameNumber = -1;
+        mLastFailedFrameNumber = -1;
+        mSaveCompletedRequests = false;
+        clearSavedRequestsLocked();
+    }
+
+  private:
+    std::mutex mMutex;
+    std::condition_variable mResultCondition;
+    int mLastSequenceIdCompleted = -1;
+    int64_t mLastSequenceFrameNumber = -1;
+    int64_t mLastCompletedFrameNumber = -1;
+    int64_t mLastLostFrameNumber = -1;
+    int64_t mLastFailedFrameNumber = -1;
+    bool    mSaveCompletedRequests = false;
+    std::vector<ACaptureRequest*> mCompletedRequests;
+
+    void clearSavedRequestsLocked() {
+        for (ACaptureRequest* req : mCompletedRequests) {
+            ACaptureRequest_free(req);
+        }
+        mCompletedRequests.clear();
+    }
+};
 
 class ImageReaderListener {
   public:
@@ -401,6 +571,7 @@
     // Free all resources except camera manager
     void resetCamera() {
         mSessionListener.reset();
+        mResultListener.reset();
         if (mSession) {
             ACameraCaptureSession_close(mSession);
             mSession = nullptr;
@@ -502,6 +673,16 @@
         return mCameraIdList->cameraIds[idx];
     }
 
+    camera_status_t updateOutput(JNIEnv* env, ACaptureSessionOutput *output) {
+        if (mSession == nullptr) {
+            ALOGE("Testcase cannot update output configuration session %p",
+                    mSession);
+            return ACAMERA_ERROR_UNKNOWN;
+        }
+
+        return ACameraCaptureSession_updateSharedOutput(mSession, output);
+    }
+
     camera_status_t openCamera(const char* cameraId) {
         if (mDevice) {
             ALOGE("Cannot open camera before closing previously open one");
@@ -573,7 +754,8 @@
         return mPreviewAnw;
     }
 
-    camera_status_t createCaptureSessionWithLog() {
+    camera_status_t createCaptureSessionWithLog(bool isPreviewShared = false,
+            ACaptureRequest *sessionParameters = nullptr) {
         if (mSession) {
             LOG_ERROR(errorString, "Cannot create session before closing existing one");
             return ACAMERA_ERROR_UNKNOWN;
@@ -611,7 +793,11 @@
         }
 
         if (mPreviewInited) {
-            ret = ACaptureSessionOutput_create(mPreviewAnw, &mPreviewOutput);
+            if (isPreviewShared) {
+                ret = ACaptureSessionSharedOutput_create(mPreviewAnw, &mPreviewOutput);
+            } else {
+                ret = ACaptureSessionOutput_create(mPreviewAnw, &mPreviewOutput);
+            }
             if (ret != ACAMERA_OK || mPreviewOutput == nullptr) {
                 LOG_ERROR(errorString,
                         "Sesssion preview output create fail! ret %d output %p",
@@ -629,8 +815,8 @@
             }
         }
 
-        ret = ACameraDevice_createCaptureSession(
-                mDevice, mOutputs, &mSessionCb, &mSession);
+        ret = ACameraDevice_createCaptureSessionWithSessionParameters(
+                mDevice, mOutputs, sessionParameters, &mSessionCb, &mSession);
         if (ret != ACAMERA_OK || mSession == nullptr) {
             LOG_ERROR(errorString, "Create session for camera %s failed. ret %d session %p",
                     mCameraId, ret, mSession);
@@ -743,15 +929,69 @@
         return ACAMERA_OK;
     }
 
-    camera_status_t startPreview() {
+    // The output ACaptureRequest* is still managed by testcase class
+    camera_status_t getStillRequest(ACaptureRequest** out) {
+        if (mStillRequest == nullptr) {
+            ALOGE("Camera %s Still capture request hasn't been created", mCameraId);
+            return ACAMERA_ERROR_INVALID_PARAMETER;
+        }
+        *out = mStillRequest;
+        return ACAMERA_OK;
+    }
+
+    camera_status_t getPreviewRequest(ACaptureRequest** out) {
+        if (mPreviewRequest == nullptr) {
+            ALOGE("Camera %s Preview capture request hasn't been created", mCameraId);
+            return ACAMERA_ERROR_INVALID_PARAMETER;
+        }
+        *out = mPreviewRequest;
+        return ACAMERA_OK;
+    }
+
+    camera_status_t startPreview(int *sequenceId = nullptr) {
         if (mSession == nullptr || mPreviewRequest == nullptr) {
             ALOGE("Testcase cannot start preview: session %p, preview request %p",
                     mSession, mPreviewRequest);
             return ACAMERA_ERROR_UNKNOWN;
         }
         int previewSeqId;
-        return ACameraCaptureSession_setRepeatingRequest(
-                mSession, nullptr, 1, &mPreviewRequest, &previewSeqId);
+        camera_status_t ret;
+        if (sequenceId == nullptr) {
+            ret = ACameraCaptureSession_setRepeatingRequest(
+                   mSession, nullptr, 1, &mPreviewRequest, &previewSeqId);
+        } else {
+            ret = ACameraCaptureSession_setRepeatingRequest(
+                   mSession, &mResultCb, 1, &mPreviewRequest, sequenceId);
+        }
+        return ret;
+    }
+
+    camera_status_t updateRepeatingRequest(ACaptureRequest *updatedRequest,
+            int *sequenceId = nullptr) {
+        if (mSession == nullptr || updatedRequest == nullptr) {
+            ALOGE("Testcase cannot update repeating request: session %p, updated request %p",
+                    mSession, updatedRequest);
+            return ACAMERA_ERROR_UNKNOWN;
+        }
+
+        int previewSeqId;
+        camera_status_t ret;
+        if (sequenceId == nullptr) {
+            ret = ACameraCaptureSession_setRepeatingRequest(
+                    mSession, nullptr, 1, &updatedRequest, &previewSeqId);
+        } else {
+            ret = ACameraCaptureSession_setRepeatingRequest(
+                    mSession, &mResultCb, 1, &updatedRequest, sequenceId);
+        }
+        return ret;
+    }
+
+    int64_t getCaptureSequenceLastFrameNumber(int64_t sequenceId, uint32_t timeoutSec) {
+        return mResultListener.getCaptureSequenceLastFrameNumber(sequenceId, timeoutSec);
+    }
+
+    bool waitForFrameNumber(int64_t frameNumber, uint32_t timeoutSec) {
+        return mResultListener.waitForFrameNumber(frameNumber, timeoutSec);
     }
 
     camera_status_t takePicture() {
@@ -765,6 +1005,18 @@
                 mSession, nullptr, 1, &mStillRequest, &seqId);
     }
 
+    camera_status_t capture(ACaptureRequest* request,
+            ACameraCaptureSession_captureCallbacks* listener,
+            /*out*/int* seqId) {
+        if (mSession == nullptr || request == nullptr) {
+            ALOGE("Testcase cannot capture session: session %p, request %p",
+                    mSession, request);
+            return ACAMERA_ERROR_UNKNOWN;
+        }
+        return ACameraCaptureSession_capture(
+                mSession, listener, 1, &request, seqId);
+    }
+
     camera_status_t resetWithErrorLog() {
         camera_status_t ret;
 
@@ -785,6 +1037,7 @@
             return ACAMERA_ERROR_UNKNOWN;
         }
         mSessionListener.reset();
+        mResultListener.reset();
 
         ret = closeCamera();
         if (ret != ACAMERA_OK) {
@@ -800,6 +1053,14 @@
         return &mSessionListener;
     }
 
+    ACameraDevice* getCameraDevice() {
+        return mDevice;
+    }
+
+    ACaptureSessionOutput *getPreviewOutput() {
+        return mPreviewOutput;
+    }
+
   private:
     ACameraManager* createManager() {
         if (!mCameraManager) {
@@ -828,6 +1089,18 @@
         CaptureSessionListener::onActive
     };
 
+    CaptureResultListener mResultListener;
+    ACameraCaptureSession_captureCallbacks mResultCb {
+        &mResultListener,
+        CaptureResultListener::onCaptureStart,
+        CaptureResultListener::onCaptureProgressed,
+        CaptureResultListener::onCaptureCompleted,
+        CaptureResultListener::onCaptureFailed,
+        CaptureResultListener::onCaptureSequenceCompleted,
+        CaptureResultListener::onCaptureSequenceAborted,
+        CaptureResultListener::onCaptureBufferLost
+    };
+
     ACameraIdList* mCameraIdList = nullptr;
     ACameraDevice* mDevice = nullptr;
     AImageReader* mImgReader = nullptr;
@@ -1241,6 +1514,34 @@
                 }
             }
 
+            void* context = nullptr;
+            ret = ACaptureRequest_getUserContext(request, &context);
+            if (ret != ACAMERA_OK) {
+                LOG_ERROR(errorString, "Get capture request context failed: ret %d", ret);
+                goto cleanup;
+            }
+            if (context != nullptr) {
+                LOG_ERROR(errorString, "Capture request context is not null: %p", context);
+                goto cleanup;
+            }
+
+            intptr_t magic_num = 0xBEEF;
+            ret = ACaptureRequest_setUserContext(request, (void*) magic_num);
+            if (ret != ACAMERA_OK) {
+                LOG_ERROR(errorString, "Set capture request context failed: ret %d", ret);
+                goto cleanup;
+            }
+
+            ret = ACaptureRequest_getUserContext(request, &context);
+            if (ret != ACAMERA_OK) {
+                LOG_ERROR(errorString, "Get capture request context failed: ret %d", ret);
+                goto cleanup;
+            }
+            if (context != (void*) magic_num) {
+                LOG_ERROR(errorString, "Capture request context is wrong: %p", context);
+                goto cleanup;
+            }
+
             // try get/set capture request fields
             ACameraMetadata_const_entry entry;
             ret = ACaptureRequest_getConstEntry(request, ACAMERA_CONTROL_AE_MODE, &entry);
@@ -1483,6 +1784,263 @@
 
 extern "C" jboolean
 Java_android_hardware_camera2_cts_NativeCameraDeviceTest_\
+testCameraDeviceSharedOutputUpdate(
+        JNIEnv* env, jclass /*clazz*/, jobject jPreviewSurface, jobject jSharedSurface) {
+    ALOGV("%s", __FUNCTION__);
+    int numCameras = 0;
+    bool pass = false;
+    PreviewTestCase testCase;
+    int sequenceId = -1;
+    int64_t lastFrameNumber = 0;
+    bool frameArrived = false;
+    ANativeWindow* previewAnw = nullptr;
+    ANativeWindow* sharedAnw = ANativeWindow_fromSurface(env, jSharedSurface);
+    ACaptureRequest* updatedRequest = nullptr;
+    ACameraOutputTarget* reqPreviewOutput = nullptr;
+    ACameraOutputTarget* reqSharedOutput = nullptr;
+    ACaptureSessionOutput *previewOutput = nullptr;
+    uint32_t timeoutSec = 1;
+    uint32_t runPreviewSec = 2;
+
+    camera_status_t ret = testCase.initWithErrorLog();
+    if (ret != ACAMERA_OK) {
+        // Don't log error here. testcase did it
+        goto cleanup;
+    }
+
+    numCameras = testCase.getNumCameras();
+    if (numCameras < 0) {
+        LOG_ERROR(errorString, "Testcase returned negavtive number of cameras: %d", numCameras);
+        goto cleanup;
+    }
+
+    for (int i = 0; i < numCameras; i++) {
+        const char* cameraId = testCase.getCameraId(i);
+        if (cameraId == nullptr) {
+            LOG_ERROR(errorString, "Testcase returned null camera id for camera %d", i);
+            goto cleanup;
+        }
+
+        ret = testCase.openCamera(cameraId);
+        if (ret != ACAMERA_OK) {
+            LOG_ERROR(errorString, "Open camera device %s failure. ret %d", cameraId, ret);
+            goto cleanup;
+        }
+
+        usleep(100000); // sleep to give some time for callbacks to happen
+
+        if (testCase.isCameraAvailable(cameraId)) {
+            LOG_ERROR(errorString, "Camera %s should be unavailable now", cameraId);
+            goto cleanup;
+        }
+
+        previewAnw = testCase.initPreviewAnw(env, jPreviewSurface);
+        if (previewAnw == nullptr) {
+            LOG_ERROR(errorString, "Null ANW from preview surface!");
+            goto cleanup;
+        }
+
+        ret = testCase.createCaptureSessionWithLog(true);
+        if (ret != ACAMERA_OK) {
+            // Don't log error here. testcase did it
+            goto cleanup;
+        }
+
+        ret = testCase.createRequestsWithErrorLog();
+        if (ret != ACAMERA_OK) {
+            // Don't log error here. testcase did it
+            goto cleanup;
+        }
+
+        ret = testCase.startPreview();
+        if (ret != ACAMERA_OK) {
+            LOG_ERROR(errorString, "Start preview failed!");
+            goto cleanup;
+        }
+
+        sleep(runPreviewSec);
+
+        previewOutput = testCase.getPreviewOutput();
+        //Try some bad input
+        ret = ACaptureSessionSharedOutput_add(previewOutput, previewAnw);
+        if (ret != ACAMERA_ERROR_INVALID_PARAMETER) {
+            LOG_ERROR(errorString, "ACaptureSessionSharedOutput_add should return invalid "
+                    "parameter! %d", ret);
+            goto cleanup;
+        }
+
+        ret = ACaptureSessionSharedOutput_remove(previewOutput, previewAnw);
+        if (ret != ACAMERA_ERROR_INVALID_PARAMETER) {
+            LOG_ERROR(errorString, "ACaptureSessionSharedOutput_remove should return invalid "
+                    "parameter! %d", ret);
+            goto cleanup;
+        }
+
+        //Now try with valid input
+        ret = ACaptureSessionSharedOutput_add(previewOutput, sharedAnw);
+        if (ret != ACAMERA_OK) {
+            LOG_ERROR(errorString, "ACaptureSessionSharedOutput_add failed!")
+            goto cleanup;
+        }
+
+        ret = testCase.updateOutput(env, previewOutput);
+        if (ret != ACAMERA_OK) {
+            LOG_ERROR(errorString, "Failed to update output configuration!")
+            goto cleanup;
+        }
+
+        ret = ACameraDevice_createCaptureRequest(
+                testCase.getCameraDevice(), TEMPLATE_PREVIEW, &updatedRequest);
+        if (ret != ACAMERA_OK) {
+            LOG_ERROR(errorString, "Camera %s create preview request failed. ret %d",
+                    cameraId, ret);
+            goto cleanup;
+        }
+
+        ret = ACameraOutputTarget_create(previewAnw, &reqPreviewOutput);
+        if (ret != ACAMERA_OK) {
+            LOG_ERROR(errorString,
+                    "Camera %s create request preview output target failed. ret %d",
+                    cameraId, ret);
+            goto cleanup;
+        }
+
+        ret = ACaptureRequest_addTarget(updatedRequest, reqPreviewOutput);
+        if (ret != ACAMERA_OK) {
+            LOG_ERROR(errorString, "Camera %s add preview request output failed. ret %d",
+                    cameraId, ret);
+            goto cleanup;
+        }
+
+        ret = ACameraOutputTarget_create(sharedAnw, &reqSharedOutput);
+        if (ret != ACAMERA_OK) {
+            LOG_ERROR(errorString,
+                    "Camera %s create request preview output target failed. ret %d",
+                    cameraId, ret);
+            goto cleanup;
+        }
+
+        ret = ACaptureRequest_addTarget(updatedRequest, reqSharedOutput);
+        if (ret != ACAMERA_OK) {
+            LOG_ERROR(errorString, "Camera %s add preview request output failed. ret %d",
+                    cameraId, ret);
+            goto cleanup;
+        }
+
+        ret = testCase.updateRepeatingRequest(updatedRequest, &sequenceId);
+        if (ret != ACAMERA_OK) {
+            LOG_ERROR(errorString, "Camera %s failed to update repeated request. ret %d",
+                    cameraId, ret);
+            goto cleanup;
+        }
+
+        sleep(runPreviewSec);
+
+        ret = ACaptureSessionSharedOutput_remove(previewOutput, sharedAnw);
+        if (ret != ACAMERA_OK) {
+            LOG_ERROR(errorString, "ACaptureSessionSharedOutput_remove failed!");
+            goto cleanup;
+        }
+
+        //Try removing shared output which still has pending camera requests
+        ret = testCase.updateOutput(env, previewOutput);
+        if (ret != ACAMERA_ERROR_INVALID_PARAMETER) {
+            LOG_ERROR(errorString, "updateOutput should fail!");
+            goto cleanup;
+        }
+
+        //Remove the shared output correctly by updating the repeating request
+        //first
+        ret = ACaptureRequest_removeTarget(updatedRequest, reqSharedOutput);
+        if (ret != ACAMERA_OK) {
+            LOG_ERROR(errorString, "Camera %s remove target output failed. ret %d",
+                    cameraId, ret);
+            goto cleanup;
+        }
+
+        ret = testCase.updateRepeatingRequest(updatedRequest);
+        if (ret != ACAMERA_OK) {
+            LOG_ERROR(errorString, "Camera %s failed to update repeated request. ret %d",
+                    cameraId, ret);
+            goto cleanup;
+        }
+
+        //Then wait for all old requests to flush
+        lastFrameNumber = testCase.getCaptureSequenceLastFrameNumber(sequenceId, timeoutSec);
+        if (lastFrameNumber < 0) {
+            LOG_ERROR(errorString, "Camera %s failed to acquire last frame number!",
+                    cameraId);
+            goto cleanup;
+        }
+
+        frameArrived = testCase.waitForFrameNumber(lastFrameNumber, timeoutSec);
+        if (!frameArrived) {
+            LOG_ERROR(errorString, "Camera %s timed out waiting on last frame number!",
+                    cameraId);
+            goto cleanup;
+        }
+
+        ret = testCase.updateOutput(env, previewOutput);
+        if (ret != ACAMERA_OK) {
+            LOG_ERROR(errorString, "updateOutput failed!");
+            goto cleanup;
+        }
+
+        sleep(runPreviewSec);
+
+        ret = testCase.resetWithErrorLog();
+        if (ret != ACAMERA_OK) {
+            // Don't log error here. testcase did it
+            goto cleanup;
+        }
+
+        usleep(100000); // sleep to give some time for callbacks to happen
+
+        if (!testCase.isCameraAvailable(cameraId)) {
+            LOG_ERROR(errorString, "Camera %s should be available now", cameraId);
+            goto cleanup;
+        }
+    }
+
+    ret = testCase.deInit();
+    if (ret != ACAMERA_OK) {
+        LOG_ERROR(errorString, "Testcase deInit failed: ret %d", ret);
+        goto cleanup;
+    }
+
+    pass = true;
+
+cleanup:
+
+    if (updatedRequest != nullptr) {
+        ACaptureRequest_free(updatedRequest);
+        updatedRequest = nullptr;
+    }
+
+    if (reqPreviewOutput != nullptr) {
+        ACameraOutputTarget_free(reqPreviewOutput);
+        reqPreviewOutput = nullptr;
+    }
+
+    if (reqSharedOutput != nullptr) {
+        ACameraOutputTarget_free(reqSharedOutput);
+        reqSharedOutput = nullptr;
+    }
+
+    if (sharedAnw) {
+        ANativeWindow_release(sharedAnw);
+        sharedAnw = nullptr;
+    }
+
+    ALOGI("%s %s", __FUNCTION__, pass ? "pass" : "failed");
+    if (!pass) {
+        throwAssertionError(env, errorString);
+    }
+    return pass;
+}
+
+extern "C" jboolean
+Java_android_hardware_camera2_cts_NativeCameraDeviceTest_\
 testCameraDeviceSimplePreviewNative(
         JNIEnv* env, jclass /*clazz*/, jobject jPreviewSurface) {
     ALOGV("%s", __FUNCTION__);
@@ -1577,6 +2135,134 @@
     return pass;
 }
 
+extern "C" jboolean
+Java_android_hardware_camera2_cts_NativeCameraDeviceTest_\
+testCameraDevicePreviewWithSessionParametersNative(
+        JNIEnv* env, jclass /*clazz*/, jobject jPreviewSurface) {
+    ALOGV("%s", __FUNCTION__);
+    int numCameras = 0;
+    bool pass = false;
+    ACameraManager* mgr = ACameraManager_create();
+    ACameraMetadata* chars = nullptr;
+    PreviewTestCase testCase;
+
+    camera_status_t ret = testCase.initWithErrorLog();
+    if (ret != ACAMERA_OK) {
+        // Don't log error here. testcase did it
+        goto cleanup;
+    }
+
+    numCameras = testCase.getNumCameras();
+    if (numCameras < 0) {
+        LOG_ERROR(errorString, "Testcase returned negavtive number of cameras: %d", numCameras);
+        goto cleanup;
+    }
+
+    for (int i = 0; i < numCameras; i++) {
+        const char* cameraId = testCase.getCameraId(i);
+        if (cameraId == nullptr) {
+            LOG_ERROR(errorString, "Testcase returned null camera id for camera %d", i);
+            goto cleanup;
+        }
+
+        ret = ACameraManager_getCameraCharacteristics(
+                mgr, cameraId, &chars);
+        if (ret != ACAMERA_OK) {
+            LOG_ERROR(errorString, "Get camera characteristics failed: ret %d", ret);
+            goto cleanup;
+        }
+
+        ACameraMetadata_const_entry sessionParamKeys{};
+        ret = ACameraMetadata_getConstEntry(chars, ACAMERA_REQUEST_AVAILABLE_SESSION_KEYS,
+                &sessionParamKeys);
+        if ((ret != ACAMERA_OK) || (sessionParamKeys.count == 0)) {
+            ACameraMetadata_free(chars);
+            chars = nullptr;
+            continue;
+        }
+
+        ret = testCase.openCamera(cameraId);
+        if (ret != ACAMERA_OK) {
+            LOG_ERROR(errorString, "Open camera device %s failure. ret %d", cameraId, ret);
+            goto cleanup;
+        }
+
+        usleep(100000); // sleep to give some time for callbacks to happen
+
+        if (testCase.isCameraAvailable(cameraId)) {
+            LOG_ERROR(errorString, "Camera %s should be unavailable now", cameraId);
+            goto cleanup;
+        }
+
+        ANativeWindow* previewAnw = testCase.initPreviewAnw(env, jPreviewSurface);
+        if (previewAnw == nullptr) {
+            LOG_ERROR(errorString, "Null ANW from preview surface!");
+            goto cleanup;
+        }
+
+        ret = testCase.createRequestsWithErrorLog();
+        if (ret != ACAMERA_OK) {
+            // Don't log error here. testcase did it
+            goto cleanup;
+        }
+
+        ACaptureRequest *previewRequest = nullptr;
+        ret = testCase.getPreviewRequest(&previewRequest);
+        if ((ret != ACAMERA_OK) || (previewRequest == nullptr)) {
+            LOG_ERROR(errorString, "Preview request query failed!");
+            goto cleanup;
+        }
+
+        ret = testCase.createCaptureSessionWithLog(/*isPreviewShared*/ false, previewRequest);
+        if (ret != ACAMERA_OK) {
+            // Don't log error here. testcase did it
+            goto cleanup;
+        }
+
+        ret = testCase.startPreview();
+        if (ret != ACAMERA_OK) {
+            LOG_ERROR(errorString, "Start preview failed!");
+            goto cleanup;
+        }
+
+        sleep(3);
+
+        ret = testCase.resetWithErrorLog();
+        if (ret != ACAMERA_OK) {
+            // Don't log error here. testcase did it
+            goto cleanup;
+        }
+
+        usleep(100000); // sleep to give some time for callbacks to happen
+
+        if (!testCase.isCameraAvailable(cameraId)) {
+            LOG_ERROR(errorString, "Camera %s should be available now", cameraId);
+            goto cleanup;
+        }
+
+        ACameraMetadata_free(chars);
+        chars = nullptr;
+    }
+
+    ret = testCase.deInit();
+    if (ret != ACAMERA_OK) {
+        LOG_ERROR(errorString, "Testcase deInit failed: ret %d", ret);
+        goto cleanup;
+    }
+
+    pass = true;
+cleanup:
+    if (chars) {
+        ACameraMetadata_free(chars);
+    }
+    ACameraManager_delete(mgr);
+    ALOGI("%s %s", __FUNCTION__, pass ? "pass" : "failed");
+    if (!pass) {
+        throwAssertionError(env, errorString);
+    }
+    return pass;
+}
+
 bool nativeImageReaderTestBase(
         JNIEnv* env, jstring jOutPath, AImageReader_ImageCallback cb) {
     const int NUM_TEST_IMAGES = 10;
@@ -1649,14 +2335,68 @@
             goto cleanup;
         }
 
+        CaptureResultListener resultListener;
+        ACameraCaptureSession_captureCallbacks resultCb {
+            &resultListener,
+            CaptureResultListener::onCaptureStart,
+            CaptureResultListener::onCaptureProgressed,
+            CaptureResultListener::onCaptureCompleted,
+            CaptureResultListener::onCaptureFailed,
+            CaptureResultListener::onCaptureSequenceCompleted,
+            CaptureResultListener::onCaptureSequenceAborted,
+            CaptureResultListener::onCaptureBufferLost
+        };
+        resultListener.setRequestSave(true);
+        ACaptureRequest* requestTemplate = nullptr;
+        ret = testCase.getStillRequest(&requestTemplate);
+        if (ret != ACAMERA_OK || requestTemplate == nullptr) {
+            // Don't log error here. testcase did it
+            goto cleanup;
+        }
+
         // Do some still capture
-        for (int capture = 0; capture < NUM_TEST_IMAGES; capture++) {
-            ret = testCase.takePicture();
+        int lastSeqId = -1;
+        for (intptr_t capture = 0; capture < NUM_TEST_IMAGES; capture++) {
+            ACaptureRequest* req = ACaptureRequest_copy(requestTemplate);
+            ACaptureRequest_setUserContext(req, (void*) capture);
+            int seqId;
+            ret = testCase.capture(req, &resultCb, &seqId);
             if (ret != ACAMERA_OK) {
-                LOG_ERROR(errorString, "Camera %s capture(%d) failed. ret %d",
+                LOG_ERROR(errorString, "Camera %s capture(%" PRIdPTR ") failed. ret %d",
                         cameraId, capture, ret);
                 goto cleanup;
             }
+            if (capture == NUM_TEST_IMAGES - 1) {
+                lastSeqId = seqId;
+            }
+            ACaptureRequest_free(req);
+        }
+
+        // wait until last sequence complete
+        resultListener.getCaptureSequenceLastFrameNumber(lastSeqId, /*timeoutSec*/ 3);
+
+        std::vector<ACaptureRequest*> completedRequests;
+        resultListener.getCompletedRequests(&completedRequests);
+
+        if (completedRequests.size() != NUM_TEST_IMAGES) {
+            LOG_ERROR(errorString, "Camera %s fails to capture %d capture results. Got %zu",
+                    cameraId, NUM_TEST_IMAGES, completedRequests.size());
+            goto cleanup;
+        }
+
+        for (intptr_t i = 0; i < NUM_TEST_IMAGES; i++) {
+            intptr_t userContext = -1;
+            ret = ACaptureRequest_getUserContext(completedRequests[i], (void**) &userContext);
+            if (ret != ACAMERA_OK) {
+                LOG_ERROR(errorString, "Camera %s fails to get request user context", cameraId);
+                goto cleanup;
+            }
+
+            if (userContext != i) {
+                LOG_ERROR(errorString, "Camera %s fails to return matching user context. "
+                        "Expect %" PRIdPTR ", got %" PRIdPTR, cameraId, i, userContext);
+                goto cleanup;
+            }
         }
 
         // wait until all capture finished
diff --git a/tests/camera/res/layout/multi_view.xml b/tests/camera/res/layout/multi_view.xml
index 4f335d3..e7c5508 100644
--- a/tests/camera/res/layout/multi_view.xml
+++ b/tests/camera/res/layout/multi_view.xml
@@ -29,6 +29,18 @@
         android:id="@+id/texture_view_2"
         android:layout_width="160dp"
         android:layout_height="120dp"/>
+    <TextureView
+        android:id="@+id/texture_view_3"
+        android:layout_width="160dp"
+        android:layout_height="120dp"/>
+    <TextureView
+        android:id="@+id/texture_view_4"
+        android:layout_width="160dp"
+        android:layout_height="120dp"/>
+    <TextureView
+        android:id="@+id/texture_view_5"
+        android:layout_width="160dp"
+        android:layout_height="120dp"/>
     <SurfaceView
         android:id="@+id/surface_view_1"
         android:layout_width="160dp"
diff --git a/tests/camera/src/android/hardware/camera2/cts/Camera2MultiViewCtsActivity.java b/tests/camera/src/android/hardware/camera2/cts/Camera2MultiViewCtsActivity.java
index d6350fc..1f4265d 100644
--- a/tests/camera/src/android/hardware/camera2/cts/Camera2MultiViewCtsActivity.java
+++ b/tests/camera/src/android/hardware/camera2/cts/Camera2MultiViewCtsActivity.java
@@ -27,7 +27,8 @@
 
 public class Camera2MultiViewCtsActivity extends Activity {
     private final static String TAG = "Camera2MultiViewCtsActivity";
-    private TextureView[] mTextureView = new TextureView[2];
+    public final static int MAX_TEXTURE_VIEWS = 5;
+    private TextureView[] mTextureView = new TextureView[MAX_TEXTURE_VIEWS];
     private SurfaceView[] mSurfaceView = new SurfaceView[2];
 
     @Override
@@ -36,6 +37,9 @@
         setContentView(R.layout.multi_view);
         mTextureView[0] = (TextureView) findViewById(R.id.texture_view_1);
         mTextureView[1] = (TextureView) findViewById(R.id.texture_view_2);
+        mTextureView[2] = (TextureView) findViewById(R.id.texture_view_3);
+        mTextureView[3] = (TextureView) findViewById(R.id.texture_view_4);
+        mTextureView[4] = (TextureView) findViewById(R.id.texture_view_5);
         mSurfaceView[0] = (SurfaceView) findViewById(R.id.surface_view_1);
         mSurfaceView[1] = (SurfaceView) findViewById(R.id.surface_view_2);
 
@@ -44,8 +48,9 @@
     }
 
     public TextureView getTextureView(int index) {
-        if (index < 0 || index > 1) {
-            throw new IllegalArgumentException("Texture view index must be 0 or 1");
+        if (index < 0 || index > (MAX_TEXTURE_VIEWS - 1)) {
+            throw new IllegalArgumentException("Texture view index must be between 0 and " +
+                    MAX_TEXTURE_VIEWS);
         }
         return mTextureView[index];
     }
diff --git a/tests/camera/src/android/hardware/camera2/cts/Camera2SurfaceViewCtsActivity.java b/tests/camera/src/android/hardware/camera2/cts/Camera2SurfaceViewCtsActivity.java
index e1dfbc1..4bd6186 100644
--- a/tests/camera/src/android/hardware/camera2/cts/Camera2SurfaceViewCtsActivity.java
+++ b/tests/camera/src/android/hardware/camera2/cts/Camera2SurfaceViewCtsActivity.java
@@ -73,12 +73,18 @@
             changeSucceeded = surfaceChangedDone.block(waitTimeMs);
             if (!changeSucceeded) {
                 Log.e(TAG, "Wait for surface change timed out after " + timeOutMs + " ms");
-                return changeSucceeded;
+                return false;
             } else {
                 // Get a surface change callback, need to check if the size is expected.
                 surfaceChangedDone.close();
-                if (currentWidth == expectWidth && currentHeight == expectHeight) {
-                    return changeSucceeded;
+                synchronized(surfaceLock) {
+                    if (expectWidth == currentWidth && expectHeight == currentHeight) {
+                        return true;
+                    } else {
+                        Log.i(TAG, "Wait for surface changed to " + expectWidth + "x" +
+                                "expectHeight. Got " + currentWidth + "x" + currentHeight +
+                                ". Keep waiting");
+                    }
                 }
                 // Do a further iteration surface change check as surfaceChanged could be called
                 // again.
@@ -88,6 +94,7 @@
         }
 
         // Couldn't get expected surface size change.
+        Log.e(TAG, "Wait for surface change timed out after " + timeOutMs + " ms");
         return false;
     }
 
diff --git a/tests/camera/src/android/hardware/camera2/cts/CameraDeviceTest.java b/tests/camera/src/android/hardware/camera2/cts/CameraDeviceTest.java
index 3d9c683..05a642c 100644
--- a/tests/camera/src/android/hardware/camera2/cts/CameraDeviceTest.java
+++ b/tests/camera/src/android/hardware/camera2/cts/CameraDeviceTest.java
@@ -35,9 +35,13 @@
 import android.hardware.camera2.CaptureResult;
 import android.hardware.camera2.TotalCaptureResult;
 import android.hardware.camera2.cts.helpers.StaticMetadata;
+import android.hardware.camera2.cts.helpers.StaticMetadata.CheckLevel;
 import android.hardware.camera2.cts.testcases.Camera2AndroidTestCase;
 import android.hardware.camera2.params.MeteringRectangle;
+import android.hardware.camera2.params.InputConfiguration;
 import android.hardware.camera2.params.OutputConfiguration;
+import android.hardware.camera2.params.SessionConfiguration;
+import android.hardware.camera2.params.StreamConfigurationMap;
 import android.media.ImageReader;
 import android.os.Handler;
 import android.os.SystemClock;
@@ -783,6 +787,213 @@
         }
     }
 
+    /**
+     * Test session configuration.
+     */
+    public void testSessionConfiguration() throws Exception {
+        ArrayList<OutputConfiguration> outConfigs = new ArrayList<OutputConfiguration> ();
+        outConfigs.add(new OutputConfiguration(new Size(1, 1), SurfaceTexture.class));
+        outConfigs.add(new OutputConfiguration(new Size(2, 2), SurfaceTexture.class));
+        mSessionMockListener = spy(new BlockingSessionCallback());
+        InputConfiguration inputConfig = new InputConfiguration(1, 1, ImageFormat.PRIVATE);
+
+        SessionConfiguration regularSessionConfig = new SessionConfiguration(
+                SessionConfiguration.SESSION_REGULAR, outConfigs, mSessionMockListener, null);
+
+        SessionConfiguration highspeedSessionConfig = new SessionConfiguration(
+                SessionConfiguration.SESSION_HIGH_SPEED, outConfigs, mSessionMockListener, null);
+
+        assertEquals("Session configuration output doesn't match",
+                regularSessionConfig.getOutputConfigurations(), outConfigs);
+
+        assertEquals("Session configuration output doesn't match",
+                regularSessionConfig.getOutputConfigurations(),
+                highspeedSessionConfig.getOutputConfigurations());
+
+        assertEquals("Session configuration callback doesn't match",
+                regularSessionConfig.getStateCallback(), mSessionMockListener);
+
+        assertEquals("Session configuration callback doesn't match",
+                regularSessionConfig.getStateCallback(),
+                highspeedSessionConfig.getStateCallback());
+
+        assertEquals("Session configuration handler doesn't match",
+                regularSessionConfig.getHandler(), null);
+
+        assertEquals("Session configuration handler doesn't match",
+                regularSessionConfig.getHandler(), highspeedSessionConfig.getHandler());
+
+        regularSessionConfig.setInputConfiguration(inputConfig);
+        assertEquals("Session configuration input doesn't match",
+                regularSessionConfig.getInputConfiguration(), inputConfig);
+
+        try {
+            highspeedSessionConfig.setInputConfiguration(inputConfig);
+            fail("No exception for valid input configuration in hight speed session configuration");
+        } catch (UnsupportedOperationException e) {
+            //expected
+        }
+
+        assertEquals("Session configuration input doesn't match",
+                highspeedSessionConfig.getInputConfiguration(), null);
+
+        for (int i = 0; i < mCameraIds.length; i++) {
+            try {
+                openDevice(mCameraIds[i], mCameraMockListener);
+                waitForDeviceState(STATE_OPENED, CAMERA_OPEN_TIMEOUT_MS);
+
+                CaptureRequest.Builder builder =
+                    mCamera.createCaptureRequest(CameraDevice.TEMPLATE_STILL_CAPTURE);
+                CaptureRequest request = builder.build();
+
+                regularSessionConfig.setSessionParameters(request);
+                highspeedSessionConfig.setSessionParameters(request);
+
+                assertEquals("Session configuration parameters doesn't match",
+                        regularSessionConfig.getSessionParameters(), request);
+
+                assertEquals("Session configuration parameters doesn't match",
+                        regularSessionConfig.getSessionParameters(),
+                        highspeedSessionConfig.getSessionParameters());
+            }
+            finally {
+                closeDevice(mCameraIds[i], mCameraMockListener);
+            }
+        }
+    }
+
+    /**
+     * Verify creating a session with additional parameters.
+     */
+    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()) {
+                    Log.i(TAG, "Camera " + mCameraIds[i] +
+                            " does not support color outputs, skipping");
+                    continue;
+                }
+
+                testCreateSessionWithParametersByCamera(mCameraIds[i], /*reprocessable*/false);
+                testCreateSessionWithParametersByCamera(mCameraIds[i], /*reprocessable*/true);
+            }
+            finally {
+                closeDevice(mCameraIds[i], mCameraMockListener);
+            }
+        }
+    }
+
+    /**
+     * Verify creating a session with additional parameters works
+     */
+    private void testCreateSessionWithParametersByCamera(String cameraId, boolean reprocessable)
+            throws Exception {
+        final int SESSION_TIMEOUT_MS = 1000;
+        final int CAPTURE_TIMEOUT_MS = 3000;
+        int inputFormat = ImageFormat.YUV_420_888;
+        int outputFormat = inputFormat;
+        Size outputSize = mOrderedPreviewSizes.get(0);
+        Size inputSize = outputSize;
+        InputConfiguration inputConfig = null;
+
+        if (VERBOSE) {
+            Log.v(TAG, "Testing creating session with parameters for camera " + cameraId);
+        }
+
+        CameraCharacteristics characteristics = mCameraManager.getCameraCharacteristics(cameraId);
+        StreamConfigurationMap config = characteristics.get(
+                CameraCharacteristics.SCALER_STREAM_CONFIGURATION_MAP);
+
+        if (reprocessable) {
+            //Pick a supported i/o format and size combination.
+            //Ideally the input format should match the output.
+            boolean found = false;
+            int inputFormats [] = config.getInputFormats();
+            if (inputFormats.length == 0) {
+                return;
+            }
+
+            for (int inFormat : inputFormats) {
+                int outputFormats [] = config.getValidOutputFormatsForInput(inputFormat);
+                for (int outFormat : outputFormats) {
+                    if (inFormat == outFormat) {
+                        inputFormat = inFormat;
+                        outputFormat = outFormat;
+                        found = true;
+                        break;
+                    }
+                }
+                if (found) {
+                    break;
+                }
+            }
+
+            //In case the above combination doesn't exist, pick the first first supported
+            //pair.
+            if (!found) {
+                inputFormat = inputFormats[0];
+                int outputFormats [] = config.getValidOutputFormatsForInput(inputFormat);
+                assertTrue("No output formats supported for input format: " + inputFormat,
+                        (outputFormats.length > 0));
+                outputFormat = outputFormats[0];
+            }
+
+            Size inputSizes[] = config.getInputSizes(inputFormat);
+            Size outputSizes[] = config.getOutputSizes(outputFormat);
+            assertTrue("No valid sizes supported for input format: " + inputFormat,
+                    (inputSizes.length > 0));
+            assertTrue("No valid sizes supported for output format: " + outputFormat,
+                    (outputSizes.length > 0));
+
+            inputSize = inputSizes[0];
+            outputSize = outputSizes[0];
+            inputConfig = new InputConfiguration(inputSize.getWidth(),
+                    inputSize.getHeight(), inputFormat);
+        } else {
+            if (config.isOutputSupportedFor(outputFormat)) {
+                outputSize = config.getOutputSizes(outputFormat)[0];
+            } else {
+                return;
+            }
+        }
+
+        ImageReader imageReader = ImageReader.newInstance(outputSize.getWidth(),
+                outputSize.getHeight(), outputFormat, /*maxImages*/1);
+
+        try {
+            mSessionMockListener = spy(new BlockingSessionCallback());
+            mSessionWaiter = mSessionMockListener.getStateWaiter();
+            List<OutputConfiguration> outputs = new ArrayList<>();
+            outputs.add(new OutputConfiguration(imageReader.getSurface()));
+            SessionConfiguration sessionConfig = new SessionConfiguration(
+                    SessionConfiguration.SESSION_REGULAR, outputs, mSessionMockListener, mHandler);
+
+            CaptureRequest.Builder builder =
+                    mCamera.createCaptureRequest(CameraDevice.TEMPLATE_STILL_CAPTURE);
+            builder.addTarget(imageReader.getSurface());
+            CaptureRequest request = builder.build();
+
+            sessionConfig.setInputConfiguration(inputConfig);
+            sessionConfig.setSessionParameters(request);
+            mCamera.createCaptureSession(sessionConfig);
+
+            mSession = mSessionMockListener.waitAndGetSession(SESSION_CONFIGURE_TIMEOUT_MS);
+
+            // Verify we can capture a frame with the session.
+            SimpleCaptureCallback captureListener = new SimpleCaptureCallback();
+            SimpleImageReaderListener imageListener = new SimpleImageReaderListener();
+            imageReader.setOnImageAvailableListener(imageListener, mHandler);
+
+            mSession.capture(request, captureListener, mHandler);
+            captureListener.getCaptureResultForRequest(request, CAPTURE_TIMEOUT_MS);
+            imageListener.getImage(CAPTURE_TIMEOUT_MS).close();
+        } finally {
+            imageReader.close();
+            mSession.close();
+        }
+    }
 
     /**
      * Verify creating sessions back to back and only the last one is valid for
diff --git a/tests/camera/src/android/hardware/camera2/cts/CaptureResultTest.java b/tests/camera/src/android/hardware/camera2/cts/CaptureResultTest.java
index b29fd57..3857b93 100644
--- a/tests/camera/src/android/hardware/camera2/cts/CaptureResultTest.java
+++ b/tests/camera/src/android/hardware/camera2/cts/CaptureResultTest.java
@@ -535,6 +535,10 @@
             waiverKeys.add(CaptureResult.CONTROL_ENABLE_ZSL);
         }
 
+        if (!mStaticInfo.isAfSceneChangeSupported()) {
+            waiverKeys.add(CaptureResult.CONTROL_AF_SCENE_CHANGE);
+        }
+
         if (mStaticInfo.isHardwareLevelAtLeastFull()) {
             return waiverKeys;
         }
@@ -770,6 +774,7 @@
         resultKeys.add(CaptureResult.CONTROL_AWB_STATE);
         resultKeys.add(CaptureResult.CONTROL_POST_RAW_SENSITIVITY_BOOST);
         resultKeys.add(CaptureResult.CONTROL_ENABLE_ZSL);
+        resultKeys.add(CaptureResult.CONTROL_AF_SCENE_CHANGE);
         resultKeys.add(CaptureResult.EDGE_MODE);
         resultKeys.add(CaptureResult.FLASH_MODE);
         resultKeys.add(CaptureResult.FLASH_STATE);
diff --git a/tests/camera/src/android/hardware/camera2/cts/ExtendedCameraCharacteristicsTest.java b/tests/camera/src/android/hardware/camera2/cts/ExtendedCameraCharacteristicsTest.java
index 000edcd..bde3aed 100644
--- a/tests/camera/src/android/hardware/camera2/cts/ExtendedCameraCharacteristicsTest.java
+++ b/tests/camera/src/android/hardware/camera2/cts/ExtendedCameraCharacteristicsTest.java
@@ -334,6 +334,7 @@
                 expectKeyAvailable(c, CameraCharacteristics.FLASH_INFO_AVAILABLE                            , OPT      ,   BC                   );
                 expectKeyAvailable(c, CameraCharacteristics.HOT_PIXEL_AVAILABLE_HOT_PIXEL_MODES             , OPT      ,   RAW                  );
                 expectKeyAvailable(c, CameraCharacteristics.INFO_SUPPORTED_HARDWARE_LEVEL                   , OPT      ,   BC                   );
+                expectKeyAvailable(c, CameraCharacteristics.INFO_VERSION                                    , OPT      ,   NONE                 );
                 expectKeyAvailable(c, CameraCharacteristics.JPEG_AVAILABLE_THUMBNAIL_SIZES                  , OPT      ,   BC                   );
                 expectKeyAvailable(c, CameraCharacteristics.LENS_FACING                                     , OPT      ,   BC                   );
                 expectKeyAvailable(c, CameraCharacteristics.LENS_INFO_AVAILABLE_APERTURES                   , FULL     ,   MANUAL_SENSOR        );
@@ -406,6 +407,19 @@
                 expectKeyAvailable(c,
                         CameraCharacteristics.CONTROL_POST_RAW_SENSITIVITY_BOOST_RANGE, OPT, BC);
             }
+
+            // Verify version is a short text string.
+            if (allKeys.contains(CameraCharacteristics.INFO_VERSION)) {
+                final String TEXT_REGEX = "[\\p{Alnum}\\p{Punct}\\p{Space}]*";
+                final int MAX_VERSION_LENGTH = 256;
+
+                String version = c.get(CameraCharacteristics.INFO_VERSION);
+                mCollector.expectTrue("Version contains non-text characters: " + version,
+                        version.matches(TEXT_REGEX));
+                mCollector.expectLessOrEqual("Version too long: " + version, MAX_VERSION_LENGTH,
+                        version.length());
+            }
+
             counter++;
         }
     }
@@ -497,6 +511,25 @@
     }
 
     /**
+     * Test values for the available session keys.
+     */
+    public void testStaticSessionKeys() throws Exception {
+        for (CameraCharacteristics c : mCharacteristics) {
+            List<CaptureRequest.Key<?>> availableSessionKeys = c.getAvailableSessionKeys();
+            if (availableSessionKeys == null) {
+                continue;
+            }
+            List<CaptureRequest.Key<?>> availableRequestKeys = c.getAvailableCaptureRequestKeys();
+
+            //Every session key should be part of the available request keys
+            for (CaptureRequest.Key<?> key : availableSessionKeys) {
+                assertTrue("Session key:" + key.getName() + " not present in the available capture "
+                        + "request keys!", availableRequestKeys.contains(key));
+            }
+        }
+    }
+
+    /**
      * Test values for static metadata used by the BURST capability.
      */
     public void testStaticBurstCharacteristics() throws Exception {
diff --git a/tests/camera/src/android/hardware/camera2/cts/FastBasicsTest.java b/tests/camera/src/android/hardware/camera2/cts/FastBasicsTest.java
index c4ae61b..488c0f3 100644
--- a/tests/camera/src/android/hardware/camera2/cts/FastBasicsTest.java
+++ b/tests/camera/src/android/hardware/camera2/cts/FastBasicsTest.java
@@ -48,7 +48,6 @@
  * May not take more than a few seconds to run, to be suitable for quick
  * testing.
  */
-@Presubmit
 public class FastBasicsTest extends Camera2SurfaceViewTestCase {
     private static final String TAG = "FastBasicsTest";
     private static final boolean VERBOSE = Log.isLoggable(TAG, Log.VERBOSE);
@@ -58,6 +57,7 @@
     private static final int WAIT_FOR_PICTURE_TIMEOUT_MS = 5000;
     private static final int FRAMES_TO_WAIT_FOR_CAPTURE = 100;
 
+    @Presubmit
     public void testCamera2() throws Exception {
         for (int i = 0; i < mCameraIds.length; i++) {
             try {
@@ -178,6 +178,7 @@
         }
     }
 
+    @Presubmit
     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/ImageReaderTest.java b/tests/camera/src/android/hardware/camera2/cts/ImageReaderTest.java
index bd01d21..96f363c 100644
--- a/tests/camera/src/android/hardware/camera2/cts/ImageReaderTest.java
+++ b/tests/camera/src/android/hardware/camera2/cts/ImageReaderTest.java
@@ -553,6 +553,21 @@
     }
 
     /**
+     * Test that images captured after discarding free buffers are valid.
+     */
+    public void testDiscardFreeBuffers() throws Exception {
+        for (String id : mCameraIds) {
+            try {
+                Log.v(TAG, "Testing jpeg capture for Camera " + id);
+                openDevice(id);
+                discardFreeBuffersTestByCamera();
+            } finally {
+                closeDevice(id);
+            }
+        }
+    }
+
+    /**
      * Convert a rectangular patch in a YUV image to an ARGB color array.
      *
      * @param w width of the patch.
@@ -762,6 +777,38 @@
         imageInvalidAccessTestAfterClose(img, firstPlane, buffer);
     }
 
+    /**
+     * Test that images captured after discarding free buffers are valid.
+     */
+    private void discardFreeBuffersTestByCamera() throws Exception {
+        final int FORMAT = mStaticInfo.isColorOutputSupported() ?
+            ImageFormat.YUV_420_888 : ImageFormat.DEPTH16;
+
+        final Size SIZE = mStaticInfo.getAvailableSizesForFormatChecked(FORMAT,
+                StaticMetadata.StreamDirection.Output)[0];
+        Image img = null;
+        // Create ImageReader.
+        mListener = new SimpleImageListener();
+        createDefaultImageReader(SIZE, FORMAT, MAX_NUM_IMAGES, mListener);
+
+        // Start capture.
+        final boolean REPEATING = true;
+        CaptureRequest request = prepareCaptureRequest();
+        SimpleCaptureCallback listener = new SimpleCaptureCallback();
+        startCapture(request, REPEATING, listener, mHandler);
+
+        // Validate images and capture results.
+        validateImage(SIZE, FORMAT, NUM_FRAME_VERIFIED, REPEATING);
+        validateCaptureResult(FORMAT, SIZE, listener, NUM_FRAME_VERIFIED);
+
+        // Discard free buffers.
+        mReader.discardFreeBuffers();
+
+        // Validate images and capture resulst again.
+        validateImage(SIZE, FORMAT, NUM_FRAME_VERIFIED, REPEATING);
+        validateCaptureResult(FORMAT, SIZE, listener, NUM_FRAME_VERIFIED);
+    }
+
     private void bufferFormatTestByCamera(int format, boolean repeating) throws Exception {
 
         Size[] availableSizes = mStaticInfo.getAvailableSizesForFormatChecked(format,
diff --git a/tests/camera/src/android/hardware/camera2/cts/MultiViewTest.java b/tests/camera/src/android/hardware/camera2/cts/MultiViewTest.java
index 265729d..e9eac65 100644
--- a/tests/camera/src/android/hardware/camera2/cts/MultiViewTest.java
+++ b/tests/camera/src/android/hardware/camera2/cts/MultiViewTest.java
@@ -21,13 +21,19 @@
 import android.graphics.ImageFormat;
 import android.graphics.SurfaceTexture;
 import android.hardware.camera2.CameraDevice;
+import android.hardware.camera2.CameraAccessException;
+import android.hardware.camera2.CaptureRequest;
+import android.hardware.camera2.CaptureFailure;
+import android.hardware.camera2.TotalCaptureResult;
 import android.hardware.camera2.cts.CameraTestUtils.ImageVerifierListener;
 import android.hardware.camera2.cts.helpers.StaticMetadata;
 import android.hardware.camera2.cts.testcases.Camera2MultiViewTestCase;
 import android.hardware.camera2.cts.testcases.Camera2MultiViewTestCase.CameraPreviewListener;
 import android.hardware.camera2.params.OutputConfiguration;
+import android.media.Image;
 import android.media.ImageReader;
 import android.os.SystemClock;
+import android.os.ConditionVariable;
 import android.util.Log;
 import android.util.Size;
 import android.view.Surface;
@@ -44,8 +50,10 @@
  */
 public class MultiViewTest extends Camera2MultiViewTestCase {
     private static final String TAG = "MultiViewTest";
-    private final static long WAIT_FOR_COMMAND_TO_COMPLETE = 2000;
+    private final static long WAIT_FOR_COMMAND_TO_COMPLETE = 5000; //ms
     private final static long PREVIEW_TIME_MS = 2000;
+    private final static int NUM_SURFACE_SWITCHES = 10;
+    private final static int IMG_READER_COUNT = 2;
 
     public void testTextureViewPreview() throws Exception {
         for (String cameraId : mCameraIds) {
@@ -235,6 +243,473 @@
     }
 
     /*
+     * Verify dynamic shared surface behavior.
+     */
+    public void testSharedSurfaceBasic() throws Exception {
+        for (String cameraId : mCameraIds) {
+            try {
+                openCamera(cameraId);
+                if (getStaticInfo(cameraId).isHardwareLevelLegacy()) {
+                    Log.i(TAG, "Camera " + cameraId + " is legacy, skipping");
+                    continue;
+                }
+                if (!getStaticInfo(cameraId).isColorOutputSupported()) {
+                    Log.i(TAG, "Camera " + cameraId +
+                            " does not support color outputs, skipping");
+                    continue;
+                }
+
+                testSharedSurfaceBasicByCamera(cameraId);
+            }
+            finally {
+                closeCamera(cameraId);
+            }
+        }
+    }
+
+    private void testSharedSurfaceBasicByCamera(String cameraId) throws Exception {
+        Size previewSize = getOrderedPreviewSizes(cameraId).get(0);
+        CameraPreviewListener[] previewListener = new CameraPreviewListener[2];
+        SurfaceTexture[] previewTexture = new SurfaceTexture[2];
+        Surface[] surfaces = new Surface[2];
+        OutputConfiguration[] outputConfigs = new OutputConfiguration[2];
+        List<OutputConfiguration> outputConfigurations = new ArrayList<>();
+
+        // Create surface textures with the same size
+        for (int i = 0; i < 2; i++) {
+            previewListener[i] = new CameraPreviewListener();
+            mTextureView[i].setSurfaceTextureListener(previewListener[i]);
+            previewTexture[i] = getAvailableSurfaceTexture(
+                    WAIT_FOR_COMMAND_TO_COMPLETE, mTextureView[i]);
+            assertNotNull("Unable to get preview surface texture", previewTexture[i]);
+            previewTexture[i].setDefaultBufferSize(previewSize.getWidth(), previewSize.getHeight());
+            // Correct the preview display rotation.
+            updatePreviewDisplayRotation(previewSize, mTextureView[i]);
+            surfaces[i] = new Surface(previewTexture[i]);
+            outputConfigs[i] = new OutputConfiguration(surfaces[i]);
+            outputConfigs[i].enableSurfaceSharing();
+            outputConfigurations.add(outputConfigs[i]);
+        }
+
+        startPreviewWithConfigs(cameraId, outputConfigurations, null);
+
+        boolean previewDone =
+                previewListener[0].waitForPreviewDone(WAIT_FOR_COMMAND_TO_COMPLETE);
+        assertTrue("Unable to start preview", previewDone);
+
+        //try to dynamically add and remove any of the initial configured outputs
+        try {
+            outputConfigs[1].addSurface(surfaces[0]);
+            updateOutputConfiguration(cameraId, outputConfigs[1]);
+            fail("should get IllegalArgumentException due to invalid output");
+        } catch (IllegalArgumentException e) {
+            // expected exception
+            outputConfigs[1].removeSurface(surfaces[0]);
+        }
+
+        try {
+            outputConfigs[1].removeSurface(surfaces[1]);
+            fail("should get IllegalArgumentException due to invalid output");
+        } catch (IllegalArgumentException e) {
+            // expected exception
+        }
+
+        try {
+            outputConfigs[0].addSurface(surfaces[1]);
+            updateOutputConfiguration(cameraId, outputConfigs[0]);
+            fail("should get IllegalArgumentException due to invalid output");
+        } catch (IllegalArgumentException e) {
+            // expected exception
+            outputConfigs[0].removeSurface(surfaces[1]);
+        }
+
+        try {
+            outputConfigs[0].removeSurface(surfaces[0]);
+            fail("should get IllegalArgumentException due to invalid output");
+        } catch (IllegalArgumentException e) {
+            // expected exception
+        }
+
+        //Check that we are able to add a shared texture surface with different size
+        List<Size> orderedPreviewSizes = getOrderedPreviewSizes(cameraId);
+        Size textureSize = previewSize;
+        for (Size s : orderedPreviewSizes) {
+            if (!s.equals(previewSize)) {
+                textureSize = s;
+                break;
+            }
+        }
+        if (textureSize.equals(previewSize)) {
+            return;
+        }
+        SurfaceTexture outputTexture = new SurfaceTexture(/* random texture ID*/ 5);
+        outputTexture.setDefaultBufferSize(textureSize.getWidth(), textureSize.getHeight());
+        Surface outputSurface = new Surface(outputTexture);
+        //Add a valid output surface and then verify that it cannot be added any more
+        outputConfigs[1].addSurface(outputSurface);
+        updateOutputConfiguration(cameraId, outputConfigs[1]);
+        try {
+            outputConfigs[1].addSurface(outputSurface);
+            fail("should get IllegalStateException due to duplicate output");
+        } catch (IllegalStateException e) {
+            // expected exception
+        }
+
+        outputConfigs[0].addSurface(outputSurface);
+        try {
+            updateOutputConfiguration(cameraId, outputConfigs[0]);
+            fail("should get IllegalArgumentException due to duplicate output");
+        } catch (IllegalArgumentException e) {
+            // expected exception
+            outputConfigs[0].removeSurface(outputSurface);
+        }
+
+        //Verify that the same surface cannot be removed twice
+        outputConfigs[1].removeSurface(outputSurface);
+        updateOutputConfiguration(cameraId, outputConfigs[1]);
+        try {
+            outputConfigs[0].removeSurface(outputSurface);
+            fail("should get IllegalArgumentException due to invalid output");
+        } catch (IllegalArgumentException e) {
+            // expected exception
+        }
+        try {
+            outputConfigs[1].removeSurface(outputSurface);
+            fail("should get IllegalArgumentException due to invalid output");
+        } catch (IllegalArgumentException e) {
+            // expected exception
+        }
+
+        stopPreview(cameraId);
+    }
+
+    /*
+     * Verify dynamic shared surface behavior using multiple ImageReaders.
+     */
+    public void testSharedSurfaceImageReaderSwitch() throws Exception {
+        for (String cameraId : mCameraIds) {
+            try {
+                openCamera(cameraId);
+                if (getStaticInfo(cameraId).isHardwareLevelLegacy()) {
+                    Log.i(TAG, "Camera " + cameraId + " is legacy, skipping");
+                    continue;
+                }
+                if (!getStaticInfo(cameraId).isColorOutputSupported()) {
+                    Log.i(TAG, "Camera " + cameraId +
+                            " does not support color outputs, skipping");
+                    continue;
+                }
+
+                testSharedSurfaceImageReaderSwitch(cameraId, NUM_SURFACE_SWITCHES);
+            }
+            finally {
+                closeCamera(cameraId);
+            }
+        }
+    }
+
+    private void testSharedSurfaceImageReaderSwitch(String cameraId, int switchCount)
+            throws Exception {
+        if (OutputConfiguration.getMaxSharedSurfaceCount() < (IMG_READER_COUNT + 1)) {
+            return;
+        }
+
+        SimpleImageListener imageListeners[] = new SimpleImageListener[IMG_READER_COUNT];
+        SimpleCaptureCallback resultListener = new SimpleCaptureCallback();
+        ImageReader imageReaders[] = new ImageReader[IMG_READER_COUNT];
+        Surface readerSurfaces[] = new Surface[IMG_READER_COUNT];
+        Size previewSize = getOrderedPreviewSizes(cameraId).get(0);
+        CameraPreviewListener previewListener = new CameraPreviewListener();
+        mTextureView[0].setSurfaceTextureListener(previewListener);
+        SurfaceTexture previewTexture = getAvailableSurfaceTexture(WAIT_FOR_COMMAND_TO_COMPLETE,
+                mTextureView[0]);
+        assertNotNull("Unable to get preview surface texture", previewTexture);
+        previewTexture.setDefaultBufferSize(previewSize.getWidth(), previewSize.getHeight());
+        updatePreviewDisplayRotation(previewSize, mTextureView[0]);
+        Surface previewSurface = new Surface(previewTexture);
+        OutputConfiguration outputConfig = new OutputConfiguration(previewSurface);
+        outputConfig.enableSurfaceSharing();
+        List<OutputConfiguration> outputConfigurations = new ArrayList<>();
+        outputConfigurations.add(outputConfig);
+
+        //Start regular preview streaming
+        startPreviewWithConfigs(cameraId, outputConfigurations, null);
+
+        boolean previewDone = previewListener.waitForPreviewDone(WAIT_FOR_COMMAND_TO_COMPLETE);
+        assertTrue("Unable to start preview", previewDone);
+
+        //Test shared image reader outputs
+        for (int i = 0; i < IMG_READER_COUNT; i++) {
+            imageListeners[i] = new SimpleImageListener();
+            imageReaders[i] = ImageReader.newInstance(previewSize.getWidth(),
+                    previewSize.getHeight(), ImageFormat.PRIVATE, 2);
+            imageReaders[i].setOnImageAvailableListener(imageListeners[i], mHandler);
+            readerSurfaces[i] = imageReaders[i].getSurface();
+        }
+
+        for (int j = 0; j < switchCount; j++) {
+            for (int i = 0; i < IMG_READER_COUNT; i++) {
+                outputConfig.addSurface(readerSurfaces[i]);
+                updateOutputConfiguration(cameraId, outputConfig);
+                CaptureRequest.Builder imageReaderRequestBuilder = getCaptureBuilder(cameraId,
+                        CameraDevice.TEMPLATE_PREVIEW);
+                imageReaderRequestBuilder.addTarget(readerSurfaces[i]);
+                capture(cameraId, imageReaderRequestBuilder.build(), resultListener);
+                imageListeners[i].waitForAnyImageAvailable(PREVIEW_TIME_MS);
+                Image img = imageReaders[i].acquireLatestImage();
+                assertNotNull("Invalid image acquired!", img);
+                img.close();
+                outputConfig.removeSurface(readerSurfaces[i]);
+                updateOutputConfiguration(cameraId, outputConfig);
+            }
+        }
+
+        for (int i = 0; i < IMG_READER_COUNT; i++) {
+            imageReaders[i].close();
+        }
+
+        stopPreview(cameraId);
+    }
+
+    /*
+     * Test the dynamic shared surface limit.
+     */
+    public void testSharedSurfaceLimit() throws Exception {
+        for (String cameraId : mCameraIds) {
+            try {
+                openCamera(cameraId);
+                if (getStaticInfo(cameraId).isHardwareLevelLegacy()) {
+                    Log.i(TAG, "Camera " + cameraId + " is legacy, skipping");
+                    continue;
+                }
+                if (!getStaticInfo(cameraId).isColorOutputSupported()) {
+                    Log.i(TAG, "Camera " + cameraId +
+                            " does not support color outputs, skipping");
+                    continue;
+                }
+
+                testSharedSurfaceLimitByCamera(cameraId,
+                        Camera2MultiViewCtsActivity.MAX_TEXTURE_VIEWS);
+            }
+            finally {
+                closeCamera(cameraId);
+            }
+        }
+    }
+
+    private void testSharedSurfaceLimitByCamera(String cameraId, int surfaceLimit)
+            throws Exception {
+        Size previewSize = getOrderedPreviewSizes(cameraId).get(0);
+        CameraPreviewListener[] previewListener = new CameraPreviewListener[surfaceLimit];
+        SurfaceTexture[] previewTexture = new SurfaceTexture[surfaceLimit];
+        Surface[] surfaces = new Surface[surfaceLimit];
+        int sequenceId = -1;
+
+        if ((surfaceLimit <= 1) ||
+                (surfaceLimit < OutputConfiguration.getMaxSharedSurfaceCount())) {
+            return;
+        }
+
+        // Create surface textures with the same size
+        for (int i = 0; i < surfaceLimit; i++) {
+            previewListener[i] = new CameraPreviewListener();
+            mTextureView[i].setSurfaceTextureListener(previewListener[i]);
+            previewTexture[i] = getAvailableSurfaceTexture(
+                    WAIT_FOR_COMMAND_TO_COMPLETE, mTextureView[i]);
+            assertNotNull("Unable to get preview surface texture", previewTexture[i]);
+            previewTexture[i].setDefaultBufferSize(previewSize.getWidth(), previewSize.getHeight());
+            // Correct the preview display rotation.
+            updatePreviewDisplayRotation(previewSize, mTextureView[i]);
+            surfaces[i] = new Surface(previewTexture[i]);
+        }
+
+        SimpleCaptureCallback resultListener = new SimpleCaptureCallback();
+
+        // Create shared outputs for the two surface textures
+        OutputConfiguration surfaceSharedOutput = new OutputConfiguration(surfaces[0]);
+        surfaceSharedOutput.enableSurfaceSharing();
+
+        List<OutputConfiguration> outputConfigurations = new ArrayList<>();
+        outputConfigurations.add(surfaceSharedOutput);
+
+        startPreviewWithConfigs(cameraId, outputConfigurations, null);
+
+        boolean previewDone =
+                previewListener[0].waitForPreviewDone(WAIT_FOR_COMMAND_TO_COMPLETE);
+        assertTrue("Unable to start preview", previewDone);
+        mTextureView[0].setSurfaceTextureListener(null);
+
+        SystemClock.sleep(PREVIEW_TIME_MS);
+
+        int i = 1;
+        for (; i < surfaceLimit; i++) {
+            //Add one more output surface while preview is streaming
+            if (i >= OutputConfiguration.getMaxSharedSurfaceCount()){
+                try {
+                    surfaceSharedOutput.addSurface(surfaces[i]);
+                    fail("should get IllegalArgumentException due to output surface limit");
+                } catch (IllegalArgumentException e) {
+                    //expected
+                    break;
+                }
+            } else {
+                surfaceSharedOutput.addSurface(surfaces[i]);
+                updateOutputConfiguration(cameraId, surfaceSharedOutput);
+                sequenceId = updateRepeatingRequest(cameraId, outputConfigurations, resultListener);
+
+                SystemClock.sleep(PREVIEW_TIME_MS);
+            }
+        }
+
+        for (; i > 0; i--) {
+            if (i >= OutputConfiguration.getMaxSharedSurfaceCount()) {
+                try {
+                    surfaceSharedOutput.removeSurface(surfaces[i]);
+                    fail("should get IllegalArgumentException due to output surface limit");
+                } catch (IllegalArgumentException e) {
+                    // expected exception
+                }
+            } else {
+                surfaceSharedOutput.removeSurface(surfaces[i]);
+
+            }
+        }
+        //Remove all previously added shared outputs in one call
+        updateRepeatingRequest(cameraId, outputConfigurations, resultListener);
+        long lastSequenceFrameNumber = resultListener.getCaptureSequenceLastFrameNumber(
+                sequenceId, PREVIEW_TIME_MS);
+        checkForLastFrameInSequence(lastSequenceFrameNumber, resultListener);
+        updateOutputConfiguration(cameraId, surfaceSharedOutput);
+        SystemClock.sleep(PREVIEW_TIME_MS);
+
+        stopPreview(cameraId);
+    }
+
+    /*
+     * Test dynamic shared surface switch behavior.
+     */
+    public void testSharedSurfaceSwitch() throws Exception {
+        for (String cameraId : mCameraIds) {
+            try {
+                openCamera(cameraId);
+                if (getStaticInfo(cameraId).isHardwareLevelLegacy()) {
+                    Log.i(TAG, "Camera " + cameraId + " is legacy, skipping");
+                    continue;
+                }
+                if (!getStaticInfo(cameraId).isColorOutputSupported()) {
+                    Log.i(TAG, "Camera " + cameraId +
+                            " does not support color outputs, skipping");
+                    continue;
+                }
+
+                testSharedSurfaceSwitchByCamera(cameraId, NUM_SURFACE_SWITCHES);
+            }
+            finally {
+                closeCamera(cameraId);
+            }
+        }
+    }
+
+    private void testSharedSurfaceSwitchByCamera(String cameraId, int switchCount)
+            throws Exception {
+        Size previewSize = getOrderedPreviewSizes(cameraId).get(0);
+        CameraPreviewListener[] previewListener = new CameraPreviewListener[2];
+        SurfaceTexture[] previewTexture = new SurfaceTexture[2];
+        Surface[] surfaces = new Surface[2];
+
+        // Create surface textures with the same size
+        for (int i = 0; i < 2; i++) {
+            previewListener[i] = new CameraPreviewListener();
+            mTextureView[i].setSurfaceTextureListener(previewListener[i]);
+            previewTexture[i] = getAvailableSurfaceTexture(
+                    WAIT_FOR_COMMAND_TO_COMPLETE, mTextureView[i]);
+            assertNotNull("Unable to get preview surface texture", previewTexture[i]);
+            previewTexture[i].setDefaultBufferSize(previewSize.getWidth(), previewSize.getHeight());
+            // Correct the preview display rotation.
+            updatePreviewDisplayRotation(previewSize, mTextureView[i]);
+            surfaces[i] = new Surface(previewTexture[i]);
+        }
+
+        SimpleCaptureCallback resultListener = new SimpleCaptureCallback();
+
+        // Create shared outputs for the two surface textures
+        OutputConfiguration surfaceSharedOutput = new OutputConfiguration(surfaces[0]);
+        surfaceSharedOutput.enableSurfaceSharing();
+
+        List<OutputConfiguration> outputConfigurations = new ArrayList<>();
+        outputConfigurations.add(surfaceSharedOutput);
+
+        startPreviewWithConfigs(cameraId, outputConfigurations, null);
+
+        boolean previewDone =
+                previewListener[0].waitForPreviewDone(WAIT_FOR_COMMAND_TO_COMPLETE);
+        assertTrue("Unable to start preview", previewDone);
+        mTextureView[0].setSurfaceTextureListener(null);
+
+        SystemClock.sleep(PREVIEW_TIME_MS);
+
+        for (int i = 0; i < switchCount; i++) {
+            //Add one more output surface while preview is streaming
+            surfaceSharedOutput.addSurface(surfaces[1]);
+            updateOutputConfiguration(cameraId, surfaceSharedOutput);
+            int sequenceId = updateRepeatingRequest(cameraId, outputConfigurations, resultListener);
+
+            SystemClock.sleep(PREVIEW_TIME_MS);
+
+            //Try to remove the shared surface while while we still have active requests that
+            //use it as output.
+            surfaceSharedOutput.removeSurface(surfaces[1]);
+            try {
+                updateOutputConfiguration(cameraId, surfaceSharedOutput);
+                fail("should get IllegalArgumentException due to pending requests");
+            } catch (IllegalArgumentException e) {
+                // expected exception
+            }
+
+            //Wait for all pending requests to arrive and remove the shared output during active
+            //streaming
+            updateRepeatingRequest(cameraId, outputConfigurations, resultListener);
+            long lastSequenceFrameNumber = resultListener.getCaptureSequenceLastFrameNumber(
+                    sequenceId, PREVIEW_TIME_MS);
+            checkForLastFrameInSequence(lastSequenceFrameNumber, resultListener);
+            updateOutputConfiguration(cameraId, surfaceSharedOutput);
+
+            SystemClock.sleep(PREVIEW_TIME_MS);
+        }
+
+        stopPreview(cameraId);
+    }
+
+    private void checkForLastFrameInSequence(long lastSequenceFrameNumber,
+            SimpleCaptureCallback listener) throws Exception {
+        // Find the last frame number received in results and failures.
+        long lastFrameNumber = -1;
+        while (listener.hasMoreResults()) {
+            TotalCaptureResult result = listener.getTotalCaptureResult(PREVIEW_TIME_MS);
+            if (lastFrameNumber < result.getFrameNumber()) {
+                lastFrameNumber = result.getFrameNumber();
+            }
+        }
+
+        while (listener.hasMoreFailures()) {
+            ArrayList<CaptureFailure> failures = listener.getCaptureFailures(
+                    /*maxNumFailures*/ 1);
+            for (CaptureFailure failure : failures) {
+                if (lastFrameNumber < failure.getFrameNumber()) {
+                    lastFrameNumber = failure.getFrameNumber();
+                }
+            }
+        }
+
+        // Verify the last frame number received from capture sequence completed matches the
+        // the last frame number of the results and failures.
+        assertEquals(String.format("Last frame number from onCaptureSequenceCompleted " +
+                "(%d) doesn't match the last frame number received from " +
+                "results/failures (%d)", lastSequenceFrameNumber, lastFrameNumber),
+                lastSequenceFrameNumber, lastFrameNumber);
+    }
+
+    /*
      * Verify behavior of sharing surfaces within one OutputConfiguration
      */
     public void testSharedSurfaces() throws Exception {
@@ -517,7 +992,7 @@
 
         // Run preview with both surfaces, and verify at least one frame is received for each
         // surface.
-        updateOutputConfigs(cameraId, deferredConfigs, null);
+        finalizeOutputConfigs(cameraId, deferredConfigs, null);
         previewDone =
                 previewListener[1].waitForPreviewDone(WAIT_FOR_COMMAND_TO_COMPLETE);
         assertTrue("Unable to start preview 1", previewDone);
@@ -547,7 +1022,7 @@
         surfaceSharedOutput.addSurface(surfaces[1]);
         deferredConfigs.clear();
         deferredConfigs.add(surfaceSharedOutput);
-        updateOutputConfigs(cameraId, deferredConfigs, null);
+        finalizeOutputConfigs(cameraId, deferredConfigs, null);
         previewDone =
                 previewListener[1].waitForPreviewDone(WAIT_FOR_COMMAND_TO_COMPLETE);
         assertTrue("Unable to start preview 1", previewDone);
@@ -574,7 +1049,7 @@
         surfaceSharedOutput.addSurface(surfaces[1]);
         deferredConfigs.clear();
         deferredConfigs.add(surfaceSharedOutput);
-        updateOutputConfigs(cameraId, deferredConfigs, null);
+        finalizeOutputConfigs(cameraId, deferredConfigs, null);
         for (int i = 0; i < 2; i++) {
             previewDone =
                     previewListener[i].waitForPreviewDone(WAIT_FOR_COMMAND_TO_COMPLETE);
@@ -584,4 +1059,20 @@
         SystemClock.sleep(PREVIEW_TIME_MS);
         stopPreview(cameraId);
     }
+
+    private final class SimpleImageListener implements ImageReader.OnImageAvailableListener {
+        private final ConditionVariable imageAvailable = new ConditionVariable();
+        @Override
+        public void onImageAvailable(ImageReader reader) {
+            imageAvailable.open();
+        }
+
+        public void waitForAnyImageAvailable(long timeout) {
+            if (imageAvailable.block(timeout)) {
+                imageAvailable.close();
+            } else {
+                fail("wait for image available timed out after " + timeout + "ms");
+            }
+        }
+    }
 }
diff --git a/tests/camera/src/android/hardware/camera2/cts/NativeCameraDeviceTest.java b/tests/camera/src/android/hardware/camera2/cts/NativeCameraDeviceTest.java
index e2a61a6..06158d8 100644
--- a/tests/camera/src/android/hardware/camera2/cts/NativeCameraDeviceTest.java
+++ b/tests/camera/src/android/hardware/camera2/cts/NativeCameraDeviceTest.java
@@ -16,6 +16,7 @@
 
 package android.hardware.camera2.cts;
 
+import android.graphics.SurfaceTexture;
 import android.hardware.camera2.cts.testcases.Camera2SurfaceViewTestCase;
 import android.util.Log;
 import android.util.Size;
@@ -59,8 +60,29 @@
                 testCameraDeviceSimplePreviewNative(mPreviewSurface));
     }
 
+    public void testCameraDevicePreviewWithSessionParameters() {
+        // Init preview surface to a guaranteed working size
+        updatePreviewSurface(new Size(640, 480));
+        assertTrue("testCameraDevicePreviewWithSessionParametersNative fail, see log for details",
+                testCameraDevicePreviewWithSessionParametersNative(mPreviewSurface));
+    }
+
+    public void testCameraDeviceSharedOutputUpdate() {
+        // Init preview surface to a guaranteed working size
+        Size previewSize = new Size(640, 480);
+        updatePreviewSurface(previewSize);
+        SurfaceTexture outputTexture = new SurfaceTexture(/* random texture ID*/ 5);
+        outputTexture.setDefaultBufferSize(previewSize.getWidth(), previewSize.getHeight());
+        Surface outputSurface = new Surface(outputTexture);
+        assertTrue("testCameraDeviceSharedWindowAddRemove fail, see log for details",
+                testCameraDeviceSharedOutputUpdate(mPreviewSurface, outputSurface));
+    }
+
     private static native boolean testCameraDeviceOpenAndCloseNative();
     private static native boolean testCameraDeviceCreateCaptureRequestNative();
     private static native boolean testCameraDeviceSessionOpenAndCloseNative(Surface preview);
     private static native boolean testCameraDeviceSimplePreviewNative(Surface preview);
+    private static native boolean testCameraDevicePreviewWithSessionParametersNative(
+            Surface preview);
+    private static native boolean testCameraDeviceSharedOutputUpdate(Surface src, Surface dst);
 }
diff --git a/tests/camera/src/android/hardware/camera2/cts/RecordingTest.java b/tests/camera/src/android/hardware/camera2/cts/RecordingTest.java
index 1e9696f..6f5ccfb 100644
--- a/tests/camera/src/android/hardware/camera2/cts/RecordingTest.java
+++ b/tests/camera/src/android/hardware/camera2/cts/RecordingTest.java
@@ -65,6 +65,7 @@
     private static final boolean VERBOSE = Log.isLoggable(TAG, Log.VERBOSE);
     private static final boolean DEBUG_DUMP = Log.isLoggable(TAG, Log.DEBUG);
     private static final int RECORDING_DURATION_MS = 3000;
+    private static final int PREVIEW_DURATION_MS = 3000;
     private static final float DURATION_MARGIN = 0.2f;
     private static final double FRAME_DURATION_ERROR_TOLERANCE_MS = 3.0;
     private static final float FRAMEDURATION_MARGIN = 0.2f;
@@ -295,7 +296,8 @@
     }
 
     public void testConstrainedHighSpeedRecording() throws Exception {
-        constrainedHighSpeedRecording();
+        constrainedHighSpeedRecording(/*enableSessionParams*/ false);
+        constrainedHighSpeedRecording(/*enableSessionParams*/ true);
     }
 
     /**
@@ -495,7 +497,8 @@
                     // Start recording
                     SimpleCaptureCallback resultListener = new SimpleCaptureCallback();
                     startSlowMotionRecording(/*useMediaRecorder*/true, videoFramerate, captureRate,
-                            fpsRange, resultListener, /*useHighSpeedSession*/false);
+                            fpsRange, resultListener, /*useHighSpeedSession*/false,
+                            /*enableHighSpeedParams*/ false);
 
                     // Record certain duration.
                     SystemClock.sleep(RECORDING_DURATION_MS);
@@ -517,7 +520,7 @@
         }
     }
 
-    private void constrainedHighSpeedRecording() throws Exception {
+    private void constrainedHighSpeedRecording(boolean enableSessionParams) throws Exception {
         for (String id : mCameraIds) {
             try {
                 Log.i(TAG, "Testing constrained high speed recording for camera " + id);
@@ -550,31 +553,47 @@
                             continue;
                         }
 
+                        SimpleCaptureCallback previewResultListener = new SimpleCaptureCallback();
+
+                        // prepare preview surface by using video size.
+                        updatePreviewSurfaceWithVideo(size, captureRate);
+
+                        startConstrainedPreview(fpsRange, previewResultListener);
+
                         mOutMediaFileName = VIDEO_FILE_PATH + "/test_cslowMo_video_" + captureRate +
                                 "fps_" + id + "_" + size.toString() + ".mp4";
 
                         prepareRecording(size, VIDEO_FRAME_RATE, captureRate);
 
-                        // prepare preview surface by using video size.
-                        updatePreviewSurfaceWithVideo(size, captureRate);
+                        SystemClock.sleep(PREVIEW_DURATION_MS);
 
-                        // Start recording
+                        stopCameraStreaming();
+
                         SimpleCaptureCallback resultListener = new SimpleCaptureCallback();
+                        // Start recording
                         startSlowMotionRecording(/*useMediaRecorder*/true, VIDEO_FRAME_RATE,
                                 captureRate, fpsRange, resultListener,
-                                /*useHighSpeedSession*/true);
+                                /*useHighSpeedSession*/true,
+                                /*enableHighSpeedParams*/ enableSessionParams);
 
                         // Record certain duration.
                         SystemClock.sleep(RECORDING_DURATION_MS);
 
                         // Stop recording and preview
                         stopRecording(/*useMediaRecorder*/true);
+
+                        startConstrainedPreview(fpsRange, previewResultListener);
+
                         // Convert number of frames camera produced into the duration in unit of ms.
                         float frameDurationMs = 1000.0f / VIDEO_FRAME_RATE;
                         float durationMs = resultListener.getTotalNumFrames() * frameDurationMs;
 
                         // Validation.
                         validateRecording(size, durationMs, frameDurationMs, FRMDRP_RATE_TOLERANCE);
+
+                        SystemClock.sleep(PREVIEW_DURATION_MS);
+
+                        stopCameraStreaming();
                     }
                 }
 
@@ -635,9 +654,30 @@
         return fixedRanges;
     }
 
+    private void startConstrainedPreview(Range<Integer> fpsRange,
+            CameraCaptureSession.CaptureCallback listener) throws Exception {
+        List<Surface> outputSurfaces = new ArrayList<Surface>(1);
+        assertTrue("Preview surface should be valid", mPreviewSurface.isValid());
+        outputSurfaces.add(mPreviewSurface);
+        mSessionListener = new BlockingSessionCallback();
+        mSession = configureCameraSession(mCamera, outputSurfaces, /*isHighSpeed*/ true,
+                mSessionListener, mHandler);
+
+        List<CaptureRequest> slowMoRequests = null;
+        CaptureRequest.Builder requestBuilder =
+            mCamera.createCaptureRequest(CameraDevice.TEMPLATE_RECORD);
+        requestBuilder.set(CaptureRequest.CONTROL_AE_TARGET_FPS_RANGE, fpsRange);
+        requestBuilder.addTarget(mPreviewSurface);
+        slowMoRequests = ((CameraConstrainedHighSpeedCaptureSession) mSession).
+            createHighSpeedRequestList(requestBuilder.build());
+
+        mSession.setRepeatingBurst(slowMoRequests, listener, mHandler);
+    }
+
     private void startSlowMotionRecording(boolean useMediaRecorder, int videoFrameRate,
             int captureRate, Range<Integer> fpsRange,
-            CameraCaptureSession.CaptureCallback listener, boolean useHighSpeedSession) throws Exception {
+            CameraCaptureSession.CaptureCallback listener, boolean useHighSpeedSession,
+            boolean enableHighSpeedParams) throws Exception {
         List<Surface> outputSurfaces = new ArrayList<Surface>(2);
         assertTrue("Both preview and recording surfaces should be valid",
                 mPreviewSurface.isValid() && mRecordingSurface.isValid());
@@ -648,8 +688,13 @@
             outputSurfaces.add(mReaderSurface);
         }
         mSessionListener = new BlockingSessionCallback();
-        mSession = configureCameraSession(mCamera, outputSurfaces, useHighSpeedSession,
-                mSessionListener, mHandler);
+        if (useHighSpeedSession && enableHighSpeedParams) {
+            mSession = buildConstrainedCameraSession(mCamera, outputSurfaces, useHighSpeedSession,
+                    mSessionListener, mHandler);
+        } else {
+            mSession = configureCameraSession(mCamera, outputSurfaces, useHighSpeedSession,
+                    mSessionListener, mHandler);
+        }
 
         // Create slow motion request list
         List<CaptureRequest> slowMoRequests = null;
diff --git a/tests/camera/src/android/hardware/camera2/cts/RobustnessTest.java b/tests/camera/src/android/hardware/camera2/cts/RobustnessTest.java
index a900b84..54405a4 100644
--- a/tests/camera/src/android/hardware/camera2/cts/RobustnessTest.java
+++ b/tests/camera/src/android/hardware/camera2/cts/RobustnessTest.java
@@ -146,6 +146,7 @@
 
                 assertTrue("Camera does not contain outputted image resolution " + actualSize,
                         testSizes.contains(actualSize));
+                imageReader.close();
             } finally {
                 closeDevice(id);
             }
@@ -957,6 +958,65 @@
         }
     }
 
+    public void testAfSceneChange() throws Exception {
+        final int NUM_FRAMES_VERIFIED = 3;
+
+        for (String id : mCameraIds) {
+            Log.i(TAG, String.format("Testing Camera %s for AF scene change", id));
+
+            StaticMetadata staticInfo =
+                    new StaticMetadata(mCameraManager.getCameraCharacteristics(id));
+            if (!staticInfo.isAfSceneChangeSupported()) {
+                continue;
+            }
+
+            openDevice(id);
+
+            try {
+                SurfaceTexture preview = new SurfaceTexture(/*random int*/ 1);
+                Surface previewSurface = new Surface(preview);
+
+                CaptureRequest.Builder previewRequest = preparePreviewTestSession(preview);
+                SimpleCaptureCallback previewListener = new CameraTestUtils.SimpleCaptureCallback();
+
+                int[] availableAfModes = mStaticInfo.getAfAvailableModesChecked();
+
+                // Test AF scene change in each AF mode.
+                for (int afMode : availableAfModes) {
+                    previewRequest.set(CaptureRequest.CONTROL_AF_MODE, afMode);
+
+                    int sequenceId = mCameraSession.setRepeatingRequest(previewRequest.build(),
+                            previewListener, mHandler);
+
+                    // Verify that AF scene change is NOT_DETECTED or DETECTED in CONTINUOUS_VIDEO
+                    // and CONTINUOUS_PICTURE AF modes, and NOT_DETECTED in other AF modes.
+                    for (int i = 0; i < NUM_FRAMES_VERIFIED; i++) {
+                        TotalCaptureResult result =
+                            previewListener.getTotalCaptureResult(CAPTURE_TIMEOUT);
+                        if (afMode == CaptureRequest.CONTROL_AF_MODE_CONTINUOUS_VIDEO ||
+                                afMode == CaptureRequest.CONTROL_AF_MODE_CONTINUOUS_PICTURE) {
+                            mCollector.expectKeyValueIsIn(result,
+                                    CaptureResult.CONTROL_AF_SCENE_CHANGE,
+                                    CaptureResult.CONTROL_AF_SCENE_CHANGE_DETECTED,
+                                    CaptureResult.CONTROL_AF_SCENE_CHANGE_NOT_DETECTED);
+                        } else {
+                            mCollector.expectKeyValueEquals(result,
+                                    CaptureResult.CONTROL_AF_SCENE_CHANGE,
+                                    CaptureResult.CONTROL_AF_SCENE_CHANGE_NOT_DETECTED);
+                        }
+                    }
+
+                    mCameraSession.stopRepeating();
+                    previewListener.getCaptureSequenceLastFrameNumber(sequenceId, CAPTURE_TIMEOUT);
+                    previewListener.drain();
+                }
+            } finally {
+                closeDevice(id);
+            }
+        }
+    }
+
+
     private CaptureRequest.Builder preparePreviewTestSession(SurfaceTexture preview)
             throws Exception {
         Surface previewSurface = new Surface(preview);
diff --git a/tests/camera/src/android/hardware/camera2/cts/rs/AllocationCache.java b/tests/camera/src/android/hardware/camera2/cts/rs/AllocationCache.java
index b89f9bb..98f1032 100644
--- a/tests/camera/src/android/hardware/camera2/cts/rs/AllocationCache.java
+++ b/tests/camera/src/android/hardware/camera2/cts/rs/AllocationCache.java
@@ -223,7 +223,7 @@
             if (other instanceof AllocationKey){
                 AllocationKey otherKey = (AllocationKey) other;
 
-                return otherKey.mType.equals(mType) && otherKey.mUsage == otherKey.mUsage;
+                return otherKey.mType.equals(mType) && otherKey.mUsage == mUsage;
             }
 
             return false;
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 a1d63d8..66324da 100644
--- a/tests/camera/src/android/hardware/camera2/cts/testcases/Camera2MultiViewTestCase.java
+++ b/tests/camera/src/android/hardware/camera2/cts/testcases/Camera2MultiViewTestCase.java
@@ -64,10 +64,10 @@
     private static final String TAG = "MultiViewTestCase";
     private static final boolean VERBOSE = Log.isLoggable(TAG, Log.VERBOSE);
 
-
     private static final long SHORT_SLEEP_WAIT_TIME_MS = 100;
 
-    protected TextureView[] mTextureView = new TextureView[2];
+    protected TextureView[] mTextureView =
+            new TextureView[Camera2MultiViewCtsActivity.MAX_TEXTURE_VIEWS];
     protected String[] mCameraIds;
     protected Handler mHandler;
 
@@ -99,8 +99,9 @@
         mHandler = new Handler(mHandlerThread.getLooper());
         mCameraListener = new BlockingStateCallback();
         Camera2MultiViewCtsActivity activity = (Camera2MultiViewCtsActivity) mContext;
-        mTextureView[0] = activity.getTextureView(0);
-        mTextureView[1] = activity.getTextureView(1);
+        for (int i = 0; i < Camera2MultiViewCtsActivity.MAX_TEXTURE_VIEWS; i++) {
+            mTextureView[i] = activity.getTextureView(i);
+        }
         assertNotNull("Unable to get texture view", mTextureView);
         mCameraIdMap = new HashMap<String, Integer>();
         int numCameras = mCameraIds.length;
@@ -246,13 +247,13 @@
         camera.startPreview(outputSurfaces, listener);
     }
 
-    protected void startPreviewWithConfigs(String cameraId,
+    protected int startPreviewWithConfigs(String cameraId,
             List<OutputConfiguration> outputConfigs,
             CaptureCallback listener)
             throws Exception {
         CameraHolder camera = getCameraHolder(cameraId);
         assertTrue("Camera " + cameraId + " is not openned", camera.isOpened());
-        camera.startPreviewWithConfigs(outputConfigs, listener);
+        return camera.startPreviewWithConfigs(outputConfigs, listener);
     }
 
     protected void stopPreview(String cameraId) throws Exception {
@@ -261,11 +262,39 @@
         camera.stopPreview();
     }
 
-    protected void updateOutputConfigs(String cameraId, List<OutputConfiguration> configs,
+    protected void finalizeOutputConfigs(String cameraId, List<OutputConfiguration> configs,
             CaptureCallback listener) throws Exception {
         CameraHolder camera = getCameraHolder(cameraId);
         assertTrue("Camera " + cameraId + " is not opened", camera.isOpened());
-        camera.updateOutputConfigs(configs, listener);
+        camera.finalizeOutputConfigs(configs, listener);
+    }
+
+    protected int updateRepeatingRequest(String cameraId, List<OutputConfiguration> configs,
+            CaptureCallback listener) throws Exception {
+        CameraHolder camera = getCameraHolder(cameraId);
+        assertTrue("Camera " + cameraId + " is not opened", camera.isOpened());
+        return camera.updateRepeatingRequest(configs, listener);
+    }
+
+    protected void updateOutputConfiguration(String cameraId, OutputConfiguration config)
+            throws Exception {
+        CameraHolder camera = getCameraHolder(cameraId);
+        assertTrue("Camera " + cameraId + " is not opened", camera.isOpened());
+        camera.updateOutputConfiguration(config);
+    }
+
+    protected void capture(String cameraId, CaptureRequest request, CaptureCallback listener)
+            throws Exception {
+        CameraHolder camera = getCameraHolder(cameraId);
+        assertTrue("Camera " + cameraId + " is not opened", camera.isOpened());
+        camera.capture(request, listener);
+    }
+
+    protected CaptureRequest.Builder getCaptureBuilder(String cameraId, int templateId)
+            throws Exception {
+        CameraHolder camera = getCameraHolder(cameraId);
+        assertTrue("Camera " + cameraId + " is not opened", camera.isOpened());
+        return camera.getCaptureBuilder(templateId);
     }
 
     protected StaticMetadata getStaticInfo(String cameraId) {
@@ -426,6 +455,9 @@
                 throws Exception {
             mSessionListener = new BlockingSessionCallback();
             mSession = configureCameraSession(mCamera, outputSurfaces, mSessionListener, mHandler);
+            if (outputSurfaces.isEmpty()) {
+                return;
+            }
 
             // TODO: vary the different settings like crop region to cover more cases.
             CaptureRequest.Builder captureBuilder =
@@ -457,7 +489,7 @@
                     state == BlockingSessionCallback.SESSION_CONFIGURE_FAILED);
         }
 
-        public void startPreviewWithConfigs(List<OutputConfiguration> outputConfigs,
+        public int startPreviewWithConfigs(List<OutputConfiguration> outputConfigs,
                 CaptureCallback listener)
                 throws Exception {
             createSessionWithConfigs(outputConfigs);
@@ -470,13 +502,11 @@
                     captureBuilder.addTarget(surface);
                 }
             }
-            mSession.setRepeatingRequest(captureBuilder.build(), listener, mHandler);
+            return mSession.setRepeatingRequest(captureBuilder.build(), listener, mHandler);
         }
 
-        public void updateOutputConfigs(List<OutputConfiguration> configs,
+        public int updateRepeatingRequest(List<OutputConfiguration> configs,
                 CaptureCallback listener) throws Exception {
-            mSession.finalizeOutputConfigurations(configs);
-
             CaptureRequest.Builder captureBuilder =
                     mCamera.createCaptureRequest(CameraDevice.TEMPLATE_PREVIEW);
 
@@ -485,7 +515,26 @@
                     captureBuilder.addTarget(surface);
                 }
             }
-            mSession.setRepeatingRequest(captureBuilder.build(), listener, mHandler);
+            return mSession.setRepeatingRequest(captureBuilder.build(), listener, mHandler);
+        }
+
+        public void updateOutputConfiguration(OutputConfiguration config) throws Exception {
+            mSession.updateOutputConfiguration(config);
+        }
+
+        public void capture(CaptureRequest request, CaptureCallback listener)
+                throws Exception {
+            mSession.capture(request, listener, mHandler);
+        }
+
+        public CaptureRequest.Builder getCaptureBuilder(int templateId) throws Exception {
+            return mCamera.createCaptureRequest(templateId);
+        }
+
+        public void finalizeOutputConfigs(List<OutputConfiguration> configs,
+                CaptureCallback listener) throws Exception {
+            mSession.finalizeOutputConfigurations(configs);
+            updateRepeatingRequest(configs, listener);
         }
 
         public boolean isPreviewStarted() {
diff --git a/tests/camera/src/android/hardware/cts/CameraTest.java b/tests/camera/src/android/hardware/cts/CameraTest.java
index 3b697ce..f5fff8e 100644
--- a/tests/camera/src/android/hardware/cts/CameraTest.java
+++ b/tests/camera/src/android/hardware/cts/CameraTest.java
@@ -462,6 +462,30 @@
     }
 
     @UiThreadTest
+    public void testStabilizationOneShotPreviewCallback() throws Exception {
+        int nCameras = Camera.getNumberOfCameras();
+        for (int id = 0; id < nCameras; id++) {
+            Log.v(TAG, "Camera id=" + id);
+            testStabilizationOneShotPreviewCallbackByCamera(id);
+        }
+    }
+
+    private void testStabilizationOneShotPreviewCallbackByCamera(int cameraId) throws Exception {
+        initializeMessageLooper(cameraId);
+        Parameters params = mCamera.getParameters();
+        if(!params.isVideoStabilizationSupported()) {
+            return;
+        }
+        //Check whether we can support preview callbacks along with stabilization
+        params.setVideoStabilization(true);
+        mCamera.setParameters(params);
+        mCamera.setOneShotPreviewCallback(mPreviewCallback);
+        checkPreviewCallback();
+        terminateMessageLooper();
+        assertEquals(PREVIEW_CALLBACK_RECEIVED, mPreviewCallbackResult);
+    }
+
+    @UiThreadTest
     public void testSetOneShotPreviewCallback() throws Exception {
         int nCameras = Camera.getNumberOfCameras();
         for (int id = 0; id < nCameras; id++) {
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 834d37c..0f48364 100644
--- a/tests/camera/src/android/hardware/multiprocess/camera/cts/CameraEvictionTest.java
+++ b/tests/camera/src/android/hardware/multiprocess/camera/cts/CameraEvictionTest.java
@@ -227,18 +227,15 @@
         verify(spyStateCb, times(1)).onOpened(any(CameraDevice.class));
 
         // Verify that we can no longer open the camera, as it is held by a higher priority process
-        boolean openException = false;
         try {
             manager.openCamera(chosenCamera, spyStateCb, cameraHandler);
+            fail("Didn't receive exception when trying to open camera held by higher priority " +
+                    "process.");
         } catch(CameraAccessException e) {
             assertTrue("Received incorrect camera exception when opening camera: " + e,
                     e.getReason() == CameraAccessException.CAMERA_IN_USE);
-            openException = true;
         }
 
-        assertTrue("Didn't receive exception when trying to open camera held by higher priority " +
-                "process.", openException);
-
         // Verify that attempting to open the camera didn't cause anything weird to happen in the
         // other process.
         List<ErrorLoggingService.LogEvent> eventList2 = null;
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 aad5fd8..b592709 100644
--- a/tests/camera/utils/src/android/hardware/camera2/cts/CameraTestUtils.java
+++ b/tests/camera/utils/src/android/hardware/camera2/cts/CameraTestUtils.java
@@ -37,6 +37,7 @@
 import android.hardware.cts.helpers.CameraUtils;
 import android.hardware.camera2.params.MeteringRectangle;
 import android.hardware.camera2.params.OutputConfiguration;
+import android.hardware.camera2.params.SessionConfiguration;
 import android.hardware.camera2.params.StreamConfigurationMap;
 import android.location.Location;
 import android.location.LocationManager;
@@ -780,6 +781,44 @@
     }
 
     /**
+     * Build a new constrained camera session with output surfaces, type and recording session
+     * parameters.
+     *
+     * @param camera The CameraDevice to be configured.
+     * @param outputSurfaces The surface list that used for camera output.
+     * @param listener The callback CameraDevice will notify when capture results are available.
+     */
+    public static CameraCaptureSession buildConstrainedCameraSession(CameraDevice camera,
+            List<Surface> outputSurfaces, boolean isHighSpeed,
+            CameraCaptureSession.StateCallback listener, Handler handler)
+            throws CameraAccessException {
+        BlockingSessionCallback sessionListener = new BlockingSessionCallback(listener);
+
+        CaptureRequest.Builder builder = camera.createCaptureRequest(CameraDevice.TEMPLATE_RECORD);
+        CaptureRequest recordSessionParams = builder.build();
+
+        List<OutputConfiguration> outConfigurations = new ArrayList<>(outputSurfaces.size());
+        for (Surface surface : outputSurfaces) {
+            outConfigurations.add(new OutputConfiguration(surface));
+        }
+        SessionConfiguration sessionConfig = new SessionConfiguration(
+                SessionConfiguration.SESSION_HIGH_SPEED, outConfigurations, sessionListener,
+                handler);
+        sessionConfig.setSessionParameters(recordSessionParams);
+        camera.createCaptureSession(sessionConfig);
+
+        CameraCaptureSession session =
+                sessionListener.waitAndGetSession(SESSION_CONFIGURE_TIMEOUT_MS);
+        assertFalse("Camera session should not be a reprocessable session",
+                session.isReprocessable());
+        assertTrue("Capture session type must be High Speed",
+                CameraConstrainedHighSpeedCaptureSession.class.isAssignableFrom(
+                        session.getClass()));
+
+        return session;
+    }
+
+    /**
      * Configure a new camera session with output configurations.
      *
      * @param camera The CameraDevice to be configured.
diff --git a/tests/camera/utils/src/android/hardware/camera2/cts/helpers/CameraErrorCollector.java b/tests/camera/utils/src/android/hardware/camera2/cts/helpers/CameraErrorCollector.java
index aa048cf..5563700 100644
--- a/tests/camera/utils/src/android/hardware/camera2/cts/helpers/CameraErrorCollector.java
+++ b/tests/camera/utils/src/android/hardware/camera2/cts/helpers/CameraErrorCollector.java
@@ -877,6 +877,24 @@
     }
 
     /**
+     * Check if the key is non-null, and the key value is one of the expected values.
+     *
+     * @param result {@link CaptureResult} to get the key from.
+     * @param key The {@link CaptureResult} key to be checked.
+     * @param expected The expected values of the CaptureResult key.
+     */
+    public <T> void expectKeyValueIsIn(CaptureResult result,
+                                       CaptureResult.Key<T> key, T... expected) {
+        T value = expectKeyValueNotNull(result, key);
+        if (value == null) {
+            return;
+        }
+        String reason = "Key " + key.getName() + " value " + value
+                + " isn't one of the expected values " + Arrays.deepToString(expected);
+        expectContains(reason, expected, value);
+    }
+
+    /**
      * Check if the key is non-null, and the key value contains the expected element.
      *
      * @param characteristics {@link CameraCharacteristics} to check.
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 319ec91..3c551f8 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
@@ -2258,6 +2258,13 @@
     }
 
     /**
+     * Check if AF scene change key is supported.
+     */
+    public boolean isAfSceneChangeSupported() {
+        return areKeysAvailable(CaptureResult.CONTROL_AF_SCENE_CHANGE);
+    }
+
+    /**
      * Get the value in index for a fixed-size array from a given key.
      *
      * <p>If the camera device is incorrectly reporting values, log a warning and return
diff --git a/tests/core/Android.mk b/tests/core/Android.mk
index 7a2a708..e9c9793 100644
--- a/tests/core/Android.mk
+++ b/tests/core/Android.mk
@@ -14,6 +14,4 @@
 
 LOCAL_PATH:= $(call my-dir)
 
-BUILD_CTSCORE_PACKAGE:=$(LOCAL_PATH)/ctscore.mk
-
 include $(call all-makefiles-under,$(LOCAL_PATH))
diff --git a/tests/core/ctscore.mk b/tests/core/ctscore.mk
deleted file mode 100644
index 10a91f1..0000000
--- a/tests/core/ctscore.mk
+++ /dev/null
@@ -1,35 +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.
-
-LOCAL_JAVA_LIBRARIES := android.test.runner bouncycastle conscrypt
-LOCAL_PROGUARD_ENABLED := disabled
-LOCAL_DEX_PREOPT := false
-
-# don't include these packages in any target
-LOCAL_MODULE_TAGS := optional
-# and when installed explicitly put them in the data partition
-LOCAL_MODULE_PATH := $(TARGET_OUT_DATA_APPS)
-# Don't delete META-INF from the core-tests jar
-LOCAL_DONT_DELETE_JAR_META_INF := true
-
-# TODO: Clean up this mess. (b/26483949). libnativehelper_compat_libc++ pulls in its own
-# static copy of libc++ and the libc++ we're bundling here is the platform libc++. This is
-# bround to break but is being submitted as a workaround for failing CTS tests.
-LOCAL_JNI_SHARED_LIBRARIES := libjavacoretests libsqlite_jni libnativehelper_compat_libc++ libc++
-
-# Include both the 32 and 64 bit versions of libjavacoretests,
-# where applicable.
-LOCAL_MULTILIB := both
-
-include $(BUILD_PACKAGE)
diff --git a/tests/core/runner/Android.mk b/tests/core/runner/Android.mk
index 7e4fb0d..f86ee2f 100644
--- a/tests/core/runner/Android.mk
+++ b/tests/core/runner/Android.mk
@@ -14,21 +14,6 @@
 
 LOCAL_PATH:= $(call my-dir)
 
-ifeq ($(BUILD_CTSCORE_PACKAGE),)
-    $(error BUILD_CTSCORE_PACKAGE must be defined)
-endif
-
-include $(CLEAR_VARS)
-
-# include this package in the tests target for continuous testing
-LOCAL_MODULE_TAGS := tests
-
-LOCAL_PACKAGE_NAME := android.core.tests.runner
-
-LOCAL_STATIC_JAVA_LIBRARIES := cts-test-runner
-
-include $(BUILD_CTSCORE_PACKAGE)
-
 #==========================================================
 # Build the core runner.
 #==========================================================
diff --git a/tests/core/runner/src/com/android/cts/core/internal/runner/TestLoader.java b/tests/core/runner/src/com/android/cts/core/internal/runner/TestLoader.java
deleted file mode 100644
index 108e8fd..0000000
--- a/tests/core/runner/src/com/android/cts/core/internal/runner/TestLoader.java
+++ /dev/null
@@ -1,258 +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.
- */
-
-/**
- * This file is a copy of https://cs.corp.google.com/android/frameworks/testing/runner/src/main/java/android/support/test/internal/runner/TestLoader.java
- * The only changes that have been made starts with // Libcore-specific
- */
-
-package com.android.cts.core.internal.runner;
-
-import android.util.Log;
-
-import org.junit.runner.Description;
-import org.junit.runner.notification.Failure;
-
-import java.lang.annotation.Annotation;
-import java.lang.reflect.Method;
-import java.lang.reflect.Modifier;
-import java.util.Collection;
-import java.util.LinkedHashMap;
-import java.util.List;
-import java.util.Map;
-
-/**
- * A class for loading JUnit3 and JUnit4 test classes given a set of potential class names.
- */
-public final class TestLoader {
-
-    private static final String LOG_TAG = "TestLoader";
-    // Libcore-specific change: Fully qualified name of TestNG annotation class.
-    private static final String TESTNG_TEST = "org.testng.annotations.Test";
-
-    private Map<String, Class<?>> mLoadedClassesMap = new LinkedHashMap<String, Class<?>>();
-    private Map<String, Failure> mLoadFailuresMap = new LinkedHashMap<String, Failure>();
-
-    private ClassLoader mClassLoader;
-
-    /**
-     * Set the {@link ClassLoader} to be used to load test cases.
-     *
-     * @param loader {@link ClassLoader} to load test cases with.
-     */
-    public void setClassLoader(ClassLoader loader) {
-        mClassLoader = loader;
-    }
-
-    /**
-     * Loads the test class from a given class name if its not already loaded.
-     * <p/>
-     * Will store the result internally. Successfully loaded classes can be retrieved via
-     * {@link #getLoadedClasses()}, failures via {@link #getLoadFailures()}.
-     *
-     * @param className the class name to attempt to load
-     * @return the loaded class or null.
-     */
-    public Class<?> loadClass(String className) {
-        Class<?> loadedClass = doLoadClass(className);
-        if (loadedClass != null) {
-            mLoadedClassesMap.put(className, loadedClass);
-        }
-        return loadedClass;
-    }
-
-    protected ClassLoader getClassLoader() {
-        if (mClassLoader != null) {
-            return mClassLoader;
-        }
-
-        // TODO: InstrumentationTestRunner uses
-        // Class.forName(className, false, getTargetContext().getClassLoader());
-        // Evaluate if that is needed. Initial testing indicates
-        // getTargetContext().getClassLoader() == this.getClass().getClassLoader()
-        return this.getClass().getClassLoader();
-    }
-
-    private Class<?> doLoadClass(String className) {
-        if (mLoadFailuresMap.containsKey(className)) {
-            // Don't load classes that already failed to load
-            return null;
-        } else if (mLoadedClassesMap.containsKey(className)) {
-            // Class with the same name was already loaded, return it
-            return mLoadedClassesMap.get(className);
-        }
-
-        try {
-            ClassLoader myClassLoader = getClassLoader();
-            return Class.forName(className, false, myClassLoader);
-        } catch (ClassNotFoundException e) {
-            String errMsg = String.format("Could not find class: %s", className);
-            Log.e(LOG_TAG, errMsg);
-            Description description = Description.createSuiteDescription(className);
-            Failure failure = new Failure(description, e);
-            mLoadFailuresMap.put(className, failure);
-        }
-        return null;
-    }
-
-    /**
-     * Loads the test class from the given class name.
-     * <p/>
-     * Similar to {@link #loadClass(String)}, but will ignore classes that are
-     * not tests.
-     *
-     * @param className the class name to attempt to load
-     * @return the loaded class or null.
-     */
-    public Class<?> loadIfTest(String className) {
-        Class<?> loadedClass = doLoadClass(className);
-        if (loadedClass != null && isTestClass(loadedClass)) {
-            mLoadedClassesMap.put(className, loadedClass);
-            return loadedClass;
-        }
-        return null;
-    }
-
-    /**
-     * @return whether this {@link TestLoader} contains any loaded classes or load failures.
-     */
-    public boolean isEmpty() {
-        return mLoadedClassesMap.isEmpty() && mLoadFailuresMap.isEmpty();
-    }
-
-    /**
-     * Get the {@link Collection) of classes successfully loaded via
-     * {@link #loadIfTest(String)} calls.
-     */
-    public Collection<Class<?>> getLoadedClasses() {
-        return mLoadedClassesMap.values();
-    }
-
-    /**
-     * Get the {@link List) of {@link Failure} that occurred during
-     * {@link #loadIfTest(String)} calls.
-     */
-    public Collection<Failure> getLoadFailures() {
-        return mLoadFailuresMap.values();
-    }
-
-    /**
-     * Determines if given class is a valid test class.
-     *
-     * @param loadedClass
-     * @return <code>true</code> if loadedClass is a test
-     */
-    private boolean isTestClass(Class<?> loadedClass) {
-        try {
-            if (Modifier.isAbstract(loadedClass.getModifiers())) {
-                logDebug(String.format("Skipping abstract class %s: not a test",
-                        loadedClass.getName()));
-                return false;
-            }
-            // Libcore-specific change: Also consider TestNG annotated classes.
-            if (isTestNgTestClass(loadedClass)) {
-              return true;
-            }
-            // TODO: try to find upstream junit calls to replace these checks
-            if (junit.framework.Test.class.isAssignableFrom(loadedClass)) {
-                // ensure that if a TestCase, it has at least one test method otherwise
-                // TestSuite will throw error
-                if (junit.framework.TestCase.class.isAssignableFrom(loadedClass)) {
-                    return hasJUnit3TestMethod(loadedClass);
-                }
-                return true;
-            }
-            // TODO: look for a 'suite' method?
-            if (loadedClass.isAnnotationPresent(org.junit.runner.RunWith.class)) {
-                return true;
-            }
-            for (Method testMethod : loadedClass.getMethods()) {
-                if (testMethod.isAnnotationPresent(org.junit.Test.class)) {
-                    return true;
-                }
-            }
-            logDebug(String.format("Skipping class %s: not a test", loadedClass.getName()));
-            return false;
-        } catch (Exception e) {
-            // Defensively catch exceptions - Will throw runtime exception if it cannot load methods.
-            // For earlier versions of Android (Pre-ICS), Dalvik might try to initialize a class
-            // during getMethods(), fail to do so, hide the error and throw a NoSuchMethodException.
-            // Since the java.lang.Class.getMethods does not declare such an exception, resort to a
-            // generic catch all.
-            // For ICS+, Dalvik will throw a NoClassDefFoundException.
-            Log.w(LOG_TAG, String.format("%s in isTestClass for %s", e.toString(),
-                    loadedClass.getName()));
-            return false;
-        } catch (Error e) {
-            // defensively catch Errors too
-            Log.w(LOG_TAG, String.format("%s in isTestClass for %s", e.toString(),
-                    loadedClass.getName()));
-            return false;
-        }
-    }
-
-    private boolean hasJUnit3TestMethod(Class<?> loadedClass) {
-        for (Method testMethod : loadedClass.getMethods()) {
-            if (isPublicTestMethod(testMethod)) {
-                return true;
-            }
-        }
-        return false;
-    }
-
-    // copied from junit.framework.TestSuite
-    private boolean isPublicTestMethod(Method m) {
-        return isTestMethod(m) && Modifier.isPublic(m.getModifiers());
-    }
-
-    // copied from junit.framework.TestSuite
-    private boolean isTestMethod(Method m) {
-        return m.getParameterTypes().length == 0 && m.getName().startsWith("test")
-                && m.getReturnType().equals(Void.TYPE);
-    }
-
-    // Libcore-specific change: Add method for checking TestNG-annotated classes.
-    private static boolean isTestNgTestClass(Class<?> cls) {
-      // TestNG test is either marked @Test at the class
-      for (Annotation a : cls.getAnnotations()) {
-          if (a.annotationType().getName().equals(TESTNG_TEST)) {
-              return true;
-          }
-      }
-
-      // Or It's marked @Test at the method level
-      for (Method m : cls.getDeclaredMethods()) {
-        for (Annotation a : m.getAnnotations()) {
-          if (a.annotationType().getName().equals(TESTNG_TEST)) {
-              return true;
-          }
-        }
-      }
-
-      return false;
-    }
-
-
-    /**
-     * Utility method for logging debug messages. Only actually logs a message if LOG_TAG is marked
-     * as loggable to limit log spam during normal use.
-     */
-    private void logDebug(String msg) {
-        if (Log.isLoggable(LOG_TAG, Log.DEBUG)) {
-            Log.d(LOG_TAG, msg);
-        }
-    }
-}
diff --git a/tests/core/runner/src/com/android/cts/core/runner/AndroidJUnitRunnerConstants.java b/tests/core/runner/src/com/android/cts/core/runner/AndroidJUnitRunnerConstants.java
deleted file mode 100644
index 4b18cdc..0000000
--- a/tests/core/runner/src/com/android/cts/core/runner/AndroidJUnitRunnerConstants.java
+++ /dev/null
@@ -1,125 +0,0 @@
-/*
- * Copyright (C) 2016 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.cts.core.runner;
-
-import android.support.test.runner.AndroidJUnitRunner;
-
-/**
- * Constants used to communicate to and from {@link AndroidJUnitRunner}.
- */
-public interface AndroidJUnitRunnerConstants {
-
-    /**
-     * The name of the file containing the names of the tests to run.
-     *
-     * <p>This is an internal constant used within
-     * {@code android.support.test.internal.runner.RunnerArgs}, which is used on both the server
-     * and
-     * client side. The constant is used when there are too many test names to pass on the command
-     * line, in which case they are stored in a file that is pushed to the device and then the
-     * location of that file is passed in this argument. The {@code RunnerArgs} on the client will
-     * read the contents of that file in order to retrieve the list of names and then return that
-     * to
-     * its client without the client being aware of how that was done.
-     */
-    String ARGUMENT_TEST_FILE = "testFile";
-
-    /**
-     * The name of the file containing the names of the tests not to run.
-     *
-     * <p>This is an internal constant used within
-     * {@code android.support.test.internal.runner.RunnerArgs}, which is used on both the server
-     * and
-     * client side. The constant is used when there are too many test names to pass on the command
-     * line, in which case they are stored in a file that is pushed to the device and then the
-     * location of that file is passed in this argument. The {@code RunnerArgs} on the client will
-     * read the contents of that file in order to retrieve the list of names and then return that
-     * to
-     * its client without the client being aware of how that was done.
-     */
-    String ARGUMENT_NOT_TEST_FILE = "notTestFile";
-
-    /**
-     * A comma separated list of the names of test classes to run.
-     *
-     * <p>The equivalent constant in {@code InstrumentationTestRunner} is hidden and so not
-     * available
-     * through the public API.
-     */
-    String ARGUMENT_TEST_CLASS = "class";
-
-    /**
-     * A comma separated list of the names of test classes not to run
-     */
-    String ARGUMENT_NOT_TEST_CLASS = "notClass";
-
-    /**
-     * A comma separated list of the names of test packages to run.
-     *
-     * <p>The equivalent constant in {@code InstrumentationTestRunner} is hidden and so not
-     * available
-     * through the public API.
-     */
-    String ARGUMENT_TEST_PACKAGE = "package";
-
-    /**
-     * A comma separated list of the names of test packages not to run.
-     */
-    String ARGUMENT_NOT_TEST_PACKAGE = "notPackage";
-
-    /**
-     * Log the results as if the tests were executed but don't actually run the tests.
-     *
-     * <p>The equivalent constant in {@code InstrumentationTestRunner} is private.
-     */
-    String ARGUMENT_LOG_ONLY = "log";
-
-    /**
-     * Wait for the debugger before starting.
-     *
-     * <p>There is no equivalent constant in {@code InstrumentationTestRunner} but the string is
-     * used
-     * within that class.
-     */
-    String ARGUMENT_DEBUG = "debug";
-
-    /**
-     * Only count the number of tests to run.
-     *
-     * <p>There is no equivalent constant in {@code InstrumentationTestRunner} but the string is
-     * used
-     * within that class.
-     */
-    String ARGUMENT_COUNT = "count";
-
-    /**
-     * The per test timeout value.
-     */
-    String ARGUMENT_TIMEOUT = "timeout_msec";
-
-    /**
-     * Token representing how long (in seconds) the current test took to execute.
-     *
-     * <p>The equivalent constant in {@code InstrumentationTestRunner} is private.
-     */
-    String REPORT_KEY_RUNTIME = "runtime";
-
-    /**
-     * An identifier for tests run using this class.
-     */
-    String REPORT_VALUE_ID = "CoreTestRunner";
-}
diff --git a/tests/core/runner/src/com/android/cts/core/runner/CoreTestRunner.java b/tests/core/runner/src/com/android/cts/core/runner/CoreTestRunner.java
deleted file mode 100644
index 42b6684..0000000
--- a/tests/core/runner/src/com/android/cts/core/runner/CoreTestRunner.java
+++ /dev/null
@@ -1,355 +0,0 @@
-/*
- * Copyright (C) 2015 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.cts.core.runner;
-
-import android.app.Activity;
-import android.app.Instrumentation;
-import android.os.Bundle;
-import android.os.Debug;
-import android.support.test.internal.runner.listener.InstrumentationResultPrinter;
-import android.support.test.internal.runner.listener.InstrumentationRunListener;
-import android.support.test.internal.util.AndroidRunnerParams;
-import android.util.Log;
-import com.android.cts.core.runner.support.ExtendedAndroidRunnerBuilder;
-import com.google.common.base.Splitter;
-import java.io.BufferedReader;
-import java.io.ByteArrayOutputStream;
-import java.io.FileReader;
-import java.io.IOException;
-import java.io.PrintStream;
-import java.util.ArrayList;
-import java.util.Arrays;
-import java.util.Collection;
-import java.util.Collections;
-import java.util.HashSet;
-import java.util.List;
-import java.util.Set;
-import org.junit.runner.Computer;
-import org.junit.runner.JUnitCore;
-import org.junit.runner.Request;
-import org.junit.runner.Result;
-import org.junit.runner.Runner;
-import org.junit.runner.manipulation.Filter;
-import org.junit.runner.manipulation.Filterable;
-import org.junit.runner.manipulation.NoTestsRemainException;
-import org.junit.runner.notification.RunListener;
-import org.junit.runners.model.InitializationError;
-import org.junit.runners.model.RunnerBuilder;
-
-import static com.android.cts.core.runner.AndroidJUnitRunnerConstants.ARGUMENT_COUNT;
-import static com.android.cts.core.runner.AndroidJUnitRunnerConstants.ARGUMENT_DEBUG;
-import static com.android.cts.core.runner.AndroidJUnitRunnerConstants.ARGUMENT_LOG_ONLY;
-import static com.android.cts.core.runner.AndroidJUnitRunnerConstants.ARGUMENT_NOT_TEST_CLASS;
-import static com.android.cts.core.runner.AndroidJUnitRunnerConstants.ARGUMENT_NOT_TEST_FILE;
-import static com.android.cts.core.runner.AndroidJUnitRunnerConstants.ARGUMENT_NOT_TEST_PACKAGE;
-import static com.android.cts.core.runner.AndroidJUnitRunnerConstants.ARGUMENT_TEST_CLASS;
-import static com.android.cts.core.runner.AndroidJUnitRunnerConstants.ARGUMENT_TEST_FILE;
-import static com.android.cts.core.runner.AndroidJUnitRunnerConstants.ARGUMENT_TEST_PACKAGE;
-import static com.android.cts.core.runner.AndroidJUnitRunnerConstants.ARGUMENT_TIMEOUT;
-
-/**
- * A drop-in replacement for AndroidJUnitTestRunner, which understands the same arguments, and has
- * similar functionality, but can filter by expectations and allows a custom runner-builder to be
- * provided.
- */
-public class CoreTestRunner extends Instrumentation {
-
-    static final String TAG = "LibcoreTestRunner";
-
-    private static final java.lang.String ARGUMENT_ROOT_CLASSES = "core-root-classes";
-
-    private static final String ARGUMENT_CORE_LISTENER = "core-listener";
-
-    private static final Splitter CLASS_LIST_SPLITTER = Splitter.on(',').trimResults();
-
-    /** The args for the runner. */
-    private Bundle args;
-
-    /** Only log the number and names of tests, and not run them. */
-    private boolean logOnly;
-
-    /** The amount of time in millis to wait for a single test to complete. */
-    private long testTimeout;
-
-    /**
-     * The list of tests to run.
-     */
-    private TestList testList;
-
-    /**
-     * The list of {@link RunListener} classes to create.
-     */
-    private List<Class<? extends RunListener>> listenerClasses;
-    private Filter expectationFilter;
-
-    @Override
-    public void onCreate(final Bundle args) {
-        super.onCreate(args);
-        this.args = args;
-
-        boolean debug = "true".equalsIgnoreCase(args.getString(ARGUMENT_DEBUG));
-        if (debug) {
-            Log.i(TAG, "Waiting for debugger to connect...");
-            Debug.waitForDebugger();
-            Log.i(TAG, "Debugger connected.");
-        }
-
-        // Log the message only after getting a value from the args so that the args are
-        // unparceled.
-        Log.d(TAG, "In OnCreate: " + args);
-
-        // Treat logOnly and count as the same. This is not quite true as count should only send
-        // the host the number of tests but logOnly should send the name and number. However,
-        // this is how this has always behaved and it does not appear to have caused any problems.
-        // Changing it seems unnecessary given that count is CTSv1 only and CTSv1 will be removed
-        // soon now that CTSv2 is ready.
-        boolean testCountOnly = args.getBoolean(ARGUMENT_COUNT);
-        this.logOnly = "true".equalsIgnoreCase(args.getString(ARGUMENT_LOG_ONLY)) || testCountOnly;
-        this.testTimeout = parseUnsignedLong(args.getString(ARGUMENT_TIMEOUT), ARGUMENT_TIMEOUT);
-
-        expectationFilter = new ExpectationBasedFilter(args);
-
-        // The test can be run specifying a list of tests to run, or as cts-tradefed does it,
-        // by passing a fileName with a test to run on each line.
-        Set<String> testNameSet = new HashSet<>();
-        String arg;
-        if ((arg = args.getString(ARGUMENT_TEST_FILE)) != null) {
-            // The tests are specified in a file.
-            try {
-                testNameSet.addAll(readTestsFromFile(arg));
-            } catch (IOException err) {
-                finish(Activity.RESULT_CANCELED, new Bundle());
-                return;
-            }
-        } else if ((arg = args.getString(ARGUMENT_TEST_CLASS)) != null) {
-            // The tests are specified in a String passed in the bundle.
-            String[] tests = arg.split(",");
-            testNameSet.addAll(Arrays.asList(tests));
-        }
-
-        // Tests may be excluded from the run by passing a list of tests not to run,
-        // or by passing a fileName with a test not to run on each line.
-        Set<String> notTestNameSet = new HashSet<>();
-        if ((arg = args.getString(ARGUMENT_NOT_TEST_FILE)) != null) {
-            // The tests are specified in a file.
-            try {
-                notTestNameSet.addAll(readTestsFromFile(arg));
-            } catch (IOException err) {
-                finish(Activity.RESULT_CANCELED, new Bundle());
-                return;
-            }
-        } else if ((arg = args.getString(ARGUMENT_NOT_TEST_CLASS)) != null) {
-            // The classes are specified in a String passed in the bundle
-            String[] tests = arg.split(",");
-            notTestNameSet.addAll(Arrays.asList(tests));
-        }
-
-        Set<String> packageNameSet = new HashSet<>();
-        if ((arg = args.getString(ARGUMENT_TEST_PACKAGE)) != null) {
-            // The packages are specified in a String passed in the bundle
-            String[] packages = arg.split(",");
-            packageNameSet.addAll(Arrays.asList(packages));
-        }
-
-        Set<String> notPackageNameSet = new HashSet<>();
-        if ((arg = args.getString(ARGUMENT_NOT_TEST_PACKAGE)) != null) {
-            // The packages are specified in a String passed in the bundle
-            String[] packages = arg.split(",");
-            notPackageNameSet.addAll(Arrays.asList(packages));
-        }
-
-        List<String> roots = getRootClassNames(args);
-        if (roots == null) {
-            // Find all test classes
-            Collection<Class<?>> classes = TestClassFinder.getClasses(
-                Collections.singletonList(getContext().getPackageCodePath()),
-                getClass().getClassLoader());
-            testList = new TestList(classes);
-        } else {
-            testList = TestList.rootList(roots);
-        }
-
-        testList.addIncludeTestPackages(packageNameSet);
-        testList.addExcludeTestPackages(notPackageNameSet);
-        testList.addIncludeTests(testNameSet);
-        testList.addExcludeTests(notTestNameSet);
-
-        listenerClasses = new ArrayList<>();
-        String listenerArg = args.getString(ARGUMENT_CORE_LISTENER);
-        if (listenerArg != null) {
-            List<String> listenerClassNames = CLASS_LIST_SPLITTER.splitToList(listenerArg);
-            for (String listenerClassName : listenerClassNames) {
-                try {
-                    Class<? extends RunListener> listenerClass = Class.forName(listenerClassName)
-                            .asSubclass(RunListener.class);
-                    listenerClasses.add(listenerClass);
-                } catch (ClassNotFoundException e) {
-                    Log.e(TAG, "Could not load listener class: " + listenerClassName, e);
-                }
-            }
-        }
-
-        start();
-    }
-
-    private List<String> getRootClassNames(Bundle args) {
-        String rootClasses = args.getString(ARGUMENT_ROOT_CLASSES);
-        List<String> roots;
-        if (rootClasses == null) {
-            roots = null;
-        } else {
-            roots = CLASS_LIST_SPLITTER.splitToList(rootClasses);
-        }
-        return roots;
-    }
-
-    @Override
-    public void onStart() {
-        if (logOnly) {
-            Log.d(TAG, "Counting/logging tests only");
-        } else {
-            Log.d(TAG, "Running tests");
-        }
-
-        AndroidRunnerParams runnerParams = new AndroidRunnerParams(this, args,
-                false, testTimeout, false /*ignoreSuiteMethods*/);
-
-        Runner runner;
-        try {
-            RunnerBuilder runnerBuilder = new ExtendedAndroidRunnerBuilder(runnerParams);
-            Class[] classes = testList.getClassesToRun();
-            for (Class cls : classes) {
-              Log.d(TAG, "Found class to run: " + cls.getName());
-            }
-            runner = new Computer().getSuite(runnerBuilder, classes);
-
-            if (runner instanceof Filterable) {
-                Log.d(TAG, "Applying filters");
-                Filterable filterable = (Filterable) runner;
-
-                // Filter out all the tests that are expected to fail.
-                try {
-                    filterable.filter(expectationFilter);
-                } catch (NoTestsRemainException e) {
-                    // Sometimes filtering will remove all tests but we do not care about that.
-                }
-                Log.d(TAG, "Applied filters");
-            }
-
-            // If the tests are only supposed to be logged and not actually run then replace the
-            // runner with a runner that will fire notifications for all the tests that would have
-            // been run. This is needed because CTSv2 does a log only run through a CTS module in
-            // order to generate a list of tests that will be run so that it can monitor them.
-            // Encapsulating that in a Runner implementation makes it easier to leverage the
-            // existing code for running tests.
-            if (logOnly) {
-                runner = new DescriptionHierarchyNotifier(runner.getDescription());
-            }
-
-        } catch (InitializationError e) {
-            throw new RuntimeException("Could not create a suite", e);
-        }
-
-        InstrumentationResultPrinter instrumentationResultPrinter =
-                new InstrumentationResultPrinter();
-        instrumentationResultPrinter.setInstrumentation(this);
-
-        JUnitCore core = new JUnitCore();
-        core.addListener(instrumentationResultPrinter);
-
-        // If not logging the list of tests then add any additional configured listeners. These
-        // must be added before firing any events.
-        if (!logOnly) {
-            // Add additional configured listeners.
-            for (Class<? extends RunListener> listenerClass : listenerClasses) {
-                try {
-                    RunListener runListener = listenerClass.newInstance();
-                    if (runListener instanceof InstrumentationRunListener) {
-                        ((InstrumentationRunListener) runListener).setInstrumentation(this);
-                    }
-                    core.addListener(runListener);
-                } catch (InstantiationException | IllegalAccessException e) {
-                    Log.e(TAG,
-                            "Could not create instance of listener: " + listenerClass, e);
-                }
-            }
-        }
-
-        Log.d(TAG, "Finished preparations, running/listing tests");
-
-        Bundle results = new Bundle();
-        Result junitResults = new Result();
-        try {
-            junitResults = core.run(Request.runner(runner));
-        } catch (RuntimeException e) {
-            final String msg = "Fatal exception when running tests";
-            Log.e(TAG, msg, e);
-            // report the exception to instrumentation out
-            results.putString(Instrumentation.REPORT_KEY_STREAMRESULT,
-                    msg + "\n" + Log.getStackTraceString(e));
-        } finally {
-            ByteArrayOutputStream summaryStream = new ByteArrayOutputStream();
-            // create the stream used to output summary data to the user
-            PrintStream summaryWriter = new PrintStream(summaryStream);
-            instrumentationResultPrinter.instrumentationRunFinished(summaryWriter,
-                    results, junitResults);
-            summaryWriter.close();
-            results.putString(Instrumentation.REPORT_KEY_STREAMRESULT,
-                    String.format("\n%s", summaryStream.toString()));
-        }
-
-
-        Log.d(TAG, "Finished");
-        finish(Activity.RESULT_OK, results);
-    }
-
-    /**
-     * Read tests from a specified file.
-     *
-     * @return class names of tests. If there was an error reading the file, null is returned.
-     */
-    private static List<String> readTestsFromFile(String fileName) throws IOException {
-        try (BufferedReader br = new BufferedReader(new FileReader(fileName))) {
-            List<String> tests = new ArrayList<>();
-            String line;
-            while ((line = br.readLine()) != null) {
-                tests.add(line);
-            }
-            return tests;
-        } catch (IOException err) {
-            Log.e(TAG, "There was an error reading the test class list: " + err.getMessage());
-            throw err;
-        }
-    }
-
-    /**
-     * Parse long from given value - except either Long or String.
-     *
-     * @return the value, -1 if not found
-     * @throws NumberFormatException if value is negative or not a number
-     */
-    private static long parseUnsignedLong(Object value, String name) {
-        if (value != null) {
-            long longValue = Long.parseLong(value.toString());
-            if (longValue < 0) {
-                throw new NumberFormatException(name + " can not be negative");
-            }
-            return longValue;
-        }
-        return -1;
-    }
-}
diff --git a/tests/core/runner/src/com/android/cts/core/runner/DescriptionHierarchyNotifier.java b/tests/core/runner/src/com/android/cts/core/runner/DescriptionHierarchyNotifier.java
deleted file mode 100644
index 5c94fd7..0000000
--- a/tests/core/runner/src/com/android/cts/core/runner/DescriptionHierarchyNotifier.java
+++ /dev/null
@@ -1,62 +0,0 @@
-/*
- * Copyright (C) 2016 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package com.android.cts.core.runner;
-
-import java.util.List;
-import org.junit.runner.Description;
-import org.junit.runner.Runner;
-import org.junit.runner.notification.RunNotifier;
-
-/**
- * A {@link Runner} that does not actually run any tests but simply fires events for all leaf
- * {@link Description} instances in the supplied {@link Description} hierarchy.
- */
-class DescriptionHierarchyNotifier extends Runner {
-
-    private final Description description;
-
-    DescriptionHierarchyNotifier(Description description) {
-        this.description = description;
-    }
-
-    @Override
-    public Description getDescription() {
-        return description;
-    }
-
-    @Override
-    public void run(RunNotifier notifier) {
-        generateListOfTests(notifier, description);
-    }
-
-    /**
-     * Generates a list of tests to run by recursing over the {@link Description} hierarchy and
-     * firing events to simulate the tests being run successfully.
-     * @param runNotifier the notifier to which the events are sent.
-     * @param description the description to traverse.
-     */
-    private void generateListOfTests(RunNotifier runNotifier, Description description) {
-        List<Description> children = description.getChildren();
-        if (children.isEmpty()) {
-            runNotifier.fireTestStarted(description);
-            runNotifier.fireTestFinished(description);
-        } else {
-            for (Description child : children) {
-                generateListOfTests(runNotifier, child);
-            }
-        }
-    }
-}
diff --git a/tests/core/runner/src/com/android/cts/core/runner/ExpectationBasedFilter.java b/tests/core/runner/src/com/android/cts/core/runner/ExpectationBasedFilter.java
index 90034ec..f409dbd 100644
--- a/tests/core/runner/src/com/android/cts/core/runner/ExpectationBasedFilter.java
+++ b/tests/core/runner/src/com/android/cts/core/runner/ExpectationBasedFilter.java
@@ -105,7 +105,7 @@
             if (expectationStore != null) {
                 Expectation expectation = expectationStore.get(testName);
                 if (expectation.getResult() != Result.SUCCESS) {
-                    Log.d(CoreTestRunner.TAG, "Excluding test " + testDescription
+                    Log.d(TAG, "Excluding test " + testDescription
                             + " as it matches expectation: " + expectation);
                     return false;
                 }
diff --git a/tests/core/runner/src/com/android/cts/core/runner/TestClassFinder.java b/tests/core/runner/src/com/android/cts/core/runner/TestClassFinder.java
deleted file mode 100644
index 6ad95f6..0000000
--- a/tests/core/runner/src/com/android/cts/core/runner/TestClassFinder.java
+++ /dev/null
@@ -1,151 +0,0 @@
-/*
- * Copyright (C) 2016 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.cts.core.runner;
-
-import android.support.test.internal.runner.ClassPathScanner;
-import android.support.test.internal.runner.ClassPathScanner.ClassNameFilter;
-import android.util.Log;
-
-import com.android.cts.core.internal.runner.TestLoader;
-
-import java.io.IOException;
-import java.util.ArrayList;
-import java.util.Collection;
-import java.util.Enumeration;
-import java.util.List;
-import java.util.Set;
-
-import dalvik.system.DexFile;
-
-/**
- * Find tests in the current APK.
- */
-public class TestClassFinder {
-
-    private static final String TAG = "TestClassFinder";
-    private static final boolean DEBUG = false;
-
-    // Excluded test packages
-    private static final String[] DEFAULT_EXCLUDED_PACKAGES = {
-            "junit",
-            "org.junit",
-            "org.hamcrest",
-            "org.mockito",// exclude Mockito for performance and to prevent JVM related errors
-            "android.support.test.internal.runner.junit3",// always skip AndroidTestSuite
-    };
-
-    static Collection<Class<?>> getClasses(List<String> apks, ClassLoader loader) {
-        if (DEBUG) {
-          Log.d(TAG, "getClasses: =======================================");
-
-          for (String apkPath : apks) {
-            Log.d(TAG, "getClasses: -------------------------------");
-            Log.d(TAG, "getClasses: APK " + apkPath);
-
-            DexFile dexFile = null;
-            try {
-                dexFile = new DexFile(apkPath);
-                Enumeration<String> apkClassNames = dexFile.entries();
-                while (apkClassNames.hasMoreElements()) {
-                    String apkClassName = apkClassNames.nextElement();
-                    Log.d(TAG, "getClasses: DexClass element " + apkClassName);
-                }
-            } catch (IOException e) {
-              throw new AssertionError(e);
-            } finally {
-                if (dexFile != null) {
-                  try {
-                    dexFile.close();
-                  } catch (IOException e) {
-                    throw new AssertionError(e);
-                  }
-                }
-            }
-            Log.d(TAG, "getClasses: -------------------------------");
-          }
-        }  // if DEBUG
-
-        List<Class<?>> classes = new ArrayList<>();
-        ClassPathScanner scanner = new ClassPathScanner(apks);
-
-        ClassPathScanner.ChainedClassNameFilter filter =
-                new ClassPathScanner.ChainedClassNameFilter();
-        // exclude inner classes
-        filter.add(new ClassPathScanner.ExternalClassNameFilter());
-
-        // exclude default classes
-        for (String defaultExcludedPackage : DEFAULT_EXCLUDED_PACKAGES) {
-            filter.add(new ExcludePackageNameFilter(defaultExcludedPackage));
-        }
-
-        // exclude any classes that aren't a "test class" (see #loadIfTest)
-        TestLoader testLoader = new TestLoader();
-        testLoader.setClassLoader(loader);
-
-        try {
-            Set<String> classNames = scanner.getClassPathEntries(filter);
-            for (String className : classNames) {
-                // Important: This further acts as an additional filter;
-                // classes that aren't a "test class" are never loaded.
-                Class<?> cls = testLoader.loadIfTest(className);
-                if (cls != null) {
-                    classes.add(cls);
-
-                    if (DEBUG) {
-                      Log.d(TAG, "getClasses: Loaded " + className);
-                    }
-                } else if (DEBUG) {
-                  Log.d(TAG, "getClasses: Failed to load class " + className);
-                }
-            }
-            return classes;
-        } catch (IOException e) {
-            Log.e(CoreTestRunner.TAG, "Failed to scan classes", e);
-        }
-
-
-        if (DEBUG) {
-            Log.d(TAG, "getClasses: =======================================");
-        }
-
-        return testLoader.getLoadedClasses();
-    }
-
-    /**
-     * A {@link ClassNameFilter} that only rejects a given package names within the given namespace.
-     */
-    public static class ExcludePackageNameFilter implements ClassNameFilter {
-
-        private final String mPkgName;
-
-        ExcludePackageNameFilter(String pkgName) {
-            if (!pkgName.endsWith(".")) {
-                mPkgName = String.format("%s.", pkgName);
-            } else {
-                mPkgName = pkgName;
-            }
-        }
-
-        /**
-         * {@inheritDoc}
-         */
-        @Override
-        public boolean accept(String pathName) {
-            return !pathName.startsWith(mPkgName);
-        }
-    }
-}
diff --git a/tests/core/runner/src/com/android/cts/core/runner/TestList.java b/tests/core/runner/src/com/android/cts/core/runner/TestList.java
deleted file mode 100644
index 9d97501..0000000
--- a/tests/core/runner/src/com/android/cts/core/runner/TestList.java
+++ /dev/null
@@ -1,137 +0,0 @@
-/*
- * Copyright (C) 2016 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package com.android.cts.core.runner;
-
-import android.util.Log;
-import java.util.ArrayList;
-import java.util.Collection;
-import java.util.HashSet;
-import java.util.LinkedHashSet;
-import java.util.List;
-import java.util.Set;
-import javax.annotation.Nullable;
-
-/**
- * A list of the tests to run.
- */
-class TestList {
-
-    /** The set of test pacakges to run */
-    private final Set<String> mIncludedPackages = new HashSet<>();
-
-    /** The set of test packages not to run */
-    private final Set<String> mExcludedPackages = new HashSet<>();
-
-    /** The set of tests (classes or methods) to run */
-    private final Set<String> mIncludedTests = new HashSet<>();
-
-    /** The set of tests (classes or methods) not to run */
-    private final Set<String> mExcludedTests = new HashSet<>();
-
-    /** The list of all test classes to run (without filtering applied)*/
-    private final Collection<Class<?>> classesToRun;
-
-    public static TestList rootList(List<String> rootList) {
-
-        // Run from the root test class.
-        Set<String> classNamesToRun = new LinkedHashSet<>(rootList);
-        Log.d(CoreTestRunner.TAG, "Running all tests rooted at " + classNamesToRun);
-
-        List<Class<?>> classesToRun1 = getClasses(classNamesToRun);
-
-        return new TestList(classesToRun1);
-    }
-
-    private static List<Class<?>> getClasses(Set<String> classNames) {
-        // Populate the list of classes to run.
-        List<Class<?>> classesToRun = new ArrayList<>();
-        for (String className : classNames) {
-            try {
-                classesToRun.add(Class.forName(className));
-            } catch (ClassNotFoundException e) {
-                throw new IllegalStateException("Could not load class '" + className, e);
-            }
-        }
-        return classesToRun;
-    }
-
-    /**
-     * @param classes The list of classes to run.
-     */
-    public TestList(Collection<Class<?>> classes) {
-        this.classesToRun = classes;
-    }
-
-    public void addIncludeTestPackages(Set<String> packageNameSet) {
-        mIncludedPackages.addAll(packageNameSet);
-    }
-
-    public void addExcludeTestPackages(Set<String> packageNameSet) {
-        mExcludedPackages.addAll(packageNameSet);
-    }
-
-    public void addIncludeTests(Set<String> testNameSet) {
-        mIncludedTests.addAll(testNameSet);
-    }
-
-    public void addExcludeTests(Set<String> testNameSet) {
-        mExcludedTests.addAll(testNameSet);
-    }
-
-    /**
-     * Return all the classes to run.
-     */
-    public Class[] getClassesToRun() {
-        return classesToRun.toArray(new Class[classesToRun.size()]);
-    }
-
-    /**
-     * Return true if the test with the specified name should be run, false otherwise.
-     */
-    public boolean shouldRunTest(String testName) {
-
-        int index = testName.indexOf('#');
-        String className;
-        if (index == -1) {
-            className = testName;
-        } else {
-            className = testName.substring(0, index);
-        }
-        try {
-            Class<?> testClass = Class.forName(className);
-            Package testPackage = testClass.getPackage();
-            String testPackageName = "";
-            if (testPackage != null) {
-                testPackageName = testPackage.getName();
-            }
-
-            boolean include =
-                    (mIncludedPackages.isEmpty() || mIncludedPackages.contains(testPackageName)) &&
-                    (mIncludedTests.isEmpty() || mIncludedTests.contains(className) ||
-                            mIncludedTests.contains(testName));
-
-            boolean exclude =
-                    mExcludedPackages.contains(testPackageName) ||
-                    mExcludedTests.contains(className) ||
-                    mExcludedTests.contains(testName);
-
-            return include && !exclude;
-        } catch (ClassNotFoundException e) {
-            Log.w("Could not load class '" + className, e);
-            return false;
-        }
-    }
-}
diff --git a/tests/core/runner/src/com/android/cts/core/runner/support/AndroidRunnerBuilder.java b/tests/core/runner/src/com/android/cts/core/runner/support/AndroidRunnerBuilder.java
deleted file mode 100644
index 91df2b9..0000000
--- a/tests/core/runner/src/com/android/cts/core/runner/support/AndroidRunnerBuilder.java
+++ /dev/null
@@ -1,82 +0,0 @@
-/*
- * Copyright (C) 2016 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package com.android.cts.core.runner.support;
-
-import android.support.test.internal.runner.junit3.AndroidJUnit3Builder;
-import android.support.test.internal.runner.junit3.AndroidSuiteBuilder;
-import android.support.test.internal.runner.junit4.AndroidAnnotatedBuilder;
-import android.support.test.internal.runner.junit4.AndroidJUnit4Builder;
-import android.support.test.internal.util.AndroidRunnerParams;
-
-import org.junit.internal.builders.AllDefaultPossibilitiesBuilder;
-import org.junit.internal.builders.AnnotatedBuilder;
-import org.junit.internal.builders.IgnoredBuilder;
-import org.junit.internal.builders.JUnit3Builder;
-import org.junit.internal.builders.JUnit4Builder;
-import org.junit.runners.model.RunnerBuilder;
-
-/**
- * A {@link RunnerBuilder} that can handle all types of tests.
- */
-// A copy of package private class android.support.test.internal.runner.AndroidRunnerBuilder.
-// Copied here so that it can be extended.
-class AndroidRunnerBuilder extends AllDefaultPossibilitiesBuilder {
-
-    private final AndroidJUnit3Builder mAndroidJUnit3Builder;
-    private final AndroidJUnit4Builder mAndroidJUnit4Builder;
-    private final AndroidSuiteBuilder mAndroidSuiteBuilder;
-    private final AndroidAnnotatedBuilder mAndroidAnnotatedBuilder;
-    // TODO: customize for Android ?
-    private final IgnoredBuilder mIgnoredBuilder;
-
-    /**
-     * @param runnerParams {@link AndroidRunnerParams} that stores common runner parameters
-     */
-    // Added canUseSuiteMethod parameter.
-    AndroidRunnerBuilder(AndroidRunnerParams runnerParams, boolean canUseSuiteMethod) {
-        super(canUseSuiteMethod);
-        mAndroidJUnit3Builder = new AndroidJUnit3Builder(runnerParams);
-        mAndroidJUnit4Builder = new AndroidJUnit4Builder(runnerParams);
-        mAndroidSuiteBuilder = new AndroidSuiteBuilder(runnerParams);
-        mAndroidAnnotatedBuilder = new AndroidAnnotatedBuilder(this, runnerParams);
-        mIgnoredBuilder = new IgnoredBuilder();
-    }
-
-    @Override
-    protected JUnit4Builder junit4Builder() {
-        return mAndroidJUnit4Builder;
-    }
-
-    @Override
-    protected JUnit3Builder junit3Builder() {
-        return mAndroidJUnit3Builder;
-    }
-
-    @Override
-    protected AnnotatedBuilder annotatedBuilder() {
-        return mAndroidAnnotatedBuilder;
-    }
-
-    @Override
-    protected IgnoredBuilder ignoredBuilder() {
-        return mIgnoredBuilder;
-    }
-
-    @Override
-    protected RunnerBuilder suiteMethodBuilder() {
-        return mAndroidSuiteBuilder;
-    }
-}
diff --git a/tests/core/runner/src/com/android/cts/core/runner/support/ExtendedAndroidRunnerBuilder.java b/tests/core/runner/src/com/android/cts/core/runner/support/ExtendedAndroidRunnerBuilder.java
deleted file mode 100644
index a2f95e2..0000000
--- a/tests/core/runner/src/com/android/cts/core/runner/support/ExtendedAndroidRunnerBuilder.java
+++ /dev/null
@@ -1,66 +0,0 @@
-/*
- * Copyright (C) 2016 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.cts.core.runner.support;
-
-import android.support.test.internal.util.AndroidRunnerParams;
-import android.util.Log;
-import org.junit.runners.model.RunnerBuilder;
-import org.junit.runner.Runner;
-
-/**
- * Extends {@link AndroidRunnerBuilder} in order to provide alternate {@link RunnerBuilder}
- * implementations.
- */
-public class ExtendedAndroidRunnerBuilder extends AndroidRunnerBuilder {
-
-    private static final boolean DEBUG = false;
-
-    private final TestNgRunnerBuilder mTestNgBuilder;
-
-    /**
-     * @param runnerParams {@link AndroidRunnerParams} that stores common runner parameters
-     */
-    public ExtendedAndroidRunnerBuilder(AndroidRunnerParams runnerParams) {
-        super(runnerParams, false /* CTSv1 filtered out Test suite() classes. */);
-        mTestNgBuilder = new TestNgRunnerBuilder();
-    }
-
-    @Override
-    public Runner runnerForClass(Class<?> testClass) throws Throwable {
-      if (DEBUG) {
-        Log.d("ExAndRunBuild", "runnerForClass: Searching runner for class " + testClass.getName());
-      }
-
-      // Give TestNG tests a chance to participate in the Runner search first.
-      // (Note that the TestNG runner handles log-only runs by itself)
-      Runner runner = mTestNgBuilder.runnerForClass(testClass);
-      if (runner == null) {
-        // Use the normal Runner search mechanism (for Junit tests).
-        runner = super.runnerForClass(testClass);
-      }
-
-      logFoundRunner(runner);
-      return runner;
-    }
-
-    private static void logFoundRunner(Runner runner) {
-      if (DEBUG) {
-        Log.d("ExAndRunBuild", "runnerForClass: Found runner of type " +
-            ((runner == null) ? "<null>" : runner.getClass().getName()));
-      }
-    }
-}
diff --git a/tests/core/runner/src/com/android/cts/core/runner/support/package-info.java b/tests/core/runner/src/com/android/cts/core/runner/support/package-info.java
deleted file mode 100644
index 3ccec3c..0000000
--- a/tests/core/runner/src/com/android/cts/core/runner/support/package-info.java
+++ /dev/null
@@ -1,26 +0,0 @@
-/*
- * Copyright (C) 2016 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-/**
- * Contains all the changes needed to the {@code android.support.test.internal.runner} classes.
- *
- * <p>As its name suggests {@code android.support.test.internal.runner} are internal classes that
- * are not designed to be extended from outside those packages. This package encapsulates all the
- * workarounds needed to overcome that limitation, from duplicating classes to using reflection.
- * The intention is that these changes are temporary and they (or a better equivalent) will be
- * quickly integrated into the internal classes.
- */
-package com.android.cts.core.runner.support;
diff --git a/tests/dram/AndroidTest.xml b/tests/dram/AndroidTest.xml
index 636c8c1..5c31a36 100644
--- a/tests/dram/AndroidTest.xml
+++ b/tests/dram/AndroidTest.xml
@@ -14,6 +14,7 @@
      limitations under the License.
 -->
 <configuration description="Config for CTS Dram test cases">
+    <option name="test-suite-tag" value="cts" />
     <option name="config-descriptor:metadata" key="component" value="systems" />
     <target_preparer class="com.android.tradefed.targetprep.suite.SuiteApkInstaller">
         <option name="cleanup-apks" value="true" />
diff --git a/tests/expectations/knownfailures.txt b/tests/expectations/knownfailures.txt
deleted file mode 100644
index 08c2a5e..0000000
--- a/tests/expectations/knownfailures.txt
+++ /dev/null
@@ -1,282 +0,0 @@
-[
-{
-  description: "Disable ListeningPortsTest",
-  names: [
-    "android.security.cts.ListeningPortsTest"
-  ],
-  bug: 31803630
-},
-{
-  description: "some AlarmClockTests are not robust across different device types",
-  names: [
-    "android.alarmclock.cts.DismissAlarmTest#testAll",
-    "android.alarmclock.cts.SetAlarmTest#testAll",
-    "android.alarmclock.cts.SnoozeAlarmTest#testAll"
-  ],
-  bug: 23776083
-},
-{
-  description: "the UsageStats is not yet stable enough",
-  names: [
-    "android.app.usage.cts.UsageStatsTest"
-  ],
-  bug: 17536113
-},
-{
-  description: "the ConnectivityConstraintTest are not yet stable",
-  names: [
-    "android.jobscheduler.cts.ConnectivityConstraintTest"
-  ],
-  bug: 18117279
-},
-{
-  description: "tests a fragile by nature as they rely on hardcoded behavior",
-  names: [
-    "android.accessibilityservice.cts.AccessibilityTextTraversalTest#testActionNextAndPreviousAtGranularityPageOverText",
-    "android.accessibilityservice.cts.AccessibilityTextTraversalTest#testActionNextAndPreviousAtGranularityPageOverTextExtend"
-  ],
-  bug: 17595050
-},
-{
-  description: "test fails on some devices",
-  names: [
-    "android.dumpsys.cts.DumpsysHostTest#testBatterystatsOutput",
-    "android.dumpsys.cts.DumpsysHostTest#testGfxinfoFramestats"
-  ],
-  bug: 23776893
-},
-{
-  description: "the SSLCertificateSocketFactoryTest often fails because of lack of live internet or short timeout, it should be refactored to do a local server testing",
-  names: [
-    "android.net.cts.SSLCertificateSocketFactoryTest#testCreateSocket",
-    "android.net.cts.SSLCertificateSocketFactoryTest#test_createSocket_bind",
-    "android.net.cts.SSLCertificateSocketFactoryTest#test_createSocket_simple",
-    "android.net.cts.SSLCertificateSocketFactoryTest#test_createSocket_wrapping"
-  ],
-  bug: 18682315
-},
-{
-  description: "the test result are too much dependent on live-internet connection, which for some devices might not exist",
-  names: [
-    "android.net.wifi.cts.NsdManagerTest#testAndroidTestCaseSetupProperly"
-  ],
-  bug: 18680089
-},
-{
-  description: "AudioPolicyBinder tests are not yet robust enough",
-  names: [
-    "android.security.cts.AudioPolicyBinderTest"
-  ],
-  bug: 18461670
-},
-{
-  description: "test not robust",
-  names: [
-    "android.telecom.cts.ExtendedInCallServiceTest#testAddNewOutgoingCallAndThenDisconnect",
-    "android.telecom.cts.RemoteConferenceTest#testRemoteConferenceCallbacks_ConferenceableConnections"
-  ],
-  bug: 23604254
-},
-{
-  description: "tests too flaky",
-  names: [
-    "android.transition.cts.ChangeScrollTest#testChangeScroll"
-  ],
-  bug: 23779020
-},
-{
-  description: "Not all jdwp features are currently supported. These tests will fail",
-  names: [
-    "org.apache.harmony.jpda.tests.jdwp.DebuggerOnDemand.OnthrowDebuggerLaunchTest#testDebuggerLaunch001",
-    "org.apache.harmony.jpda.tests.jdwp.DebuggerOnDemand.OnthrowDebuggerLaunchTest#testDebuggerLaunch002",
-    "org.apache.harmony.jpda.tests.jdwp.DebuggerOnDemand.OnthrowDebuggerLaunchTest#testDebuggerLaunch003",
-    "org.apache.harmony.jpda.tests.jdwp.DebuggerOnDemand.OnthrowDebuggerLaunchTest#testDebuggerLaunch004",
-    "org.apache.harmony.jpda.tests.jdwp.DebuggerOnDemand.OnthrowLaunchDebugger001#testDebugger002",
-    "org.apache.harmony.jpda.tests.jdwp.DebuggerOnDemand.OnthrowLaunchDebugger002#testDebugger",
-    "org.apache.harmony.jpda.tests.jdwp.Events.ClassUnloadTest#testClassUnloadEvent",
-    "org.apache.harmony.jpda.tests.jdwp.Events.MonitorContendedEnterTest#testMonitorContendedEnterForClassMatch",
-    "org.apache.harmony.jpda.tests.jdwp.Events.MonitorContendedEnteredTest#testMonitorContendedEnteredForClassMatch",
-    "org.apache.harmony.jpda.tests.jdwp.Events.MonitorWaitTest#testMonitorWaitForClassExclude",
-    "org.apache.harmony.jpda.tests.jdwp.Events.MonitorWaitTest#testMonitorWaitForClassMatchExact",
-    "org.apache.harmony.jpda.tests.jdwp.Events.MonitorWaitTest#testMonitorWaitForClassMatchFirst",
-    "org.apache.harmony.jpda.tests.jdwp.Events.MonitorWaitTest#testMonitorWaitForClassMatchSecond",
-    "org.apache.harmony.jpda.tests.jdwp.Events.MonitorWaitTest#testMonitorWaitForClassOnly",
-    "org.apache.harmony.jpda.tests.jdwp.Events.MonitorWaitedTest#testMonitorWaitedForClassExclude",
-    "org.apache.harmony.jpda.tests.jdwp.Events.MonitorWaitedTest#testMonitorWaitedForClassMatchExact",
-    "org.apache.harmony.jpda.tests.jdwp.Events.MonitorWaitedTest#testMonitorWaitedForClassMatchFirst",
-    "org.apache.harmony.jpda.tests.jdwp.Events.MonitorWaitedTest#testMonitorWaitedForClassMatchSecond",
-    "org.apache.harmony.jpda.tests.jdwp.Events.MonitorWaitedTest#testMonitorWaitedForClassOnly",
-    "org.apache.harmony.jpda.tests.jdwp.ReferenceType.ClassFileVersionTest#testClassFileVersion001",
-    "org.apache.harmony.jpda.tests.jdwp.ReferenceType.NestedTypesTest#testNestedTypes001",
-    "org.apache.harmony.jpda.tests.jdwp.ThreadReference.StopTest#testStop001",
-    "org.apache.harmony.jpda.tests.jdwp.VirtualMachine.HoldEventsTest#testHoldEvents001",
-    "org.apache.harmony.jpda.tests.jdwp.VirtualMachine.ReleaseEventsTest#testReleaseEvents001"
-  ],
-  bug: 16720689
-},
-{
-  description: "test can only run properly on a user build device when the bug is resolved",
-  names: [
-    "android.appwidget.cts.AppWidgetTest#testAppWidgetProviderCallbacks",
-    "android.appwidget.cts.AppWidgetTest#testBindAppWidget",
-    "android.appwidget.cts.AppWidgetTest#testCollectionWidgets",
-    "android.appwidget.cts.AppWidgetTest#testDeleteHost",
-    "android.appwidget.cts.AppWidgetTest#testDeleteHosts",
-    "android.appwidget.cts.AppWidgetTest#testGetAppWidgetIds",
-    "android.appwidget.cts.AppWidgetTest#testGetAppWidgetInfo",
-    "android.appwidget.cts.AppWidgetTest#testGetAppWidgetOptions",
-    "android.appwidget.cts.AppWidgetTest#testPartiallyUpdateAppWidgetViaWidgetId",
-    "android.appwidget.cts.AppWidgetTest#testPartiallyUpdateAppWidgetViaWidgetIds",
-    "android.appwidget.cts.AppWidgetTest#testTwoAppWidgetProviderCallbacks",
-    "android.appwidget.cts.AppWidgetTest#testUpdateAppWidgetViaComponentName",
-    "android.appwidget.cts.AppWidgetTest#testUpdateAppWidgetViaWidgetId",
-    "android.appwidget.cts.AppWidgetTest#testUpdateAppWidgetViaWidgetIds"
-  ],
-  bug: 17993121
-},
-{
-  description: "permissions for the API previously used in the test has changed, making it impossible to pass",
-  names: [
-    "android.openglperf.cts.GlAppSwitchTest#testGlActivitySwitchingFast",
-    "android.openglperf.cts.GlAppSwitchTest#testGlActivitySwitchingSlow"
-  ],
-  bug: 17394321
-},
-{
-  description: "unexpected failures",
-  names: [
-    "android.openglperf.cts.GlVboPerfTest#testVboWithVaryingIndexBufferNumbers"
-  ],
-  bug: 18091590
-},
-{
-  description: "Test is not yet properly implemented",
-  names: [
-    "android.voicesettings.cts.ZenModeTest#testAll"
-  ],
-  bug: 23238984
-},
-{
-  description: "This test failed on devices that use effect off loading. In addition it uses hidden apis",
-  names: [
-    "android.media.cts.AudioEffectTest#test1_1ConstructorFromUuid"
-  ],
-  bug: 17605875
-},
-{
-  description: "This test failed on hw decoder that doesn't output frame with the configured format.",
-  names: [
-    "android.media.cts.ImageReaderDecoderTest#testHwAVCDecode360pForFlexibleYuv"
-  ],
-  bug: 17144778
-},
-{
-  description: "android.keystore tests will replace these tests",
-  names: [
-    "com.android.org.conscrypt.MacTest#test_getInstance_OpenSSL_ENGINE",
-    "com.android.org.conscrypt.NativeCryptoTest#test_ENGINE_by_id_TestEngine",
-    "com.android.org.conscrypt.SignatureTest#test_getInstance_OpenSSL_ENGINE"
-  ],
-  bug: 18030049
-},
-{
-  description: "The new prepare performance test is not yet passing on all devices",
-  names: [
-    "android.hardware.camera2.cts.SurfaceViewPreviewTest#testPreparePerformance"
-  ],
-  bug: 17989532
-},
-{
-  description: "The timing measurements for preview callbacks are not reliable",
-  names: [
-    "android.hardware.cts.CameraTest#testPreviewFpsRange"
-  ],
-  bug: 23008511
-},
-{
-  description: "Light status bar CTS coming in late",
-  names: [
-    "android.systemui.cts.LightStatusBarTests#testLightStatusBarIcons"
-  ],
-  bug: 23427621
-},
-{
-  description: "tests are not yet ready",
-  names: [
-    "com.android.cts.app.os.OsHostTests#testNonExportedActivities"
-  ],
-  bug: 23779168
-},
-{
-  description: "ConnectivityConstraintTest job scheduler not working.",
-  names: [
-     "android.jobscheduler.cts.ConnectivityConstraintTest#testConnectivityConstraintExecutes_withWifi",
-     "android.jobscheduler.cts.ConnectivityConstraintTest#testUnmeteredConstraintExecutes_withWifi",
-     "android.jobscheduler.cts.ConnectivityConstraintTest#testConnectivityConstraintExecutes_withMobile"
-  ],
-  bug: 21262226
-},
-{
-   description: "ConnectivityConstraintTest times out.",
-   names: [
-     "android.jobscheduler.cts.TimingConstraintsTest#testJobParameters_unexpiredDeadline"
-   ],
-   bug: 23144425
-},
-{
-   description: "Video encoding tests are timing out.",
-   names: [
-     "android.media.cts.VideoEncoderTest#testGoogH264FlexArbitraryW",
-     "android.media.cts.VideoEncoderTest#testGoogH264SurfArbitraryW"
-   ],
-   bug: 23827982
-},
-{
-  description: "protected broadcast not working",
-  names: [
-   "android.permission2.cts.ProtectedBroadcastsTest#testSendProtectedBroadcasts"
-  ],
-  bug: 23192492
-},
-{
-  description: "restricted network is not working",
-  names: [
-    "android.net.cts.ConnectivityManagerTest#testRestrictedNetworks"
-  ],
-  bug: 25651805
-},
-{
-  description: "unit testing for MediaPreparer lives within mediastress module",
-  names: [
-    "android.mediastress.cts.preconditions.MediaPreparerTest"
-  ],
-  bug: 25850508
-},
-{
-  description: "Tests for the signature tests should not be in CTS",
-  names: [
-    "android.signature.cts.tests"
-  ],
-  bug: 26150806
-},
-{
-  description: "android.security.cts is using a Non-NDK library, libmedia_jni.so",
-  names: [
-      "android.security.cts.MediaCryptoTest#testMediaCryptoClearKey",
-      "android.security.cts.MediaCryptoTest#testMediaCryptoWidevine"
-  ],
-  bug: 27218502
-},
-{
-  description: "Still investigating this, root cause unknown yet",
-  bug: 27578806,
-  names: ["com.android.cts.cpptools.RunAsHostTest#testRunAs"]
-},
-{
-  description: "Wired headset tests are no longer a requirement per CDD",
-  names: [
-    "android.telecom.cts.WiredHeadsetTest"
-  ],
-  bug: 26149528
-}
-]
diff --git a/tests/filesystem/AndroidTest.xml b/tests/filesystem/AndroidTest.xml
index 5f3923b..fed22df 100644
--- a/tests/filesystem/AndroidTest.xml
+++ b/tests/filesystem/AndroidTest.xml
@@ -14,6 +14,7 @@
      limitations under the License.
 -->
 <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" />
     <target_preparer class="com.android.tradefed.targetprep.suite.SuiteApkInstaller">
         <option name="cleanup-apks" value="true" />
diff --git a/tests/fragment/Android.mk b/tests/fragment/Android.mk
index 1785097..0a54354 100644
--- a/tests/fragment/Android.mk
+++ b/tests/fragment/Android.mk
@@ -33,8 +33,8 @@
     mockito-target-minus-junit4 \
     android-common \
     compatibility-device-util \
-    ctstestrunner \
-    legacy-android-test
+    ctstestrunner
+LOCAL_JAVA_LIBRARIES := android.test.base.stubs
 #LOCAL_STATIC_JAVA_LIBRARIES := compatibility-device-util android-support-test
 
 LOCAL_SRC_FILES := $(call all-java-files-under, src)
diff --git a/tests/fragment/AndroidTest.xml b/tests/fragment/AndroidTest.xml
index 4e563d9..f352ef2 100644
--- a/tests/fragment/AndroidTest.xml
+++ b/tests/fragment/AndroidTest.xml
@@ -14,6 +14,7 @@
      limitations under the License.
 -->
 <configuration description="Configuration for app.usage Tests">
+    <option name="test-suite-tag" value="cts" />
     <option name="config-descriptor:metadata" key="component" value="uitoolkit" />
     <target_preparer class="com.android.tradefed.targetprep.suite.SuiteApkInstaller">
         <option name="cleanup-apks" value="true" />
diff --git a/hostsidetests/services/Android.mk b/tests/framework/Android.mk
similarity index 100%
rename from hostsidetests/services/Android.mk
rename to tests/framework/Android.mk
diff --git a/hostsidetests/services/Android.mk b/tests/framework/base/Android.mk
similarity index 100%
copy from hostsidetests/services/Android.mk
copy to tests/framework/base/Android.mk
diff --git a/tests/framework/base/activitymanager/Android.mk b/tests/framework/base/activitymanager/Android.mk
new file mode 100644
index 0000000..ee5ff8c
--- /dev/null
+++ b/tests/framework/base/activitymanager/Android.mk
@@ -0,0 +1,39 @@
+# 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.
+
+LOCAL_PATH := $(call my-dir)
+
+include $(CLEAR_VARS)
+
+LOCAL_MODULE_TAGS := tests optional
+
+# Must match the package name in CtsTestCaseList.mk
+LOCAL_PACKAGE_NAME := CtsActivityManagerDeviceTestCases
+
+LOCAL_SRC_FILES := $(call all-java-files-under, src)
+
+LOCAL_STATIC_JAVA_LIBRARIES := \
+    android-support-test \
+    cts-amwm-util \
+    cts-display-service-app-util
+
+LOCAL_CTS_TEST_PACKAGE := android.server
+
+# Tag this module as a cts test artifact
+LOCAL_COMPATIBILITY_SUITE := cts vts general-tests
+
+include $(BUILD_CTS_PACKAGE)
+
+# Build the test APKs using their own makefiles
+include $(call all-makefiles-under,$(LOCAL_PATH))
diff --git a/tests/framework/base/activitymanager/AndroidManifest.xml b/tests/framework/base/activitymanager/AndroidManifest.xml
new file mode 100644
index 0000000..15c07be
--- /dev/null
+++ b/tests/framework/base/activitymanager/AndroidManifest.xml
@@ -0,0 +1,67 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT 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.server.cts.am">
+
+    <uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />
+    <uses-permission android:name="android.permission.READ_PHONE_STATE" />
+    <uses-permission android:name="android.permission.MANAGE_ACTIVITY_STACKS" />
+    <uses-permission android:name="android.permission.ACTIVITY_EMBEDDING" />
+    <uses-permission android:name="android.permission.WRITE_SECURE_SETTINGS" />
+
+    <application android:label="CtsActivityManagerDeviceTestCases">
+        <uses-library android:name="android.test.runner" />
+
+        <activity
+            android:name="android.server.am.AspectRatioTests$MaxAspectRatioActivity"
+            android:label="MaxAspectRatioActivity"
+            android:maxAspectRatio="1.0"
+            android:resizeableActivity="false" />
+
+        <activity
+            android:name="android.server.am.AspectRatioTests$MetaDataMaxAspectRatioActivity"
+            android:label="MetaDataMaxAspectRatioActivity"
+            android:resizeableActivity="false">
+            <meta-data
+                android:name="android.max_aspect"
+                android:value="1.0" />
+        </activity>
+
+        <activity
+            android:name="android.server.am.AspectRatioTests$MaxAspectRatioResizeableActivity"
+            android:label="MaxAspectRatioResizeableActivity"
+            android:maxAspectRatio="1.0"
+            android:resizeableActivity="true" />
+
+        <activity
+            android:name="android.server.am.AspectRatioTests$MaxAspectRatioUnsetActivity"
+            android:label="MaxAspectRatioUnsetActivity"
+            android:resizeableActivity="false" />
+
+        <activity android:name="android.server.am.lifecycle.ActivityLifecycleClientTestBase$FirstActivity" />
+
+        <activity android:name="android.server.am.lifecycle.ActivityLifecycleClientTestBase$SecondActivity"/>
+
+    </application>
+
+    <instrumentation
+        android:name="android.support.test.runner.AndroidJUnitRunner"
+        android:label="CTS tests of ActivityManager"
+        android:targetPackage="android.server.cts.am" />
+
+</manifest>
diff --git a/tests/framework/base/activitymanager/AndroidTest.xml b/tests/framework/base/activitymanager/AndroidTest.xml
new file mode 100644
index 0000000..bcdaf5c
--- /dev/null
+++ b/tests/framework/base/activitymanager/AndroidTest.xml
@@ -0,0 +1,37 @@
+<?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 ActivityManager test cases">
+    <option name="test-suite-tag" value="cts" />
+    <option name="config-descriptor:metadata" key="component" value="framework" />
+    <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" />
+        <option name="test-file-name" value="CtsActivityManagerDeviceTestCases.apk" />
+        <option name="test-file-name" value="CtsDeviceServicesTestApp.apk" />
+        <option name="test-file-name" value="CtsDeviceServicesTestSecondApp.apk" />
+        <option name="test-file-name" value="CtsDeviceServicesTestThirdApp.apk" />
+        <option name="test-file-name" value="CtsDeviceDebuggableApp.apk" />
+        <option name="test-file-name" value="CtsDeviceDisplaySizeApp.apk" />
+        <option name="test-file-name" value="CtsDevicePrereleaseSdkApp.apk" />
+        <option name="test-file-name" value="CtsDisplayServiceApp.apk" />
+        <option name="test-file-name" value="CtsDeviceTranslucentTestApp.apk" />
+        <option name="test-file-name" value="CtsDeviceTranslucentTestApp26.apk" />
+    </target_preparer>
+    <test class="com.android.tradefed.testtype.AndroidJUnitTest">
+        <option name="package" value="android.server.cts.am"/>
+        <option name="runtime-hint" value="1h"/>
+    </test>
+</configuration>
diff --git a/hostsidetests/services/activityandwindowmanager/activitymanager/app/Android.mk b/tests/framework/base/activitymanager/app/Android.mk
similarity index 100%
rename from hostsidetests/services/activityandwindowmanager/activitymanager/app/Android.mk
rename to tests/framework/base/activitymanager/app/Android.mk
diff --git a/tests/framework/base/activitymanager/app/AndroidManifest.xml b/tests/framework/base/activitymanager/app/AndroidManifest.xml
new file mode 100755
index 0000000..2bed053
--- /dev/null
+++ b/tests/framework/base/activitymanager/app/AndroidManifest.xml
@@ -0,0 +1,410 @@
+<?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.
+ -->
+
+<manifest xmlns:android="http://schemas.android.com/apk/res/android"
+          xmlns:androidprv="http://schemas.android.com/apk/prv/res/android"
+          package="android.server.am">
+
+    <!-- virtual display test permissions -->
+    <uses-permission android:name="android.permission.CAPTURE_VIDEO_OUTPUT" />
+    <uses-permission android:name="android.permission.DISABLE_KEYGUARD" />
+    <uses-permission android:name="android.permission.BIND_VOICE_INTERACTION" />
+
+    <application>
+        <activity android:name=".TestActivity"
+                android:resizeableActivity="true"
+                android:supportsPictureInPicture="true"
+                android:exported="true"
+        />
+        <activity android:name=".TestActivityWithSameAffinity"
+                android:resizeableActivity="true"
+                android:supportsPictureInPicture="true"
+                android:exported="true"
+                android:taskAffinity="nobody.but.PipActivitySameAffinity"
+        />
+        <activity android:name=".TranslucentTestActivity"
+                android:resizeableActivity="true"
+                android:supportsPictureInPicture="true"
+                android:configChanges="orientation|screenSize|smallestScreenSize|screenLayout"
+                android:theme="@style/Theme.Transparent" />
+        <activity android:name=".VrTestActivity"
+                android:resizeableActivity="true"
+                android:exported="true"
+        />
+        <activity android:name=".ResumeWhilePausingActivity"
+                android:allowEmbedded="true"
+                android:resumeWhilePausing="true"
+                android:taskAffinity=""
+                android:exported="true"
+        />
+        <activity android:name=".ResizeableActivity"
+                android:resizeableActivity="true"
+                android:allowEmbedded="true"
+                android:exported="true"
+                android:configChanges="orientation|screenSize|smallestScreenSize|screenLayout|colorMode|density"
+        />
+        <activity android:name=".NonResizeableActivity"
+                android:resizeableActivity="false"
+                android:exported="true"
+        />
+        <activity android:name=".DockedActivity"
+                android:resizeableActivity="true"
+                android:exported="true"
+                android:taskAffinity="nobody.but.DockedActivity"
+        />
+        <activity android:name=".TranslucentActivity"
+            android:theme="@android:style/Theme.Translucent.NoTitleBar"
+            android:resizeableActivity="true"
+            android:taskAffinity="nobody.but.TranslucentActivity"
+            android:exported="true"
+        />
+        <activity android:name=".DialogWhenLargeActivity"
+                android:exported="true"
+                android:theme="@android:style/Theme.DeviceDefault.Light.DialogWhenLarge"
+        />
+        <activity android:name=".NoRelaunchActivity"
+                android:resizeableActivity="true"
+                android:configChanges="orientation|screenSize|smallestScreenSize|screenLayout|fontScale"
+                android:exported="true"
+                android:taskAffinity="nobody.but.NoRelaunchActivity"
+        />
+        <activity android:name=".SlowCreateActivity"
+                android:resizeableActivity="true"
+                android:exported="true"
+        />
+        <activity android:name=".LaunchingActivity"
+                android:resizeableActivity="true"
+                android:exported="true"
+                android:taskAffinity="nobody.but.LaunchingActivity"
+        />
+        <activity android:name=".AltLaunchingActivity"
+                android:resizeableActivity="true"
+                android:exported="true"
+                android:taskAffinity="nobody.but.LaunchingActivity"
+        />
+        <activity android:name=".PipActivity"
+                android:resizeableActivity="false"
+                android:supportsPictureInPicture="true"
+                android:configChanges="orientation|screenSize|smallestScreenSize|screenLayout"
+                android:exported="true"
+                android:taskAffinity="nobody.but.PipActivity"
+        />
+        <activity android:name=".PipActivity2"
+                  android:resizeableActivity="false"
+                  android:supportsPictureInPicture="true"
+                  android:configChanges="orientation|screenSize|smallestScreenSize|screenLayout"
+                  android:exported="true"
+                  android:taskAffinity="nobody.but.PipActivity2"
+        />
+        <activity android:name=".PipOnStopActivity"
+                  android:resizeableActivity="false"
+                  android:supportsPictureInPicture="true"
+                  android:configChanges="orientation|screenSize|smallestScreenSize|screenLayout"
+                  android:exported="true"
+                  android:taskAffinity="nobody.but.PipOnStopActivity"
+        />
+        <activity android:name=".PipActivityWithSameAffinity"
+                  android:resizeableActivity="false"
+                  android:supportsPictureInPicture="true"
+                  android:configChanges="orientation|screenSize|smallestScreenSize|screenLayout"
+                  android:exported="true"
+                  android:taskAffinity="nobody.but.PipActivitySameAffinity"
+        />
+        <activity android:name=".AlwaysFocusablePipActivity"
+                  android:theme="@style/Theme.Transparent"
+                  android:resizeableActivity="false"
+                  android:supportsPictureInPicture="true"
+                  androidprv:alwaysFocusable="true"
+                  android:exported="true"
+                  android:taskAffinity="nobody.but.AlwaysFocusablePipActivity"
+        />
+        <activity android:name=".LaunchIntoPinnedStackPipActivity"
+                  android:resizeableActivity="false"
+                  android:supportsPictureInPicture="true"
+                  androidprv:alwaysFocusable="true"
+                  android:configChanges="orientation|screenSize|smallestScreenSize|screenLayout"
+                  android:exported="true"
+        />
+        <activity android:name=".LaunchPipOnPipActivity"
+                  android:resizeableActivity="false"
+                  android:supportsPictureInPicture="true"
+                  android:taskAffinity="nobody.but.LaunchPipOnPipActivity"
+                  android:configChanges="orientation|screenSize|smallestScreenSize|screenLayout"
+                  android:exported="true"
+        />
+        <activity android:name=".LaunchEnterPipActivity"
+                  android:resizeableActivity="false"
+                  android:supportsPictureInPicture="true"
+                  androidprv:alwaysFocusable="true"
+                  android:configChanges="orientation|screenSize|smallestScreenSize|screenLayout"
+                  android:exported="true"
+        />
+        <activity android:name=".FreeformActivity"
+                  android:resizeableActivity="true"
+                  android:taskAffinity="nobody.but.FreeformActivity"
+                  android:exported="true"
+        />
+        <activity android:name=".TopLeftLayoutActivity"
+                  android:resizeableActivity="true"
+                  android:configChanges="orientation|screenSize|smallestScreenSize|screenLayout"
+                  android:exported="true">
+                  <layout android:defaultWidth="240dp"
+                          android:defaultHeight="160dp"
+                          android:gravity="top|left"
+                          android:minWidth="100dp"
+                          android:minHeight="80dp"
+                  />
+        </activity>
+        <activity android:name=".TopRightLayoutActivity"
+                  android:resizeableActivity="true"
+                  android:configChanges="orientation|screenSize|smallestScreenSize|screenLayout"
+                  android:exported="true">
+                  <layout android:defaultWidth="25%"
+                          android:defaultHeight="35%"
+                          android:gravity="top|right"
+                          android:minWidth="90dp"
+                          android:minHeight="80dp"
+                  />
+        </activity>
+        <activity android:name=".BottomLeftLayoutActivity"
+                  android:resizeableActivity="true"
+                  android:configChanges="orientation|screenSize|smallestScreenSize|screenLayout"
+                  android:exported="true">
+                  <layout android:defaultWidth="25%"
+                          android:defaultHeight="35%"
+                          android:gravity="bottom|left"
+                          android:minWidth="90dp"
+                          android:minHeight="80dp"
+                  />
+        </activity>
+        <activity android:name=".BottomRightLayoutActivity"
+                  android:resizeableActivity="true"
+                  android:configChanges="orientation|screenSize|smallestScreenSize|screenLayout"
+                  android:exported="true">
+                  <layout android:defaultWidth="240dp"
+                          android:defaultHeight="160dp"
+                          android:gravity="bottom|right"
+                          android:minWidth="100dp"
+                          android:minHeight="80dp"
+                  />
+        </activity>
+        <activity android:name=".TurnScreenOnActivity"
+                  android:exported="true"
+        />
+        <activity android:name=".TurnScreenOnDismissKeyguardActivity"
+            android:exported="true"
+        />
+        <activity android:name=".SingleTaskActivity"
+            android:exported="true"
+            android:launchMode="singleTask"
+        />
+        <activity android:name=".SingleInstanceActivity"
+            android:exported="true"
+            android:launchMode="singleInstance"
+        />
+        <activity android:name=".TrampolineActivity"
+                  android:exported="true"
+                  android:theme="@android:style/Theme.NoDisplay"
+        />
+        <activity android:name=".BroadcastReceiverActivity"
+                  android:resizeableActivity="true"
+                  android:exported="true"
+        />
+        <activity-alias android:enabled="true"
+                android:exported="true"
+                android:name=".EntryPointAliasActivity"
+                android:targetActivity=".TrampolineActivity" >
+        </activity-alias>
+        <activity android:name=".BottomActivity"
+                  android:exported="true"
+                  android:theme="@style/NoPreview"
+        />
+        <activity android:name=".TopActivity"
+                  android:process=".top_process"
+                  android:exported="true"
+                  android:theme="@style/NoPreview"
+        />
+        <activity android:name=".TranslucentTopActivity"
+                  android:process=".top_process"
+                  android:exported="true"
+                  android:theme="@style/TranslucentTheme"
+        />
+        <!-- An animation test with an explicitly opaque theme, overriding device defaults, as the
+             animation background being tested is not used in translucent activities. -->
+        <activity android:name=".AnimationTestActivity"
+                  android:theme="@style/OpaqueTheme"
+                  android:exported="true"
+        />
+        <activity android:name=".VirtualDisplayActivity"
+                  android:resizeableActivity="true"
+                  android:exported="true"
+                  android:configChanges="orientation|screenSize|smallestScreenSize|screenLayout"
+        />
+        <activity android:name=".ShowWhenLockedActivity"
+                  android:exported="true"
+        />
+        <activity android:name=".ShowWhenLockedWithDialogActivity"
+                  android:exported="true"
+        />
+        <activity android:name=".ShowWhenLockedDialogActivity"
+            android:exported="true"
+            android:theme="@android:style/Theme.Material.Dialog"
+        />
+        <activity android:name=".ShowWhenLockedTranslucentActivity"
+                  android:exported="true"
+                  android:theme="@android:style/Theme.Translucent"
+        />
+        <activity android:name=".DismissKeyguardActivity"
+                  android:exported="true"
+        />
+        <activity android:name=".DismissKeyguardMethodActivity"
+            android:exported="true"
+        />
+        <activity android:name=".WallpaperActivity"
+            android:exported="true"
+            android:theme="@style/WallpaperTheme"
+        />
+        <activity android:name=".KeyguardLockActivity"
+                  android:exported="true"
+        />
+        <activity android:name=".LogConfigurationActivity"
+            android:exported="true"
+            android:configChanges="orientation|screenSize|smallestScreenSize|screenLayout"
+        />
+        <activity android:name=".PortraitOrientationActivity"
+                  android:exported="true"
+                  android:screenOrientation="portrait"
+                  android:documentLaunchMode="always"
+        />
+        <activity android:name=".LandscapeOrientationActivity"
+                  android:exported="true"
+                  android:screenOrientation="landscape"
+                  android:documentLaunchMode="always"
+        />
+        <activity android:name=".MoveTaskToBackActivity"
+                  android:exported="true"
+                  android:launchMode="singleInstance"
+        />
+        <activity android:name=".NightModeActivity"
+                  android:exported="true"
+                  android:configChanges="uiMode"
+        />
+        <activity android:name=".FontScaleActivity"
+                  android:exported="true"
+        />
+        <activity android:name=".FontScaleNoRelaunchActivity"
+                  android:exported="true"
+                  android:configChanges="fontScale"
+        />
+        <receiver
+            android:name=".LaunchBroadcastReceiver"
+            android:enabled="true"
+            android:exported="true" >
+            <intent-filter>
+                <action android:name="android.server.am.LAUNCH_BROADCAST_ACTION"/>
+            </intent-filter>
+        </receiver>
+
+        <activity android:name=".AssistantActivity"
+            android:exported="true" />
+        <activity android:name=".TranslucentAssistantActivity"
+            android:exported="true"
+            android:theme="@style/Theme.Transparent" />
+        <activity android:name=".LaunchAssistantActivityFromSession"
+            android:taskAffinity="nobody.but.LaunchAssistantActivityFromSession"
+            android:exported="true" />
+        <activity android:name=".LaunchAssistantActivityIntoAssistantStack"
+            android:taskAffinity="nobody.but.LaunchAssistantActivityIntoAssistantStack"
+            android:exported="true" />
+
+        <service android:name=".AssistantVoiceInteractionService"
+                 android:permission="android.permission.BIND_VOICE_INTERACTION"
+                 android:exported="true">
+            <meta-data android:name="android.voice_interaction"
+                       android:resource="@xml/interaction_service" />
+            <intent-filter>
+                <action android:name="android.service.voice.VoiceInteractionService" />
+            </intent-filter>
+        </service>
+
+        <service android:name=".AssistantVoiceInteractionSessionService"
+                 android:permission="android.permission.BIND_VOICE_INTERACTION"
+                 android:exported="true" />
+
+        <activity android:name=".SplashscreenActivity"
+            android:taskAffinity="nobody.but.SplashscreenActivity"
+            android:theme="@style/SplashscreenTheme"
+            android:exported="true" />
+
+
+        <activity android:name=".SwipeRefreshActivity"
+                  android:exported="true" />
+
+        <activity android:name=".NoHistoryActivity"
+                  android:noHistory="true"
+                  android:exported="true" />
+
+        <activity android:name=".ShowWhenLockedAttrActivity"
+                  android:showWhenLocked="true"
+                  android:exported="true" />
+
+        <activity android:name=".ShowWhenLockedAttrRemoveAttrActivity"
+                  android:showWhenLocked="true"
+                  android:exported="true" />
+
+        <activity android:name=".ShowWhenLockedAttrWithDialogActivity"
+                  android:showWhenLocked="true"
+                  android:exported="true" />
+
+        <activity android:name=".TurnScreenOnAttrActivity"
+                  android:turnScreenOn="true"
+                  android:exported="true" />
+
+        <activity android:name=".TurnScreenOnShowOnLockActivity"
+                  android:showWhenLocked="true"
+                  android:turnScreenOn="true"
+                  android:exported="true" />
+
+        <activity android:name=".TurnScreenOnAttrRemoveAttrActivity"
+                  android:turnScreenOn="true"
+                  android:showWhenLocked="true"
+                  android:exported="true" />
+
+        <activity android:name=".TurnScreenOnSingleTaskActivity"
+                  android:turnScreenOn="true"
+                  android:showWhenLocked="true"
+                  android:exported="true"
+                  android:launchMode="singleTask" />
+
+        <activity android:name=".TurnScreenOnAttrDismissKeyguardActivity"
+                  android:turnScreenOn="true"
+                  android:exported="true"/>
+
+        <activity android:name=".TurnScreenOnWithRelayoutActivity"
+                  android:exported="true"/>
+
+        <service android:name="com.android.cts.verifier.vr.MockVrListenerService"
+                 android:exported="true"
+                 android:enabled="true"
+                 android:permission="android.permission.BIND_VR_LISTENER_SERVICE">
+           <intent-filter>
+               <action android:name="android.service.vr.VrListenerService" />
+           </intent-filter>
+        </service>
+    </application>
+</manifest>
+
diff --git a/hostsidetests/services/activityandwindowmanager/activitymanager/app/res/anim/animation_with_background.xml b/tests/framework/base/activitymanager/app/res/anim/animation_with_background.xml
similarity index 100%
rename from hostsidetests/services/activityandwindowmanager/activitymanager/app/res/anim/animation_with_background.xml
rename to tests/framework/base/activitymanager/app/res/anim/animation_with_background.xml
diff --git a/hostsidetests/services/activityandwindowmanager/activitymanager/app/res/layout/assistant.xml b/tests/framework/base/activitymanager/app/res/layout/assistant.xml
similarity index 100%
rename from hostsidetests/services/activityandwindowmanager/activitymanager/app/res/layout/assistant.xml
rename to tests/framework/base/activitymanager/app/res/layout/assistant.xml
diff --git a/hostsidetests/services/activityandwindowmanager/activitymanager/app/res/layout/floating.xml b/tests/framework/base/activitymanager/app/res/layout/floating.xml
similarity index 100%
rename from hostsidetests/services/activityandwindowmanager/activitymanager/app/res/layout/floating.xml
rename to tests/framework/base/activitymanager/app/res/layout/floating.xml
diff --git a/hostsidetests/services/activityandwindowmanager/activitymanager/app/res/layout/font_scale.xml b/tests/framework/base/activitymanager/app/res/layout/font_scale.xml
similarity index 100%
rename from hostsidetests/services/activityandwindowmanager/activitymanager/app/res/layout/font_scale.xml
rename to tests/framework/base/activitymanager/app/res/layout/font_scale.xml
diff --git a/hostsidetests/services/activityandwindowmanager/activitymanager/app/res/layout/main.xml b/tests/framework/base/activitymanager/app/res/layout/main.xml
similarity index 100%
rename from hostsidetests/services/activityandwindowmanager/activitymanager/app/res/layout/main.xml
rename to tests/framework/base/activitymanager/app/res/layout/main.xml
diff --git a/tests/framework/base/activitymanager/app/res/layout/resizeable_activity.xml b/tests/framework/base/activitymanager/app/res/layout/resizeable_activity.xml
new file mode 100644
index 0000000..3c249bf
--- /dev/null
+++ b/tests/framework/base/activitymanager/app/res/layout/resizeable_activity.xml
@@ -0,0 +1,21 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+  ~ Copyright (C) 2017 The Android Open Source Project
+  ~
+  ~ Licensed under the Apache License, Version 2.0 (the "License");
+  ~ you may not use this file except in compliance with the License.
+  ~ You may obtain a copy of the License at
+  ~
+  ~      http://www.apache.org/licenses/LICENSE-2.0
+  ~
+  ~ Unless required by applicable law or agreed to in writing, software
+  ~ distributed under the License is distributed on an "AS IS" BASIS,
+  ~ WITHOUT 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.server.am.LifecycleLogView xmlns:android="http://schemas.android.com/apk/res/android"
+                android:layout_width="match_parent"
+                android:layout_height="match_parent">
+</android.server.am.LifecycleLogView>
\ No newline at end of file
diff --git a/hostsidetests/services/activityandwindowmanager/activitymanager/app/res/layout/tap_to_finish_pip_layout.xml b/tests/framework/base/activitymanager/app/res/layout/tap_to_finish_pip_layout.xml
similarity index 100%
rename from hostsidetests/services/activityandwindowmanager/activitymanager/app/res/layout/tap_to_finish_pip_layout.xml
rename to tests/framework/base/activitymanager/app/res/layout/tap_to_finish_pip_layout.xml
diff --git a/hostsidetests/services/activityandwindowmanager/activitymanager/app/res/layout/task_overlay.xml b/tests/framework/base/activitymanager/app/res/layout/task_overlay.xml
similarity index 100%
rename from hostsidetests/services/activityandwindowmanager/activitymanager/app/res/layout/task_overlay.xml
rename to tests/framework/base/activitymanager/app/res/layout/task_overlay.xml
diff --git a/hostsidetests/services/activityandwindowmanager/activitymanager/app/res/layout/translucent.xml b/tests/framework/base/activitymanager/app/res/layout/translucent.xml
similarity index 100%
rename from hostsidetests/services/activityandwindowmanager/activitymanager/app/res/layout/translucent.xml
rename to tests/framework/base/activitymanager/app/res/layout/translucent.xml
diff --git a/hostsidetests/services/activityandwindowmanager/activitymanager/app/res/layout/virtual_display_layout.xml b/tests/framework/base/activitymanager/app/res/layout/virtual_display_layout.xml
similarity index 100%
rename from hostsidetests/services/activityandwindowmanager/activitymanager/app/res/layout/virtual_display_layout.xml
rename to tests/framework/base/activitymanager/app/res/layout/virtual_display_layout.xml
diff --git a/hostsidetests/services/activityandwindowmanager/activitymanager/app/res/values/colors.xml b/tests/framework/base/activitymanager/app/res/values/colors.xml
similarity index 100%
rename from hostsidetests/services/activityandwindowmanager/activitymanager/app/res/values/colors.xml
rename to tests/framework/base/activitymanager/app/res/values/colors.xml
diff --git a/hostsidetests/services/activityandwindowmanager/activitymanager/app/res/values/styles.xml b/tests/framework/base/activitymanager/app/res/values/styles.xml
similarity index 100%
rename from hostsidetests/services/activityandwindowmanager/activitymanager/app/res/values/styles.xml
rename to tests/framework/base/activitymanager/app/res/values/styles.xml
diff --git a/tests/framework/base/activitymanager/app/res/xml/interaction_service.xml b/tests/framework/base/activitymanager/app/res/xml/interaction_service.xml
new file mode 100644
index 0000000..f586037
--- /dev/null
+++ b/tests/framework/base/activitymanager/app/res/xml/interaction_service.xml
@@ -0,0 +1,20 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2017 The Android Open Source Project
+
+     Licensed under the Apache License, Version 2.0 (the "License");
+     you may not use this file except in compliance with the License.
+     You may obtain a copy of the License at
+
+          http://www.apache.org/licenses/LICENSE-2.0
+
+     Unless required by applicable law or agreed to in writing, software
+     distributed under the License is distributed on an "AS IS" BASIS,
+     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+     See the License for the specific language governing permissions and
+     limitations under the License.
+-->
+
+<voice-interaction-service xmlns:android="http://schemas.android.com/apk/res/android"
+    android:sessionService="android.server.am.AssistantVoiceInteractionSessionService"
+    android:recognitionService="android.server.am.AssistantVoiceInteractionSessionService"
+    android:supportsAssist="true" />
diff --git a/tests/framework/base/activitymanager/app/src/android/server/am/AbstractLifecycleLogActivity.java b/tests/framework/base/activitymanager/app/src/android/server/am/AbstractLifecycleLogActivity.java
new file mode 100644
index 0000000..28e6576
--- /dev/null
+++ b/tests/framework/base/activitymanager/app/src/android/server/am/AbstractLifecycleLogActivity.java
@@ -0,0 +1,119 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License
+ */
+
+package android.server.am;
+
+import android.app.Activity;
+import android.content.res.Configuration;
+import android.graphics.Point;
+import android.os.Bundle;
+import android.util.DisplayMetrics;
+import android.util.Log;
+import android.view.Display;
+import android.view.WindowManager;
+
+public abstract class AbstractLifecycleLogActivity extends Activity {
+
+    @Override
+    protected void onCreate(Bundle icicle) {
+        super.onCreate(icicle);
+        Log.i(getTag(), "onCreate");
+    }
+
+    @Override
+    protected void onStart() {
+        super.onResume();
+        Log.i(getTag(), "onStart");
+    }
+
+    @Override
+    protected void onResume() {
+        super.onResume();
+        Log.i(getTag(), "onResume");
+    }
+
+    @Override
+    public void onConfigurationChanged(Configuration newConfig) {
+        super.onConfigurationChanged(newConfig);
+        Log.i(getTag(), "onConfigurationChanged");
+    }
+
+    @Override
+    public void onMultiWindowModeChanged(boolean isInMultiWindowMode, Configuration newConfig) {
+        super.onMultiWindowModeChanged(isInMultiWindowMode, newConfig);
+        Log.i(getTag(), "onMultiWindowModeChanged");
+    }
+
+    @Override
+    public void onPictureInPictureModeChanged(boolean isInPictureInPictureMode,
+            Configuration newConfig) {
+        super.onPictureInPictureModeChanged(isInPictureInPictureMode, newConfig);
+        Log.i(getTag(), "onPictureInPictureModeChanged");
+    }
+
+    @Override
+    protected void onPause() {
+        super.onPause();
+        Log.i(getTag(), "onPause");
+    }
+
+    @Override
+    protected void onStop() {
+        super.onStop();
+        Log.i(getTag(), "onStop");
+    }
+
+    @Override
+    protected void onDestroy() {
+        super.onDestroy();
+        Log.i(getTag(), "onDestroy");
+    }
+
+    @Override
+    protected void onUserLeaveHint() {
+        super.onUserLeaveHint();
+        Log.i(getTag(), "onUserLeaveHint");
+    }
+
+    protected abstract String getTag();
+
+    protected void dumpConfiguration(Configuration config) {
+        Log.i(getTag(), "Configuration: " + config);
+    }
+
+    protected void dumpDisplaySize(Configuration config) {
+        // Dump the display size as seen by this Activity.
+        final WindowManager wm = getSystemService(WindowManager.class);
+        final Display display = wm.getDefaultDisplay();
+        final Point point = new Point();
+        display.getSize(point);
+        final DisplayMetrics metrics = getResources().getDisplayMetrics();
+
+        final String line = "config"
+                + " size=" + buildCoordString(config.screenWidthDp, config.screenHeightDp)
+                + " displaySize=" + buildCoordString(point.x, point.y)
+                + " metricsSize=" + buildCoordString(metrics.widthPixels, metrics.heightPixels)
+                + " smallestScreenWidth=" + config.smallestScreenWidthDp
+                + " densityDpi=" + config.densityDpi
+                + " orientation=" + config.orientation;
+
+        Log.i(getTag(), line);
+    }
+
+    protected static String buildCoordString(int x, int y) {
+        return "(" + x + "," + y + ")";
+    }
+}
diff --git a/tests/framework/base/activitymanager/app/src/android/server/am/AltLaunchingActivity.java b/tests/framework/base/activitymanager/app/src/android/server/am/AltLaunchingActivity.java
new file mode 100644
index 0000000..3d7aa8a
--- /dev/null
+++ b/tests/framework/base/activitymanager/app/src/android/server/am/AltLaunchingActivity.java
@@ -0,0 +1,23 @@
+/*
+ * 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.server.am;
+
+/**
+ * An additional launching activity used for alternating between two activities.
+ */
+public class AltLaunchingActivity extends LaunchingActivity {
+}
diff --git a/tests/framework/base/activitymanager/app/src/android/server/am/AlwaysFocusablePipActivity.java b/tests/framework/base/activitymanager/app/src/android/server/am/AlwaysFocusablePipActivity.java
new file mode 100644
index 0000000..0e74977
--- /dev/null
+++ b/tests/framework/base/activitymanager/app/src/android/server/am/AlwaysFocusablePipActivity.java
@@ -0,0 +1,44 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License
+ */
+
+package android.server.am;
+
+import static android.app.WindowConfiguration.WINDOWING_MODE_PINNED;
+import static android.content.Intent.FLAG_ACTIVITY_CLEAR_TASK;
+import static android.content.Intent.FLAG_ACTIVITY_NEW_TASK;
+
+import android.app.Activity;
+import android.app.ActivityOptions;
+import android.content.Intent;
+import android.content.pm.PackageManager;
+import android.graphics.Rect;
+
+public class AlwaysFocusablePipActivity extends Activity {
+
+    static void launchAlwaysFocusablePipActivity(Activity caller, boolean newTask) {
+        final Intent intent = new Intent(caller, AlwaysFocusablePipActivity.class);
+
+        intent.setFlags(FLAG_ACTIVITY_CLEAR_TASK);
+        if (newTask) {
+            intent.addFlags(FLAG_ACTIVITY_NEW_TASK);
+        }
+
+        final ActivityOptions options = ActivityOptions.makeBasic();
+        options.setLaunchBounds(new Rect(0, 0, 500, 500));
+        options.setLaunchWindowingMode(WINDOWING_MODE_PINNED);
+        caller.startActivity(intent, options.toBundle());
+    }
+}
diff --git a/tests/framework/base/activitymanager/app/src/android/server/am/AnimationTestActivity.java b/tests/framework/base/activitymanager/app/src/android/server/am/AnimationTestActivity.java
new file mode 100644
index 0000000..987ac87
--- /dev/null
+++ b/tests/framework/base/activitymanager/app/src/android/server/am/AnimationTestActivity.java
@@ -0,0 +1,28 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.server.am;
+
+import android.app.Activity;
+
+public class AnimationTestActivity extends Activity {
+
+    @Override
+    protected void onResume() {
+        super.onResume();
+        overridePendingTransition(R.anim.animation_with_background, -1);
+    }
+}
diff --git a/tests/framework/base/activitymanager/app/src/android/server/am/AssistantActivity.java b/tests/framework/base/activitymanager/app/src/android/server/am/AssistantActivity.java
new file mode 100644
index 0000000..0e0e1fe
--- /dev/null
+++ b/tests/framework/base/activitymanager/app/src/android/server/am/AssistantActivity.java
@@ -0,0 +1,84 @@
+/*
+ * 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.server.am;
+
+import static android.app.WindowConfiguration.ACTIVITY_TYPE_ASSISTANT;
+import static android.content.Intent.FLAG_ACTIVITY_CLEAR_TASK;
+import static android.content.Intent.FLAG_ACTIVITY_NEW_TASK;
+
+import android.app.Activity;
+import android.app.ActivityOptions;
+import android.content.ComponentName;
+import android.content.Intent;
+import android.os.Bundle;
+
+public class AssistantActivity extends Activity {
+
+    // Launches the given activity in onResume
+    public static final String EXTRA_LAUNCH_NEW_TASK = "launch_new_task";
+    // Finishes this activity in onResume, this happens after EXTRA_LAUNCH_NEW_TASK
+    public static final String EXTRA_FINISH_SELF = "finish_self";
+    // Attempts to enter picture-in-picture in onResume
+    public static final String EXTRA_ENTER_PIP = "enter_pip";
+
+    @Override
+    protected void onCreate(Bundle savedInstanceState) {
+        super.onCreate(savedInstanceState);
+
+        // Set the layout
+        setContentView(R.layout.assistant);
+
+        // Launch the new activity if requested
+        if (getIntent().hasExtra(EXTRA_LAUNCH_NEW_TASK)) {
+            Intent i = new Intent();
+            i.setComponent(new ComponentName(this, getPackageName() + "."
+                    + getIntent().getStringExtra(EXTRA_LAUNCH_NEW_TASK)));
+            i.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_CLEAR_TASK);
+            startActivity(i);
+        }
+
+        // Enter pip if requested
+        if (getIntent().hasExtra(EXTRA_ENTER_PIP)) {
+            try {
+                enterPictureInPictureMode();
+            } catch (IllegalStateException e) {
+                finish();
+                return;
+            }
+        }
+
+        // Finish this activity if requested
+        if (getIntent().hasExtra(EXTRA_FINISH_SELF)) {
+            finish();
+        }
+    }
+
+    /**
+     * Launches a new instance of the AssistantActivity directly into the assistant stack.
+     */
+    static void launchActivityIntoAssistantStack(Activity caller, Bundle extras) {
+        final Intent intent = new Intent(caller, AssistantActivity.class);
+        intent.setFlags(FLAG_ACTIVITY_CLEAR_TASK | FLAG_ACTIVITY_NEW_TASK);
+        if (extras != null) {
+            intent.putExtras(extras);
+        }
+
+        final ActivityOptions options = ActivityOptions.makeBasic();
+        options.setLaunchActivityType(ACTIVITY_TYPE_ASSISTANT);
+        caller.startActivity(intent, options.toBundle());
+    }
+}
diff --git a/tests/framework/base/activitymanager/app/src/android/server/am/AssistantVoiceInteractionService.java b/tests/framework/base/activitymanager/app/src/android/server/am/AssistantVoiceInteractionService.java
new file mode 100644
index 0000000..b772ce2
--- /dev/null
+++ b/tests/framework/base/activitymanager/app/src/android/server/am/AssistantVoiceInteractionService.java
@@ -0,0 +1,64 @@
+/*
+ * 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.server.am;
+
+import android.content.ComponentName;
+import android.content.Context;
+import android.content.Intent;
+import android.os.Bundle;
+import android.service.voice.VoiceInteractionService;
+import android.util.Log;
+
+public class AssistantVoiceInteractionService extends VoiceInteractionService {
+
+    private static final String TAG = AssistantVoiceInteractionService.class.getSimpleName();
+
+    private boolean mReady;
+
+    @Override
+    public void onReady() {
+        super.onReady();
+        mReady = true;
+    }
+
+    @Override
+    public int onStartCommand(Intent intent, int flags, int startId) {
+        if (!isActiveService(this, new ComponentName(this, getClass()))) {
+            Log.wtf(TAG, "**** Not starting AssistantVoiceInteractionService because" +
+                    " it is not set as the current voice interaction service");
+            stopSelf();
+            return START_NOT_STICKY;
+        }
+        if (mReady) {
+            Bundle extras = intent.getExtras() != null ? intent.getExtras() : new Bundle();
+            showSession(extras, 0);
+        }
+        return START_NOT_STICKY;
+    }
+
+    /**
+     * Starts the assistant voice interaction service, which initiates a new session that starts
+     * the assistant activity.
+     */
+    public static void launchAssistantActivity(Context context, Bundle extras) {
+        Intent i = new Intent(context, AssistantVoiceInteractionService.class);
+        if (extras != null) {
+            i.putExtras(extras);
+        }
+        context.startService(i);
+    }
+}
diff --git a/tests/framework/base/activitymanager/app/src/android/server/am/AssistantVoiceInteractionSessionService.java b/tests/framework/base/activitymanager/app/src/android/server/am/AssistantVoiceInteractionSessionService.java
new file mode 100644
index 0000000..81ed202
--- /dev/null
+++ b/tests/framework/base/activitymanager/app/src/android/server/am/AssistantVoiceInteractionSessionService.java
@@ -0,0 +1,45 @@
+/*
+ * 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.server.am;
+
+import android.content.Intent;
+import android.os.Bundle;
+import android.service.voice.VoiceInteractionSession;
+import android.service.voice.VoiceInteractionSessionService;
+
+public class AssistantVoiceInteractionSessionService extends VoiceInteractionSessionService {
+
+    @Override
+    public VoiceInteractionSession onNewSession(Bundle args) {
+        return new VoiceInteractionSession(this) {
+            @Override
+            public void onPrepareShow(Bundle args, int showFlags) {
+                setUiEnabled(false);
+            }
+
+            @Override
+            public void onShow(Bundle args, int showFlags) {
+                Intent i = new Intent(AssistantVoiceInteractionSessionService.this,
+                        AssistantActivity.class);
+                if (args != null) {
+                    i.putExtras(args);
+                }
+                startAssistantActivity(i);
+            }
+        };
+    }
+}
diff --git a/tests/framework/base/activitymanager/app/src/android/server/am/BottomActivity.java b/tests/framework/base/activitymanager/app/src/android/server/am/BottomActivity.java
new file mode 100644
index 0000000..de56159
--- /dev/null
+++ b/tests/framework/base/activitymanager/app/src/android/server/am/BottomActivity.java
@@ -0,0 +1,102 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.server.am;
+
+import android.content.Context;
+import android.os.Bundle;
+import android.util.Log;
+import android.view.LayoutInflater;
+import android.view.View;
+import android.view.WindowManager;
+
+public class BottomActivity extends AbstractLifecycleLogActivity {
+
+    private static final String TAG = BottomActivity.class.getSimpleName();
+
+    private int mStopDelay;
+    private View mFloatingWindow;
+
+    @Override
+    protected String getTag() {
+        return TAG;
+    }
+
+    @Override
+    protected void onCreate(Bundle savedInstanceState) {
+        super.onCreate(savedInstanceState);
+
+        final boolean useWallpaper = getIntent().getBooleanExtra("USE_WALLPAPER", false);
+        if (useWallpaper) {
+            setTheme(R.style.WallpaperTheme);
+        }
+        setContentView(R.layout.main);
+
+        // Delayed stop is for simulating a case where resume happens before
+        // activityStopped() is received by AM, and the transition starts without
+        // going through fully stopped state (see b/30255354).
+        // If enabled, we stall onStop() of BottomActivity, open TopActivity but make
+        // it finish before onStop() ends. This will cause BottomActivity to resume before
+        // it notifies AM of activityStopped(). We also add a second window of
+        // TYPE_BASE_APPLICATION, so that the transition animation could start earlier.
+        // Otherwise the main window has to relayout to visible first and the error won't occur.
+        // Note that if the test fails, we shouldn't try to change the app here to make
+        // it pass. The test app is artificially made to simulate an failure case, but
+        // it's not doing anything wrong.
+        mStopDelay = getIntent().getIntExtra("STOP_DELAY", 0);
+        if (mStopDelay > 0) {
+            LayoutInflater inflater = getLayoutInflater();
+            mFloatingWindow = inflater.inflate(R.layout.floating, null);
+
+            WindowManager.LayoutParams params = new WindowManager.LayoutParams();
+            params.height = WindowManager.LayoutParams.WRAP_CONTENT;
+            params.width = WindowManager.LayoutParams.WRAP_CONTENT;
+            params.type = WindowManager.LayoutParams.TYPE_BASE_APPLICATION;
+            params.setTitle("Floating");
+            getWindowManager().addView(mFloatingWindow, params);
+        }
+    }
+
+    @Override
+    public void onResume() {
+        Log.d(TAG, "onResume() E");
+        super.onResume();
+
+        if (mStopDelay > 0) {
+            // Refresh floating window
+            Log.d(TAG, "Scheuling invalidate Floating Window in onResume()");
+            mFloatingWindow.invalidate();
+        }
+
+        Log.d(TAG, "onResume() X");
+    }
+
+    @Override
+    protected void onStop() {
+        super.onStop();
+
+        if (mStopDelay > 0) {
+            try {
+                Log.d(TAG, "Stalling onStop() by " + mStopDelay + " ms...");
+                Thread.sleep(mStopDelay);
+            } catch(InterruptedException e) {}
+
+            // Refresh floating window
+            Log.d(TAG, "Scheuling invalidate Floating Window in onStop()");
+            mFloatingWindow.invalidate();
+        }
+    }
+}
diff --git a/tests/framework/base/activitymanager/app/src/android/server/am/BottomLeftLayoutActivity.java b/tests/framework/base/activitymanager/app/src/android/server/am/BottomLeftLayoutActivity.java
new file mode 100644
index 0000000..3c4fcb0
--- /dev/null
+++ b/tests/framework/base/activitymanager/app/src/android/server/am/BottomLeftLayoutActivity.java
@@ -0,0 +1,22 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.server.am;
+
+import android.app.Activity;
+
+public class BottomLeftLayoutActivity extends Activity {
+}
diff --git a/tests/framework/base/activitymanager/app/src/android/server/am/BottomRightLayoutActivity.java b/tests/framework/base/activitymanager/app/src/android/server/am/BottomRightLayoutActivity.java
new file mode 100644
index 0000000..8d0e1e2
--- /dev/null
+++ b/tests/framework/base/activitymanager/app/src/android/server/am/BottomRightLayoutActivity.java
@@ -0,0 +1,22 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.server.am;
+
+import android.app.Activity;
+
+public class BottomRightLayoutActivity extends Activity {
+}
diff --git a/tests/framework/base/activitymanager/app/src/android/server/am/BroadcastReceiverActivity.java b/tests/framework/base/activitymanager/app/src/android/server/am/BroadcastReceiverActivity.java
new file mode 100644
index 0000000..cd0c745
--- /dev/null
+++ b/tests/framework/base/activitymanager/app/src/android/server/am/BroadcastReceiverActivity.java
@@ -0,0 +1,86 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License
+ */
+
+package android.server.am;
+
+import static android.view.WindowManager.LayoutParams.FLAG_DISMISS_KEYGUARD;
+
+import android.app.Activity;
+import android.app.KeyguardManager;
+import android.content.BroadcastReceiver;
+import android.content.Context;
+import android.content.Intent;
+import android.content.IntentFilter;
+import android.os.Bundle;
+import android.server.am.tools.ActivityLauncher;
+import android.util.Log;
+
+/**
+ * Activity that registers broadcast receiver .
+ */
+public class BroadcastReceiverActivity extends Activity {
+
+    public static final String ACTION_TRIGGER_BROADCAST = "trigger_broadcast";
+    private static final String TAG = BroadcastReceiverActivity.class.getSimpleName();
+
+    private TestBroadcastReceiver mBroadcastReceiver = new TestBroadcastReceiver();
+
+    @Override
+    protected void onCreate(Bundle icicle) {
+        super.onCreate(icicle);
+
+        IntentFilter broadcastFilter = new IntentFilter(ACTION_TRIGGER_BROADCAST);
+
+        registerReceiver(mBroadcastReceiver, broadcastFilter);
+    }
+
+    @Override
+    protected void onDestroy() {
+        super.onDestroy();
+
+        unregisterReceiver(mBroadcastReceiver);
+    }
+
+    public class TestBroadcastReceiver extends BroadcastReceiver {
+        @Override
+        public void onReceive(Context context, Intent intent) {
+            final Bundle extras = intent.getExtras();
+            Log.i(TAG, "onReceive: extras=" + extras);
+
+            if (extras == null) {
+                return;
+            }
+            if (extras.getBoolean("finish")) {
+                finish();
+            }
+            if (extras.getBoolean("moveToBack")) {
+                moveTaskToBack(true);
+            }
+            if (extras.containsKey("orientation")) {
+                setRequestedOrientation(extras.getInt("orientation"));
+            }
+            if (extras.getBoolean("dismissKeyguard")) {
+                getWindow().addFlags(FLAG_DISMISS_KEYGUARD);
+            }
+            if (extras.getBoolean("dismissKeyguardMethod")) {
+                getSystemService(KeyguardManager.class).requestDismissKeyguard(
+                        BroadcastReceiverActivity.this, new KeyguardDismissLoggerCallback(context));
+            }
+
+            ActivityLauncher.launchActivityFromExtras(BroadcastReceiverActivity.this, extras);
+        }
+    }
+}
diff --git a/tests/framework/base/activitymanager/app/src/android/server/am/DialogWhenLargeActivity.java b/tests/framework/base/activitymanager/app/src/android/server/am/DialogWhenLargeActivity.java
new file mode 100644
index 0000000..0e73939
--- /dev/null
+++ b/tests/framework/base/activitymanager/app/src/android/server/am/DialogWhenLargeActivity.java
@@ -0,0 +1,28 @@
+/*
+ * 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.server.am;
+
+/**
+ * Activity with DialogWhenLarge Theme.
+ */
+public class DialogWhenLargeActivity extends AbstractLifecycleLogActivity {
+    private static final String TAG = DialogWhenLargeActivity.class.getSimpleName();
+    @Override
+    protected String getTag() {
+        return TAG;
+    }
+}
diff --git a/tests/framework/base/activitymanager/app/src/android/server/am/DismissKeyguardActivity.java b/tests/framework/base/activitymanager/app/src/android/server/am/DismissKeyguardActivity.java
new file mode 100644
index 0000000..30dc69c
--- /dev/null
+++ b/tests/framework/base/activitymanager/app/src/android/server/am/DismissKeyguardActivity.java
@@ -0,0 +1,30 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License
+ */
+
+package android.server.am;
+
+import android.app.Activity;
+import android.os.Bundle;
+import android.view.WindowManager;
+
+public class DismissKeyguardActivity extends Activity {
+
+    @Override
+    protected void onCreate(Bundle savedInstanceState) {
+        super.onCreate(savedInstanceState);
+        getWindow().addFlags(WindowManager.LayoutParams.FLAG_DISMISS_KEYGUARD);
+    }
+}
diff --git a/tests/framework/base/activitymanager/app/src/android/server/am/DismissKeyguardMethodActivity.java b/tests/framework/base/activitymanager/app/src/android/server/am/DismissKeyguardMethodActivity.java
new file mode 100644
index 0000000..e19c4a1
--- /dev/null
+++ b/tests/framework/base/activitymanager/app/src/android/server/am/DismissKeyguardMethodActivity.java
@@ -0,0 +1,31 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License
+ */
+
+package android.server.am;
+
+import android.app.Activity;
+import android.app.KeyguardManager;
+import android.os.Bundle;
+
+public class DismissKeyguardMethodActivity extends Activity {
+
+    @Override
+    protected void onCreate(Bundle savedInstanceState) {
+        super.onCreate(savedInstanceState);
+        getSystemService(KeyguardManager.class).requestDismissKeyguard(this,
+                new KeyguardDismissLoggerCallback(this));
+    }
+}
diff --git a/tests/framework/base/activitymanager/app/src/android/server/am/DockedActivity.java b/tests/framework/base/activitymanager/app/src/android/server/am/DockedActivity.java
new file mode 100644
index 0000000..ce6dbb2
--- /dev/null
+++ b/tests/framework/base/activitymanager/app/src/android/server/am/DockedActivity.java
@@ -0,0 +1,22 @@
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.server.am;
+
+import android.app.Activity;
+
+public class DockedActivity extends Activity {
+}
diff --git a/tests/framework/base/activitymanager/app/src/android/server/am/FontScaleActivity.java b/tests/framework/base/activitymanager/app/src/android/server/am/FontScaleActivity.java
new file mode 100644
index 0000000..d46ab38
--- /dev/null
+++ b/tests/framework/base/activitymanager/app/src/android/server/am/FontScaleActivity.java
@@ -0,0 +1,76 @@
+/*
+ * 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.server.am;
+
+import android.content.res.Configuration;
+import android.content.res.TypedArray;
+import android.content.res.XmlResourceParser;
+import android.os.Bundle;
+import android.util.AttributeSet;
+import android.util.Log;
+import android.util.Xml;
+
+import org.xmlpull.v1.XmlPullParser;
+import org.xmlpull.v1.XmlPullParserException;
+
+import java.io.IOException;
+
+public class FontScaleActivity extends AbstractLifecycleLogActivity {
+    private static final String TAG = FontScaleActivity.class.getSimpleName();
+
+    @Override
+    protected void onCreate(Bundle icicle) {
+        super.onCreate(icicle);
+        dumpActivityDpi();
+        dumpFontSize();
+    }
+
+    // We're basically ensuring that no matter what happens to the resources underneath the
+    // Activity, any TypedArrays obtained from the pool have the correct DisplayMetrics.
+    protected void dumpFontSize() {
+        try (XmlResourceParser parser = getResources().getXml(R.layout.font_scale)) {
+            //noinspection StatementWithEmptyBody
+            while (parser.next() != XmlPullParser.START_TAG) { }
+
+            final AttributeSet attrs = Xml.asAttributeSet(parser);
+            TypedArray ta = getTheme().obtainStyledAttributes(attrs,
+                    new int[] { android.R.attr.textSize }, 0, 0);
+            try {
+                final int fontPixelSize = ta.getDimensionPixelSize(0, -1);
+                if (fontPixelSize == -1) {
+                    throw new AssertionError("android:attr/textSize not found");
+                }
+
+                Log.i(getTag(), "fontPixelSize=" + fontPixelSize);
+            } finally {
+                ta.recycle();
+            }
+        } catch (XmlPullParserException | IOException e) {
+            throw new RuntimeException(e);
+        }
+    }
+
+    protected void dumpActivityDpi() {
+        final int fontActivityDpi = getResources().getDisplayMetrics().densityDpi;
+        Log.i(getTag(), "fontActivityDpi=" + fontActivityDpi);
+    }
+
+    @Override
+    protected String getTag() {
+        return TAG;
+    }
+
+}
diff --git a/tests/framework/base/activitymanager/app/src/android/server/am/FontScaleNoRelaunchActivity.java b/tests/framework/base/activitymanager/app/src/android/server/am/FontScaleNoRelaunchActivity.java
new file mode 100644
index 0000000..c7744b0
--- /dev/null
+++ b/tests/framework/base/activitymanager/app/src/android/server/am/FontScaleNoRelaunchActivity.java
@@ -0,0 +1,32 @@
+/*
+ * 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.server.am;
+
+import android.content.res.Configuration;
+
+public class FontScaleNoRelaunchActivity extends FontScaleActivity {
+    @Override
+    public void onConfigurationChanged(Configuration newConfig) {
+        super.onConfigurationChanged(newConfig);
+        dumpActivityDpi();
+        dumpFontSize();
+    }
+
+    @Override
+    protected String getTag() {
+        return FontScaleNoRelaunchActivity.class.getSimpleName();
+    }
+}
diff --git a/tests/framework/base/activitymanager/app/src/android/server/am/FreeformActivity.java b/tests/framework/base/activitymanager/app/src/android/server/am/FreeformActivity.java
new file mode 100644
index 0000000..0d60e66
--- /dev/null
+++ b/tests/framework/base/activitymanager/app/src/android/server/am/FreeformActivity.java
@@ -0,0 +1,39 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License
+ */
+
+package android.server.am;
+
+import static android.content.Intent.FLAG_ACTIVITY_CLEAR_TASK;
+import static android.content.Intent.FLAG_ACTIVITY_NEW_TASK;
+
+import android.app.Activity;
+import android.app.ActivityOptions;
+import android.content.Intent;
+import android.graphics.Rect;
+
+public class FreeformActivity extends Activity {
+
+    @Override
+    public void onResume() {
+        super.onResume();
+        final Intent intent = new Intent(this, TestActivity.class);
+        intent.setFlags(FLAG_ACTIVITY_CLEAR_TASK | FLAG_ACTIVITY_NEW_TASK);
+
+        final ActivityOptions options = ActivityOptions.makeBasic();
+        options.setLaunchBounds(new Rect(0, 0, 900, 900));
+        this.startActivity(intent, options.toBundle());
+    }
+}
diff --git a/tests/framework/base/activitymanager/app/src/android/server/am/KeyguardDismissLoggerCallback.java b/tests/framework/base/activitymanager/app/src/android/server/am/KeyguardDismissLoggerCallback.java
new file mode 100644
index 0000000..c7113e6
--- /dev/null
+++ b/tests/framework/base/activitymanager/app/src/android/server/am/KeyguardDismissLoggerCallback.java
@@ -0,0 +1,54 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License
+ */
+
+package android.server.am;
+
+import android.app.KeyguardManager;
+import android.app.KeyguardManager.KeyguardDismissCallback;
+import android.content.Context;
+import android.util.Log;
+
+public class KeyguardDismissLoggerCallback extends KeyguardDismissCallback {
+
+    private final String TAG = "KeyguardDismissLoggerCallback";
+
+    private final Context mContext;
+
+    public KeyguardDismissLoggerCallback(Context context) {
+        mContext = context;
+    }
+
+    @Override
+    public void onDismissError() {
+        Log.i(TAG, "onDismissError");
+    }
+
+    @Override
+    public void onDismissSucceeded() {
+        if (mContext.getSystemService(KeyguardManager.class).isDeviceLocked()) {
+            // Device is still locked? What a fail. Don't print "onDismissSucceded" such that the
+            // log fails.
+            Log.i(TAG, "dismiss succedded was called but device is still locked.");
+        } else {
+            Log.i(TAG, "onDismissSucceeded");
+        }
+    }
+
+    @Override
+    public void onDismissCancelled() {
+        Log.i(TAG, "onDismissCancelled");
+    }
+}
diff --git a/tests/framework/base/activitymanager/app/src/android/server/am/KeyguardLockActivity.java b/tests/framework/base/activitymanager/app/src/android/server/am/KeyguardLockActivity.java
new file mode 100644
index 0000000..f69dc5d
--- /dev/null
+++ b/tests/framework/base/activitymanager/app/src/android/server/am/KeyguardLockActivity.java
@@ -0,0 +1,38 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License
+ */
+
+package android.server.am;
+
+import android.app.KeyguardManager;
+import android.os.Bundle;
+
+public class KeyguardLockActivity extends BroadcastReceiverActivity {
+
+    private KeyguardManager.KeyguardLock mKeyguardLock;
+
+    @Override
+    protected void onCreate(Bundle savedInstanceState) {
+        super.onCreate(savedInstanceState);
+        mKeyguardLock = getSystemService(KeyguardManager.class).newKeyguardLock("test");
+        mKeyguardLock.disableKeyguard();
+    }
+
+    @Override
+    protected void onDestroy() {
+        mKeyguardLock.reenableKeyguard();
+        super.onDestroy();
+    }
+}
diff --git a/tests/framework/base/activitymanager/app/src/android/server/am/LandscapeOrientationActivity.java b/tests/framework/base/activitymanager/app/src/android/server/am/LandscapeOrientationActivity.java
new file mode 100644
index 0000000..2d7ca6f
--- /dev/null
+++ b/tests/framework/base/activitymanager/app/src/android/server/am/LandscapeOrientationActivity.java
@@ -0,0 +1,39 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License
+ */
+
+package android.server.am;
+
+import android.content.res.Configuration;
+
+public class LandscapeOrientationActivity extends AbstractLifecycleLogActivity {
+    @Override
+    protected String getTag() {
+        return "LandscapeOrientationActivity";
+    }
+
+    @Override
+    protected void onResume() {
+        super.onResume();
+        final Configuration config = getResources().getConfiguration();
+        dumpDisplaySize(config);
+    }
+
+    @Override
+    public void onConfigurationChanged(Configuration newConfig) {
+        super.onConfigurationChanged(newConfig);
+        dumpDisplaySize(newConfig);
+    }
+}
diff --git a/tests/framework/base/activitymanager/app/src/android/server/am/LaunchAssistantActivityFromSession.java b/tests/framework/base/activitymanager/app/src/android/server/am/LaunchAssistantActivityFromSession.java
new file mode 100644
index 0000000..c67e9dd
--- /dev/null
+++ b/tests/framework/base/activitymanager/app/src/android/server/am/LaunchAssistantActivityFromSession.java
@@ -0,0 +1,28 @@
+/*
+ * 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.server.am;
+
+import android.app.Activity;
+
+public class LaunchAssistantActivityFromSession extends Activity {
+    @Override
+    protected void onResume() {
+        super.onResume();
+        AssistantVoiceInteractionService.launchAssistantActivity(this, getIntent().getExtras());
+        finishAndRemoveTask();
+    }
+}
diff --git a/tests/framework/base/activitymanager/app/src/android/server/am/LaunchAssistantActivityIntoAssistantStack.java b/tests/framework/base/activitymanager/app/src/android/server/am/LaunchAssistantActivityIntoAssistantStack.java
new file mode 100644
index 0000000..116bd22
--- /dev/null
+++ b/tests/framework/base/activitymanager/app/src/android/server/am/LaunchAssistantActivityIntoAssistantStack.java
@@ -0,0 +1,39 @@
+/*
+ * 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.server.am;
+
+import android.app.Activity;
+
+public class LaunchAssistantActivityIntoAssistantStack extends Activity {
+
+    // Launches the translucent assist activity
+    public static final String EXTRA_IS_TRANSLUCENT = "is_translucent";
+
+    @Override
+    protected void onResume() {
+        super.onResume();
+
+        if (getIntent().hasExtra(EXTRA_IS_TRANSLUCENT) &&
+                Boolean.valueOf(getIntent().getStringExtra(EXTRA_IS_TRANSLUCENT))) {
+            TranslucentAssistantActivity.launchActivityIntoAssistantStack(this,
+                    getIntent().getExtras());
+        } else {
+            AssistantActivity.launchActivityIntoAssistantStack(this, getIntent().getExtras());
+        }
+        finishAndRemoveTask();
+    }
+}
diff --git a/tests/framework/base/activitymanager/app/src/android/server/am/LaunchBroadcastReceiver.java b/tests/framework/base/activitymanager/app/src/android/server/am/LaunchBroadcastReceiver.java
new file mode 100644
index 0000000..01a2fb1
--- /dev/null
+++ b/tests/framework/base/activitymanager/app/src/android/server/am/LaunchBroadcastReceiver.java
@@ -0,0 +1,37 @@
+/*
+ * 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.server.am;
+
+import android.content.BroadcastReceiver;
+import android.content.Context;
+import android.content.Intent;
+import android.server.am.tools.ActivityLauncher;
+import android.util.Log;
+
+/** Broadcast receiver that can launch activities. */
+public class LaunchBroadcastReceiver extends BroadcastReceiver {
+    private static final String TAG = LaunchBroadcastReceiver.class.getSimpleName();
+
+    @Override
+    public void onReceive(Context context, Intent intent) {
+        try {
+            ActivityLauncher.launchActivityFromExtras(context, intent.getExtras());
+        } catch (SecurityException e) {
+            Log.e(TAG, "SecurityException launching activity");
+        }
+    }
+}
diff --git a/tests/framework/base/activitymanager/app/src/android/server/am/LaunchEnterPipActivity.java b/tests/framework/base/activitymanager/app/src/android/server/am/LaunchEnterPipActivity.java
new file mode 100644
index 0000000..db1119d
--- /dev/null
+++ b/tests/framework/base/activitymanager/app/src/android/server/am/LaunchEnterPipActivity.java
@@ -0,0 +1,29 @@
+/*
+ * 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.server.am;
+
+import android.app.Activity;
+import android.graphics.Rect;
+import android.os.Bundle;
+
+public class LaunchEnterPipActivity extends Activity {
+    @Override
+    protected void onCreate(Bundle bundle) {
+        super.onCreate(bundle);
+        PipActivity.launchEnterPipActivity(this);
+    }
+}
diff --git a/tests/framework/base/activitymanager/app/src/android/server/am/LaunchIntoPinnedStackPipActivity.java b/tests/framework/base/activitymanager/app/src/android/server/am/LaunchIntoPinnedStackPipActivity.java
new file mode 100644
index 0000000..bb97261
--- /dev/null
+++ b/tests/framework/base/activitymanager/app/src/android/server/am/LaunchIntoPinnedStackPipActivity.java
@@ -0,0 +1,27 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License
+ */
+
+package android.server.am;
+
+import android.app.Activity;
+
+public class LaunchIntoPinnedStackPipActivity extends Activity {
+    @Override
+    protected void onResume() {
+        super.onResume();
+        AlwaysFocusablePipActivity.launchAlwaysFocusablePipActivity(this, true /* newTask */);
+    }
+}
diff --git a/tests/framework/base/activitymanager/app/src/android/server/am/LaunchPipOnPipActivity.java b/tests/framework/base/activitymanager/app/src/android/server/am/LaunchPipOnPipActivity.java
new file mode 100644
index 0000000..52263f2
--- /dev/null
+++ b/tests/framework/base/activitymanager/app/src/android/server/am/LaunchPipOnPipActivity.java
@@ -0,0 +1,29 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License
+ */
+
+package android.server.am;
+
+import android.app.Activity;
+import android.content.pm.PackageManager;
+
+public class LaunchPipOnPipActivity extends Activity {
+    @Override
+    public void onPictureInPictureModeChanged(boolean isInPictureInPictureMode) {
+        super.onPictureInPictureModeChanged(isInPictureInPictureMode);
+        AlwaysFocusablePipActivity.launchAlwaysFocusablePipActivity(this,
+            getPackageManager().hasSystemFeature(PackageManager.FEATURE_LEANBACK));
+    }
+}
diff --git a/tests/framework/base/activitymanager/app/src/android/server/am/LaunchingActivity.java b/tests/framework/base/activitymanager/app/src/android/server/am/LaunchingActivity.java
new file mode 100644
index 0000000..814ed60
--- /dev/null
+++ b/tests/framework/base/activitymanager/app/src/android/server/am/LaunchingActivity.java
@@ -0,0 +1,32 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License
+ */
+
+package android.server.am;
+
+import android.app.Activity;
+import android.content.Intent;
+import android.server.am.tools.ActivityLauncher;
+
+/**
+ * Activity that launches another activities when new intent is received.
+ */
+public class LaunchingActivity extends Activity {
+    @Override
+    protected void onNewIntent(Intent intent) {
+        super.onNewIntent(intent);
+        ActivityLauncher.launchActivityFromExtras(this, intent.getExtras());
+    }
+}
diff --git a/tests/framework/base/activitymanager/app/src/android/server/am/LifecycleLogView.java b/tests/framework/base/activitymanager/app/src/android/server/am/LifecycleLogView.java
new file mode 100644
index 0000000..e9cc897
--- /dev/null
+++ b/tests/framework/base/activitymanager/app/src/android/server/am/LifecycleLogView.java
@@ -0,0 +1,50 @@
+/*
+ * 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.server.am;
+
+import android.content.Context;
+import android.content.res.Configuration;
+import android.util.AttributeSet;
+import android.util.Log;
+import android.view.View;
+
+public class LifecycleLogView extends View {
+    private final String TAG = "LifecycleLogView";
+
+    public LifecycleLogView(Context context) {
+        super(context);
+    }
+
+    public LifecycleLogView(Context context, AttributeSet attrs) {
+        this(context, attrs, 0);
+    }
+
+    public LifecycleLogView(Context context, AttributeSet attrs, int defStyleAttr) {
+        this(context, attrs, defStyleAttr, 0);
+    }
+
+    public LifecycleLogView(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes)
+    {
+        super(context, attrs, defStyleAttr, defStyleRes);
+    }
+
+    @Override
+    protected void onConfigurationChanged(Configuration newConfig) {
+        super.onConfigurationChanged(newConfig);
+        Log.i(TAG, "onConfigurationChanged");
+    }
+}
diff --git a/tests/framework/base/activitymanager/app/src/android/server/am/LogConfigurationActivity.java b/tests/framework/base/activitymanager/app/src/android/server/am/LogConfigurationActivity.java
new file mode 100644
index 0000000..5ed370e
--- /dev/null
+++ b/tests/framework/base/activitymanager/app/src/android/server/am/LogConfigurationActivity.java
@@ -0,0 +1,36 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License
+ */
+
+package android.server.am;
+
+import android.app.Activity;
+import android.content.res.Configuration;
+import android.util.Log;
+
+/**
+ * Activity that logs configuration changes.
+ */
+public class LogConfigurationActivity extends Activity {
+
+    private static final String TAG = "LogConfigurationActivity";
+
+    @Override
+    public void onConfigurationChanged(Configuration newConfig) {
+        super.onConfigurationChanged(newConfig);
+        Log.i(TAG, "Configuration changed: " + newConfig.screenWidthDp + ","
+                + newConfig.screenHeightDp);
+    }
+}
diff --git a/tests/framework/base/activitymanager/app/src/android/server/am/MoveTaskToBackActivity.java b/tests/framework/base/activitymanager/app/src/android/server/am/MoveTaskToBackActivity.java
new file mode 100644
index 0000000..6c37115
--- /dev/null
+++ b/tests/framework/base/activitymanager/app/src/android/server/am/MoveTaskToBackActivity.java
@@ -0,0 +1,62 @@
+/*
+ * 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.server.am;
+
+import android.content.Intent;
+import android.os.Bundle;
+
+/**
+ * Activity that finishes itself using "moveTaskToBack".
+ */
+public class MoveTaskToBackActivity extends AbstractLifecycleLogActivity {
+
+    private static final String TAG = MoveTaskToBackActivity.class.getSimpleName();
+
+    private String mFinishPoint;
+
+    @Override
+    protected void onCreate(Bundle icicle) {
+        super.onCreate(icicle);
+
+        final Intent intent = getIntent();
+        mFinishPoint = intent.getExtras().getString("finish_point");
+    }
+
+    @Override
+    protected void onPause() {
+        super.onPause();
+
+        if (mFinishPoint.equals("on_pause")) {
+            moveTaskToBack(true /* nonRoot */);
+        }
+    }
+
+    @Override
+    protected void onStop() {
+        super.onStop();
+
+        if (mFinishPoint.equals("on_stop")) {
+            moveTaskToBack(true /* nonRoot */);
+        }
+    }
+
+    @Override
+    protected String getTag() {
+        return TAG;
+    }
+
+}
diff --git a/tests/framework/base/activitymanager/app/src/android/server/am/NightModeActivity.java b/tests/framework/base/activitymanager/app/src/android/server/am/NightModeActivity.java
new file mode 100644
index 0000000..7c73900
--- /dev/null
+++ b/tests/framework/base/activitymanager/app/src/android/server/am/NightModeActivity.java
@@ -0,0 +1,42 @@
+/*
+ * 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.server.am;
+
+import android.app.UiModeManager;
+import android.content.Context;
+import android.os.Bundle;
+
+/** Activity that changes UI mode on creation and handles corresponding configuration change. */
+public class NightModeActivity extends AbstractLifecycleLogActivity {
+
+    private static final String TAG = NightModeActivity.class.getSimpleName();
+
+    @Override
+    protected String getTag() {
+        return TAG;
+    }
+
+    @Override
+    protected void onCreate(Bundle icicle) {
+        super.onCreate(icicle);
+
+        UiModeManager uiManager = (UiModeManager) getSystemService(Context.UI_MODE_SERVICE);
+        // Switch the mode two times to make sure it is independent of the current setting.
+        uiManager.setNightMode(UiModeManager.MODE_NIGHT_YES);
+        uiManager.setNightMode(UiModeManager.MODE_NIGHT_NO);
+    }
+}
diff --git a/tests/framework/base/activitymanager/app/src/android/server/am/NoHistoryActivity.java b/tests/framework/base/activitymanager/app/src/android/server/am/NoHistoryActivity.java
new file mode 100644
index 0000000..3080fac
--- /dev/null
+++ b/tests/framework/base/activitymanager/app/src/android/server/am/NoHistoryActivity.java
@@ -0,0 +1,29 @@
+/*
+ * 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.server.am;
+
+/**
+ * An activity that has the noHistory flag set.
+ */
+public class NoHistoryActivity extends AbstractLifecycleLogActivity {
+    private static final String TAG = NoHistoryActivity.class.getSimpleName();
+
+    @Override
+    protected String getTag() {
+        return TAG;
+    }
+}
diff --git a/tests/framework/base/activitymanager/app/src/android/server/am/NoRelaunchActivity.java b/tests/framework/base/activitymanager/app/src/android/server/am/NoRelaunchActivity.java
new file mode 100644
index 0000000..b173f8d
--- /dev/null
+++ b/tests/framework/base/activitymanager/app/src/android/server/am/NoRelaunchActivity.java
@@ -0,0 +1,27 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.server.am;
+
+public class NoRelaunchActivity extends AbstractLifecycleLogActivity {
+
+    private static final String TAG = NoRelaunchActivity.class.getSimpleName();
+
+    @Override
+    protected String getTag() {
+        return TAG;
+    }
+}
diff --git a/tests/framework/base/activitymanager/app/src/android/server/am/NonResizeableActivity.java b/tests/framework/base/activitymanager/app/src/android/server/am/NonResizeableActivity.java
new file mode 100644
index 0000000..e3468e3
--- /dev/null
+++ b/tests/framework/base/activitymanager/app/src/android/server/am/NonResizeableActivity.java
@@ -0,0 +1,27 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.server.am;
+
+public class NonResizeableActivity extends AbstractLifecycleLogActivity {
+
+     private static final String TAG = NonResizeableActivity.class.getSimpleName();
+
+    @Override
+    protected String getTag() {
+        return TAG;
+    }
+}
diff --git a/tests/framework/base/activitymanager/app/src/android/server/am/PipActivity.java b/tests/framework/base/activitymanager/app/src/android/server/am/PipActivity.java
new file mode 100644
index 0000000..f5397e6
--- /dev/null
+++ b/tests/framework/base/activitymanager/app/src/android/server/am/PipActivity.java
@@ -0,0 +1,339 @@
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License
+ */
+
+package android.server.am;
+
+import static android.app.WindowConfiguration.WINDOWING_MODE_PINNED;
+import static android.content.Intent.FLAG_ACTIVITY_CLEAR_TASK;
+import static android.content.Intent.FLAG_ACTIVITY_NEW_TASK;
+
+import android.app.Activity;
+import android.app.ActivityOptions;
+import android.app.PictureInPictureParams;
+import android.content.res.Configuration;
+import android.content.BroadcastReceiver;
+import android.content.ComponentName;
+import android.content.Context;
+import android.content.Intent;
+import android.content.IntentFilter;
+import android.graphics.Rect;
+import android.os.Bundle;
+import android.os.Handler;
+import android.os.SystemClock;
+import android.util.Log;
+import android.util.Rational;
+import android.view.WindowManager;
+
+public class PipActivity extends AbstractLifecycleLogActivity {
+
+    private static final String TAG = "PipActivity";
+
+    // Intent action that this activity dynamically registers to enter picture-in-picture
+    private static final String ACTION_ENTER_PIP = "android.server.am.PipActivity.enter_pip";
+    // Intent action that this activity dynamically registers to move itself to the back
+    private static final String ACTION_MOVE_TO_BACK = "android.server.am.PipActivity.move_to_back";
+    // Intent action that this activity dynamically registers to expand itself.
+    // If EXTRA_SET_ASPECT_RATIO_WITH_DELAY is set, it will also attempt to apply the aspect ratio
+    // after a short delay.
+    private static final String ACTION_EXPAND_PIP = "android.server.am.PipActivity.expand_pip";
+    // Intent action that this activity dynamically registers to set requested orientation.
+    // Will apply the oriention to the value set in the EXTRA_FIXED_ORIENTATION extra.
+    private static final String ACTION_SET_REQUESTED_ORIENTATION =
+            "android.server.am.PipActivity.set_requested_orientation";
+    // Intent action that will finish this activity
+    private static final String ACTION_FINISH = "android.server.am.PipActivity.finish";
+
+    // Sets the fixed orientation (can be one of {@link ActivityInfo.ScreenOrientation}
+    private static final String EXTRA_FIXED_ORIENTATION = "fixed_orientation";
+    // Calls enterPictureInPicture() on creation
+    private static final String EXTRA_ENTER_PIP = "enter_pip";
+    // Used with EXTRA_AUTO_ENTER_PIP, value specifies the aspect ratio to enter PIP with
+    private static final String EXTRA_ENTER_PIP_ASPECT_RATIO_NUMERATOR =
+            "enter_pip_aspect_ratio_numerator";
+    // Used with EXTRA_AUTO_ENTER_PIP, value specifies the aspect ratio to enter PIP with
+    private static final String EXTRA_ENTER_PIP_ASPECT_RATIO_DENOMINATOR =
+            "enter_pip_aspect_ratio_denominator";
+    // Calls setPictureInPictureAspectRatio with the aspect ratio specified in the value
+    private static final String EXTRA_SET_ASPECT_RATIO_NUMERATOR = "set_aspect_ratio_numerator";
+    // Calls setPictureInPictureAspectRatio with the aspect ratio specified in the value
+    private static final String EXTRA_SET_ASPECT_RATIO_DENOMINATOR = "set_aspect_ratio_denominator";
+    // Calls setPictureInPictureAspectRatio with the aspect ratio specified in the value with a
+    // fixed delay
+    private static final String EXTRA_SET_ASPECT_RATIO_WITH_DELAY_NUMERATOR =
+            "set_aspect_ratio_with_delay_numerator";
+    // Calls setPictureInPictureAspectRatio with the aspect ratio specified in the value with a
+    // fixed delay
+    private static final String EXTRA_SET_ASPECT_RATIO_WITH_DELAY_DENOMINATOR =
+            "set_aspect_ratio_with_delay_denominator";
+    // Adds a click listener to finish this activity when it is clicked
+    private static final String EXTRA_TAP_TO_FINISH = "tap_to_finish";
+    // Calls requestAutoEnterPictureInPicture() with the value provided
+    private static final String EXTRA_ENTER_PIP_ON_PAUSE = "enter_pip_on_pause";
+    // Starts the activity (component name) provided by the value at the end of onCreate
+    private static final String EXTRA_START_ACTIVITY = "start_activity";
+    // Finishes the activity at the end of onResume (after EXTRA_START_ACTIVITY is handled)
+    private static final String EXTRA_FINISH_SELF_ON_RESUME = "finish_self_on_resume";
+    // Calls enterPictureInPicture() again after onPictureInPictureModeChanged(false) is called
+    private static final String EXTRA_REENTER_PIP_ON_EXIT = "reenter_pip_on_exit";
+    // Shows this activity over the keyguard
+    private static final String EXTRA_SHOW_OVER_KEYGUARD = "show_over_keyguard";
+    // Adds an assertion that we do not ever get onStop() before we enter picture in picture
+    private static final String EXTRA_ASSERT_NO_ON_STOP_BEFORE_PIP = "assert_no_on_stop_before_pip";
+    // The amount to delay to artificially introduce in onPause() (before EXTRA_ENTER_PIP_ON_PAUSE
+    // is processed)
+    private static final String EXTRA_ON_PAUSE_DELAY = "on_pause_delay";
+
+    private boolean mEnteredPictureInPicture;
+
+    private Handler mHandler = new Handler();
+    private BroadcastReceiver mReceiver = new BroadcastReceiver() {
+        @Override
+        public void onReceive(Context context, Intent intent) {
+            if (intent != null) {
+                switch (intent.getAction()) {
+                    case ACTION_ENTER_PIP:
+                        enterPictureInPictureMode();
+                        break;
+                    case ACTION_MOVE_TO_BACK:
+                        moveTaskToBack(false /* nonRoot */);
+                        break;
+                    case ACTION_EXPAND_PIP:
+                        // Trigger the activity to expand
+                        Intent startIntent = new Intent(PipActivity.this, PipActivity.class);
+                        startIntent.setFlags(Intent.FLAG_ACTIVITY_REORDER_TO_FRONT);
+                        startActivity(startIntent);
+
+                        if (intent.hasExtra(EXTRA_SET_ASPECT_RATIO_WITH_DELAY_NUMERATOR)
+                                && intent.hasExtra(EXTRA_SET_ASPECT_RATIO_WITH_DELAY_DENOMINATOR)) {
+                            // Ugly, but required to wait for the startActivity to actually start
+                            // the activity...
+                            mHandler.postDelayed(() -> {
+                                final PictureInPictureParams.Builder builder =
+                                        new PictureInPictureParams.Builder();
+                                builder.setAspectRatio(getAspectRatio(intent,
+                                        EXTRA_SET_ASPECT_RATIO_WITH_DELAY_NUMERATOR,
+                                        EXTRA_SET_ASPECT_RATIO_WITH_DELAY_DENOMINATOR));
+                                setPictureInPictureParams(builder.build());
+                            }, 100);
+                        }
+                        break;
+                    case ACTION_SET_REQUESTED_ORIENTATION:
+                        setRequestedOrientation(Integer.parseInt(intent.getStringExtra(
+                                EXTRA_FIXED_ORIENTATION)));
+                        break;
+                    case ACTION_FINISH:
+                        finish();
+                        break;
+                }
+            }
+        }
+    };
+
+    @Override
+    protected void onCreate(Bundle savedInstanceState) {
+        super.onCreate(savedInstanceState);
+
+        // Set the fixed orientation if requested
+        if (getIntent().hasExtra(EXTRA_FIXED_ORIENTATION)) {
+            final int ori = Integer.parseInt(getIntent().getStringExtra(EXTRA_FIXED_ORIENTATION));
+            setRequestedOrientation(ori);
+        }
+
+        // Set the window flag to show over the keyguard
+        if (getIntent().hasExtra(EXTRA_SHOW_OVER_KEYGUARD)) {
+            getWindow().addFlags(WindowManager.LayoutParams.FLAG_SHOW_WHEN_LOCKED);
+        }
+
+        // Enter picture in picture with the given aspect ratio if provided
+        if (getIntent().hasExtra(EXTRA_ENTER_PIP)) {
+            if (getIntent().hasExtra(EXTRA_ENTER_PIP_ASPECT_RATIO_NUMERATOR)
+                    && getIntent().hasExtra(EXTRA_ENTER_PIP_ASPECT_RATIO_DENOMINATOR)) {
+                try {
+                    final PictureInPictureParams.Builder builder =
+                            new PictureInPictureParams.Builder();
+                    builder.setAspectRatio(getAspectRatio(getIntent(),
+                            EXTRA_ENTER_PIP_ASPECT_RATIO_NUMERATOR,
+                            EXTRA_ENTER_PIP_ASPECT_RATIO_DENOMINATOR));
+                    enterPictureInPictureMode(builder.build());
+                } catch (Exception e) {
+                    // This call can fail intentionally if the aspect ratio is too extreme
+                }
+            } else {
+                enterPictureInPictureMode(new PictureInPictureParams.Builder().build());
+            }
+        }
+
+        // We need to wait for either enterPictureInPicture() or requestAutoEnterPictureInPicture()
+        // to be called before setting the aspect ratio
+        if (getIntent().hasExtra(EXTRA_SET_ASPECT_RATIO_NUMERATOR)
+                && getIntent().hasExtra(EXTRA_SET_ASPECT_RATIO_DENOMINATOR)) {
+            final PictureInPictureParams.Builder builder =
+                    new PictureInPictureParams.Builder();
+            builder.setAspectRatio(getAspectRatio(getIntent(),
+                    EXTRA_SET_ASPECT_RATIO_NUMERATOR, EXTRA_SET_ASPECT_RATIO_DENOMINATOR));
+            try {
+                setPictureInPictureParams(builder.build());
+            } catch (Exception e) {
+                // This call can fail intentionally if the aspect ratio is too extreme
+            }
+        }
+
+        // Enable tap to finish if necessary
+        if (getIntent().hasExtra(EXTRA_TAP_TO_FINISH)) {
+            setContentView(R.layout.tap_to_finish_pip_layout);
+            findViewById(R.id.content).setOnClickListener(v -> {
+                finish();
+            });
+        }
+
+        // Launch a new activity if requested
+        String launchActivityComponent = getIntent().getStringExtra(EXTRA_START_ACTIVITY);
+        if (launchActivityComponent != null) {
+            Intent launchIntent = new Intent();
+            launchIntent.setComponent(ComponentName.unflattenFromString(launchActivityComponent));
+            startActivity(launchIntent);
+        }
+
+        // Register the broadcast receiver
+        IntentFilter filter = new IntentFilter();
+        filter.addAction(ACTION_ENTER_PIP);
+        filter.addAction(ACTION_MOVE_TO_BACK);
+        filter.addAction(ACTION_EXPAND_PIP);
+        filter.addAction(ACTION_SET_REQUESTED_ORIENTATION);
+        filter.addAction(ACTION_FINISH);
+        registerReceiver(mReceiver, filter);
+
+        // Dump applied display metrics.
+        Configuration config = getResources().getConfiguration();
+        dumpDisplaySize(config);
+        dumpConfiguration(config);
+    }
+
+    @Override
+    protected void onResume() {
+        super.onResume();
+
+        // Finish self if requested
+        if (getIntent().hasExtra(EXTRA_FINISH_SELF_ON_RESUME)) {
+            finish();
+        }
+    }
+
+    @Override
+    protected void onPause() {
+        super.onPause();
+
+        // Pause if requested
+        if (getIntent().hasExtra(EXTRA_ON_PAUSE_DELAY)) {
+            SystemClock.sleep(Long.valueOf(getIntent().getStringExtra(EXTRA_ON_PAUSE_DELAY)));
+        }
+
+        // Enter PIP on move to background
+        if (getIntent().hasExtra(EXTRA_ENTER_PIP_ON_PAUSE)) {
+            enterPictureInPictureMode(new PictureInPictureParams.Builder().build());
+        }
+    }
+
+    @Override
+    protected void onStop() {
+        super.onStop();
+
+        if (getIntent().hasExtra(EXTRA_ASSERT_NO_ON_STOP_BEFORE_PIP) && !mEnteredPictureInPicture) {
+            Log.w(TAG, "Unexpected onStop() called before entering picture-in-picture");
+            finish();
+        }
+    }
+
+    @Override
+    protected void onDestroy() {
+        super.onDestroy();
+
+        unregisterReceiver(mReceiver);
+    }
+
+    @Override
+    public void onPictureInPictureModeChanged(boolean isInPictureInPictureMode) {
+        super.onPictureInPictureModeChanged(isInPictureInPictureMode);
+
+        // Fail early if the activity state does not match the dispatched state
+        if (isInPictureInPictureMode() != isInPictureInPictureMode) {
+            Log.w(TAG, "Received onPictureInPictureModeChanged mode=" + isInPictureInPictureMode
+                    + " activityState=" + isInPictureInPictureMode());
+            finish();
+        }
+
+        // Mark that we've entered picture-in-picture so that we can stop checking for
+        // EXTRA_ASSERT_NO_ON_STOP_BEFORE_PIP
+        if (isInPictureInPictureMode) {
+            mEnteredPictureInPicture = true;
+        }
+
+        if (!isInPictureInPictureMode && getIntent().hasExtra(EXTRA_REENTER_PIP_ON_EXIT)) {
+            // This call to re-enter PIP can happen too quickly (host side tests can have difficulty
+            // checking that the stacks ever changed). Therefor, we need to delay here slightly to
+            // allow the tests to verify that the stacks have changed before re-entering.
+            mHandler.postDelayed(() -> {
+                enterPictureInPictureMode(new PictureInPictureParams.Builder().build());
+            }, 1000);
+        }
+    }
+
+    @Override
+    public void onConfigurationChanged(Configuration newConfig) {
+        super.onConfigurationChanged(newConfig);
+        dumpDisplaySize(newConfig);
+        dumpConfiguration(newConfig);
+    }
+
+    /**
+     * Launches a new instance of the PipActivity directly into the pinned stack.
+     */
+    static void launchActivityIntoPinnedStack(Activity caller, Rect bounds) {
+        final Intent intent = new Intent(caller, PipActivity.class);
+        intent.setFlags(FLAG_ACTIVITY_CLEAR_TASK | FLAG_ACTIVITY_NEW_TASK);
+        intent.putExtra(EXTRA_ASSERT_NO_ON_STOP_BEFORE_PIP, "true");
+
+        final ActivityOptions options = ActivityOptions.makeBasic();
+        options.setLaunchBounds(bounds);
+        options.setLaunchWindowingMode(WINDOWING_MODE_PINNED);
+        caller.startActivity(intent, options.toBundle());
+    }
+
+    /**
+     * Launches a new instance of the PipActivity in the same task that will automatically enter
+     * PiP.
+     */
+    static void launchEnterPipActivity(Activity caller) {
+        final Intent intent = new Intent(caller, PipActivity.class);
+        intent.putExtra(EXTRA_ENTER_PIP, "true");
+        intent.putExtra(EXTRA_ASSERT_NO_ON_STOP_BEFORE_PIP, "true");
+        caller.startActivity(intent);
+    }
+
+    @Override
+    protected String getTag() {
+        return TAG;
+    }
+
+    /**
+     * @return a {@link Rational} aspect ratio from the given intent and extras.
+     */
+    private Rational getAspectRatio(Intent intent, String extraNum, String extraDenom) {
+        return new Rational(
+                Integer.valueOf(intent.getStringExtra(extraNum)),
+                Integer.valueOf(intent.getStringExtra(extraDenom)));
+    }
+}
diff --git a/tests/framework/base/activitymanager/app/src/android/server/am/PipActivity2.java b/tests/framework/base/activitymanager/app/src/android/server/am/PipActivity2.java
new file mode 100644
index 0000000..c7c9858
--- /dev/null
+++ b/tests/framework/base/activitymanager/app/src/android/server/am/PipActivity2.java
@@ -0,0 +1,23 @@
+/*
+ * 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.server.am;
+
+/**
+ * A secondary activity that has the same behavior as {@link PipActivity}.
+ */
+public class PipActivity2 extends PipActivity {
+}
diff --git a/tests/framework/base/activitymanager/app/src/android/server/am/PipActivityWithSameAffinity.java b/tests/framework/base/activitymanager/app/src/android/server/am/PipActivityWithSameAffinity.java
new file mode 100644
index 0000000..12ff39f
--- /dev/null
+++ b/tests/framework/base/activitymanager/app/src/android/server/am/PipActivityWithSameAffinity.java
@@ -0,0 +1,34 @@
+/*
+ * 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.server.am;
+
+import android.app.Activity;
+import android.app.PictureInPictureParams;
+
+/**
+ * An activity that can enter PiP with a fixed affinity to match
+ * {@link TestActivityWithSameAffinity}.
+ */
+public class PipActivityWithSameAffinity extends Activity {
+
+    @Override
+    protected void onResume() {
+        super.onResume();
+
+        enterPictureInPictureMode(new PictureInPictureParams.Builder().build());
+    }
+}
diff --git a/tests/framework/base/activitymanager/app/src/android/server/am/PipOnStopActivity.java b/tests/framework/base/activitymanager/app/src/android/server/am/PipOnStopActivity.java
new file mode 100644
index 0000000..264e14c
--- /dev/null
+++ b/tests/framework/base/activitymanager/app/src/android/server/am/PipOnStopActivity.java
@@ -0,0 +1,52 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License
+ */
+
+package android.server.am;
+
+import android.app.Activity;
+import android.content.Intent;
+import android.os.Bundle;
+
+/**
+ * This activity will try and enter picture in picture when it is stopped.
+ */
+public class PipOnStopActivity extends Activity {
+
+    @Override
+    protected void onCreate(Bundle savedInstanceState) {
+        super.onCreate(savedInstanceState);
+
+        setContentView(R.layout.tap_to_finish_pip_layout);
+    }
+
+    @Override
+    protected void onResume() {
+        super.onResume();
+
+        startActivity(new Intent(this, PipActivity.class));
+    }
+
+    @Override
+    protected void onStop() {
+        super.onStop();
+
+        try {
+            enterPictureInPictureMode();
+        } catch (RuntimeException e) {
+            // Known failure, we expect this call to throw an exception
+        }
+    }
+}
diff --git a/tests/framework/base/activitymanager/app/src/android/server/am/PortraitOrientationActivity.java b/tests/framework/base/activitymanager/app/src/android/server/am/PortraitOrientationActivity.java
new file mode 100644
index 0000000..a63e3b3
--- /dev/null
+++ b/tests/framework/base/activitymanager/app/src/android/server/am/PortraitOrientationActivity.java
@@ -0,0 +1,40 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License
+ */
+
+package android.server.am;
+
+import android.content.res.Configuration;
+
+public class PortraitOrientationActivity extends AbstractLifecycleLogActivity {
+
+    @Override
+    protected String getTag() {
+        return "PortraitOrientationActivity";
+    }
+
+    @Override
+    protected void onResume() {
+        super.onResume();
+        final Configuration config = getResources().getConfiguration();
+        dumpDisplaySize(config);
+    }
+
+    @Override
+    public void onConfigurationChanged(Configuration newConfig) {
+        super.onConfigurationChanged(newConfig);
+        dumpDisplaySize(newConfig);
+    }
+}
diff --git a/tests/framework/base/activitymanager/app/src/android/server/am/ResizeableActivity.java b/tests/framework/base/activitymanager/app/src/android/server/am/ResizeableActivity.java
new file mode 100644
index 0000000..31aa9f2
--- /dev/null
+++ b/tests/framework/base/activitymanager/app/src/android/server/am/ResizeableActivity.java
@@ -0,0 +1,39 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not
+ * use this file except in compliance with the License. You may obtain a copy of
+ * the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+ * License for the specific language governing permissions and limitations under
+ * the License.
+ */
+package android.server.am;
+
+import android.content.res.Configuration;
+import android.os.Bundle;
+
+public class ResizeableActivity extends AbstractLifecycleLogActivity {
+    @Override
+    protected String getTag() {
+        return "ResizeableActivity";
+    }
+
+    @Override
+    protected void onCreate(Bundle icicle) {
+        super.onCreate(icicle);
+        setContentView(R.layout.resizeable_activity);
+        dumpDisplaySize(getResources().getConfiguration());
+    }
+
+    @Override
+    public void onConfigurationChanged(Configuration newConfig) {
+        super.onConfigurationChanged(newConfig);
+        dumpDisplaySize(newConfig);
+    }
+}
diff --git a/tests/framework/base/activitymanager/app/src/android/server/am/ResumeWhilePausingActivity.java b/tests/framework/base/activitymanager/app/src/android/server/am/ResumeWhilePausingActivity.java
new file mode 100644
index 0000000..62fbb8b
--- /dev/null
+++ b/tests/framework/base/activitymanager/app/src/android/server/am/ResumeWhilePausingActivity.java
@@ -0,0 +1,23 @@
+/*
+ * 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.server.am;
+
+import android.app.Activity;
+
+public class ResumeWhilePausingActivity extends Activity {
+    // Empty
+}
diff --git a/tests/framework/base/activitymanager/app/src/android/server/am/ShowWhenLockedActivity.java b/tests/framework/base/activitymanager/app/src/android/server/am/ShowWhenLockedActivity.java
new file mode 100644
index 0000000..1fc7b74
--- /dev/null
+++ b/tests/framework/base/activitymanager/app/src/android/server/am/ShowWhenLockedActivity.java
@@ -0,0 +1,30 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License
+ */
+
+package android.server.am;
+
+import android.app.Activity;
+import android.os.Bundle;
+import android.view.WindowManager;
+
+public class ShowWhenLockedActivity extends BroadcastReceiverActivity {
+
+    @Override
+    protected void onCreate(Bundle savedInstanceState) {
+        super.onCreate(savedInstanceState);
+        getWindow().addFlags(WindowManager.LayoutParams.FLAG_SHOW_WHEN_LOCKED);
+    }
+}
diff --git a/tests/framework/base/activitymanager/app/src/android/server/am/ShowWhenLockedAttrActivity.java b/tests/framework/base/activitymanager/app/src/android/server/am/ShowWhenLockedAttrActivity.java
new file mode 100644
index 0000000..2814361
--- /dev/null
+++ b/tests/framework/base/activitymanager/app/src/android/server/am/ShowWhenLockedAttrActivity.java
@@ -0,0 +1,27 @@
+/*
+ * 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.server.am;
+
+public class ShowWhenLockedAttrActivity extends AbstractLifecycleLogActivity {
+    private static final String TAG = ShowWhenLockedAttrActivity.class.getSimpleName();
+
+    @Override
+    protected String getTag() {
+        return TAG;
+    }
+
+}
diff --git a/tests/framework/base/activitymanager/app/src/android/server/am/ShowWhenLockedAttrRemoveAttrActivity.java b/tests/framework/base/activitymanager/app/src/android/server/am/ShowWhenLockedAttrRemoveAttrActivity.java
new file mode 100644
index 0000000..414d021
--- /dev/null
+++ b/tests/framework/base/activitymanager/app/src/android/server/am/ShowWhenLockedAttrRemoveAttrActivity.java
@@ -0,0 +1,35 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License
+ */
+
+package android.server.am;
+
+import android.os.Bundle;
+
+public class ShowWhenLockedAttrRemoveAttrActivity extends AbstractLifecycleLogActivity {
+    private static final String TAG = ShowWhenLockedAttrRemoveAttrActivity.class.getSimpleName();
+
+    @Override
+    protected void onStop() {
+        super.onStop();
+        setShowWhenLocked(false);
+    }
+
+    @Override
+    protected String getTag() {
+        return TAG;
+    }
+
+}
diff --git a/tests/framework/base/activitymanager/app/src/android/server/am/ShowWhenLockedAttrWithDialogActivity.java b/tests/framework/base/activitymanager/app/src/android/server/am/ShowWhenLockedAttrWithDialogActivity.java
new file mode 100644
index 0000000..4f9b214
--- /dev/null
+++ b/tests/framework/base/activitymanager/app/src/android/server/am/ShowWhenLockedAttrWithDialogActivity.java
@@ -0,0 +1,33 @@
+/*
+ * 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.server.am;
+
+import android.app.Activity;
+import android.app.AlertDialog;
+import android.os.Bundle;
+import android.view.WindowManager;
+
+public class ShowWhenLockedAttrWithDialogActivity extends Activity {
+
+    @Override
+    protected void onCreate(Bundle savedInstanceState) {
+        super.onCreate(savedInstanceState);
+        new AlertDialog.Builder(this)
+                .setTitle("Dialog")
+                .show();
+    }
+}
diff --git a/tests/framework/base/activitymanager/app/src/android/server/am/ShowWhenLockedDialogActivity.java b/tests/framework/base/activitymanager/app/src/android/server/am/ShowWhenLockedDialogActivity.java
new file mode 100644
index 0000000..56eb154
--- /dev/null
+++ b/tests/framework/base/activitymanager/app/src/android/server/am/ShowWhenLockedDialogActivity.java
@@ -0,0 +1,30 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License
+ */
+
+package android.server.am;
+
+import android.app.Activity;
+import android.os.Bundle;
+import android.view.WindowManager;
+
+public class ShowWhenLockedDialogActivity extends Activity {
+
+    @Override
+    protected void onCreate(Bundle savedInstanceState) {
+        super.onCreate(savedInstanceState);
+        getWindow().addFlags(WindowManager.LayoutParams.FLAG_SHOW_WHEN_LOCKED);
+    }
+}
diff --git a/tests/framework/base/activitymanager/app/src/android/server/am/ShowWhenLockedTranslucentActivity.java b/tests/framework/base/activitymanager/app/src/android/server/am/ShowWhenLockedTranslucentActivity.java
new file mode 100644
index 0000000..6422104
--- /dev/null
+++ b/tests/framework/base/activitymanager/app/src/android/server/am/ShowWhenLockedTranslucentActivity.java
@@ -0,0 +1,31 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License
+ */
+
+package android.server.am;
+
+import android.app.Activity;
+import android.os.Bundle;
+import android.view.WindowManager;
+
+public class ShowWhenLockedTranslucentActivity extends Activity {
+
+    @Override
+    protected void onCreate(Bundle savedInstanceState) {
+        super.onCreate(savedInstanceState);
+        getWindow().addFlags(WindowManager.LayoutParams.FLAG_SHOW_WHEN_LOCKED);
+        setContentView(R.layout.translucent);
+    }
+}
diff --git a/tests/framework/base/activitymanager/app/src/android/server/am/ShowWhenLockedWithDialogActivity.java b/tests/framework/base/activitymanager/app/src/android/server/am/ShowWhenLockedWithDialogActivity.java
new file mode 100644
index 0000000..3951532
--- /dev/null
+++ b/tests/framework/base/activitymanager/app/src/android/server/am/ShowWhenLockedWithDialogActivity.java
@@ -0,0 +1,34 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License
+ */
+
+package android.server.am;
+
+import android.app.Activity;
+import android.app.AlertDialog;
+import android.os.Bundle;
+import android.view.WindowManager;
+
+public class ShowWhenLockedWithDialogActivity extends Activity {
+
+    @Override
+    protected void onCreate(Bundle savedInstanceState) {
+        super.onCreate(savedInstanceState);
+        getWindow().addFlags(WindowManager.LayoutParams.FLAG_SHOW_WHEN_LOCKED);
+        new AlertDialog.Builder(this)
+                .setTitle("Dialog")
+                .show();
+    }
+}
diff --git a/tests/framework/base/activitymanager/app/src/android/server/am/SingleInstanceActivity.java b/tests/framework/base/activitymanager/app/src/android/server/am/SingleInstanceActivity.java
new file mode 100644
index 0000000..2c8b40f
--- /dev/null
+++ b/tests/framework/base/activitymanager/app/src/android/server/am/SingleInstanceActivity.java
@@ -0,0 +1,22 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License
+ */
+
+package android.server.am;
+
+import android.app.Activity;
+
+public class SingleInstanceActivity extends Activity {
+}
diff --git a/tests/framework/base/activitymanager/app/src/android/server/am/SingleTaskActivity.java b/tests/framework/base/activitymanager/app/src/android/server/am/SingleTaskActivity.java
new file mode 100644
index 0000000..d95aa3f
--- /dev/null
+++ b/tests/framework/base/activitymanager/app/src/android/server/am/SingleTaskActivity.java
@@ -0,0 +1,22 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License
+ */
+
+package android.server.am;
+
+import android.app.Activity;
+
+public class SingleTaskActivity extends Activity {
+}
diff --git a/tests/framework/base/activitymanager/app/src/android/server/am/SlowCreateActivity.java b/tests/framework/base/activitymanager/app/src/android/server/am/SlowCreateActivity.java
new file mode 100644
index 0000000..26992c1
--- /dev/null
+++ b/tests/framework/base/activitymanager/app/src/android/server/am/SlowCreateActivity.java
@@ -0,0 +1,30 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.server.am;
+
+import android.app.Activity;
+import android.os.Bundle;
+
+public class SlowCreateActivity extends Activity {
+    @Override
+    protected void onCreate(Bundle savedInstanceState) {
+        try {
+            Thread.sleep(2000);
+        } catch(InterruptedException e) {}
+        super.onCreate(savedInstanceState);
+    }
+}
diff --git a/tests/framework/base/activitymanager/app/src/android/server/am/SplashscreenActivity.java b/tests/framework/base/activitymanager/app/src/android/server/am/SplashscreenActivity.java
new file mode 100644
index 0000000..2d8fbae
--- /dev/null
+++ b/tests/framework/base/activitymanager/app/src/android/server/am/SplashscreenActivity.java
@@ -0,0 +1,36 @@
+/*
+ * 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.server.am;
+
+import android.app.Activity;
+import android.os.Bundle;
+import android.os.SystemClock;
+
+/**
+ * Activity that shows a custom splashscreen when being launched.
+ */
+public class SplashscreenActivity extends Activity {
+
+    @Override
+    protected void onCreate(Bundle savedInstanceState) {
+        super.onCreate(savedInstanceState);
+        // Make sure splash screen is visible. The test won't take 5 seconds because the condition
+        // such that we can dump the state will trigger much earlier and then the test will just
+        // kill us.
+        SystemClock.sleep(5000);
+    }
+}
diff --git a/tests/framework/base/activitymanager/app/src/android/server/am/SwipeRefreshActivity.java b/tests/framework/base/activitymanager/app/src/android/server/am/SwipeRefreshActivity.java
new file mode 100644
index 0000000..8e2b781
--- /dev/null
+++ b/tests/framework/base/activitymanager/app/src/android/server/am/SwipeRefreshActivity.java
@@ -0,0 +1,40 @@
+/*
+ * 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.server.am;
+
+import android.app.Activity;
+import android.os.Bundle;
+import android.server.am.SwipeRefreshLayout;
+
+
+/**
+ * An activity containing a SwipeRefreshLayout which prevents activity idle.
+ */
+public class SwipeRefreshActivity extends AbstractLifecycleLogActivity {
+    private static final String TAG = SwipeRefreshActivity.class.getSimpleName();
+
+    @Override
+    protected void onCreate(Bundle savedInstanceState) {
+        super.onCreate(savedInstanceState);
+        setContentView(new SwipeRefreshLayout(this));
+    }
+
+    @Override
+    protected String getTag() {
+        return TAG;
+    }
+}
diff --git a/tests/framework/base/activitymanager/app/src/android/server/am/SwipeRefreshLayout.java b/tests/framework/base/activitymanager/app/src/android/server/am/SwipeRefreshLayout.java
new file mode 100644
index 0000000..2b3071c
--- /dev/null
+++ b/tests/framework/base/activitymanager/app/src/android/server/am/SwipeRefreshLayout.java
@@ -0,0 +1,40 @@
+/*
+ * 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.server.am;
+
+import android.content.Context;
+import android.util.AttributeSet;
+
+/**
+ * An extension of {@link android.support.v4.widget.SwipeRefreshLayout} that calls
+ * {@link #setRefreshing} during construction, preventing activity idle.
+ */
+public class SwipeRefreshLayout extends android.support.v4.widget.SwipeRefreshLayout {
+    public SwipeRefreshLayout(Context context) {
+        super(context);
+        initialize();
+    }
+
+    public SwipeRefreshLayout(Context context, AttributeSet attrs) {
+        super(context, attrs);
+        initialize();
+    }
+
+    private void initialize() {
+        setRefreshing(true);
+    }
+}
diff --git a/tests/framework/base/activitymanager/app/src/android/server/am/TestActivity.java b/tests/framework/base/activitymanager/app/src/android/server/am/TestActivity.java
new file mode 100644
index 0000000..a71ab34
--- /dev/null
+++ b/tests/framework/base/activitymanager/app/src/android/server/am/TestActivity.java
@@ -0,0 +1,88 @@
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.server.am;
+
+import android.content.BroadcastReceiver;
+import android.content.Context;
+import android.content.Intent;
+import android.content.IntentFilter;
+import android.content.res.Configuration;
+import android.os.Bundle;
+import android.util.Log;
+
+public class TestActivity extends AbstractLifecycleLogActivity {
+
+    private static final String TAG = TestActivity.class.getSimpleName();
+
+    // Sets the fixed orientation (can be one of {@link ActivityInfo.ScreenOrientation}
+    private static final String EXTRA_FIXED_ORIENTATION = "fixed_orientation";
+
+    // Finishes the activity
+    private static final String ACTION_FINISH_SELF = "android.server.am.TestActivity.finish_self";
+
+    private BroadcastReceiver mReceiver = new BroadcastReceiver() {
+        @Override
+        public void onReceive(Context context, Intent intent) {
+            if (intent != null && intent.getAction().equals(ACTION_FINISH_SELF)) {
+                finish();
+            }
+        }
+    };
+
+    @Override
+    protected void onCreate(Bundle icicle) {
+        super.onCreate(icicle);
+
+        // Set the fixed orientation if requested
+        if (getIntent().hasExtra(EXTRA_FIXED_ORIENTATION)) {
+            final int ori = Integer.parseInt(getIntent().getStringExtra(EXTRA_FIXED_ORIENTATION));
+            setRequestedOrientation(ori);
+        }
+    }
+
+    @Override
+    protected void onStart() {
+        super.onStart();
+        registerReceiver(mReceiver, new IntentFilter(ACTION_FINISH_SELF));
+    }
+
+    @Override
+    protected void onResume() {
+        super.onResume();
+        final Configuration config = getResources().getConfiguration();
+        dumpDisplaySize(config);
+        dumpConfiguration(config);
+    }
+
+    @Override
+    protected void onStop() {
+        super.onStop();
+        unregisterReceiver(mReceiver);
+    }
+
+    @Override
+    public void onConfigurationChanged(Configuration newConfig) {
+        super.onConfigurationChanged(newConfig);
+        dumpDisplaySize(newConfig);
+        dumpConfiguration(newConfig);
+    }
+
+    @Override
+    protected String getTag() {
+        return TAG;
+    }
+}
diff --git a/tests/framework/base/activitymanager/app/src/android/server/am/TestActivityWithSameAffinity.java b/tests/framework/base/activitymanager/app/src/android/server/am/TestActivityWithSameAffinity.java
new file mode 100644
index 0000000..d89d786
--- /dev/null
+++ b/tests/framework/base/activitymanager/app/src/android/server/am/TestActivityWithSameAffinity.java
@@ -0,0 +1,72 @@
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.server.am;
+
+import android.app.PictureInPictureParams;
+import android.content.BroadcastReceiver;
+import android.content.ComponentName;
+import android.content.Context;
+import android.content.Intent;
+import android.content.IntentFilter;
+import android.content.res.Configuration;
+import android.os.Bundle;
+import android.util.Log;
+
+public class TestActivityWithSameAffinity extends TestActivity {
+
+    private static final String TAG = TestActivityWithSameAffinity.class.getSimpleName();
+
+    // Calls enterPictureInPicture() on creation
+    private static final String EXTRA_ENTER_PIP = "enter_pip";
+    // Starts the activity (component name) provided by the value at the end of onCreate
+    private static final String EXTRA_START_ACTIVITY = "start_activity";
+    // Finishes the activity at the end of onResume (after EXTRA_START_ACTIVITY is handled)
+    private static final String EXTRA_FINISH_SELF_ON_RESUME = "finish_self_on_resume";
+
+    @Override
+    protected void onCreate(Bundle icicle) {
+        super.onCreate(icicle);
+
+        // Enter picture in picture if requested
+        if (getIntent().hasExtra(EXTRA_ENTER_PIP)) {
+            enterPictureInPictureMode(new PictureInPictureParams.Builder().build());
+        }
+
+        // Launch a new activity if requested
+        String launchActivityComponent = getIntent().getStringExtra(EXTRA_START_ACTIVITY);
+        if (launchActivityComponent != null) {
+            Intent launchIntent = new Intent();
+            launchIntent.setComponent(ComponentName.unflattenFromString(launchActivityComponent));
+            startActivity(launchIntent);
+        }
+    }
+
+    @Override
+    protected void onResume() {
+        super.onResume();
+
+        // Finish self if requested
+        if (getIntent().hasExtra(EXTRA_FINISH_SELF_ON_RESUME)) {
+            finish();
+        }
+    }
+
+    @Override
+    protected String getTag() {
+        return TAG;
+    }
+}
diff --git a/tests/framework/base/activitymanager/app/src/android/server/am/TopActivity.java b/tests/framework/base/activitymanager/app/src/android/server/am/TopActivity.java
new file mode 100644
index 0000000..7d69ef9
--- /dev/null
+++ b/tests/framework/base/activitymanager/app/src/android/server/am/TopActivity.java
@@ -0,0 +1,53 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.server.am;
+
+import android.os.Handler;
+import android.os.Bundle;
+import android.util.Log;
+
+public class TopActivity extends AbstractLifecycleLogActivity {
+
+    private static final String TAG = TopActivity.class.getSimpleName();
+
+    @Override
+    protected String getTag() {
+        return TAG;
+    }
+
+    @Override
+    protected void onCreate(Bundle savedInstanceState) {
+        super.onCreate(savedInstanceState);
+
+        final boolean useWallpaper = getIntent().getBooleanExtra("USE_WALLPAPER", false);
+        if (useWallpaper) {
+            setTheme(R.style.WallpaperTheme);
+        }
+
+        final int finishDelay = getIntent().getIntExtra("FINISH_DELAY", 0);
+        if (finishDelay > 0) {
+            Handler handler = new Handler();
+            handler.postDelayed(new Runnable() {
+                @Override
+                public void run() {
+                    Log.d(TAG, "Calling finish()");
+                    finish();
+                }
+            }, finishDelay);
+        }
+    }
+}
diff --git a/tests/framework/base/activitymanager/app/src/android/server/am/TopLeftLayoutActivity.java b/tests/framework/base/activitymanager/app/src/android/server/am/TopLeftLayoutActivity.java
new file mode 100644
index 0000000..1302b22
--- /dev/null
+++ b/tests/framework/base/activitymanager/app/src/android/server/am/TopLeftLayoutActivity.java
@@ -0,0 +1,22 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.server.am;
+
+import android.app.Activity;
+
+public class TopLeftLayoutActivity extends Activity {
+}
diff --git a/tests/framework/base/activitymanager/app/src/android/server/am/TopRightLayoutActivity.java b/tests/framework/base/activitymanager/app/src/android/server/am/TopRightLayoutActivity.java
new file mode 100644
index 0000000..bb13c01
--- /dev/null
+++ b/tests/framework/base/activitymanager/app/src/android/server/am/TopRightLayoutActivity.java
@@ -0,0 +1,22 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.server.am;
+
+import android.app.Activity;
+
+public class TopRightLayoutActivity extends Activity {
+}
diff --git a/tests/framework/base/activitymanager/app/src/android/server/am/TrampolineActivity.java b/tests/framework/base/activitymanager/app/src/android/server/am/TrampolineActivity.java
new file mode 100644
index 0000000..7391b04
--- /dev/null
+++ b/tests/framework/base/activitymanager/app/src/android/server/am/TrampolineActivity.java
@@ -0,0 +1,50 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.server.am;
+
+import android.os.Bundle;
+
+import static android.content.Intent.FLAG_ACTIVITY_CLEAR_TOP;
+import static android.content.Intent.FLAG_ACTIVITY_NEW_TASK;
+
+import android.content.Intent;
+
+public class TrampolineActivity extends AbstractLifecycleLogActivity {
+
+    private static final String TAG = TrampolineActivity.class.getSimpleName();
+
+    @Override
+    protected void onCreate(Bundle savedInstanceState) {
+        super.onCreate(savedInstanceState);
+        // Add a delay here to expose more easily a failure case where the real target
+        // activity is visible before it's launched, because its task is being brought
+        // to foreground. We need to verify that 'am start' is unblocked correctly.
+        try {
+            Thread.sleep(2000);
+        } catch(InterruptedException e) {}
+        Intent intent = new Intent(this, SingleTaskActivity.class);
+        intent.setFlags(FLAG_ACTIVITY_CLEAR_TOP | FLAG_ACTIVITY_NEW_TASK);
+
+        startActivity(intent);
+        finish();
+    }
+
+    @Override
+    protected String getTag() {
+        return TAG;
+    }
+}
diff --git a/tests/framework/base/activitymanager/app/src/android/server/am/TranslucentActivity.java b/tests/framework/base/activitymanager/app/src/android/server/am/TranslucentActivity.java
new file mode 100644
index 0000000..a83ceaf
--- /dev/null
+++ b/tests/framework/base/activitymanager/app/src/android/server/am/TranslucentActivity.java
@@ -0,0 +1,23 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License
+ */
+
+package android.server.am;
+
+import android.app.Activity;
+
+public class TranslucentActivity extends Activity {
+
+}
diff --git a/tests/framework/base/activitymanager/app/src/android/server/am/TranslucentAssistantActivity.java b/tests/framework/base/activitymanager/app/src/android/server/am/TranslucentAssistantActivity.java
new file mode 100644
index 0000000..2ad5e17
--- /dev/null
+++ b/tests/framework/base/activitymanager/app/src/android/server/am/TranslucentAssistantActivity.java
@@ -0,0 +1,45 @@
+/*
+ * 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.server.am;
+
+import static android.app.WindowConfiguration.ACTIVITY_TYPE_ASSISTANT;
+import static android.content.Intent.FLAG_ACTIVITY_CLEAR_TASK;
+import static android.content.Intent.FLAG_ACTIVITY_NEW_TASK;
+
+import android.app.Activity;
+import android.app.ActivityOptions;
+import android.content.Intent;
+import android.os.Bundle;
+
+public class TranslucentAssistantActivity extends AssistantActivity {
+
+    /**
+     * Launches a new instance of the TranslucentAssistantActivity directly into the assistant
+     * stack.
+     */
+    static void launchActivityIntoAssistantStack(Activity caller, Bundle extras) {
+        final Intent intent = new Intent(caller, TranslucentAssistantActivity.class);
+        intent.setFlags(FLAG_ACTIVITY_CLEAR_TASK | FLAG_ACTIVITY_NEW_TASK);
+        if (extras != null) {
+            intent.putExtras(extras);
+        }
+
+        final ActivityOptions options = ActivityOptions.makeBasic();
+        options.setLaunchActivityType(ACTIVITY_TYPE_ASSISTANT);
+        caller.startActivity(intent, options.toBundle());
+    }
+}
diff --git a/tests/framework/base/activitymanager/app/src/android/server/am/TranslucentTestActivity.java b/tests/framework/base/activitymanager/app/src/android/server/am/TranslucentTestActivity.java
new file mode 100644
index 0000000..ba60f67
--- /dev/null
+++ b/tests/framework/base/activitymanager/app/src/android/server/am/TranslucentTestActivity.java
@@ -0,0 +1,36 @@
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.server.am;
+
+import android.os.Bundle;
+
+public class TranslucentTestActivity extends TestActivity {
+
+    private static final String TAG = TranslucentTestActivity.class.getSimpleName();
+
+    @Override
+    protected void onCreate(Bundle icicle) {
+        super.onCreate(icicle);
+
+        setContentView(R.layout.task_overlay);
+    }
+
+    @Override
+    protected String getTag() {
+        return TAG;
+    }
+}
diff --git a/tests/framework/base/activitymanager/app/src/android/server/am/TranslucentTopActivity.java b/tests/framework/base/activitymanager/app/src/android/server/am/TranslucentTopActivity.java
new file mode 100644
index 0000000..5d639db
--- /dev/null
+++ b/tests/framework/base/activitymanager/app/src/android/server/am/TranslucentTopActivity.java
@@ -0,0 +1,53 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.server.am;
+
+import android.os.Handler;
+import android.os.Bundle;
+import android.util.Log;
+
+public class TranslucentTopActivity extends AbstractLifecycleLogActivity {
+
+    private static final String TAG = TranslucentTopActivity.class.getSimpleName();
+
+    @Override
+    protected String getTag() {
+        return TAG;
+    }
+
+    @Override
+    protected void onCreate(Bundle savedInstanceState) {
+        super.onCreate(savedInstanceState);
+
+        final boolean useWallpaper = getIntent().getBooleanExtra("USE_WALLPAPER", false);
+        if (useWallpaper) {
+            setTheme(R.style.TranslucentWallpaperTheme);
+        }
+
+        final int finishDelay = getIntent().getIntExtra("FINISH_DELAY", 0);
+        if (finishDelay > 0) {
+            Handler handler = new Handler();
+            handler.postDelayed(new Runnable() {
+                @Override
+                public void run() {
+                    Log.d(TAG, "Calling finish()");
+                    finish();
+                }
+            }, finishDelay);
+        }
+    }
+}
diff --git a/tests/framework/base/activitymanager/app/src/android/server/am/TurnScreenOnActivity.java b/tests/framework/base/activitymanager/app/src/android/server/am/TurnScreenOnActivity.java
new file mode 100644
index 0000000..4334503
--- /dev/null
+++ b/tests/framework/base/activitymanager/app/src/android/server/am/TurnScreenOnActivity.java
@@ -0,0 +1,32 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License
+ */
+
+package android.server.am;
+
+import android.app.Activity;
+import android.os.Bundle;
+import android.view.WindowManager;
+
+public class TurnScreenOnActivity extends Activity {
+    @Override
+    protected void onCreate(Bundle savedInstanceState) {
+        getWindow().addFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON
+                | WindowManager.LayoutParams.FLAG_SHOW_WHEN_LOCKED
+                | WindowManager.LayoutParams.FLAG_DISMISS_KEYGUARD
+                | WindowManager.LayoutParams.FLAG_TURN_SCREEN_ON);
+        super.onCreate(savedInstanceState);
+    }
+}
diff --git a/tests/framework/base/activitymanager/app/src/android/server/am/TurnScreenOnAttrActivity.java b/tests/framework/base/activitymanager/app/src/android/server/am/TurnScreenOnAttrActivity.java
new file mode 100644
index 0000000..f644eb2
--- /dev/null
+++ b/tests/framework/base/activitymanager/app/src/android/server/am/TurnScreenOnAttrActivity.java
@@ -0,0 +1,26 @@
+/*
+ * 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.server.am;
+
+public class TurnScreenOnAttrActivity extends AbstractLifecycleLogActivity {
+    private static final String TAG = TurnScreenOnAttrActivity.class.getSimpleName();
+
+    @Override
+    protected String getTag() {
+        return TAG;
+    }
+}
diff --git a/tests/framework/base/activitymanager/app/src/android/server/am/TurnScreenOnAttrDismissKeyguardActivity.java b/tests/framework/base/activitymanager/app/src/android/server/am/TurnScreenOnAttrDismissKeyguardActivity.java
new file mode 100644
index 0000000..acc92b6
--- /dev/null
+++ b/tests/framework/base/activitymanager/app/src/android/server/am/TurnScreenOnAttrDismissKeyguardActivity.java
@@ -0,0 +1,36 @@
+/*
+ * 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.server.am;
+
+import android.app.KeyguardManager;
+import android.os.Bundle;
+
+public class TurnScreenOnAttrDismissKeyguardActivity extends AbstractLifecycleLogActivity {
+    private static final String TAG = TurnScreenOnAttrDismissKeyguardActivity.class.getSimpleName();
+
+    @Override
+    protected void onCreate(Bundle icicle) {
+        super.onCreate(icicle);
+        ((KeyguardManager) getSystemService(KEYGUARD_SERVICE))
+                .requestDismissKeyguard(this, new KeyguardDismissLoggerCallback(this));
+    }
+
+    @Override
+    protected String getTag() {
+        return TAG;
+    }
+}
diff --git a/tests/framework/base/activitymanager/app/src/android/server/am/TurnScreenOnAttrRemoveAttrActivity.java b/tests/framework/base/activitymanager/app/src/android/server/am/TurnScreenOnAttrRemoveAttrActivity.java
new file mode 100644
index 0000000..9ad7ddd
--- /dev/null
+++ b/tests/framework/base/activitymanager/app/src/android/server/am/TurnScreenOnAttrRemoveAttrActivity.java
@@ -0,0 +1,32 @@
+/*
+ * 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.server.am;
+
+public class TurnScreenOnAttrRemoveAttrActivity extends AbstractLifecycleLogActivity {
+    private static final String TAG = TurnScreenOnAttrRemoveAttrActivity.class.getSimpleName();
+
+    @Override
+    protected void onStop() {
+        super.onStop();
+        setTurnScreenOn(false);
+    }
+
+    @Override
+    protected String getTag() {
+        return TAG;
+    }
+}
diff --git a/tests/framework/base/activitymanager/app/src/android/server/am/TurnScreenOnDismissKeyguardActivity.java b/tests/framework/base/activitymanager/app/src/android/server/am/TurnScreenOnDismissKeyguardActivity.java
new file mode 100644
index 0000000..8f4b947
--- /dev/null
+++ b/tests/framework/base/activitymanager/app/src/android/server/am/TurnScreenOnDismissKeyguardActivity.java
@@ -0,0 +1,33 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License
+ */
+
+package android.server.am;
+
+import android.app.Activity;
+import android.app.KeyguardManager;
+import android.os.Bundle;
+import android.view.WindowManager;
+
+public class TurnScreenOnDismissKeyguardActivity extends Activity {
+    @Override
+    protected void onCreate(Bundle savedInstanceState) {
+        super.onCreate(savedInstanceState);
+        getWindow().addFlags(WindowManager.LayoutParams.FLAG_SHOW_WHEN_LOCKED
+                | WindowManager.LayoutParams.FLAG_TURN_SCREEN_ON);
+        getSystemService(KeyguardManager.class).requestDismissKeyguard(this,
+                new KeyguardDismissLoggerCallback(this));
+    }
+}
diff --git a/tests/framework/base/activitymanager/app/src/android/server/am/TurnScreenOnShowOnLockActivity.java b/tests/framework/base/activitymanager/app/src/android/server/am/TurnScreenOnShowOnLockActivity.java
new file mode 100644
index 0000000..6113a23
--- /dev/null
+++ b/tests/framework/base/activitymanager/app/src/android/server/am/TurnScreenOnShowOnLockActivity.java
@@ -0,0 +1,26 @@
+/*
+ * 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.server.am;
+
+public class TurnScreenOnShowOnLockActivity extends AbstractLifecycleLogActivity {
+    private static final String TAG = TurnScreenOnShowOnLockActivity.class.getSimpleName();
+
+    @Override
+    protected String getTag() {
+        return TAG;
+    }
+}
diff --git a/tests/framework/base/activitymanager/app/src/android/server/am/TurnScreenOnSingleTaskActivity.java b/tests/framework/base/activitymanager/app/src/android/server/am/TurnScreenOnSingleTaskActivity.java
new file mode 100644
index 0000000..2dddb9b
--- /dev/null
+++ b/tests/framework/base/activitymanager/app/src/android/server/am/TurnScreenOnSingleTaskActivity.java
@@ -0,0 +1,26 @@
+/*
+ * 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.server.am;
+
+public class TurnScreenOnSingleTaskActivity extends AbstractLifecycleLogActivity {
+    private static final String TAG = TurnScreenOnSingleTaskActivity.class.getSimpleName();
+
+    @Override
+    protected String getTag() {
+        return TAG;
+    }
+}
diff --git a/tests/framework/base/activitymanager/app/src/android/server/am/TurnScreenOnWithRelayoutActivity.java b/tests/framework/base/activitymanager/app/src/android/server/am/TurnScreenOnWithRelayoutActivity.java
new file mode 100644
index 0000000..a1ebbab
--- /dev/null
+++ b/tests/framework/base/activitymanager/app/src/android/server/am/TurnScreenOnWithRelayoutActivity.java
@@ -0,0 +1,43 @@
+/*
+ * 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.server.am;
+import android.view.WindowManager;
+
+public class TurnScreenOnWithRelayoutActivity extends AbstractLifecycleLogActivity {
+    private static final String TAG = TurnScreenOnWithRelayoutActivity.class.getSimpleName();
+
+    @Override
+    protected String getTag() {
+        return TAG;
+    }
+
+    @Override
+    protected void onStart() {
+        super.onStart();
+        getWindow().addFlags(WindowManager.LayoutParams.FLAG_SHOW_WHEN_LOCKED
+                | WindowManager.LayoutParams.FLAG_TURN_SCREEN_ON);
+    }
+
+    @Override
+    protected void onStop() {
+        super.onStop();
+
+        // This is to force a relayout, specifically with insets changed. When the insets are
+        // changed, it will trigger a performShow that could turn the screen on.
+        getWindow().addFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN);
+    }
+}
diff --git a/tests/framework/base/activitymanager/app/src/android/server/am/VirtualDisplayActivity.java b/tests/framework/base/activitymanager/app/src/android/server/am/VirtualDisplayActivity.java
new file mode 100644
index 0000000..ade1f74
--- /dev/null
+++ b/tests/framework/base/activitymanager/app/src/android/server/am/VirtualDisplayActivity.java
@@ -0,0 +1,235 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License
+ */
+
+package android.server.am;
+
+import static android.hardware.display.DisplayManager.VIRTUAL_DISPLAY_FLAG_OWN_CONTENT_ONLY;
+import static android.hardware.display.DisplayManager.VIRTUAL_DISPLAY_FLAG_PUBLIC;
+
+import android.app.Activity;
+import android.content.Intent;
+import android.hardware.display.DisplayManager;
+import android.hardware.display.VirtualDisplay;
+import android.os.Bundle;
+import android.server.am.tools.ActivityLauncher;
+import android.util.Log;
+import android.view.Surface;
+import android.view.SurfaceHolder;
+import android.view.SurfaceView;
+import android.view.ViewGroup;
+
+import java.util.HashMap;
+
+/**
+ * Activity that is able to create and destroy a virtual display.
+ */
+public class VirtualDisplayActivity extends Activity implements SurfaceHolder.Callback {
+    private static final String TAG = "VirtualDisplayActivity";
+
+    private static final int DEFAULT_DENSITY_DPI = 160;
+    private static final String KEY_DENSITY_DPI = "density_dpi";
+    private static final String KEY_CAN_SHOW_WITH_INSECURE_KEYGUARD
+            = "can_show_with_insecure_keyguard";
+    private static final String KEY_PUBLIC_DISPLAY = "public_display";
+    private static final String KEY_RESIZE_DISPLAY = "resize_display";
+    private static final String KEY_LAUNCH_TARGET_ACTIVITY = "launch_target_activity";
+    private static final String KEY_COUNT = "count";
+
+    private DisplayManager mDisplayManager;
+
+    // Container for details about a pending virtual display creation request.
+    private static class VirtualDisplayRequest {
+        public final SurfaceView surfaceView;
+        public final Bundle extras;
+
+        public VirtualDisplayRequest(SurfaceView surfaceView, Bundle extras) {
+            this.surfaceView = surfaceView;
+            this.extras = extras;
+        }
+    }
+
+    // Container to hold association between an active virtual display and surface view.
+    private static class VirtualDisplayEntry {
+        public final VirtualDisplay display;
+        public final SurfaceView surfaceView;
+        public final boolean resizeDisplay;
+        public final int density;
+
+        public VirtualDisplayEntry(VirtualDisplay display, SurfaceView surfaceView, int density,
+                boolean resizeDisplay) {
+            this.display = display;
+            this.surfaceView = surfaceView;
+            this.density = density;
+            this.resizeDisplay = resizeDisplay;
+        }
+    }
+
+    private HashMap<Surface, VirtualDisplayRequest> mPendingDisplayRequests;
+    private HashMap<Surface, VirtualDisplayEntry> mVirtualDisplays;
+
+    @Override
+    protected void onCreate(Bundle savedInstanceState) {
+        super.onCreate(savedInstanceState);
+        setContentView(R.layout.virtual_display_layout);
+
+        mVirtualDisplays = new HashMap<>();
+        mPendingDisplayRequests = new HashMap<>();
+        mDisplayManager = (DisplayManager) getSystemService(DISPLAY_SERVICE);
+    }
+
+    @Override
+    protected void onNewIntent(Intent intent) {
+        super.onNewIntent(intent);
+        final Bundle extras = intent.getExtras();
+        if (extras == null) {
+            return;
+        }
+
+        String command = extras.getString("command");
+        switch (command) {
+            case "create_display":
+                createVirtualDisplay(extras);
+                break;
+            case "destroy_display":
+                destroyVirtualDisplays();
+                break;
+            case "resize_display":
+                resizeDisplay();
+                break;
+        }
+    }
+
+    @Override
+    protected void onDestroy() {
+        super.onDestroy();
+        destroyVirtualDisplays();
+    }
+
+    private void createVirtualDisplay(Bundle extras) {
+        final int requestedCount = extras.getInt(KEY_COUNT, 1);
+        Log.d(TAG, "createVirtualDisplays. requested count:" + requestedCount);
+
+        for (int displayCount = 0; displayCount < requestedCount; ++displayCount) {
+            final ViewGroup root = (ViewGroup) findViewById(android.R.id.content);
+            final SurfaceView surfaceView = new SurfaceView(this);
+            surfaceView.setLayoutParams(new ViewGroup.LayoutParams(
+                    ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.MATCH_PARENT));
+            surfaceView.getHolder().addCallback(this);
+            mPendingDisplayRequests.put(surfaceView.getHolder().getSurface(),
+                    new VirtualDisplayRequest(surfaceView, extras));
+            root.addView(surfaceView);
+        }
+    }
+
+    private void destroyVirtualDisplays() {
+        Log.d(TAG, "destroyVirtualDisplays");
+        final ViewGroup root = (ViewGroup) findViewById(android.R.id.content);
+
+        for (VirtualDisplayEntry entry : mVirtualDisplays.values()) {
+            Log.d(TAG, "destroying:" + entry.display);
+            entry.display.release();
+            root.removeView(entry.surfaceView);
+        }
+
+        mPendingDisplayRequests.clear();
+        mVirtualDisplays.clear();
+    }
+
+    @Override
+    public void surfaceCreated(SurfaceHolder surfaceHolder) {
+        final VirtualDisplayRequest entry =
+                mPendingDisplayRequests.remove(surfaceHolder.getSurface());
+
+        if (entry == null) {
+            return;
+        }
+
+        final int densityDpi = entry.extras.getInt(KEY_DENSITY_DPI, DEFAULT_DENSITY_DPI);
+        final boolean resizeDisplay = entry.extras.getBoolean(KEY_RESIZE_DISPLAY);
+        final String launchActivityName = entry.extras.getString(KEY_LAUNCH_TARGET_ACTIVITY);
+        final Surface surface = surfaceHolder.getSurface();
+
+        // Initially, the surface will not have a set width or height so rely on the parent.
+        // This should be accurate with match parent on both params.
+        final int width = surfaceHolder.getSurfaceFrame().width();
+        final int height = surfaceHolder.getSurfaceFrame().height();
+
+        int flags = 0;
+
+        final boolean canShowWithInsecureKeyguard
+                = entry.extras.getBoolean(KEY_CAN_SHOW_WITH_INSECURE_KEYGUARD);
+        if (canShowWithInsecureKeyguard) {
+            flags |= 1 << 5; // VIRTUAL_DISPLAY_FLAG_CAN_SHOW_WITH_INSECURE_KEYGUARD
+        }
+
+        final boolean publicDisplay = entry.extras.getBoolean(KEY_PUBLIC_DISPLAY);
+        if (publicDisplay) {
+            flags |= VIRTUAL_DISPLAY_FLAG_PUBLIC | VIRTUAL_DISPLAY_FLAG_OWN_CONTENT_ONLY;
+        }
+
+        Log.d(TAG, "createVirtualDisplay: " + width + "x" + height + ", dpi: "
+                + densityDpi + ", canShowWithInsecureKeyguard=" + canShowWithInsecureKeyguard
+                + ", publicDisplay=" + publicDisplay);
+        try {
+            VirtualDisplay virtualDisplay = mDisplayManager.createVirtualDisplay(
+                    "VirtualDisplay" + mVirtualDisplays.size(), width,
+                    height, densityDpi, surface, flags);
+            mVirtualDisplays.put(surface,
+                    new VirtualDisplayEntry(virtualDisplay, entry.surfaceView, densityDpi,
+                            resizeDisplay));
+            if (launchActivityName != null) {
+                final int displayId = virtualDisplay.getDisplay().getDisplayId();
+                Log.d(TAG, "Launch activity after display created: activityName="
+                        + launchActivityName + ", displayId=" + displayId);
+                launchActivity(launchActivityName, displayId);
+            }
+        } catch (IllegalArgumentException e) {
+            final ViewGroup root = (ViewGroup) findViewById(android.R.id.content);
+            // This is expected when trying to create show-when-locked public display.
+            root.removeView(entry.surfaceView);
+        }
+
+    }
+
+    @Override
+    public void surfaceChanged(SurfaceHolder surfaceHolder, int format, int width, int height) {
+        final VirtualDisplayEntry entry = mVirtualDisplays.get(surfaceHolder.getSurface());
+
+        if (entry != null && entry.resizeDisplay) {
+            entry.display.resize(width, height, entry.density);
+        }
+    }
+
+    @Override
+    public void surfaceDestroyed(SurfaceHolder surfaceHolder) {
+    }
+
+    /** Resize virtual display to half of the surface frame size. */
+    private void resizeDisplay() {
+        final VirtualDisplayEntry vd = (VirtualDisplayEntry) mVirtualDisplays.values().toArray()[0];
+        final SurfaceHolder surfaceHolder = vd.surfaceView.getHolder();
+        vd.display.resize(surfaceHolder.getSurfaceFrame().width() / 2,
+                surfaceHolder.getSurfaceFrame().height() / 2, vd.density);
+    }
+
+    private void launchActivity(String activityName, int displayId) {
+        final Bundle extras = new Bundle();
+        extras.putBoolean("launch_activity", true);
+        extras.putString("target_activity", activityName);
+        extras.putInt("display_id", displayId);
+        ActivityLauncher.launchActivityFromExtras(this, extras);
+    }
+}
diff --git a/tests/framework/base/activitymanager/app/src/android/server/am/VrTestActivity.java b/tests/framework/base/activitymanager/app/src/android/server/am/VrTestActivity.java
new file mode 100644
index 0000000..e163aa8
--- /dev/null
+++ b/tests/framework/base/activitymanager/app/src/android/server/am/VrTestActivity.java
@@ -0,0 +1,62 @@
+/*
+ * 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.server.am;
+
+import android.app.Activity;
+import android.content.ComponentName;
+import android.content.pm.PackageManager;
+import android.os.Bundle;
+import android.util.Log;
+
+import com.android.cts.verifier.vr.MockVrListenerService;
+
+/**
+ * Activity that is able to create and destroy a virtual display.
+ */
+public class VrTestActivity extends Activity {
+    private static final String TAG = "VrTestActivity";
+    private static final boolean DEBUG = false;
+
+    @Override
+    protected void onCreate(Bundle savedInstanceState) {
+        if (DEBUG) Log.i(TAG, "onCreate called.");
+        super.onCreate(savedInstanceState);
+        try {
+            setVrModeEnabled(true, new ComponentName(this, MockVrListenerService.class));
+        } catch (PackageManager.NameNotFoundException e) {
+            Log.e(TAG, "Could not set VR mode: " + e);
+        }
+    }
+
+    @Override
+    protected void onResume() {
+        if (DEBUG) Log.i(TAG, "onResume called.");
+        super.onResume();
+    }
+
+    @Override
+    public void onWindowFocusChanged(boolean hasFocus) {
+        if (DEBUG) Log.i(TAG, "onWindowFocusChanged called with " + hasFocus);
+        super.onWindowFocusChanged(hasFocus);
+    }
+
+    @Override
+    protected void onPause() {
+        if (DEBUG) Log.i(TAG, "onPause called.");
+        super.onPause();
+    }
+}
diff --git a/tests/framework/base/activitymanager/app/src/android/server/am/WallpaperActivity.java b/tests/framework/base/activitymanager/app/src/android/server/am/WallpaperActivity.java
new file mode 100644
index 0000000..2bbe1b3
--- /dev/null
+++ b/tests/framework/base/activitymanager/app/src/android/server/am/WallpaperActivity.java
@@ -0,0 +1,22 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License
+ */
+
+package android.server.am;
+
+import android.app.Activity;
+
+public class WallpaperActivity extends Activity {
+}
diff --git a/tests/framework/base/activitymanager/app/src/android/server/am/tools/ActivityLauncher.java b/tests/framework/base/activitymanager/app/src/android/server/am/tools/ActivityLauncher.java
new file mode 100644
index 0000000..4b35ccf
--- /dev/null
+++ b/tests/framework/base/activitymanager/app/src/android/server/am/tools/ActivityLauncher.java
@@ -0,0 +1,90 @@
+/*
+ * 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.server.am.tools;
+
+import static android.content.Intent.FLAG_ACTIVITY_LAUNCH_ADJACENT;
+import static android.content.Intent.FLAG_ACTIVITY_MULTIPLE_TASK;
+import static android.content.Intent.FLAG_ACTIVITY_NEW_TASK;
+import static android.content.Intent.FLAG_ACTIVITY_REORDER_TO_FRONT;
+
+import android.app.ActivityOptions;
+import android.content.Intent;
+import android.content.ComponentName;
+import android.content.Context;
+import android.net.Uri;
+import android.os.Bundle;
+import android.server.am.TestActivity;
+import android.util.Log;
+
+/** Utility class which contains common code for launching activities. */
+public class ActivityLauncher {
+    private static final String TAG = ActivityLauncher.class.getSimpleName();
+
+    public static void launchActivityFromExtras(final Context context, Bundle extras) {
+        if (extras == null || !extras.getBoolean("launch_activity")) {
+            return;
+        }
+
+        Log.i(TAG, "launchActivityFromExtras: extras=" + extras);
+
+        final Intent newIntent = new Intent();
+        final String targetActivity = extras.getString("target_activity");
+        if (targetActivity != null) {
+            final String extraPackageName = extras.getString("package_name");
+            final String packageName = extraPackageName != null ? extraPackageName
+                    : context.getApplicationContext().getPackageName();
+            newIntent.setComponent(new ComponentName(packageName,
+                    packageName + "." + targetActivity));
+        } else {
+            newIntent.setClass(context, TestActivity.class);
+        }
+
+        if (extras.getBoolean("launch_to_the_side")) {
+            newIntent.addFlags(FLAG_ACTIVITY_NEW_TASK | FLAG_ACTIVITY_LAUNCH_ADJACENT);
+            if (extras.getBoolean("random_data")) {
+                final Uri data = new Uri.Builder()
+                        .path(String.valueOf(System.currentTimeMillis()))
+                        .build();
+                newIntent.setData(data);
+            }
+        }
+        if (extras.getBoolean("multiple_task")) {
+            newIntent.addFlags(FLAG_ACTIVITY_MULTIPLE_TASK);
+        }
+        if (extras.getBoolean("new_task")) {
+            newIntent.addFlags(FLAG_ACTIVITY_NEW_TASK);
+        }
+
+        if (extras.getBoolean("reorder_to_front")) {
+            newIntent.addFlags(FLAG_ACTIVITY_REORDER_TO_FRONT);
+        }
+
+        ActivityOptions options = null;
+        final int displayId = extras.getInt("display_id", -1);
+        if (displayId != -1) {
+            options = ActivityOptions.makeBasic();
+            options.setLaunchDisplayId(displayId);
+            newIntent.addFlags(FLAG_ACTIVITY_NEW_TASK | FLAG_ACTIVITY_MULTIPLE_TASK);
+        }
+
+        try {
+            context.startActivity(newIntent, options != null ? options.toBundle() : null);
+        } catch (SecurityException e) {
+            Log.e(TAG, "SecurityException launching activity");
+        }
+    }
+}
diff --git a/hostsidetests/services/activityandwindowmanager/activitymanager/appDebuggable/Android.mk b/tests/framework/base/activitymanager/appDebuggable/Android.mk
similarity index 100%
rename from hostsidetests/services/activityandwindowmanager/activitymanager/appDebuggable/Android.mk
rename to tests/framework/base/activitymanager/appDebuggable/Android.mk
diff --git a/tests/framework/base/activitymanager/appDebuggable/AndroidManifest.xml b/tests/framework/base/activitymanager/appDebuggable/AndroidManifest.xml
new file mode 100644
index 0000000..e8a2452
--- /dev/null
+++ b/tests/framework/base/activitymanager/appDebuggable/AndroidManifest.xml
@@ -0,0 +1,31 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+     Copyright (C) 2017 The Android Open Source Project
+
+     Licensed under the Apache License, Version 2.0 (the "License");
+     you may not use this file except in compliance with the License.
+     You may obtain a copy of the License at
+
+          http://www.apache.org/licenses/LICENSE-2.0
+
+     Unless required by applicable law or agreed to in writing, software
+     distributed under the License is distributed on an "AS IS" BASIS,
+     WITHOUT 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.server.am.debuggable">
+
+    <!--
+     * Security policy requires that only debuggable processes can be profiled
+     * which is tested by ActivityManagerAmProfileTests.
+     -->
+    <application android:debuggable="true">
+        <activity android:name=".DebuggableAppActivity"
+                  android:resizeableActivity="true"
+                  android:exported="true" />
+    </application>
+
+</manifest>
diff --git a/tests/framework/base/activitymanager/appDebuggable/src/android/server/am/debuggable/DebuggableAppActivity.java b/tests/framework/base/activitymanager/appDebuggable/src/android/server/am/debuggable/DebuggableAppActivity.java
new file mode 100644
index 0000000..33034b3
--- /dev/null
+++ b/tests/framework/base/activitymanager/appDebuggable/src/android/server/am/debuggable/DebuggableAppActivity.java
@@ -0,0 +1,22 @@
+/*
+ * 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.server.am.debuggable;
+
+import android.app.Activity;
+
+public class DebuggableAppActivity extends Activity {
+}
diff --git a/hostsidetests/services/activityandwindowmanager/activitymanager/appDisplaySize/Android.mk b/tests/framework/base/activitymanager/appDisplaySize/Android.mk
similarity index 100%
rename from hostsidetests/services/activityandwindowmanager/activitymanager/appDisplaySize/Android.mk
rename to tests/framework/base/activitymanager/appDisplaySize/Android.mk
diff --git a/tests/framework/base/activitymanager/appDisplaySize/AndroidManifest.xml b/tests/framework/base/activitymanager/appDisplaySize/AndroidManifest.xml
new file mode 100644
index 0000000..a3bef31
--- /dev/null
+++ b/tests/framework/base/activitymanager/appDisplaySize/AndroidManifest.xml
@@ -0,0 +1,32 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+     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.
+-->
+
+<manifest xmlns:android="http://schemas.android.com/apk/res/android"
+          package="android.server.am.displaysize">
+
+    <uses-permission android:name="android.permission.DISABLE_KEYGUARD" />
+
+    <!-- Set a ridiculously high value for required smallest width. Hopefully
+         we never have to run CTS on Times Square display. -->
+    <supports-screens android:requiresSmallestWidthDp="100000" />
+
+    <application>
+        <activity android:name=".SmallestWidthActivity"
+                  android:exported="true" />
+    </application>
+
+</manifest>
diff --git a/tests/framework/base/activitymanager/appDisplaySize/src/android/server/am/displaysize/SmallestWidthActivity.java b/tests/framework/base/activitymanager/appDisplaySize/src/android/server/am/displaysize/SmallestWidthActivity.java
new file mode 100644
index 0000000..1ac4c0f
--- /dev/null
+++ b/tests/framework/base/activitymanager/appDisplaySize/src/android/server/am/displaysize/SmallestWidthActivity.java
@@ -0,0 +1,39 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.server.am.displaysize;
+
+import android.app.Activity;
+import android.content.ComponentName;
+import android.content.Intent;
+
+public class SmallestWidthActivity extends Activity {
+
+    /**
+     * Extra key to launch another activity. The extra value is activity's component name.
+     */
+    private static final String EXTRA_LAUNCH_ANOTHER_ACTIVITY = "launch_another_activity";
+
+    @Override
+    protected void onNewIntent(final Intent intent) {
+        super.onNewIntent(intent);
+
+        if (intent.hasExtra(EXTRA_LAUNCH_ANOTHER_ACTIVITY)) {
+            startActivity(new Intent().setComponent(ComponentName.unflattenFromString(
+                    intent.getStringExtra(EXTRA_LAUNCH_ANOTHER_ACTIVITY))));
+        }
+    }
+}
diff --git a/tests/framework/base/activitymanager/appPrereleaseSdk/Android.mk b/tests/framework/base/activitymanager/appPrereleaseSdk/Android.mk
new file mode 100644
index 0000000..45942def
--- /dev/null
+++ b/tests/framework/base/activitymanager/appPrereleaseSdk/Android.mk
@@ -0,0 +1,31 @@
+# Copyright (C) 2016 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#      http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+LOCAL_PATH:= $(call my-dir)
+
+include $(CLEAR_VARS)
+
+# don't include this package in any target
+LOCAL_MODULE_TAGS := optional
+
+LOCAL_SRC_FILES := $(call all-java-files-under, src)
+
+LOCAL_SDK_VERSION := test_current
+
+# Tag this module as a cts test artifact
+LOCAL_COMPATIBILITY_SUITE := cts vts general-tests
+
+LOCAL_PACKAGE_NAME := CtsDevicePrereleaseSdkApp
+
+include $(BUILD_CTS_SUPPORT_PACKAGE)
diff --git a/tests/framework/base/activitymanager/appPrereleaseSdk/AndroidManifest.xml b/tests/framework/base/activitymanager/appPrereleaseSdk/AndroidManifest.xml
new file mode 100644
index 0000000..76cc2b6
--- /dev/null
+++ b/tests/framework/base/activitymanager/appPrereleaseSdk/AndroidManifest.xml
@@ -0,0 +1,37 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+     Copyright (C) 2017 The Android Open Source Project
+
+     Licensed under the Apache License, Version 2.0 (the "License");
+     you may not use this file except in compliance with the License.
+     You may obtain a copy of the License at
+
+          http://www.apache.org/licenses/LICENSE-2.0
+
+     Unless required by applicable law or agreed to in writing, software
+     distributed under the License is distributed on an "AS IS" BASIS,
+     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+     See the License for the specific language governing permissions and
+     limitations under the License.
+-->
+
+<!-- Manually set the compileSdkVersion and codename to simulate being compiled against
+     pre-release O SDK. -->
+<manifest xmlns:android="http://schemas.android.com/apk/res/android"
+          package="android.server.am.prerelease"
+          android:compileSdkVersion="25"
+          android:compileSdkVersionCodename="O">
+
+    <uses-permission android:name="android.permission.DISABLE_KEYGUARD" />
+
+    <application>
+        <activity android:name=".MainActivity"
+                  android:exported="true">
+            <intent-filter>
+                <action android:name="android.intent.action.MAIN"/>
+                <category android:name="android.intent.category.LAUNCHER"/>
+            </intent-filter>
+        </activity>
+    </application>
+
+</manifest>
diff --git a/tests/framework/base/activitymanager/appPrereleaseSdk/src/android/server/am/prerelease/MainActivity.java b/tests/framework/base/activitymanager/appPrereleaseSdk/src/android/server/am/prerelease/MainActivity.java
new file mode 100644
index 0000000..8684faf
--- /dev/null
+++ b/tests/framework/base/activitymanager/appPrereleaseSdk/src/android/server/am/prerelease/MainActivity.java
@@ -0,0 +1,23 @@
+/*
+ * 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.server.am.prerelease;
+
+import android.app.Activity;
+
+public class MainActivity extends Activity {
+
+}
diff --git a/hostsidetests/services/activityandwindowmanager/activitymanager/appSecondUid/Android.mk b/tests/framework/base/activitymanager/appSecondUid/Android.mk
similarity index 100%
rename from hostsidetests/services/activityandwindowmanager/activitymanager/appSecondUid/Android.mk
rename to tests/framework/base/activitymanager/appSecondUid/Android.mk
diff --git a/tests/framework/base/activitymanager/appSecondUid/AndroidManifest.xml b/tests/framework/base/activitymanager/appSecondUid/AndroidManifest.xml
new file mode 100644
index 0000000..4d4e98d
--- /dev/null
+++ b/tests/framework/base/activitymanager/appSecondUid/AndroidManifest.xml
@@ -0,0 +1,42 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+     Copyright (C) 2017 The Android Open Source Project
+
+     Licensed under the Apache License, Version 2.0 (the "License");
+     you may not use this file except in compliance with the License.
+     You may obtain a copy of the License at
+
+          http://www.apache.org/licenses/LICENSE-2.0
+
+     Unless required by applicable law or agreed to in writing, software
+     distributed under the License is distributed on an "AS IS" BASIS,
+     WITHOUT 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.server.am.second">
+
+    <application>
+        <activity
+            android:name=".SecondActivity"
+            android:resizeableActivity="true"
+            android:allowEmbedded="true"
+            android:exported="true" />
+        <activity
+            android:name=".SecondActivityNoEmbedding"
+            android:resizeableActivity="true"
+            android:allowEmbedded="false"
+            android:exported="true" />
+        <receiver
+            android:name=".LaunchBroadcastReceiver"
+            android:enabled="true"
+            android:exported="true" >
+            <intent-filter>
+                <action android:name="android.server.am.second.LAUNCH_BROADCAST_ACTION"/>
+            </intent-filter>
+        </receiver>
+    </application>
+
+</manifest>
diff --git a/tests/framework/base/activitymanager/appSecondUid/src/android/server/am/second/LaunchBroadcastReceiver.java b/tests/framework/base/activitymanager/appSecondUid/src/android/server/am/second/LaunchBroadcastReceiver.java
new file mode 100644
index 0000000..196be3e
--- /dev/null
+++ b/tests/framework/base/activitymanager/appSecondUid/src/android/server/am/second/LaunchBroadcastReceiver.java
@@ -0,0 +1,60 @@
+/*
+ * 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.server.am.second;
+
+import android.app.ActivityOptions;
+import android.content.BroadcastReceiver;
+import android.content.ComponentName;
+import android.content.Context;
+import android.content.Intent;
+import android.os.Bundle;
+import android.util.Log;
+
+/** Broadcast receiver that can launch activities. */
+public class LaunchBroadcastReceiver extends BroadcastReceiver {
+    private static final String TAG = LaunchBroadcastReceiver.class.getSimpleName();
+
+    @Override
+    public void onReceive(Context context, Intent intent) {
+        final Bundle extras = intent.getExtras();
+        final Intent newIntent = new Intent();
+
+        final String targetActivity = extras != null ? extras.getString("target_activity") : null;
+        if (targetActivity != null) {
+            String packageName = extras.getString("package_name");
+            newIntent.setComponent(new ComponentName(packageName,
+                    packageName + "." + targetActivity));
+        } else {
+            newIntent.setClass(context, SecondActivity.class);
+        }
+
+        ActivityOptions options = ActivityOptions.makeBasic();
+        int displayId = extras.getInt("display_id", -1);
+        if (displayId != -1) {
+            options.setLaunchDisplayId(displayId);
+            newIntent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_MULTIPLE_TASK);
+        } else {
+            newIntent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
+        }
+
+        try {
+            context.startActivity(newIntent, options.toBundle());
+        } catch (SecurityException e) {
+            Log.e(TAG, "SecurityException launching activity");
+        }
+    }
+}
diff --git a/tests/framework/base/activitymanager/appSecondUid/src/android/server/am/second/SecondActivity.java b/tests/framework/base/activitymanager/appSecondUid/src/android/server/am/second/SecondActivity.java
new file mode 100644
index 0000000..9607ee2
--- /dev/null
+++ b/tests/framework/base/activitymanager/appSecondUid/src/android/server/am/second/SecondActivity.java
@@ -0,0 +1,22 @@
+/*
+ * 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.server.am.second;
+
+import android.app.Activity;
+
+public class SecondActivity extends Activity {
+}
diff --git a/tests/framework/base/activitymanager/appSecondUid/src/android/server/am/second/SecondActivityNoEmbedding.java b/tests/framework/base/activitymanager/appSecondUid/src/android/server/am/second/SecondActivityNoEmbedding.java
new file mode 100644
index 0000000..43a967b
--- /dev/null
+++ b/tests/framework/base/activitymanager/appSecondUid/src/android/server/am/second/SecondActivityNoEmbedding.java
@@ -0,0 +1,22 @@
+/*
+ * 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.server.am.second;
+
+import android.app.Activity;
+
+public class SecondActivityNoEmbedding extends Activity {
+}
\ No newline at end of file
diff --git a/hostsidetests/services/activityandwindowmanager/activitymanager/appThirdUid/Android.mk b/tests/framework/base/activitymanager/appThirdUid/Android.mk
similarity index 100%
rename from hostsidetests/services/activityandwindowmanager/activitymanager/appThirdUid/Android.mk
rename to tests/framework/base/activitymanager/appThirdUid/Android.mk
diff --git a/tests/framework/base/activitymanager/appThirdUid/AndroidManifest.xml b/tests/framework/base/activitymanager/appThirdUid/AndroidManifest.xml
new file mode 100644
index 0000000..ddd89b0
--- /dev/null
+++ b/tests/framework/base/activitymanager/appThirdUid/AndroidManifest.xml
@@ -0,0 +1,28 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+     Copyright (C) 2017 The Android Open Source Project
+
+     Licensed under the Apache License, Version 2.0 (the "License");
+     you may not use this file except in compliance with the License.
+     You may obtain a copy of the License at
+
+          http://www.apache.org/licenses/LICENSE-2.0
+
+     Unless required by applicable law or agreed to in writing, software
+     distributed under the License is distributed on an "AS IS" BASIS,
+     WITHOUT 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.server.am.third">
+
+    <application>
+        <activity android:name=".ThirdActivity"
+                  android:resizeableActivity="true"
+                  android:allowEmbedded="true"
+                  android:exported="true" />
+    </application>
+
+</manifest>
diff --git a/tests/framework/base/activitymanager/appThirdUid/src/android/server/am/third/ThirdActivity.java b/tests/framework/base/activitymanager/appThirdUid/src/android/server/am/third/ThirdActivity.java
new file mode 100644
index 0000000..4327d5f
--- /dev/null
+++ b/tests/framework/base/activitymanager/appThirdUid/src/android/server/am/third/ThirdActivity.java
@@ -0,0 +1,22 @@
+/*
+ * 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.server.am.third;
+
+import android.app.Activity;
+
+public class ThirdActivity extends Activity {
+}
diff --git a/hostsidetests/services/activityandwindowmanager/displayserviceapp/Android.mk b/tests/framework/base/activitymanager/displayserviceapp/Android.mk
similarity index 100%
rename from hostsidetests/services/activityandwindowmanager/displayserviceapp/Android.mk
rename to tests/framework/base/activitymanager/displayserviceapp/Android.mk
diff --git a/hostsidetests/services/activityandwindowmanager/displayserviceapp/app/Android.mk b/tests/framework/base/activitymanager/displayserviceapp/app/Android.mk
similarity index 100%
rename from hostsidetests/services/activityandwindowmanager/displayserviceapp/app/Android.mk
rename to tests/framework/base/activitymanager/displayserviceapp/app/Android.mk
diff --git a/tests/framework/base/activitymanager/displayserviceapp/app/AndroidManifest.xml b/tests/framework/base/activitymanager/displayserviceapp/app/AndroidManifest.xml
new file mode 100644
index 0000000..add9f60
--- /dev/null
+++ b/tests/framework/base/activitymanager/displayserviceapp/app/AndroidManifest.xml
@@ -0,0 +1,25 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT 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"
+          xmlns:androidprv="http://schemas.android.com/apk/prv/res/android"
+          package="android.server.am.displayservice">
+    <uses-permission android:name="android.permission.WAKE_LOCK" />
+    <application android:label="CtsDisplayService">
+        <service android:name=".VirtualDisplayService"
+                 android:exported="true" />
+    </application>
+</manifest>
diff --git a/tests/framework/base/activitymanager/displayserviceapp/app/src/android/server/am/displayservice/VirtualDisplayService.java b/tests/framework/base/activitymanager/displayserviceapp/app/src/android/server/am/displayservice/VirtualDisplayService.java
new file mode 100644
index 0000000..bf6b87e
--- /dev/null
+++ b/tests/framework/base/activitymanager/displayserviceapp/app/src/android/server/am/displayservice/VirtualDisplayService.java
@@ -0,0 +1,105 @@
+/*
+ * 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.server.am.displayservice;
+
+import static android.hardware.display.DisplayManager.VIRTUAL_DISPLAY_FLAG_OWN_CONTENT_ONLY;
+import static android.hardware.display.DisplayManager.VIRTUAL_DISPLAY_FLAG_PRESENTATION;
+
+import android.app.Notification;
+import android.app.NotificationChannel;
+import android.app.NotificationManager;
+import android.app.Service;
+import android.content.Intent;
+import android.graphics.PixelFormat;
+import android.hardware.display.DisplayManager;
+import android.hardware.display.VirtualDisplay;
+import android.media.ImageReader;
+import android.os.Bundle;
+import android.os.IBinder;
+import android.util.Log;
+import android.view.Surface;
+
+public class VirtualDisplayService extends Service {
+    private static final String NOTIFICATION_CHANNEL_ID = "cts/VirtualDisplayService";
+    private static final String TAG = "VirtualDisplayService";
+
+    private static final int FOREGROUND_ID = 1;
+
+    private static final int DENSITY = 160;
+    private static final int HEIGHT = 480;
+    private static final int WIDTH = 800;
+
+    private ImageReader mReader;
+    private VirtualDisplay mVirtualDisplay;
+
+    @Override
+    public void onCreate() {
+        super.onCreate();
+
+        NotificationManager notificationManager = getSystemService(NotificationManager.class);
+        notificationManager.createNotificationChannel(new NotificationChannel(
+            NOTIFICATION_CHANNEL_ID, NOTIFICATION_CHANNEL_ID,
+            NotificationManager.IMPORTANCE_DEFAULT));
+        Notification notif = new Notification.Builder(this, NOTIFICATION_CHANNEL_ID)
+                .setSmallIcon(android.R.drawable.ic_dialog_alert)
+                .build();
+        startForeground(FOREGROUND_ID, notif);
+    }
+
+    @Override
+    public int onStartCommand(Intent intent, int flags, int startId) {
+        String command = intent.getStringExtra("command");
+        Log.d(TAG, "Got command: " + command);
+
+        if ("create".equals(command)) {
+            createVirtualDisplay(intent);
+        } if ("off".equals(command)) {
+            mVirtualDisplay.setSurface(null);
+        } else if ("on".equals(command)) {
+            mVirtualDisplay.setSurface(mReader.getSurface());
+        } else if ("destroy".equals(command)) {
+            destroyVirtualDisplay();
+            stopSelf();
+        }
+
+        return START_NOT_STICKY;
+    }
+
+    @Override
+    public IBinder onBind(Intent intent) {
+        return null;
+    }
+
+    private void createVirtualDisplay(Intent intent) {
+        mReader = ImageReader.newInstance(WIDTH, HEIGHT, PixelFormat.RGBA_8888, 2);
+
+        final DisplayManager displayManager = getSystemService(DisplayManager.class);
+        final String name = "CtsVirtualDisplay";
+
+        int flags = VIRTUAL_DISPLAY_FLAG_PRESENTATION | VIRTUAL_DISPLAY_FLAG_OWN_CONTENT_ONLY;
+        if (intent.getBooleanExtra("show_content_when_locked", false /* defaultValue */)) {
+            flags |= 1 << 5; // VIRTUAL_DISPLAY_FLAG_CAN_SHOW_WITH_INSECURE_KEYGUARD
+        }
+        mVirtualDisplay = displayManager.createVirtualDisplay(
+                name, WIDTH, HEIGHT, DENSITY, mReader.getSurface(), flags);
+    }
+
+    private void destroyVirtualDisplay() {
+        mVirtualDisplay.release();
+        mReader.close();
+    }
+}
diff --git a/tests/framework/base/activitymanager/displayserviceapp/util/Android.mk b/tests/framework/base/activitymanager/displayserviceapp/util/Android.mk
new file mode 100644
index 0000000..613888a
--- /dev/null
+++ b/tests/framework/base/activitymanager/displayserviceapp/util/Android.mk
@@ -0,0 +1,33 @@
+# 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_SRC_FILES := $(call all-java-files-under, src)
+
+LOCAL_ADDITIONAL_DEPENDENCIES := $(LOCAL_PATH)/Android.mk
+
+LOCAL_STATIC_JAVA_LIBRARIES := \
+    compatibility-device-util \
+    android-support-test
+
+LOCAL_MODULE := cts-display-service-app-util
+
+LOCAL_SDK_VERSION := current
+
+include $(BUILD_STATIC_JAVA_LIBRARY)
diff --git a/tests/framework/base/activitymanager/displayserviceapp/util/src/android/server/am/displayservice/DisplayHelper.java b/tests/framework/base/activitymanager/displayserviceapp/util/src/android/server/am/displayservice/DisplayHelper.java
new file mode 100644
index 0000000..f541770
--- /dev/null
+++ b/tests/framework/base/activitymanager/displayserviceapp/util/src/android/server/am/displayservice/DisplayHelper.java
@@ -0,0 +1,134 @@
+/*
+ * 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.server.am.displayservice;
+
+import static junit.framework.Assert.assertTrue;
+
+import android.support.test.InstrumentationRegistry;
+
+import com.android.compatibility.common.util.SystemUtil;
+
+import java.io.IOException;
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
+
+public class DisplayHelper {
+    private static final String VIRTUAL_DISPLAY_NAME = "CtsVirtualDisplay";
+    private static final String VIRTUAL_DISPLAY_SERVICE =
+            "android.server.am.displayservice/.VirtualDisplayService";
+    private static final Pattern mDisplayDevicePattern = Pattern.compile(
+            ".*DisplayDeviceInfo\\{\"([^\"]+)\":.*, state (\\S+),.*\\}.*");
+
+    private boolean mCreated;
+
+    public DisplayHelper() {
+    }
+
+    public void createAndWaitForDisplay(boolean external, boolean requestShowWhenLocked)
+            {
+        StringBuilder command =
+                new StringBuilder("am startfgservice -n " + VIRTUAL_DISPLAY_SERVICE);
+        command.append(" --es command create");
+        if (external) {
+            command.append(" --ez external_display true");
+        }
+        if (requestShowWhenLocked) {
+            command.append(" --ez show_content_when_locked true");
+        }
+        executeShellCommand(command.toString());
+
+        waitForDisplayState(false /* default */, true /* exists */, true /* on */);
+        mCreated = true;
+    }
+
+    public void turnDisplayOff() {
+        executeShellCommand(
+                "am start-service -n " + VIRTUAL_DISPLAY_SERVICE + " --es command off");
+        waitForDisplayState(false /* default */, true /* exists */, false /* on */);
+    }
+
+    public void turnDisplayOn() {
+        executeShellCommand(
+                "am start-service -n " + VIRTUAL_DISPLAY_SERVICE + " --es command on");
+        waitForDisplayState(false /* default */, true /* exists */, true /* on */);
+    }
+
+    public void releaseDisplay() {
+        if (mCreated) {
+            executeShellCommand(
+                    "am start-service -n " + VIRTUAL_DISPLAY_SERVICE + " --es command destroy");
+            waitForDisplayState(false /* default */, false /* exists */, true /* on */);
+        }
+        mCreated = false;
+    }
+
+    public static void waitForDefaultDisplayState(boolean wantOn) {
+        waitForDisplayState(true /* default */, true /* exists */, wantOn);
+    }
+
+    public static boolean getDefaultDisplayState() {
+        return getDisplayState(true);
+    }
+
+    private static void waitForDisplayState(boolean defaultDisplay, boolean wantExists, boolean wantOn) {
+        int tries = 0;
+        boolean done = false;
+        do {
+            if (tries > 0) {
+                try {
+                    Thread.sleep(500);
+                } catch (InterruptedException e) {
+                    // Oh well
+                }
+            }
+
+            Boolean state = getDisplayState(defaultDisplay);
+            done = (!wantExists && state == null)
+                    || (wantExists && state != null && state == wantOn);
+
+            tries++;
+        } while (tries < 10 && !done);
+
+        assertTrue(done);
+    }
+
+    private static Boolean getDisplayState(boolean defaultDisplay) {
+        String dump = executeShellCommand("dumpsys display");
+
+        boolean displayExists = false;
+        boolean displayOn = false;
+        for (String line : dump.split("\\n")) {
+            Matcher matcher = mDisplayDevicePattern.matcher(line);
+            if (matcher.matches()) {
+                if ((defaultDisplay && line.contains("FLAG_DEFAULT_DISPLAY"))
+                        || (!defaultDisplay && VIRTUAL_DISPLAY_NAME.equals(matcher.group(1)))) {
+                    return "ON".equals(matcher.group(2));
+                }
+            }
+        }
+        return null;
+    }
+
+    private static String executeShellCommand(String command) {
+        try {
+            return SystemUtil
+                    .runShellCommand(InstrumentationRegistry.getInstrumentation(), command);
+        } catch (IOException e) {
+            //bubble it up
+            throw new RuntimeException(e);
+        }
+    }
+}
diff --git a/tests/framework/base/activitymanager/src/android/server/am/ActivityAndWindowManagerOverrideConfigTests.java b/tests/framework/base/activitymanager/src/android/server/am/ActivityAndWindowManagerOverrideConfigTests.java
new file mode 100644
index 0000000..116c026
--- /dev/null
+++ b/tests/framework/base/activitymanager/src/android/server/am/ActivityAndWindowManagerOverrideConfigTests.java
@@ -0,0 +1,95 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.server.am;
+
+import static android.app.WindowConfiguration.WINDOWING_MODE_FREEFORM;
+import static android.server.am.StateLogger.log;
+import static android.view.Surface.ROTATION_0;
+import static android.view.Surface.ROTATION_180;
+
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertTrue;
+import static org.junit.Assume.assumeTrue;
+
+import org.junit.Test;
+
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
+
+/**
+ * Build/Install/Run:
+ *     atest CtsActivityManagerDeviceTestCases:ActivityAndWindowManagerOverrideConfigTests
+ */
+public class ActivityAndWindowManagerOverrideConfigTests extends ActivityManagerTestBase {
+    private static final String TEST_ACTIVITY_NAME = "LogConfigurationActivity";
+
+    private static class ConfigurationChangeObserver {
+        private final Pattern mConfigurationChangedPattern =
+            Pattern.compile("(.+)Configuration changed: (\\d+),(\\d+)");
+
+        private ConfigurationChangeObserver() {
+        }
+
+        private boolean findConfigurationChange(String activityName, String logSeparator)
+                throws InterruptedException {
+            int tries = 0;
+            boolean observedChange = false;
+            while (tries < 5 && !observedChange) {
+                final String[] lines = getDeviceLogsForComponent(activityName, logSeparator);
+                log("Looking at logcat");
+                for (int i = lines.length - 1; i >= 0; i--) {
+                    final String line = lines[i].trim();
+                    log(line);
+                    Matcher matcher = mConfigurationChangedPattern.matcher(line);
+                    if (matcher.matches()) {
+                        observedChange = true;
+                        break;
+                    }
+                }
+                tries++;
+                Thread.sleep(500);
+            }
+            return observedChange;
+        }
+    }
+
+    @Test
+    public void testReceiveOverrideConfigFromRelayout() throws Exception {
+        assumeTrue("Device doesn't support freeform. Skipping test.", supportsFreeform());
+
+        launchActivity(TEST_ACTIVITY_NAME, WINDOWING_MODE_FREEFORM);
+
+        try (final RotationSession rotationSession = new RotationSession()) {
+            rotationSession.set(ROTATION_0);
+            String logSeparator = clearLogcat();
+            resizeActivityTask(TEST_ACTIVITY_NAME, 0, 0, 100, 100);
+            ConfigurationChangeObserver c = new ConfigurationChangeObserver();
+            final boolean reportedSizeAfterResize = c.findConfigurationChange(TEST_ACTIVITY_NAME,
+                    logSeparator);
+            assertTrue("Expected to observe configuration change when resizing",
+                    reportedSizeAfterResize);
+
+            logSeparator = clearLogcat();
+            rotationSession.set(ROTATION_180);
+            final boolean reportedSizeAfterRotation = c.findConfigurationChange(TEST_ACTIVITY_NAME,
+                    logSeparator);
+            assertFalse("Not expected to observe configuration change after flip rotation",
+                    reportedSizeAfterRotation);
+        }
+    }
+}
+
diff --git a/tests/framework/base/activitymanager/src/android/server/am/ActivityManagerActivityVisibilityTests.java b/tests/framework/base/activitymanager/src/android/server/am/ActivityManagerActivityVisibilityTests.java
new file mode 100644
index 0000000..33c5b49
--- /dev/null
+++ b/tests/framework/base/activitymanager/src/android/server/am/ActivityManagerActivityVisibilityTests.java
@@ -0,0 +1,427 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License
+ */
+
+package android.server.am;
+
+import static android.app.ActivityManager.StackId.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;
+import static android.app.WindowConfiguration.WINDOWING_MODE_PINNED;
+import static android.app.WindowConfiguration.WINDOWING_MODE_SPLIT_SCREEN_PRIMARY;
+import static android.app.WindowConfiguration.WINDOWING_MODE_SPLIT_SCREEN_SECONDARY;
+import static android.server.am.ActivityManagerState.STATE_PAUSED;
+import static android.server.am.ActivityManagerState.STATE_RESUMED;
+
+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.support.test.filters.FlakyTest;
+import android.platform.test.annotations.Presubmit;
+
+import org.junit.After;
+import org.junit.Test;
+
+/**
+ * Build/Install/Run:
+ *     atest CtsActivityManagerDeviceTestCases:ActivityManagerActivityVisibilityTests
+ */
+public class ActivityManagerActivityVisibilityTests extends ActivityManagerTestBase {
+    private static final String TRANSLUCENT_ACTIVITY = "AlwaysFocusablePipActivity";
+    private static final String PIP_ON_PIP_ACTIVITY = "LaunchPipOnPipActivity";
+    private static final String TEST_ACTIVITY_NAME = "TestActivity";
+    private static final String TRANSLUCENT_ACTIVITY_NAME = "TranslucentActivity";
+    private static final String DOCKED_ACTIVITY_NAME = "DockedActivity";
+    private static final String TURN_SCREEN_ON_ACTIVITY_NAME = "TurnScreenOnActivity";
+    private static final String MOVE_TASK_TO_BACK_ACTIVITY_NAME = "MoveTaskToBackActivity";
+    private static final String SWIPE_REFRESH_ACTIVITY = "SwipeRefreshActivity";
+
+    private static final String NOHISTORY_ACTIVITY = "NoHistoryActivity";
+    private static final String TURN_SCREEN_ON_ATTR_ACTIVITY_NAME = "TurnScreenOnAttrActivity";
+    private static final String TURN_SCREEN_ON_SHOW_ON_LOCK_ACTIVITY_NAME = "TurnScreenOnShowOnLockActivity";
+    private static final String TURN_SCREEN_ON_ATTR_REMOVE_ATTR_ACTIVITY_NAME = "TurnScreenOnAttrRemoveAttrActivity";
+    private static final String TURN_SCREEN_ON_SINGLE_TASK_ACTIVITY_NAME = "TurnScreenOnSingleTaskActivity";
+    private static final String TURN_SCREEN_ON_WITH_RELAYOUT_ACTIVITY =
+            "TurnScreenOnWithRelayoutActivity";
+
+    @After
+    @Override
+    public void tearDown() throws Exception {
+        super.tearDown();
+        tearDownLockCredentials();
+    }
+
+    @Presubmit
+    @Test
+    public void testTranslucentActivityOnTopOfPinnedStack() throws Exception {
+        assumeTrue(supportsPip());
+
+        executeShellCommand(getAmStartCmdOverHome(PIP_ON_PIP_ACTIVITY));
+        mAmWmState.waitForValidState(PIP_ON_PIP_ACTIVITY);
+        // NOTE: moving to pinned stack will trigger the pip-on-pip activity to launch the
+        // translucent activity.
+        final int stackId = mAmWmState.getAmState().getStackIdByActivityName(PIP_ON_PIP_ACTIVITY);
+
+        assertNotEquals(stackId, INVALID_STACK_ID);
+        executeShellCommand(getMoveToPinnedStackCommand(stackId));
+
+        mAmWmState.computeState(new String[] {PIP_ON_PIP_ACTIVITY, TRANSLUCENT_ACTIVITY});
+        mAmWmState.assertFrontStack("Pinned stack must be the front stack.",
+                WINDOWING_MODE_PINNED, ACTIVITY_TYPE_STANDARD);
+        mAmWmState.assertVisibility(PIP_ON_PIP_ACTIVITY, true);
+        mAmWmState.assertVisibility(TRANSLUCENT_ACTIVITY, true);
+    }
+
+    /**
+     * Asserts that the home activity is visible when a translucent activity is launched in the
+     * fullscreen stack over the home activity.
+     */
+    @Test
+    public void testTranslucentActivityOnTopOfHome() throws Exception {
+        assumeTrue(hasHomeScreen());
+
+        launchHomeActivity();
+        launchActivity(TRANSLUCENT_ACTIVITY);
+
+        mAmWmState.computeState( new String[]{TRANSLUCENT_ACTIVITY});
+        mAmWmState.assertFrontStack("Fullscreen stack must be the front stack.",
+                WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_STANDARD);
+        mAmWmState.assertVisibility(TRANSLUCENT_ACTIVITY, true);
+        mAmWmState.assertHomeActivityVisible(true);
+    }
+
+    /**
+     * Assert that the home activity is visible if a task that was launched from home is pinned
+     * and also assert the next task in the fullscreen stack isn't visible.
+     */
+    @Presubmit
+    @Test
+    public void testHomeVisibleOnActivityTaskPinned() throws Exception {
+        assumeTrue(supportsPip());
+
+        launchHomeActivity();
+        launchActivity(TEST_ACTIVITY_NAME);
+        launchHomeActivity();
+        launchActivity(TRANSLUCENT_ACTIVITY);
+        final int stackId = mAmWmState.getAmState().getStackIdByActivityName(TRANSLUCENT_ACTIVITY);
+
+        assertNotEquals(stackId, INVALID_STACK_ID);
+        executeShellCommand(getMoveToPinnedStackCommand(stackId));
+
+        mAmWmState.computeState(new String[]{TRANSLUCENT_ACTIVITY});
+
+        mAmWmState.assertVisibility(TRANSLUCENT_ACTIVITY, true);
+        mAmWmState.assertVisibility(TEST_ACTIVITY_NAME, false);
+        mAmWmState.assertHomeActivityVisible(true);
+    }
+
+    @Presubmit
+    @Test
+    public void testTranslucentActivityOverDockedStack() throws Exception {
+        assumeTrue("Skipping test: no multi-window support", supportsSplitScreenMultiWindow());
+
+        launchActivitiesInSplitScreen(DOCKED_ACTIVITY_NAME, TEST_ACTIVITY_NAME);
+        launchActivity(TRANSLUCENT_ACTIVITY_NAME, WINDOWING_MODE_SPLIT_SCREEN_PRIMARY);
+        mAmWmState.computeState(false /* compareTaskAndStackBounds */,
+                new WaitForValidActivityState.Builder(TEST_ACTIVITY_NAME).build(),
+                new WaitForValidActivityState.Builder(DOCKED_ACTIVITY_NAME).build(),
+                new WaitForValidActivityState.Builder(TRANSLUCENT_ACTIVITY_NAME).build());
+        mAmWmState.assertContainsStack("Must contain fullscreen stack.",
+                WINDOWING_MODE_SPLIT_SCREEN_SECONDARY, ACTIVITY_TYPE_STANDARD);
+        mAmWmState.assertContainsStack("Must contain docked stack.",
+                WINDOWING_MODE_SPLIT_SCREEN_PRIMARY, ACTIVITY_TYPE_STANDARD);
+        mAmWmState.assertVisibility(DOCKED_ACTIVITY_NAME, true);
+        mAmWmState.assertVisibility(TEST_ACTIVITY_NAME, true);
+        mAmWmState.assertVisibility(TRANSLUCENT_ACTIVITY_NAME, true);
+    }
+
+    @Presubmit
+    @Test
+    public void testTurnScreenOnActivity() throws Exception {
+        sleepDevice();
+        launchActivity(TURN_SCREEN_ON_ACTIVITY_NAME);
+        mAmWmState.computeState(new String[] { TURN_SCREEN_ON_ACTIVITY_NAME });
+        mAmWmState.assertVisibility(TURN_SCREEN_ON_ACTIVITY_NAME, true);
+    }
+
+    @Presubmit
+    @Test
+    public void testFinishActivityInNonFocusedStack() throws Exception {
+        assumeTrue("Skipping test: no multi-window support", supportsSplitScreenMultiWindow());
+
+        // Launch two activities in docked stack.
+        launchActivityInSplitScreenWithRecents(LAUNCHING_ACTIVITY);
+        getLaunchActivityBuilder().setTargetActivityName(BROADCAST_RECEIVER_ACTIVITY).execute();
+        mAmWmState.computeState(new String[] { BROADCAST_RECEIVER_ACTIVITY });
+        mAmWmState.assertVisibility(BROADCAST_RECEIVER_ACTIVITY, true);
+        // Launch something to fullscreen stack to make it focused.
+        launchActivity(TEST_ACTIVITY_NAME, WINDOWING_MODE_FULLSCREEN_OR_SPLIT_SCREEN_SECONDARY);
+        mAmWmState.computeState(new String[] { TEST_ACTIVITY_NAME });
+        mAmWmState.assertVisibility(TEST_ACTIVITY_NAME, true);
+        // Finish activity in non-focused (docked) stack.
+        executeShellCommand(FINISH_ACTIVITY_BROADCAST);
+
+        mAmWmState.waitForActivityState(LAUNCHING_ACTIVITY, STATE_PAUSED);
+        mAmWmState.waitForAllExitingWindows();
+
+        mAmWmState.computeState(new String[] { LAUNCHING_ACTIVITY });
+        mAmWmState.assertVisibility(LAUNCHING_ACTIVITY, true);
+        mAmWmState.assertVisibility(BROADCAST_RECEIVER_ACTIVITY, false);
+    }
+
+    @Test
+    public void testFinishActivityWithMoveTaskToBackAfterPause() throws Exception {
+        performFinishActivityWithMoveTaskToBack("on_pause");
+    }
+
+    @Test
+    public void testFinishActivityWithMoveTaskToBackAfterStop() throws Exception {
+        performFinishActivityWithMoveTaskToBack("on_stop");
+    }
+
+    private void performFinishActivityWithMoveTaskToBack(String finishPoint) throws Exception {
+        // Make sure home activity is visible.
+        launchHomeActivity();
+        if (hasHomeScreen()) {
+            mAmWmState.assertHomeActivityVisible(true /* visible */);
+        }
+
+        // Launch an activity that calls "moveTaskToBack" to finish itself.
+        launchActivity(MOVE_TASK_TO_BACK_ACTIVITY_NAME, "finish_point", finishPoint);
+        mAmWmState.waitForValidState(MOVE_TASK_TO_BACK_ACTIVITY_NAME);
+        mAmWmState.assertVisibility(MOVE_TASK_TO_BACK_ACTIVITY_NAME, true);
+
+        // Launch a different activity on top.
+        launchActivity(BROADCAST_RECEIVER_ACTIVITY);
+        mAmWmState.waitForValidState(BROADCAST_RECEIVER_ACTIVITY);
+        mAmWmState.waitForActivityState(BROADCAST_RECEIVER_ACTIVITY, STATE_RESUMED);
+        mAmWmState.assertVisibility(MOVE_TASK_TO_BACK_ACTIVITY_NAME, false);
+        mAmWmState.assertVisibility(BROADCAST_RECEIVER_ACTIVITY, true);
+
+        // Finish the top-most activity.
+        executeShellCommand(FINISH_ACTIVITY_BROADCAST);
+        //TODO: BUG: MoveTaskToBackActivity returns to the top of the stack when
+        // BroadcastActivity finishes, so homeActivity is not visible afterwards
+
+        // Home must be visible.
+        if (hasHomeScreen()) {
+            mAmWmState.waitForHomeActivityVisible();
+            mAmWmState.assertHomeActivityVisible(true /* visible */);
+        }
+    }
+
+    /**
+     * Asserts that launching between reorder to front activities exhibits the correct backstack
+     * behavior.
+     */
+    @Test
+    public void testReorderToFrontBackstack() throws Exception {
+        // Start with home on top
+        launchHomeActivity();
+        if (hasHomeScreen()) {
+            mAmWmState.assertHomeActivityVisible(true /* visible */);
+        }
+
+        // Launch the launching activity to the foreground
+        launchActivity(LAUNCHING_ACTIVITY);
+
+        // Launch the alternate launching activity from launching activity with reorder to front.
+        getLaunchActivityBuilder().setTargetActivityName(ALT_LAUNCHING_ACTIVITY)
+                .setReorderToFront(true).execute();
+
+        // Launch the launching activity from the alternate launching activity with reorder to
+        // front.
+        getLaunchActivityBuilder().setTargetActivityName(LAUNCHING_ACTIVITY)
+                .setLaunchingActivityName(ALT_LAUNCHING_ACTIVITY).setReorderToFront(true)
+                .execute();
+
+        // Press back
+        pressBackButton();
+
+        mAmWmState.waitForValidState(ALT_LAUNCHING_ACTIVITY);
+
+        // Ensure the alternate launching activity is in focus
+        mAmWmState.assertFocusedActivity("Alt Launching Activity must be focused",
+                ALT_LAUNCHING_ACTIVITY);
+    }
+
+    /**
+     * Asserts that the activity focus and history is preserved moving between the activity and
+     * home stack.
+     */
+    @Test
+    public void testReorderToFrontChangingStack() throws Exception {
+        // Start with home on top
+        launchHomeActivity();
+        if (hasHomeScreen()) {
+            mAmWmState.assertHomeActivityVisible(true /* visible */);
+        }
+
+        // Launch the launching activity to the foreground
+        launchActivity(LAUNCHING_ACTIVITY);
+
+        // Launch the alternate launching activity from launching activity with reorder to front.
+        getLaunchActivityBuilder().setTargetActivityName(ALT_LAUNCHING_ACTIVITY)
+                .setReorderToFront(true).execute();
+
+        // Return home
+        launchHomeActivity();
+        if (hasHomeScreen()) {
+            mAmWmState.assertHomeActivityVisible(true /* visible */);
+        }
+        // Launch the launching activity from the alternate launching activity with reorder to
+        // front.
+
+        // Bring launching activity back to the foreground
+        launchActivity(LAUNCHING_ACTIVITY);
+        mAmWmState.waitForValidState(LAUNCHING_ACTIVITY);
+
+        // Ensure the alternate launching activity is still in focus.
+        mAmWmState.assertFocusedActivity("Alt Launching Activity must be focused",
+                ALT_LAUNCHING_ACTIVITY);
+
+        pressBackButton();
+
+        mAmWmState.waitForValidState(LAUNCHING_ACTIVITY);
+
+        // Ensure launching activity was brought forward.
+        mAmWmState.assertFocusedActivity("Launching Activity must be focused",
+                LAUNCHING_ACTIVITY);
+    }
+
+    /**
+     * Asserts that a nohistory activity is stopped and removed immediately after a resumed activity
+     * above becomes visible and does not idle.
+     */
+    @Test
+    public void testNoHistoryActivityFinishedResumedActivityNotIdle() throws Exception {
+        assumeTrue(hasHomeScreen());
+
+        // Start with home on top
+        launchHomeActivity();
+
+        // Launch no history activity
+        launchActivity(NOHISTORY_ACTIVITY);
+
+        // Launch an activity with a swipe refresh layout configured to prevent idle.
+        launchActivity(SWIPE_REFRESH_ACTIVITY);
+
+        pressBackButton();
+        mAmWmState.waitForHomeActivityVisible();
+        mAmWmState.assertHomeActivityVisible(true);
+    }
+
+    @Test
+    public void testTurnScreenOnAttrNoLockScreen() throws Exception {
+        wakeUpAndRemoveLock();
+        sleepDevice();
+        final String logSeparator = clearLogcat();
+        launchActivity(TURN_SCREEN_ON_ATTR_ACTIVITY_NAME);
+        mAmWmState.computeState(new String[] { TURN_SCREEN_ON_ATTR_ACTIVITY_NAME });
+        mAmWmState.assertVisibility(TURN_SCREEN_ON_ATTR_ACTIVITY_NAME, true);
+        assertTrue(isDisplayOn());
+        assertSingleLaunch(TURN_SCREEN_ON_ATTR_ACTIVITY_NAME, logSeparator);
+    }
+
+    @Test
+    public void testTurnScreenOnAttrWithLockScreen() throws Exception {
+        assumeTrue(isHandheld());
+
+        setLockCredential();
+        sleepDevice();
+        final String logSeparator = clearLogcat();
+        launchActivity(TURN_SCREEN_ON_ATTR_ACTIVITY_NAME);
+        mAmWmState.computeState(new String[] { TURN_SCREEN_ON_ATTR_ACTIVITY_NAME });
+        assertFalse(isDisplayOn());
+        assertSingleLaunchAndStop(TURN_SCREEN_ON_ATTR_ACTIVITY_NAME, logSeparator);
+    }
+
+    @Test
+    public void testTurnScreenOnShowOnLockAttr() throws Exception {
+        sleepDevice();
+        mAmWmState.waitForAllStoppedActivities();
+        final String logSeparator = clearLogcat();
+        launchActivity(TURN_SCREEN_ON_SHOW_ON_LOCK_ACTIVITY_NAME);
+        mAmWmState.computeState(new String[] { TURN_SCREEN_ON_SHOW_ON_LOCK_ACTIVITY_NAME });
+        mAmWmState.assertVisibility(TURN_SCREEN_ON_SHOW_ON_LOCK_ACTIVITY_NAME, true);
+        assertTrue(isDisplayOn());
+        assertSingleLaunch(TURN_SCREEN_ON_SHOW_ON_LOCK_ACTIVITY_NAME, logSeparator);
+    }
+
+    @Test
+    public void testTurnScreenOnAttrRemove() throws Exception {
+        sleepDevice();
+        mAmWmState.waitForAllStoppedActivities();
+        String logSeparator = clearLogcat();
+        launchActivity(TURN_SCREEN_ON_ATTR_REMOVE_ATTR_ACTIVITY_NAME);
+        mAmWmState.computeState(new String[] {
+                TURN_SCREEN_ON_ATTR_REMOVE_ATTR_ACTIVITY_NAME});
+        assertTrue(isDisplayOn());
+        assertSingleLaunch(TURN_SCREEN_ON_ATTR_REMOVE_ATTR_ACTIVITY_NAME, logSeparator);
+
+        sleepDevice();
+        mAmWmState.waitForAllStoppedActivities();
+        logSeparator = clearLogcat();
+        launchActivity(TURN_SCREEN_ON_ATTR_REMOVE_ATTR_ACTIVITY_NAME);
+        assertFalse(isDisplayOn());
+        assertSingleStartAndStop(TURN_SCREEN_ON_ATTR_REMOVE_ATTR_ACTIVITY_NAME, logSeparator);
+    }
+
+    @Test
+    @Presubmit
+    public void testTurnScreenOnSingleTask() throws Exception {
+        sleepDevice();
+        String logSeparator = clearLogcat();
+        launchActivity(TURN_SCREEN_ON_SINGLE_TASK_ACTIVITY_NAME);
+        mAmWmState.computeState(new String[] { TURN_SCREEN_ON_SINGLE_TASK_ACTIVITY_NAME });
+        mAmWmState.assertVisibility(TURN_SCREEN_ON_SINGLE_TASK_ACTIVITY_NAME, true);
+        assertTrue(isDisplayOn());
+        assertSingleLaunch(TURN_SCREEN_ON_SINGLE_TASK_ACTIVITY_NAME, logSeparator);
+
+        sleepDevice();
+        logSeparator = clearLogcat();
+        launchActivity(TURN_SCREEN_ON_SINGLE_TASK_ACTIVITY_NAME);
+        mAmWmState.computeState(new String[] { TURN_SCREEN_ON_SINGLE_TASK_ACTIVITY_NAME });
+        mAmWmState.assertVisibility(TURN_SCREEN_ON_SINGLE_TASK_ACTIVITY_NAME, true);
+        assertTrue(isDisplayOn());
+        assertSingleStart(TURN_SCREEN_ON_SINGLE_TASK_ACTIVITY_NAME, logSeparator);
+    }
+
+    @Test
+    public void testTurnScreenOnActivity_withRelayout() throws Exception {
+        sleepDevice();
+        launchActivity(TURN_SCREEN_ON_WITH_RELAYOUT_ACTIVITY);
+        mAmWmState.computeState(new String[] { TURN_SCREEN_ON_WITH_RELAYOUT_ACTIVITY });
+        mAmWmState.assertVisibility(TURN_SCREEN_ON_WITH_RELAYOUT_ACTIVITY, true);
+
+        String logSeparator = clearLogcat();
+        sleepDevice();
+        mAmWmState.waitFor("Waiting for stopped state", () ->
+                lifecycleStopOccurred(TURN_SCREEN_ON_WITH_RELAYOUT_ACTIVITY, logSeparator));
+
+        // Ensure there was an actual stop if the waitFor timed out.
+        assertTrue(lifecycleStopOccurred(TURN_SCREEN_ON_WITH_RELAYOUT_ACTIVITY, logSeparator));
+        assertFalse(isDisplayOn());
+    }
+
+    private boolean lifecycleStopOccurred(String activityName, String logSeparator) {
+        ActivityLifecycleCounts lifecycleCounts = new ActivityLifecycleCounts(activityName,
+                logSeparator);
+        return lifecycleCounts.mStopCount > 0;
+    }
+}
diff --git a/tests/framework/base/activitymanager/src/android/server/am/ActivityManagerAmProfileTests.java b/tests/framework/base/activitymanager/src/android/server/am/ActivityManagerAmProfileTests.java
new file mode 100644
index 0000000..ea2e2b3
--- /dev/null
+++ b/tests/framework/base/activitymanager/src/android/server/am/ActivityManagerAmProfileTests.java
@@ -0,0 +1,183 @@
+/*
+ * 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.server.am;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertTrue;
+
+import android.content.ComponentName;
+import android.support.test.InstrumentationRegistry;
+
+import org.junit.Before;
+import org.junit.Test;
+
+import java.io.File;
+import java.io.FileInputStream;
+
+/**
+ * Build/Install/Run:
+ *     atest CtsActivityManagerDeviceTestCases:ActivityManagerAmProfileTests
+ *
+ * Please talk to Android Studio team first if you want to modify or delete these tests.
+ */
+public class ActivityManagerAmProfileTests extends ActivityManagerTestBase {
+
+    private static final ComponentName DEBUGGABLE_APP_ACTIVITY = ComponentName.createRelative(
+            "android.server.am.debuggable", ".DebuggableAppActivity");
+    private static final String OUTPUT_FILE_PATH = "/data/local/tmp/profile.trace";
+    private static final String FIRST_WORD_NO_STREAMING = "*version\n";
+    private static final String FIRST_WORD_STREAMING = "SLOW";  // Magic word set by runtime.
+
+    private String mReadableFilePath = null;
+
+    @Before
+    @Override
+    public void setUp() throws Exception {
+        super.setUp();
+        mReadableFilePath = InstrumentationRegistry.getContext()
+            .getExternalFilesDir(null)
+            .getPath() + "/profile.trace";
+    }
+
+    /**
+     * Test am profile functionality with the following 3 configurable options:
+     *    starting the activity before start profiling? yes;
+     *    sampling-based profiling? no;
+     *    using streaming output mode? no.
+     */
+    @Test
+    public void testAmProfileStartNoSamplingNoStreaming() throws Exception {
+        // am profile start ... , and the same to the following 3 test methods.
+        testProfile(true, false, false);
+    }
+
+    /**
+     * The following tests are similar to testAmProfileStartNoSamplingNoStreaming(),
+     * only different in the three configuration options.
+     */
+    @Test
+    public void testAmProfileStartNoSamplingStreaming() throws Exception {
+        testProfile(true, false, true);
+    }
+
+    @Test
+    public void testAmProfileStartSamplingNoStreaming() throws Exception {
+        testProfile(true, true, false);
+    }
+
+    @Test
+    public void testAmProfileStartSamplingStreaming() throws Exception {
+        testProfile(true, true, true);
+    }
+
+    @Test
+    public void testAmStartStartProfilerNoSamplingNoStreaming() throws Exception {
+        // am start --start-profiler ..., and the same to the following 3 test methods.
+        testProfile(false, false, false);
+    }
+
+    @Test
+    public void testAmStartStartProfilerNoSamplingStreaming() throws Exception {
+        testProfile(false, false, true);
+    }
+
+    @Test
+    public void testAmStartStartProfilerSamplingNoStreaming() throws Exception {
+        testProfile(false, true, false);
+    }
+
+    @Test
+    public void testAmStartStartProfilerSamplingStreaming() throws Exception {
+        testProfile(false, true, true);
+    }
+
+    private void testProfile(final boolean startActivityFirst, final boolean sampling,
+            final boolean streaming) throws Exception {
+        if (startActivityFirst) {
+            launchActivity(DEBUGGABLE_APP_ACTIVITY);
+        }
+
+        executeShellCommand(
+                getStartCmd(DEBUGGABLE_APP_ACTIVITY, startActivityFirst, sampling, streaming));
+        // Go to home screen and then warm start the activity to generate some interesting trace.
+        pressHomeButton();
+        launchActivity(DEBUGGABLE_APP_ACTIVITY);
+
+        executeShellCommand(getStopProfileCmd(DEBUGGABLE_APP_ACTIVITY));
+        // Sleep for 0.1 second (100 milliseconds) so the generation of the profiling
+        // file is complete.
+        try {
+            Thread.sleep(100);
+        } catch (InterruptedException e) {
+            //ignored
+        }
+        verifyOutputFileFormat(streaming);
+    }
+
+    private static String getStartCmd(final ComponentName activityName,
+            final boolean activityAlreadyStarted, final boolean sampling, final boolean streaming) {
+        final StringBuilder builder = new StringBuilder("am");
+        if (activityAlreadyStarted) {
+            builder.append(" profile start");
+        } else {
+            builder.append(String.format(" start -n %s -W -S --start-profiler %s",
+                    activityName.flattenToShortString(), OUTPUT_FILE_PATH));
+        }
+        if (sampling) {
+            builder.append(" --sampling 1000");
+        }
+        if (streaming) {
+            builder.append(" --streaming");
+        }
+        if (activityAlreadyStarted) {
+            builder.append(String.format(
+                    " %s %s", activityName.getPackageName(), OUTPUT_FILE_PATH));
+        }
+        return builder.toString();
+    }
+
+    private static String getStopProfileCmd(final ComponentName activityName) {
+        return "am profile stop " + activityName.getPackageName();
+    }
+
+    private void verifyOutputFileFormat(final boolean streaming) throws Exception {
+        // This is a hack. The am service has to write to /data/local/tmp because it doesn't have
+        // access to the sdcard but the test app can't read there
+        executeShellCommand("mv " + OUTPUT_FILE_PATH + " " + mReadableFilePath);
+
+        final String expectedFirstWord = streaming ? FIRST_WORD_STREAMING : FIRST_WORD_NO_STREAMING;
+        final byte[] data = readFile(mReadableFilePath);
+        assertTrue("data size=" + data.length, data.length >= expectedFirstWord.length());
+        final String actualFirstWord = new String(data, 0, expectedFirstWord.length());
+        assertEquals("Unexpected first word", expectedFirstWord, actualFirstWord);
+
+        // Clean up.
+        executeShellCommand("rm -f " + OUTPUT_FILE_PATH + " " + mReadableFilePath);
+    }
+
+    private static byte[] readFile(String clientPath) throws Exception {
+        final File file = new File(clientPath);
+        assertTrue("File not found on client: " + clientPath, file.isFile());
+        final int size = (int) file.length();
+        final byte[] bytes = new byte[size];
+        try (final FileInputStream fis = new FileInputStream(file)) {
+            final int readSize = fis.read(bytes, 0, bytes.length);
+            assertEquals("Read all data", bytes.length, readSize);
+            return bytes;
+        }
+    }
+}
diff --git a/tests/framework/base/activitymanager/src/android/server/am/ActivityManagerAmStartOptionsTests.java b/tests/framework/base/activitymanager/src/android/server/am/ActivityManagerAmStartOptionsTests.java
new file mode 100644
index 0000000..6c37afb
--- /dev/null
+++ b/tests/framework/base/activitymanager/src/android/server/am/ActivityManagerAmStartOptionsTests.java
@@ -0,0 +1,190 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.server.am;
+
+import static org.junit.Assert.assertTrue;
+import static org.junit.Assert.fail;
+
+import org.junit.Test;
+
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
+
+/**
+ * Build/Install/Run:
+ *     atest CtsActivityManagerDeviceTestCases:ActivityManagerAmStartOptionsTests
+ */
+public class ActivityManagerAmStartOptionsTests extends ActivityManagerTestBase {
+
+    private static final String TEST_ACTIVITY_NAME = "TestActivity";
+    private static final String ENTRYPOINT_ACTIVITY_NAME = "EntryPointAliasActivity";
+    private static final String SINGLE_TASK_ACTIVITY_NAME = "SingleTaskActivity";
+
+    @Test
+    public void testDashD() throws Exception {
+        final String activityComponentName =
+                ActivityManagerTestBase.getActivityComponentName(TEST_ACTIVITY_NAME);
+
+        final String[] waitForActivityRecords = new String[] {activityComponentName};
+
+        // Run at least 2 rounds to verify that -D works with an existing process.
+        // -D could fail in this case if the force stop of process is broken.
+        int prevProcId = -1;
+        for (int i = 0; i < 2; i++) {
+            executeShellCommand("am start -n " + getActivityComponentName(TEST_ACTIVITY_NAME) + " -D");
+
+            mAmWmState.waitForDebuggerWindowVisible(waitForActivityRecords);
+            int procId = mAmWmState.getAmState().getActivityProcId(activityComponentName);
+
+            assertTrue("Invalid ProcId.", procId >= 0);
+            if (i > 0) {
+                assertTrue("Run " + i + " didn't start new proc.", prevProcId != procId);
+            }
+            prevProcId = procId;
+        }
+    }
+
+    @Test
+    public void testDashW_Direct() throws Exception {
+        testDashW(SINGLE_TASK_ACTIVITY_NAME, SINGLE_TASK_ACTIVITY_NAME);
+    }
+
+    @Test
+    public void testDashW_Indirect() throws Exception {
+        testDashW(ENTRYPOINT_ACTIVITY_NAME, SINGLE_TASK_ACTIVITY_NAME);
+    }
+
+    private void testDashW(final String entryActivity, final String actualActivity)
+            throws Exception {
+        // Test cold start
+        startActivityAndVerifyResult(entryActivity, actualActivity, true);
+
+        // Test warm start
+        pressHomeButton();
+        startActivityAndVerifyResult(entryActivity, actualActivity, false);
+
+        // Test "hot" start (app already in front)
+        startActivityAndVerifyResult(entryActivity, actualActivity, false);
+    }
+
+    private void startActivityAndVerifyResult(final String entryActivity,
+            final String actualActivity, boolean shouldStart) throws Exception {
+        // See TODO below
+        // final String logSeparator = clearLogcat();
+
+        // Pass in different data only when cold starting. This is to make the intent
+        // different in subsequent warm/hot launches, so that the entrypoint alias
+        // activity is always started, but the actual activity is not started again
+        // because of the NEW_TASK and singleTask flags.
+        final String result = executeShellCommand("am start -n " + getActivityComponentName(entryActivity) + " -W"
+                + (shouldStart ? " -d about:blank" : ""));
+
+        // Verify shell command return value
+        verifyShellOutput(result, actualActivity, shouldStart);
+
+        // TODO: Disable logcat check for now.
+        // Logcat of WM or AM tag could be lost (eg. chatty if earlier events generated
+        // too many lines), and make the test look flaky. We need to either use event
+        // log or swith to other mechanisms. Only verify shell output for now, it should
+        // still catch most failures.
+
+        // Verify adb logcat log
+        //verifyLogcat(actualActivity, shouldStart, logSeparator);
+    }
+
+    private static final Pattern sNotStartedWarningPattern = Pattern.compile(
+            "Warning: Activity not started(.*)");
+    private static final Pattern sStatusPattern = Pattern.compile(
+            "Status: (.*)");
+    private static final Pattern sActivityPattern = Pattern.compile(
+            "Activity: (.*)");
+    private static final String sStatusOk = "ok";
+
+    private void verifyShellOutput(
+            final String result, final String activity, boolean shouldStart) {
+        boolean warningFound = false;
+        String status = null;
+        String reportedActivity = null;
+        String componentActivityName = getActivityComponentName(activity);
+
+        final String[] lines = result.split("\\n");
+        // Going from the end of logs to beginning in case if some other activity is started first.
+        for (int i = lines.length - 1; i >= 0; i--) {
+            final String line = lines[i].trim();
+            Matcher matcher = sNotStartedWarningPattern.matcher(line);
+            if (matcher.matches()) {
+                warningFound = true;
+                continue;
+            }
+            matcher = sStatusPattern.matcher(line);
+            if (matcher.matches()) {
+                status = matcher.group(1);
+                continue;
+            }
+            matcher = sActivityPattern.matcher(line);
+            if (matcher.matches()) {
+                reportedActivity = matcher.group(1);
+                continue;
+            }
+        }
+
+        assertTrue("Status " + status + " is not ok", sStatusOk.equals(status));
+        assertTrue("Reported activity is " + reportedActivity + " not " + componentActivityName,
+                componentActivityName.equals(reportedActivity));
+
+        if (shouldStart && warningFound) {
+            fail("Should start new activity but brought something to front.");
+        } else if (!shouldStart && !warningFound){
+            fail("Should bring existing activity to front but started new activity.");
+        }
+    }
+
+    private static final Pattern sDisplayTimePattern =
+            Pattern.compile("(.+): Displayed (.*): (\\+{0,1})([0-9]+)ms(.*)");
+
+    void verifyLogcat(String actualActivityName, boolean shouldStart, String logSeparator) {
+        int displayCount = 0;
+        String activityName = null;
+
+        for (String line : getDeviceLogsForComponent("ActivityManager", logSeparator)) {
+            line = line.trim();
+
+            Matcher matcher = sDisplayTimePattern.matcher(line);
+            if (matcher.matches()) {
+                activityName = matcher.group(2);
+                // Ignore activitiy displays from other packages, we don't
+                // want some random activity starts to ruin our test.
+                if (!activityName.startsWith("android.server.am")) {
+                    continue;
+                }
+                if (!shouldStart) {
+                    fail("Shouldn't display anything but displayed " + activityName);
+                }
+                displayCount++;
+            }
+        }
+        final String expectedActivityName = getActivityComponentName(actualActivityName);
+        if (shouldStart) {
+            if (displayCount != 1) {
+                fail("Should display exactly one activity but displayed " + displayCount);
+            } else if (!expectedActivityName.equals(activityName)) {
+                fail("Should display " + expectedActivityName +
+                        " but displayed " + activityName);
+            }
+        }
+    }
+}
diff --git a/tests/framework/base/activitymanager/src/android/server/am/ActivityManagerAppConfigurationTests.java b/tests/framework/base/activitymanager/src/android/server/am/ActivityManagerAppConfigurationTests.java
new file mode 100644
index 0000000..b669db1
--- /dev/null
+++ b/tests/framework/base/activitymanager/src/android/server/am/ActivityManagerAppConfigurationTests.java
@@ -0,0 +1,634 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not
+ * use this file except in compliance with the License. You may obtain a copy of
+ * the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+ * License for the specific language governing permissions and limitations under
+ * the License.
+ */
+
+package android.server.am;
+
+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;
+import static android.app.WindowConfiguration.WINDOWING_MODE_SPLIT_SCREEN_PRIMARY;
+import static android.server.am.ActivityAndWindowManagersState.dpToPx;
+import static android.server.am.ActivityManagerState.STATE_RESUMED;
+import static android.server.am.StateLogger.log;
+import static android.server.am.StateLogger.logE;
+import static android.view.Surface.ROTATION_0;
+import static android.view.Surface.ROTATION_180;
+import static android.view.Surface.ROTATION_270;
+import static android.view.Surface.ROTATION_90;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertTrue;
+import static org.junit.Assume.assumeTrue;
+
+import android.content.ComponentName;
+import android.graphics.Rect;
+import android.platform.test.annotations.Presubmit;
+import android.support.test.filters.FlakyTest;
+
+import org.junit.Test;
+
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * Build/Install/Run:
+ *     atest CtsActivityManagerDeviceTestCases:ActivityManagerAppConfigurationTests
+ */
+public class ActivityManagerAppConfigurationTests extends ActivityManagerTestBase {
+    private static final String RESIZEABLE_ACTIVITY_NAME = "ResizeableActivity";
+    private static final String TEST_ACTIVITY_NAME = "TestActivity";
+    private static final String PORTRAIT_ACTIVITY_NAME = "PortraitOrientationActivity";
+    private static final String LANDSCAPE_ACTIVITY_NAME = "LandscapeOrientationActivity";
+    private static final String NIGHT_MODE_ACTIVITY = "NightModeActivity";
+    private static final String DIALOG_WHEN_LARGE_ACTIVITY = "DialogWhenLargeActivity";
+
+    private static final String TRANSLUCENT_ACTIVITY_NAME =
+            "android.server.am.translucentapp.TranslucentLandscapeActivity";
+    private static final ComponentName TRANSLUCENT_LANDSCAPE_ACTIVITY = new ComponentName(
+            "android.server.am.translucentapp", TRANSLUCENT_ACTIVITY_NAME);
+    private static final ComponentName SDK26_TRANSLUCENT_LANDSCAPE_ACTIVITY = new ComponentName(
+            "android.server.am.translucentapp26", TRANSLUCENT_ACTIVITY_NAME);
+
+    private static final String EXTRA_LAUNCH_NEW_TASK = "launch_new_task";
+
+    private static final int SMALL_WIDTH_DP = 426;
+    private static final int SMALL_HEIGHT_DP = 320;
+
+    /**
+     * Tests that the WindowManager#getDefaultDisplay() and the Configuration of the Activity
+     * has an updated size when the Activity is resized from fullscreen to docked state.
+     *
+     * The Activity handles configuration changes, so it will not be restarted between resizes.
+     * On Configuration changes, the Activity logs the Display size and Configuration width
+     * and heights. The values reported in fullscreen should be larger than those reported in
+     * docked state.
+     */
+    @Test
+    public void testConfigurationUpdatesWhenResizedFromFullscreen() throws Exception {
+        assumeTrue("Skipping test: no multi-window support", supportsSplitScreenMultiWindow());
+
+        String logSeparator = clearLogcat();
+        launchActivity(RESIZEABLE_ACTIVITY_NAME, WINDOWING_MODE_FULLSCREEN_OR_SPLIT_SCREEN_SECONDARY);
+        final ReportedSizes fullscreenSizes = getActivityDisplaySize(RESIZEABLE_ACTIVITY_NAME,
+                logSeparator);
+
+        logSeparator = clearLogcat();
+        setActivityTaskWindowingMode(RESIZEABLE_ACTIVITY_NAME, WINDOWING_MODE_SPLIT_SCREEN_PRIMARY);
+        final ReportedSizes dockedSizes = getActivityDisplaySize(RESIZEABLE_ACTIVITY_NAME,
+                logSeparator);
+
+        assertSizesAreSane(fullscreenSizes, dockedSizes);
+    }
+
+    /**
+     * Same as {@link #testConfigurationUpdatesWhenResizedFromFullscreen()} but resizing
+     * from docked state to fullscreen (reverse).
+     */
+    @Presubmit
+    @Test
+    @FlakyTest(bugId = 71792393)
+    public void testConfigurationUpdatesWhenResizedFromDockedStack() throws Exception {
+        assumeTrue("Skipping test: no multi-window support", supportsSplitScreenMultiWindow());
+
+        String logSeparator = clearLogcat();
+        launchActivity(RESIZEABLE_ACTIVITY_NAME, WINDOWING_MODE_SPLIT_SCREEN_PRIMARY);
+        final ReportedSizes dockedSizes = getActivityDisplaySize(RESIZEABLE_ACTIVITY_NAME,
+                logSeparator);
+
+        logSeparator = clearLogcat();
+        setActivityTaskWindowingMode(RESIZEABLE_ACTIVITY_NAME, WINDOWING_MODE_FULLSCREEN);
+        final ReportedSizes fullscreenSizes = getActivityDisplaySize(RESIZEABLE_ACTIVITY_NAME,
+                logSeparator);
+
+        assertSizesAreSane(fullscreenSizes, dockedSizes);
+    }
+
+    /**
+     * Tests whether the Display sizes change when rotating the device.
+     */
+    @Test
+    public void testConfigurationUpdatesWhenRotatingWhileFullscreen() throws Exception {
+        assumeTrue("Skipping test: no rotation support", supportsRotation());
+
+        try (final RotationSession rotationSession = new RotationSession()) {
+            rotationSession.set(ROTATION_0);
+
+            final String logSeparator = clearLogcat();
+            launchActivity(RESIZEABLE_ACTIVITY_NAME,
+                    WINDOWING_MODE_FULLSCREEN_OR_SPLIT_SCREEN_SECONDARY);
+            final ReportedSizes initialSizes = getActivityDisplaySize(RESIZEABLE_ACTIVITY_NAME,
+                    logSeparator);
+
+            rotateAndCheckSizes(rotationSession, initialSizes);
+        }
+    }
+
+    /**
+     * Same as {@link #testConfigurationUpdatesWhenRotatingWhileFullscreen()} but when the Activity
+     * is in the docked stack.
+     */
+    @Presubmit
+    @Test
+    public void testConfigurationUpdatesWhenRotatingWhileDocked() throws Exception {
+        assumeTrue("Skipping test: no multi-window support", supportsSplitScreenMultiWindow());
+
+        try (final RotationSession rotationSession = new RotationSession()) {
+            rotationSession.set(ROTATION_0);
+
+            final String logSeparator = clearLogcat();
+            // Launch our own activity to side in case Recents (or other activity to side) doesn't
+            // support rotation.
+            launchActivitiesInSplitScreen(LAUNCHING_ACTIVITY, TEST_ACTIVITY_NAME);
+            // Launch target activity in docked stack.
+            getLaunchActivityBuilder().setTargetActivityName(RESIZEABLE_ACTIVITY_NAME).execute();
+            final ReportedSizes initialSizes = getActivityDisplaySize(RESIZEABLE_ACTIVITY_NAME,
+                    logSeparator);
+
+            rotateAndCheckSizes(rotationSession, initialSizes);
+        }
+    }
+
+    /**
+     * Same as {@link #testConfigurationUpdatesWhenRotatingWhileDocked()} but when the Activity
+     * is launched to side from docked stack.
+     */
+    @Presubmit
+    @Test
+    public void testConfigurationUpdatesWhenRotatingToSideFromDocked() throws Exception {
+        assumeTrue("Skipping test: no multi-window support", supportsSplitScreenMultiWindow());
+
+        try (final RotationSession rotationSession = new RotationSession()) {
+            rotationSession.set(ROTATION_0);
+
+            final String logSeparator = clearLogcat();
+            launchActivitiesInSplitScreen(LAUNCHING_ACTIVITY, RESIZEABLE_ACTIVITY_NAME);
+            final ReportedSizes initialSizes = getActivityDisplaySize(RESIZEABLE_ACTIVITY_NAME,
+                    logSeparator);
+
+            rotateAndCheckSizes(rotationSession, initialSizes);
+        }
+    }
+
+    private void rotateAndCheckSizes(RotationSession rotationSession, ReportedSizes prevSizes)
+            throws Exception {
+        final int[] rotations = { ROTATION_270, ROTATION_180, ROTATION_90, ROTATION_0 };
+        for (final int rotation : rotations) {
+            final String logSeparator = clearLogcat();
+            final int actualStackId = mAmWmState.getAmState().getTaskByActivityName(
+                    RESIZEABLE_ACTIVITY_NAME).mStackId;
+            final int displayId = mAmWmState.getAmState().getStackById(actualStackId).mDisplayId;
+            rotationSession.set(rotation);
+            final int newDeviceRotation = getDeviceRotation(displayId);
+            if (newDeviceRotation == INVALID_DEVICE_ROTATION) {
+                logE("Got an invalid device rotation value. "
+                        + "Continuing the test despite of that, but it is likely to fail.");
+            } else if (rotation != newDeviceRotation) {
+                log("This device doesn't support locked user "
+                        + "rotation mode. Not continuing the rotation checks.");
+                return;
+            }
+
+            final ReportedSizes rotatedSizes = getActivityDisplaySize(RESIZEABLE_ACTIVITY_NAME,
+                    logSeparator);
+            assertSizesRotate(prevSizes, rotatedSizes);
+            prevSizes = rotatedSizes;
+        }
+    }
+
+    /**
+     * Tests when activity moved from fullscreen stack to docked and back. Activity will be
+     * relaunched twice and it should have same config as initial one.
+     */
+    @Test
+    public void testSameConfigurationFullSplitFullRelaunch() throws Exception {
+        moveActivityFullSplitFull(TEST_ACTIVITY_NAME);
+    }
+
+    /**
+     * Same as {@link #testSameConfigurationFullSplitFullRelaunch} but without relaunch.
+     */
+    @Presubmit
+    @Test
+    public void testSameConfigurationFullSplitFullNoRelaunch() throws Exception {
+        moveActivityFullSplitFull(RESIZEABLE_ACTIVITY_NAME);
+    }
+
+    /**
+     * Launches activity in fullscreen stack, moves to docked stack and back to fullscreen stack.
+     * Last operation is done in a way which simulates split-screen divider movement maximizing
+     * docked stack size and then moving task to fullscreen stack - the same way it is done when
+     * user long-presses overview/recents button to exit split-screen.
+     * Asserts that initial and final reported sizes in fullscreen stack are the same.
+     */
+    private void moveActivityFullSplitFull(String activityName) throws Exception {
+        assumeTrue("Skipping test: no multi-window support", supportsSplitScreenMultiWindow());
+
+        // Launch to fullscreen stack and record size.
+        String logSeparator = clearLogcat();
+        launchActivity(activityName, WINDOWING_MODE_FULLSCREEN_OR_SPLIT_SCREEN_SECONDARY);
+        final ReportedSizes initialFullscreenSizes = getActivityDisplaySize(activityName,
+                logSeparator);
+        final Rect displayRect = getDisplayRect(activityName);
+
+        // Move to docked stack.
+        logSeparator = clearLogcat();
+        setActivityTaskWindowingMode(activityName, WINDOWING_MODE_SPLIT_SCREEN_PRIMARY);
+        final ReportedSizes dockedSizes = getActivityDisplaySize(activityName, logSeparator);
+        assertSizesAreSane(initialFullscreenSizes, dockedSizes);
+        // Make sure docked stack is focused. This way when we dismiss it later fullscreen stack
+        // will come up.
+        launchActivity(activityName, WINDOWING_MODE_SPLIT_SCREEN_PRIMARY);
+        mAmWmState.computeState(false /* compareTaskAndStackBounds */,
+                new WaitForValidActivityState.Builder(activityName).build());
+        final ActivityManagerState.ActivityStack stack = mAmWmState.getAmState()
+                .getStandardStackByWindowingMode(WINDOWING_MODE_SPLIT_SCREEN_PRIMARY);
+
+        // Resize docked stack to fullscreen size. This will trigger activity relaunch with
+        // non-empty override configuration corresponding to fullscreen size.
+        logSeparator = clearLogcat();
+        mAm.resizeStack(stack.mStackId, displayRect);
+
+        // Move activity back to fullscreen stack.
+        setActivityTaskWindowingMode(activityName,
+                WINDOWING_MODE_FULLSCREEN_OR_SPLIT_SCREEN_SECONDARY);
+        final ReportedSizes finalFullscreenSizes = getActivityDisplaySize(activityName,
+                logSeparator);
+
+        // After activity configuration was changed twice it must report same size as original one.
+        assertSizesAreSame(initialFullscreenSizes, finalFullscreenSizes);
+    }
+
+    /**
+     * Tests when activity moved from docked stack to fullscreen and back. Activity will be
+     * relaunched twice and it should have same config as initial one.
+     */
+    @Test
+    public void testSameConfigurationSplitFullSplitRelaunch() throws Exception {
+        moveActivitySplitFullSplit(TEST_ACTIVITY_NAME);
+    }
+
+    /**
+     * Same as {@link #testSameConfigurationSplitFullSplitRelaunch} but without relaunch.
+     */
+    @Test
+    public void testSameConfigurationSplitFullSplitNoRelaunch() throws Exception {
+        moveActivitySplitFullSplit(RESIZEABLE_ACTIVITY_NAME);
+    }
+
+    /**
+     * Tests that an activity with the DialogWhenLarge theme can transform properly when in split
+     * screen.
+     */
+    @Presubmit
+    @Test
+    public void testDialogWhenLargeSplitSmall() throws Exception {
+        assumeTrue("Skipping test: no multi-window support", supportsSplitScreenMultiWindow());
+
+        launchActivity(DIALOG_WHEN_LARGE_ACTIVITY, WINDOWING_MODE_SPLIT_SCREEN_PRIMARY);
+        final ActivityManagerState.ActivityStack stack = mAmWmState.getAmState()
+                .getStandardStackByWindowingMode(WINDOWING_MODE_SPLIT_SCREEN_PRIMARY);
+        final WindowManagerState.Display display =
+                mAmWmState.getWmState().getDisplay(stack.mDisplayId);
+        final int density = display.getDpi();
+        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));
+        mAmWmState.waitForValidState(DIALOG_WHEN_LARGE_ACTIVITY,
+                WINDOWING_MODE_SPLIT_SCREEN_PRIMARY, ACTIVITY_TYPE_STANDARD);
+    }
+
+    /**
+     * Test that device handles consequent requested orientations and displays the activities.
+     */
+    @Presubmit
+    @Test
+    public void testFullscreenAppOrientationRequests() throws Exception {
+        launchActivity(PORTRAIT_ACTIVITY_NAME);
+        mAmWmState.assertVisibility(PORTRAIT_ACTIVITY_NAME, true /* visible */);
+        assertEquals("Fullscreen app requested portrait orientation",
+                1 /* portrait */, mAmWmState.getWmState().getLastOrientation());
+
+        launchActivity(LANDSCAPE_ACTIVITY_NAME);
+        mAmWmState.assertVisibility(LANDSCAPE_ACTIVITY_NAME, true /* visible */);
+        assertEquals("Fullscreen app requested landscape orientation",
+                0 /* landscape */, mAmWmState.getWmState().getLastOrientation());
+
+        launchActivity(PORTRAIT_ACTIVITY_NAME);
+        mAmWmState.assertVisibility(PORTRAIT_ACTIVITY_NAME, true /* visible */);
+        assertEquals("Fullscreen app requested portrait orientation",
+                1 /* portrait */, mAmWmState.getWmState().getLastOrientation());
+    }
+
+    @Test
+    public void testNonfullscreenAppOrientationRequests() throws Exception {
+        String logSeparator = clearLogcat();
+        launchActivity(PORTRAIT_ACTIVITY_NAME);
+        final ReportedSizes initialReportedSizes =
+                getLastReportedSizesForActivity(PORTRAIT_ACTIVITY_NAME, logSeparator);
+        assertEquals("portrait activity should be in portrait",
+                1 /* portrait */, initialReportedSizes.orientation);
+        logSeparator = clearLogcat();
+
+        launchActivity(SDK26_TRANSLUCENT_LANDSCAPE_ACTIVITY);
+        assertEquals("Legacy non-fullscreen activity requested landscape orientation",
+                0 /* landscape */, mAmWmState.getWmState().getLastOrientation());
+
+        // TODO(b/36897968): uncomment once we can suppress unsupported configurations
+        // final ReportedSizes updatedReportedSizes =
+        //      getLastReportedSizesForActivity(PORTRAIT_ACTIVITY_NAME, logSeparator);
+        // assertEquals("portrait activity should not have moved from portrait",
+        //         1 /* portrait */, updatedReportedSizes.orientation);
+    }
+
+    // TODO(b/70870253): This test seems malfunction.
+    // @Test
+    public void testNonFullscreenActivityProhibited() throws Exception {
+        // We do not wait for the activity as it should not launch based on the restrictions around
+        // specifying orientation. We instead start an activity known to launch immediately after
+        // so that we can ensure processing the first activity occurred.
+        launchActivityNoWait(TRANSLUCENT_LANDSCAPE_ACTIVITY);
+        launchActivity(PORTRAIT_ACTIVITY_NAME);
+
+        assertFalse("target SDK > 26 non-fullscreen activity should not reach onResume",
+                mAmWmState.getAmState().containsActivity(
+                        TRANSLUCENT_LANDSCAPE_ACTIVITY.flattenToShortString()));
+    }
+
+    @Test
+    public void testNonFullscreenActivityPermitted() throws Exception {
+        try (final RotationSession rotationSession = new RotationSession()) {
+            rotationSession.set(ROTATION_0);
+
+            launchActivity(SDK26_TRANSLUCENT_LANDSCAPE_ACTIVITY);
+            mAmWmState.assertResumedActivity(
+                    "target SDK <= 26 non-fullscreen activity should be allowed to launch",
+                    SDK26_TRANSLUCENT_LANDSCAPE_ACTIVITY);
+            assertEquals("non-fullscreen activity requested landscape orientation",
+                    0 /* landscape */, mAmWmState.getWmState().getLastOrientation());
+        }
+    }
+
+    /**
+     * Test that device handles moving between two tasks with different orientations.
+     */
+    @Test
+    public void testTaskCloseRestoreOrientation() throws Exception {
+        // Start landscape activity.
+        launchActivity(LANDSCAPE_ACTIVITY_NAME);
+        mAmWmState.assertVisibility(LANDSCAPE_ACTIVITY_NAME, true /* visible */);
+        assertEquals("Fullscreen app requested landscape orientation",
+                0 /* landscape */, mAmWmState.getWmState().getLastOrientation());
+
+        // Start another activity in a different task.
+        launchActivityInNewTask(BROADCAST_RECEIVER_ACTIVITY);
+
+        // Request portrait
+        executeShellCommand(getOrientationBroadcast(1 /*portrait*/));
+        mAmWmState.waitForRotation(1);
+
+        // Finish activity
+        executeShellCommand(FINISH_ACTIVITY_BROADCAST);
+
+        // Verify that activity brought to front is in originally requested orientation.
+        mAmWmState.computeState(
+            new WaitForValidActivityState.Builder(LANDSCAPE_ACTIVITY_NAME).build());
+        assertEquals("Should return to app in landscape orientation",
+                0 /* landscape */, mAmWmState.getWmState().getLastOrientation());
+    }
+
+    /**
+     * Test that device handles moving between two tasks with different orientations.
+     */
+    @Presubmit
+    @Test
+    @FlakyTest(bugId = 71792393)
+    public void testTaskMoveToBackOrientation() throws Exception {
+        // Start landscape activity.
+        launchActivity(LANDSCAPE_ACTIVITY_NAME);
+        mAmWmState.assertVisibility(LANDSCAPE_ACTIVITY_NAME, true /* visible */);
+        assertEquals("Fullscreen app requested landscape orientation",
+                0 /* landscape */, mAmWmState.getWmState().getLastOrientation());
+
+        // Start another activity in a different task.
+        launchActivityInNewTask(BROADCAST_RECEIVER_ACTIVITY);
+
+        // Request portrait
+        executeShellCommand(getOrientationBroadcast(1 /*portrait*/));
+        mAmWmState.waitForRotation(1);
+
+        // Finish activity
+        executeShellCommand(MOVE_TASK_TO_BACK_BROADCAST);
+
+        // Verify that activity brought to front is in originally requested orientation.
+        mAmWmState.waitForValidState(LANDSCAPE_ACTIVITY_NAME);
+        assertEquals("Should return to app in landscape orientation",
+                0 /* landscape */, mAmWmState.getWmState().getLastOrientation());
+    }
+
+    /**
+     * Test that device doesn't change device orientation by app request while in multi-window.
+     */
+    @Presubmit
+    @Test
+    public void testSplitscreenPortraitAppOrientationRequests() throws Exception {
+        assumeTrue("Skipping test: no multi-window support", supportsSplitScreenMultiWindow());
+
+        try (final RotationSession rotationSession = new RotationSession()) {
+            requestOrientationInSplitScreen(rotationSession,
+                    ROTATION_90 /* portrait */, LANDSCAPE_ACTIVITY_NAME);
+        }
+    }
+
+    /**
+     * Test that device doesn't change device orientation by app request while in multi-window.
+     */
+    @Presubmit
+    @Test
+    public void testSplitscreenLandscapeAppOrientationRequests() throws Exception {
+        assumeTrue("Skipping test: no multi-window support", supportsSplitScreenMultiWindow());
+
+        try (final RotationSession rotationSession = new RotationSession()) {
+            requestOrientationInSplitScreen(rotationSession,
+                    ROTATION_0 /* landscape */, PORTRAIT_ACTIVITY_NAME);
+        }
+    }
+
+    /**
+     * Rotate the device and launch specified activity in split-screen, checking if orientation
+     * didn't change.
+     */
+    private void requestOrientationInSplitScreen(RotationSession rotationSession, int orientation,
+            String activity) throws Exception {
+        assumeTrue("Skipping test: no multi-window support", supportsSplitScreenMultiWindow());
+
+        // Set initial orientation.
+        rotationSession.set(orientation);
+
+        // Launch activities that request orientations and check that device doesn't rotate.
+        launchActivitiesInSplitScreen(
+                getLaunchActivityBuilder().setTargetActivityName(LAUNCHING_ACTIVITY),
+                getLaunchActivityBuilder().setTargetActivityName(activity).setMultipleTask(true));
+
+        mAmWmState.assertVisibility(activity, true /* visible */);
+        assertEquals("Split-screen apps shouldn't influence device orientation",
+                orientation, mAmWmState.getWmState().getRotation());
+
+        getLaunchActivityBuilder().setMultipleTask(true).setTargetActivityName(activity).execute();
+        mAmWmState.computeState(new String[] {activity});
+        mAmWmState.assertVisibility(activity, true /* visible */);
+        assertEquals("Split-screen apps shouldn't influence device orientation",
+                orientation, mAmWmState.getWmState().getRotation());
+    }
+
+    /**
+     * Launches activity in docked stack, moves to fullscreen stack and back to docked stack.
+     * Asserts that initial and final reported sizes in docked stack are the same.
+     */
+    private void moveActivitySplitFullSplit(String activityName) throws Exception {
+        assumeTrue("Skipping test: no multi-window support", supportsSplitScreenMultiWindow());
+
+        // Launch to docked stack and record size.
+        String logSeparator = clearLogcat();
+        launchActivity(activityName, WINDOWING_MODE_SPLIT_SCREEN_PRIMARY);
+        final ReportedSizes initialDockedSizes = getActivityDisplaySize(activityName, logSeparator);
+        // Make sure docked stack is focused. This way when we dismiss it later fullscreen stack
+        // will come up.
+        launchActivity(activityName, WINDOWING_MODE_SPLIT_SCREEN_PRIMARY);
+        mAmWmState.computeState(false /* compareTaskAndStackBounds */,
+                new WaitForValidActivityState.Builder(activityName).build());
+
+        // Move to fullscreen stack.
+        logSeparator = clearLogcat();
+        setActivityTaskWindowingMode(
+                activityName, WINDOWING_MODE_FULLSCREEN_OR_SPLIT_SCREEN_SECONDARY);
+        final ReportedSizes fullscreenSizes = getActivityDisplaySize(activityName, logSeparator);
+        assertSizesAreSane(fullscreenSizes, initialDockedSizes);
+
+        // Move activity back to docked stack.
+        logSeparator = clearLogcat();
+        setActivityTaskWindowingMode(activityName, WINDOWING_MODE_SPLIT_SCREEN_PRIMARY);
+        final ReportedSizes finalDockedSizes = getActivityDisplaySize(activityName, logSeparator);
+
+        // After activity configuration was changed twice it must report same size as original one.
+        assertSizesAreSame(initialDockedSizes, finalDockedSizes);
+    }
+
+    /**
+     * Asserts that after rotation, the aspect ratios of display size, metrics, and configuration
+     * have flipped.
+     */
+    private static void assertSizesRotate(ReportedSizes rotationA, ReportedSizes rotationB)
+            throws Exception {
+        assertEquals(rotationA.displayWidth, rotationA.metricsWidth);
+        assertEquals(rotationA.displayHeight, rotationA.metricsHeight);
+        assertEquals(rotationB.displayWidth, rotationB.metricsWidth);
+        assertEquals(rotationB.displayHeight, rotationB.metricsHeight);
+
+        final boolean beforePortrait = rotationA.displayWidth < rotationA.displayHeight;
+        final boolean afterPortrait = rotationB.displayWidth < rotationB.displayHeight;
+        assertFalse(beforePortrait == afterPortrait);
+
+        final boolean beforeConfigPortrait = rotationA.widthDp < rotationA.heightDp;
+        final boolean afterConfigPortrait = rotationB.widthDp < rotationB.heightDp;
+        assertEquals(beforePortrait, beforeConfigPortrait);
+        assertEquals(afterPortrait, afterConfigPortrait);
+
+        assertEquals(rotationA.smallestWidthDp, rotationB.smallestWidthDp);
+    }
+
+    /**
+     * Throws an AssertionError if fullscreenSizes has widths/heights (depending on aspect ratio)
+     * that are smaller than the dockedSizes.
+     */
+    private static void assertSizesAreSane(ReportedSizes fullscreenSizes, ReportedSizes dockedSizes)
+            throws Exception {
+        final boolean portrait = fullscreenSizes.displayWidth < fullscreenSizes.displayHeight;
+        if (portrait) {
+            assertTrue(dockedSizes.displayHeight < fullscreenSizes.displayHeight);
+            assertTrue(dockedSizes.heightDp < fullscreenSizes.heightDp);
+            assertTrue(dockedSizes.metricsHeight < fullscreenSizes.metricsHeight);
+        } else {
+            assertTrue(dockedSizes.displayWidth < fullscreenSizes.displayWidth);
+            assertTrue(dockedSizes.widthDp < fullscreenSizes.widthDp);
+            assertTrue(dockedSizes.metricsWidth < fullscreenSizes.metricsWidth);
+        }
+    }
+
+    /**
+     * Throws an AssertionError if sizes are different.
+     */
+    private static void assertSizesAreSame(ReportedSizes firstSize, ReportedSizes secondSize)
+            throws Exception {
+        assertEquals(firstSize.widthDp, secondSize.widthDp);
+        assertEquals(firstSize.heightDp, secondSize.heightDp);
+        assertEquals(firstSize.displayWidth, secondSize.displayWidth);
+        assertEquals(firstSize.displayHeight, secondSize.displayHeight);
+        assertEquals(firstSize.metricsWidth, secondSize.metricsWidth);
+        assertEquals(firstSize.metricsHeight, secondSize.metricsHeight);
+        assertEquals(firstSize.smallestWidthDp, secondSize.smallestWidthDp);
+    }
+
+    private ReportedSizes getActivityDisplaySize(String activityName, String logSeparator)
+            throws Exception {
+        mAmWmState.computeState(false /* compareTaskAndStackBounds */,
+                new WaitForValidActivityState.Builder(activityName).build());
+        final ReportedSizes details = getLastReportedSizesForActivity(activityName, logSeparator);
+        assertNotNull(details);
+        return details;
+    }
+
+    private Rect getDisplayRect(String activityName)
+            throws Exception {
+        final String windowName = getWindowName(activityName);
+
+        mAmWmState.computeState(new String[] {activityName});
+        mAmWmState.assertFocusedWindow("Test window must be the front window.", windowName);
+
+        final List<WindowManagerState.WindowState> tempWindowList = new ArrayList<>();
+        mAmWmState.getWmState().getMatchingVisibleWindowState(windowName, tempWindowList);
+
+        assertEquals("Should have exactly one window state for the activity.", 1,
+                tempWindowList.size());
+
+        WindowManagerState.WindowState windowState = tempWindowList.get(0);
+        assertNotNull("Should have a valid window", windowState);
+
+        WindowManagerState.Display display = mAmWmState.getWmState()
+                .getDisplay(windowState.getDisplayId());
+        assertNotNull("Should be on a display", display);
+
+        return display.getDisplayRect();
+    }
+
+    /**
+     * Test launching an activity which requests specific UI mode during creation.
+     */
+    @Test
+    public void testLaunchWithUiModeChange() throws Exception {
+        // Launch activity that changes UI mode and handles this configuration change.
+        launchActivity(NIGHT_MODE_ACTIVITY);
+        mAmWmState.waitForActivityState(NIGHT_MODE_ACTIVITY, STATE_RESUMED);
+
+        // Check if activity is launched successfully.
+        mAmWmState.assertVisibility(NIGHT_MODE_ACTIVITY, true /* visible */);
+        mAmWmState.assertFocusedActivity("Launched activity should be focused",
+                NIGHT_MODE_ACTIVITY);
+        mAmWmState.assertResumedActivity("Launched activity must be resumed", NIGHT_MODE_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
new file mode 100644
index 0000000..718fe85
--- /dev/null
+++ b/tests/framework/base/activitymanager/src/android/server/am/ActivityManagerAssistantStackTests.java
@@ -0,0 +1,360 @@
+/*
+ * 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.server.am;
+
+import static android.app.WindowConfiguration.ACTIVITY_TYPE_ASSISTANT;
+import static android.app.WindowConfiguration.ACTIVITY_TYPE_STANDARD;
+import static android.app.WindowConfiguration.WINDOWING_MODE_FULLSCREEN;
+import static android.app.WindowConfiguration.WINDOWING_MODE_PINNED;
+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.ActivityManagerState.STATE_RESUMED;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertTrue;
+import static org.junit.Assume.assumeTrue;
+
+import android.platform.test.annotations.Presubmit;
+import android.provider.Settings;
+import android.server.am.settings.SettingsSession;
+import android.support.test.filters.FlakyTest;
+
+import org.junit.Test;
+
+/**
+ * Build/Install/Run:
+ *     atest CtsActivityManagerDeviceTestCases:ActivityManagerAssistantStackTests
+ */
+//@Presubmit b/67706642
+public class ActivityManagerAssistantStackTests extends ActivityManagerTestBase {
+
+    private static final String VOICE_INTERACTION_SERVICE = "AssistantVoiceInteractionService";
+
+    private static final String TEST_ACTIVITY = "TestActivity";
+    private static final String ANIMATION_TEST_ACTIVITY = "AnimationTestActivity";
+    private static final String DOCKED_ACTIVITY = "DockedActivity";
+    private static final String ASSISTANT_ACTIVITY = "AssistantActivity";
+    private static final String TRANSLUCENT_ASSISTANT_ACTIVITY = "TranslucentAssistantActivity";
+    private static final String LAUNCH_ASSISTANT_ACTIVITY_FROM_SESSION =
+            "LaunchAssistantActivityFromSession";
+    private static final String LAUNCH_ASSISTANT_ACTIVITY_INTO_STACK =
+            "LaunchAssistantActivityIntoAssistantStack";
+    private static final String PIP_ACTIVITY = "PipActivity";
+
+    private static final String EXTRA_ENTER_PIP = "enter_pip";
+    private static final String EXTRA_LAUNCH_NEW_TASK = "launch_new_task";
+    private static final String EXTRA_FINISH_SELF = "finish_self";
+    private static final String EXTRA_IS_TRANSLUCENT = "is_translucent";
+
+    private static final String TEST_ACTIVITY_ACTION_FINISH_SELF =
+            "android.server.am.TestActivity.finish_self";
+
+    @Test
+    @Presubmit
+    public void testLaunchingAssistantActivityIntoAssistantStack() throws Exception {
+        // Enable the assistant and launch an assistant activity
+        try (final AssistantSession assistantSession = new AssistantSession()) {
+            assistantSession.set(getActivityComponentName(VOICE_INTERACTION_SERVICE));
+
+            launchActivity(LAUNCH_ASSISTANT_ACTIVITY_FROM_SESSION);
+            mAmWmState.waitForValidStateWithActivityType(ASSISTANT_ACTIVITY,
+                    ACTIVITY_TYPE_ASSISTANT);
+
+            // Ensure that the activity launched in the fullscreen assistant stack
+            assertAssistantStackExists();
+            assertTrue("Expected assistant stack to be fullscreen",
+                    mAmWmState.getAmState().getStackByActivityType(
+                            ACTIVITY_TYPE_ASSISTANT).isFullscreen());
+        }
+    }
+
+    @FlakyTest(bugId = 69573940)
+    @Presubmit
+    @Test
+    public void testAssistantStackZOrder() throws Exception {
+        assumeTrue(supportsPip());
+        assumeTrue(supportsSplitScreenMultiWindow());
+
+        // Launch a pinned stack task
+        launchActivity(PIP_ACTIVITY, EXTRA_ENTER_PIP, "true");
+        mAmWmState.waitForValidState(PIP_ACTIVITY, WINDOWING_MODE_PINNED, ACTIVITY_TYPE_STANDARD);
+        mAmWmState.assertContainsStack("Must contain pinned stack.",
+                WINDOWING_MODE_PINNED, ACTIVITY_TYPE_STANDARD);
+
+        // Dock a task
+        launchActivitiesInSplitScreen(DOCKED_ACTIVITY, TEST_ACTIVITY);
+        mAmWmState.assertContainsStack("Must contain fullscreen stack.",
+                WINDOWING_MODE_SPLIT_SCREEN_SECONDARY, ACTIVITY_TYPE_STANDARD);
+        mAmWmState.assertContainsStack("Must contain docked stack.",
+                WINDOWING_MODE_SPLIT_SCREEN_PRIMARY, ACTIVITY_TYPE_STANDARD);
+
+        // Enable the assistant and launch an assistant activity, ensure it is on top
+        try (final AssistantSession assistantSession = new AssistantSession()) {
+            assistantSession.set(getActivityComponentName(VOICE_INTERACTION_SERVICE));
+
+            launchActivity(LAUNCH_ASSISTANT_ACTIVITY_FROM_SESSION);
+            mAmWmState.waitForValidStateWithActivityType(ASSISTANT_ACTIVITY,
+                    ACTIVITY_TYPE_ASSISTANT);
+            assertAssistantStackExists();
+
+            mAmWmState.assertFrontStack("Pinned stack should be on top.",
+                    WINDOWING_MODE_PINNED, ACTIVITY_TYPE_STANDARD);
+            mAmWmState.assertFocusedStack("Assistant stack should be focused.",
+                    WINDOWING_MODE_UNDEFINED, ACTIVITY_TYPE_ASSISTANT);
+        }
+    }
+
+    @Test
+    @Presubmit
+    public void testAssistantStackLaunchNewTask() throws Exception {
+        assertAssistantStackCanLaunchAndReturnFromNewTask();
+    }
+
+    @Test
+    @Presubmit
+    public void testAssistantStackLaunchNewTaskWithDockedStack() throws Exception {
+        assumeTrue(supportsSplitScreenMultiWindow());
+
+        // Dock a task
+        launchActivitiesInSplitScreen(DOCKED_ACTIVITY, TEST_ACTIVITY);
+        mAmWmState.assertContainsStack("Must contain fullscreen stack.",
+                WINDOWING_MODE_SPLIT_SCREEN_SECONDARY, ACTIVITY_TYPE_STANDARD);
+        mAmWmState.assertContainsStack("Must contain docked stack.",
+                WINDOWING_MODE_SPLIT_SCREEN_PRIMARY, ACTIVITY_TYPE_STANDARD);
+
+        assertAssistantStackCanLaunchAndReturnFromNewTask();
+    }
+
+    private void assertAssistantStackCanLaunchAndReturnFromNewTask() throws Exception {
+        final boolean inSplitScreenMode = mAmWmState.getAmState().containsStack(
+                WINDOWING_MODE_SPLIT_SCREEN_PRIMARY, ACTIVITY_TYPE_STANDARD);
+
+        // Enable the assistant and launch an assistant activity which will launch a new task
+        try (final AssistantSession assistantSession = new AssistantSession()) {
+            assistantSession.set(getActivityComponentName(VOICE_INTERACTION_SERVICE));
+
+            launchActivity(LAUNCH_ASSISTANT_ACTIVITY_INTO_STACK,
+                    EXTRA_LAUNCH_NEW_TASK, TEST_ACTIVITY);
+        }
+
+        final int expectedWindowingMode = inSplitScreenMode
+                ? WINDOWING_MODE_SPLIT_SCREEN_SECONDARY
+                : WINDOWING_MODE_FULLSCREEN;
+        // Ensure that the fullscreen stack is on top and the test activity is now visible
+        mAmWmState.waitForValidState(TEST_ACTIVITY, expectedWindowingMode, ACTIVITY_TYPE_STANDARD);
+        mAmWmState.assertFocusedActivity("TestActivity should be resumed", TEST_ACTIVITY);
+        mAmWmState.assertFrontStack("Fullscreen stack should be on top.",
+                expectedWindowingMode, ACTIVITY_TYPE_STANDARD);
+        mAmWmState.assertFocusedStack("Fullscreen stack should be focused.",
+                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);
+        mAmWmState.waitForFocusedStack(WINDOWING_MODE_UNDEFINED, ACTIVITY_TYPE_ASSISTANT);
+        mAmWmState.assertFrontStackActivityType(
+                "Assistant stack should be on top.", ACTIVITY_TYPE_ASSISTANT);
+        mAmWmState.assertFocusedStack("Assistant stack should be focused.",
+                WINDOWING_MODE_UNDEFINED, ACTIVITY_TYPE_ASSISTANT);
+    }
+
+    @Test
+    @Presubmit
+    public void testAssistantStackFinishToPreviousApp() throws Exception {
+        // Launch an assistant activity on top of an existing fullscreen activity, and ensure that
+        // the fullscreen activity is still visible and on top after the assistant activity finishes
+        launchActivity(TEST_ACTIVITY);
+        try (final AssistantSession assistantSession = new AssistantSession()) {
+            assistantSession.set(getActivityComponentName(VOICE_INTERACTION_SERVICE));
+
+            launchActivity(LAUNCH_ASSISTANT_ACTIVITY_INTO_STACK,
+                    EXTRA_FINISH_SELF, "true");
+        }
+        mAmWmState.waitForValidState(TEST_ACTIVITY,
+                WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_STANDARD);
+        mAmWmState.waitForActivityState(TEST_ACTIVITY, STATE_RESUMED);
+        mAmWmState.assertFocusedActivity("TestActivity should be resumed", TEST_ACTIVITY);
+        mAmWmState.assertFrontStack("Fullscreen stack should be on top.",
+                WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_STANDARD);
+        mAmWmState.assertFocusedStack("Fullscreen stack should be focused.",
+                WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_STANDARD);
+    }
+
+    @Test
+    @Presubmit
+    public void testDisallowEnterPiPFromAssistantStack() throws Exception {
+        try (final AssistantSession assistantSession = new AssistantSession()) {
+            assistantSession.set(getActivityComponentName(VOICE_INTERACTION_SERVICE));
+
+            launchActivity(LAUNCH_ASSISTANT_ACTIVITY_INTO_STACK,
+                    EXTRA_ENTER_PIP, "true");
+        }
+        mAmWmState.waitForValidStateWithActivityType(ASSISTANT_ACTIVITY, ACTIVITY_TYPE_ASSISTANT);
+        mAmWmState.assertDoesNotContainStack("Must not contain pinned stack.",
+                WINDOWING_MODE_PINNED, ACTIVITY_TYPE_STANDARD);
+    }
+
+    @FlakyTest(bugId = 69573940)
+    @Presubmit
+    @Test
+    public void testTranslucentAssistantActivityStackVisibility() throws Exception {
+        try (final AssistantSession assistantSession = new AssistantSession()) {
+            assistantSession.set(getActivityComponentName(VOICE_INTERACTION_SERVICE));
+
+            // Go home, launch the assistant and check to see that home is visible
+            removeStacksInWindowingModes(WINDOWING_MODE_FULLSCREEN,
+                    WINDOWING_MODE_SPLIT_SCREEN_SECONDARY);
+            launchHomeActivity();
+            launchActivity(LAUNCH_ASSISTANT_ACTIVITY_INTO_STACK,
+                    EXTRA_IS_TRANSLUCENT, String.valueOf(true));
+            mAmWmState.waitForValidStateWithActivityType(
+                    TRANSLUCENT_ASSISTANT_ACTIVITY, ACTIVITY_TYPE_ASSISTANT);
+            assertAssistantStackExists();
+            mAmWmState.waitForHomeActivityVisible();
+            if (hasHomeScreen()) {
+                mAmWmState.assertHomeActivityVisible(true);
+            }
+
+            // Launch a fullscreen app and then launch the assistant and check to see that it is
+            // also visible
+            removeStacksWithActivityTypes(ACTIVITY_TYPE_ASSISTANT);
+            launchActivity(TEST_ACTIVITY);
+            launchActivity(LAUNCH_ASSISTANT_ACTIVITY_INTO_STACK,
+                    EXTRA_IS_TRANSLUCENT, String.valueOf(true));
+            mAmWmState.waitForValidStateWithActivityType(
+                    TRANSLUCENT_ASSISTANT_ACTIVITY, ACTIVITY_TYPE_ASSISTANT);
+            assertAssistantStackExists();
+            mAmWmState.assertVisibility(TEST_ACTIVITY, true);
+
+            // Go home, launch assistant, launch app into fullscreen with activity present, and go back.
+
+            // Ensure home is visible.
+            removeStacksWithActivityTypes(ACTIVITY_TYPE_ASSISTANT);
+            launchHomeActivity();
+            launchActivity(LAUNCH_ASSISTANT_ACTIVITY_INTO_STACK,
+                    EXTRA_IS_TRANSLUCENT, String.valueOf(true), EXTRA_LAUNCH_NEW_TASK,
+                    TEST_ACTIVITY);
+            mAmWmState.waitForValidState(TEST_ACTIVITY,
+                    WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_STANDARD);
+            mAmWmState.assertHomeActivityVisible(false);
+            pressBackButton();
+            mAmWmState.waitForFocusedStack(WINDOWING_MODE_UNDEFINED, ACTIVITY_TYPE_ASSISTANT);
+            assertAssistantStackExists();
+            mAmWmState.waitForHomeActivityVisible();
+            if (hasHomeScreen()) {
+                mAmWmState.assertHomeActivityVisible(true);
+            }
+
+            // Launch a fullscreen and docked app and then launch the assistant and check to see
+            // that it
+            // is also visible
+            if (supportsSplitScreenMultiWindow()) {
+                removeStacksWithActivityTypes(ACTIVITY_TYPE_ASSISTANT);
+                launchActivitiesInSplitScreen(DOCKED_ACTIVITY, TEST_ACTIVITY);
+                mAmWmState.assertContainsStack("Must contain docked stack.",
+                        WINDOWING_MODE_SPLIT_SCREEN_PRIMARY, ACTIVITY_TYPE_STANDARD);
+                launchActivity(LAUNCH_ASSISTANT_ACTIVITY_INTO_STACK,
+                        EXTRA_IS_TRANSLUCENT, String.valueOf(true));
+                mAmWmState.waitForValidStateWithActivityType(
+                        TRANSLUCENT_ASSISTANT_ACTIVITY, ACTIVITY_TYPE_ASSISTANT);
+                assertAssistantStackExists();
+                mAmWmState.assertVisibility(DOCKED_ACTIVITY, true);
+                mAmWmState.assertVisibility(TEST_ACTIVITY, true);
+            }
+        }
+    }
+
+    @FlakyTest(bugId = 69229402)
+    @Test
+    @Presubmit
+    public void testLaunchIntoSameTask() throws Exception {
+        try (final AssistantSession assistantSession = new AssistantSession()) {
+            assistantSession.set(getActivityComponentName(VOICE_INTERACTION_SERVICE));
+
+            // Launch the assistant
+            launchActivity(LAUNCH_ASSISTANT_ACTIVITY_FROM_SESSION);
+            assertAssistantStackExists();
+            mAmWmState.assertVisibility(ASSISTANT_ACTIVITY, true);
+            mAmWmState.assertFocusedStack("Expected assistant stack focused",
+                    WINDOWING_MODE_UNDEFINED, ACTIVITY_TYPE_ASSISTANT);
+            assertEquals(1, mAmWmState.getAmState().getStackByActivityType(
+                    ACTIVITY_TYPE_ASSISTANT).getTasks().size());
+            final int taskId = mAmWmState.getAmState().getTaskByActivityName(ASSISTANT_ACTIVITY)
+                    .mTaskId;
+
+            // Launch a new fullscreen activity
+            // Using Animation Test Activity because it is opaque on all devices.
+            launchActivity(ANIMATION_TEST_ACTIVITY);
+            mAmWmState.assertVisibility(ASSISTANT_ACTIVITY, false);
+
+            // Launch the assistant again and ensure that it goes into the same task
+            launchActivity(LAUNCH_ASSISTANT_ACTIVITY_FROM_SESSION);
+            assertAssistantStackExists();
+            mAmWmState.assertVisibility(ASSISTANT_ACTIVITY, true);
+            mAmWmState.assertFocusedStack("Expected assistant stack focused",
+                    WINDOWING_MODE_UNDEFINED, ACTIVITY_TYPE_ASSISTANT);
+            assertEquals(1, mAmWmState.getAmState().getStackByActivityType(
+                    ACTIVITY_TYPE_ASSISTANT).getTasks().size());
+            assertEquals(taskId,
+                    mAmWmState.getAmState().getTaskByActivityName(ASSISTANT_ACTIVITY).mTaskId);
+
+        }
+    }
+
+    @Test
+    public void testPinnedStackWithAssistant() throws Exception {
+        assumeTrue(supportsPip());
+        assumeTrue(supportsSplitScreenMultiWindow());
+
+        try (final AssistantSession assistantSession = new AssistantSession()) {
+            assistantSession.set(getActivityComponentName(VOICE_INTERACTION_SERVICE));
+
+            // Launch a fullscreen activity and a PIP activity, then launch the assistant, and ensure
+
+            // that the test activity is still visible
+            launchActivity(TEST_ACTIVITY);
+            launchActivity(PIP_ACTIVITY, EXTRA_ENTER_PIP, "true");
+            launchActivity(LAUNCH_ASSISTANT_ACTIVITY_INTO_STACK,
+                    EXTRA_IS_TRANSLUCENT, String.valueOf(true));
+            mAmWmState.waitForValidStateWithActivityType(
+                    TRANSLUCENT_ASSISTANT_ACTIVITY, ACTIVITY_TYPE_ASSISTANT);
+            assertAssistantStackExists();
+            mAmWmState.assertVisibility(TRANSLUCENT_ASSISTANT_ACTIVITY, true);
+            mAmWmState.assertVisibility(PIP_ACTIVITY, true);
+            mAmWmState.assertVisibility(TEST_ACTIVITY, true);
+
+        }
+    }
+
+    /**
+     * Asserts that the assistant stack exists.
+     */
+    private void assertAssistantStackExists() throws Exception {
+        mAmWmState.assertContainsStack("Must contain assistant stack.",
+                WINDOWING_MODE_UNDEFINED, ACTIVITY_TYPE_ASSISTANT);
+    }
+
+    /** Helper class to save, set, and restore
+     * {@link Settings.Secure#VOICE_INTERACTION_SERVICE} system preference.
+     */
+    private static class AssistantSession extends SettingsSession<String> {
+        AssistantSession() {
+            super(Settings.Secure.getUriFor(Settings.Secure.VOICE_INTERACTION_SERVICE),
+                    Settings.Secure::getString, Settings.Secure::putString);
+        }
+    }
+}
diff --git a/tests/framework/base/activitymanager/src/android/server/am/ActivityManagerConfigChangeTests.java b/tests/framework/base/activitymanager/src/android/server/am/ActivityManagerConfigChangeTests.java
new file mode 100644
index 0000000..a6760c3
--- /dev/null
+++ b/tests/framework/base/activitymanager/src/android/server/am/ActivityManagerConfigChangeTests.java
@@ -0,0 +1,250 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.server.am;
+
+import static android.server.am.ActivityManagerState.STATE_RESUMED;
+import static android.server.am.StateLogger.log;
+import static android.server.am.StateLogger.logE;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.fail;
+
+import android.platform.test.annotations.Presubmit;
+import android.provider.Settings;
+import android.server.am.settings.SettingsSession;
+
+import org.junit.Test;
+
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
+
+/**
+ * Build/Install/Run:
+ *     atest CtsActivityManagerDeviceTestCases:ActivityManagerConfigChangeTests
+ */
+public class ActivityManagerConfigChangeTests extends ActivityManagerTestBase {
+    private static final String TEST_ACTIVITY_NAME = "TestActivity";
+    private static final String NO_RELAUNCH_ACTIVITY_NAME = "NoRelaunchActivity";
+
+    private static final String FONT_SCALE_ACTIVITY_NAME = "FontScaleActivity";
+    private static final String FONT_SCALE_NO_RELAUNCH_ACTIVITY_NAME =
+            "FontScaleNoRelaunchActivity";
+
+    private static final float EXPECTED_FONT_SIZE_SP = 10.0f;
+
+    @Test
+    public void testRotation90Relaunch() throws Exception{
+        // Should relaunch on every rotation and receive no onConfigurationChanged()
+        testRotation(TEST_ACTIVITY_NAME, 1, 1, 0);
+    }
+
+    @Test
+    public void testRotation90NoRelaunch() throws Exception {
+        // Should receive onConfigurationChanged() on every rotation and no relaunch
+        testRotation(NO_RELAUNCH_ACTIVITY_NAME, 1, 0, 1);
+    }
+
+    @Test
+    public void testRotation180Relaunch() throws Exception {
+        // Should receive nothing
+        testRotation(TEST_ACTIVITY_NAME, 2, 0, 0);
+    }
+
+    @Test
+    public void testRotation180NoRelaunch() throws Exception {
+        // Should receive nothing
+        testRotation(NO_RELAUNCH_ACTIVITY_NAME, 2, 0, 0);
+    }
+
+    @Presubmit
+    @Test
+    public void testChangeFontScaleRelaunch() throws Exception {
+        // Should relaunch and receive no onConfigurationChanged()
+        testChangeFontScale(FONT_SCALE_ACTIVITY_NAME, true /* relaunch */);
+    }
+
+    @Presubmit
+    @Test
+    public void testChangeFontScaleNoRelaunch() throws Exception {
+        // Should receive onConfigurationChanged() and no relaunch
+        testChangeFontScale(FONT_SCALE_NO_RELAUNCH_ACTIVITY_NAME, false /* relaunch */);
+    }
+
+    private void testRotation(String activityName, int rotationStep, int numRelaunch,
+            int numConfigChange) throws Exception {
+        launchActivity(activityName);
+
+        final String[] waitForActivitiesVisible = new String[] {activityName};
+        mAmWmState.computeState(waitForActivitiesVisible);
+
+        final int initialRotation = 4 - rotationStep;
+        try (final RotationSession rotationSession = new RotationSession()) {
+            rotationSession.set(initialRotation);
+            mAmWmState.computeState(waitForActivitiesVisible);
+            final int actualStackId = mAmWmState.getAmState().getTaskByActivityName(
+                    activityName).mStackId;
+            final int displayId = mAmWmState.getAmState().getStackById(actualStackId).mDisplayId;
+            final int newDeviceRotation = getDeviceRotation(displayId);
+            if (newDeviceRotation == INVALID_DEVICE_ROTATION) {
+                logE("Got an invalid device rotation value. "
+                        + "Continuing the test despite of that, but it is likely to fail.");
+            } else if (newDeviceRotation != initialRotation) {
+                log("This device doesn't support user rotation "
+                        + "mode. Not continuing the rotation checks.");
+                return;
+            }
+
+            for (int rotation = 0; rotation < 4; rotation += rotationStep) {
+                final String logSeparator = clearLogcat();
+                rotationSession.set(rotation);
+                mAmWmState.computeState(waitForActivitiesVisible);
+                assertRelaunchOrConfigChanged(activityName, numRelaunch, numConfigChange,
+                        logSeparator);
+            }
+        }
+    }
+
+    /** Helper class to save, set, and restore font_scale preferences. */
+    private static class FontScaleSession extends SettingsSession<Float> {
+        FontScaleSession() {
+            super(Settings.System.getUriFor(Settings.System.FONT_SCALE),
+                    Settings.System::getFloat,
+                    Settings.System::putFloat);
+        }
+    }
+
+    private void testChangeFontScale(
+            String activityName, boolean relaunch) throws Exception {
+        try (final FontScaleSession fontScaleSession = new FontScaleSession()) {
+            fontScaleSession.set(1.0f);
+            String logSeparator = clearLogcat();
+            launchActivity(activityName);
+            final String[] waitForActivitiesVisible = new String[]{activityName};
+            mAmWmState.computeState(waitForActivitiesVisible);
+
+            final int densityDpi = getActivityDensityDpi(activityName, logSeparator);
+
+            for (float fontScale = 0.85f; fontScale <= 1.3f; fontScale += 0.15f) {
+                logSeparator = clearLogcat();
+                fontScaleSession.set(fontScale);
+                mAmWmState.computeState(waitForActivitiesVisible);
+                assertRelaunchOrConfigChanged(activityName, relaunch ? 1 : 0, relaunch ? 0 : 1,
+                        logSeparator);
+
+                // Verify that the display metrics are updated, and therefore the text size is also
+                // updated accordingly.
+                assertExpectedFontPixelSize(activityName,
+                        scaledPixelsToPixels(EXPECTED_FONT_SIZE_SP, fontScale, densityDpi),
+                        logSeparator);
+            }
+        }
+    }
+
+    /**
+     * Test updating application info when app is running. An activity with matching package name
+     * must be recreated and its asset sequence number must be incremented.
+     */
+    @Test
+    public void testUpdateApplicationInfo() throws Exception {
+        final String firstLogSeparator = clearLogcat();
+
+        // Launch an activity that prints applied config.
+        launchActivity(TEST_ACTIVITY_NAME);
+        final int assetSeq = readAssetSeqNumber(TEST_ACTIVITY_NAME, firstLogSeparator);
+
+        final String logSeparator = clearLogcat();
+        // Update package info.
+        executeShellCommand("am update-appinfo all " + componentName);
+        mAmWmState.waitForWithAmState((amState) -> {
+            // Wait for activity to be resumed and asset seq number to be updated.
+            try {
+                return readAssetSeqNumber(TEST_ACTIVITY_NAME, logSeparator) == assetSeq + 1
+                        && amState.hasActivityState(TEST_ACTIVITY_NAME, STATE_RESUMED);
+            } catch (Exception e) {
+                logE("Error waiting for valid state: " + e.getMessage());
+                return false;
+            }
+        }, "Waiting asset sequence number to be updated and for activity to be resumed.");
+
+        // Check if activity is relaunched and asset seq is updated.
+        assertRelaunchOrConfigChanged(TEST_ACTIVITY_NAME, 1 /* numRelaunch */,
+                0 /* numConfigChange */, logSeparator);
+        final int newAssetSeq = readAssetSeqNumber(TEST_ACTIVITY_NAME, logSeparator);
+        assertEquals("Asset sequence number must be incremented.", assetSeq + 1, newAssetSeq);
+    }
+
+    private static final Pattern sConfigurationPattern = Pattern.compile(
+            "(.+): Configuration: \\{(.*) as.(\\d+)(.*)\\}");
+
+    /** Read asset sequence number in last applied configuration from logs. */
+    private int readAssetSeqNumber(String activityName, String logSeparator) throws Exception {
+        final String[] lines = getDeviceLogsForComponent(activityName, logSeparator);
+        for (int i = lines.length - 1; i >= 0; i--) {
+            final String line = lines[i].trim();
+            final Matcher matcher = sConfigurationPattern.matcher(line);
+            if (matcher.matches()) {
+                final String assetSeqNumber = matcher.group(3);
+                try {
+                    return Integer.valueOf(assetSeqNumber);
+                } catch (NumberFormatException e) {
+                    // Ignore, asset seq number is not printed when not set.
+                }
+            }
+        }
+        return 0;
+    }
+
+    // Calculate the scaled pixel size just like the device is supposed to.
+    private static int scaledPixelsToPixels(float sp, float fontScale, int densityDpi) {
+        final int DEFAULT_DENSITY = 160;
+        float f = densityDpi * (1.0f / DEFAULT_DENSITY) * fontScale * sp;
+        return (int) ((f >= 0) ? (f + 0.5f) : (f - 0.5f));
+    }
+
+    private static Pattern sDeviceDensityPattern = Pattern.compile("^(.+): fontActivityDpi=(.+)$");
+
+    private int getActivityDensityDpi(String activityName, String logSeparator) throws Exception {
+        final String[] lines = getDeviceLogsForComponent(activityName, logSeparator);
+        for (int i = lines.length - 1; i >= 0; i--) {
+            final String line = lines[i].trim();
+            final Matcher matcher = sDeviceDensityPattern.matcher(line);
+            if (matcher.matches()) {
+                return Integer.parseInt(matcher.group(2));
+            }
+        }
+        fail("No fontActivityDpi reported from activity " + activityName);
+        return -1;
+    }
+
+    private static final Pattern sFontSizePattern = Pattern.compile("^(.+): fontPixelSize=(.+)$");
+
+    /** Read the font size in the last log line. */
+    private void assertExpectedFontPixelSize(String activityName, int fontPixelSize,
+            String logSeparator) throws Exception {
+        final String[] lines = getDeviceLogsForComponent(activityName, logSeparator);
+        for (int i = lines.length - 1; i >= 0; i--) {
+            final String line = lines[i].trim();
+            final Matcher matcher = sFontSizePattern.matcher(line);
+            if (matcher.matches()) {
+                assertEquals("Expected font pixel size does not match", fontPixelSize,
+                        Integer.parseInt(matcher.group(2)));
+                return;
+            }
+        }
+        fail("No fontPixelSize reported from activity " + activityName);
+    }
+}
diff --git a/tests/framework/base/activitymanager/src/android/server/am/ActivityManagerDisplayKeyguardTests.java b/tests/framework/base/activitymanager/src/android/server/am/ActivityManagerDisplayKeyguardTests.java
new file mode 100644
index 0000000..4d6827e
--- /dev/null
+++ b/tests/framework/base/activitymanager/src/android/server/am/ActivityManagerDisplayKeyguardTests.java
@@ -0,0 +1,71 @@
+/*
+ * 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.server.am;
+
+import static org.junit.Assume.assumeTrue;
+import android.server.am.ActivityManagerState.ActivityDisplay;
+
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Test;
+
+/**
+ * Display tests that require a keyguard.
+ *
+ * <p>Build/Install/Run:
+ *     atest CtsActivityManagerDeviceTestCases:ActivityManagerDisplayKeyguardTests
+ */
+public class ActivityManagerDisplayKeyguardTests extends ActivityManagerDisplayTestBase {
+    private static final String DISMISS_KEYGUARD_ACTIVITY = "DismissKeyguardActivity";
+
+    @Before
+    @Override
+    public void setUp() throws Exception {
+        super.setUp();
+
+        assumeTrue(supportsMultiDisplay());
+        assumeTrue(isHandheld());
+
+        setLockDisabled(false);
+    }
+
+    @After
+    @Override
+    public void tearDown() throws Exception {
+        super.tearDown();
+        tearDownLockCredentials();
+    }
+
+    /**
+     * Tests whether a FLAG_DISMISS_KEYGUARD activity on a secondary display is visible (for an
+     * insecure keyguard).
+     */
+    @Test
+    public void testDismissKeyguardActivity_secondaryDisplay() throws Exception {
+        try (final VirtualDisplaySession virtualDisplaySession = new VirtualDisplaySession()) {
+            final ActivityDisplay newDisplay = virtualDisplaySession.createDisplay();
+
+            gotoKeyguard();
+            mAmWmState.waitForKeyguardShowingAndNotOccluded();
+            mAmWmState.assertKeyguardShowingAndNotOccluded();
+            launchActivityOnDisplay(DISMISS_KEYGUARD_ACTIVITY, newDisplay.mId);
+            mAmWmState.waitForKeyguardShowingAndNotOccluded();
+            mAmWmState.assertKeyguardShowingAndNotOccluded();
+            mAmWmState.assertVisibility(DISMISS_KEYGUARD_ACTIVITY, true);
+        }
+    }
+}
diff --git a/tests/framework/base/activitymanager/src/android/server/am/ActivityManagerDisplayLockedKeyguardTests.java b/tests/framework/base/activitymanager/src/android/server/am/ActivityManagerDisplayLockedKeyguardTests.java
new file mode 100644
index 0000000..6f9e9d0
--- /dev/null
+++ b/tests/framework/base/activitymanager/src/android/server/am/ActivityManagerDisplayLockedKeyguardTests.java
@@ -0,0 +1,128 @@
+/*
+ * 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.server.am;
+
+import static android.server.am.ActivityManagerState.STATE_RESUMED;
+import static android.server.am.ActivityManagerState.STATE_STOPPED;
+
+import static org.junit.Assume.assumeTrue;
+
+import android.server.am.ActivityManagerState.ActivityDisplay;
+
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Test;
+
+/**
+ * Display tests that require a locked keyguard.
+ *
+ * <p>Build/Install/Run:
+ *     atest CtsActivityManagerDeviceTestCases:ActivityManagerDisplayLockedKeyguardTests
+ */
+public class ActivityManagerDisplayLockedKeyguardTests extends ActivityManagerDisplayTestBase {
+
+    private static final String TEST_ACTIVITY_NAME = "TestActivity";
+    private static final String VIRTUAL_DISPLAY_ACTIVITY = "VirtualDisplayActivity";
+    private static final String DISMISS_KEYGUARD_ACTIVITY = "DismissKeyguardActivity";
+    private static final String SHOW_WHEN_LOCKED_ACTIVITY = "ShowWhenLockedActivity";
+
+    @Before
+    @Override
+    public void setUp() throws Exception {
+        super.setUp();
+
+        assumeTrue(supportsMultiDisplay());
+        assumeTrue(isHandheld());
+
+        setLockCredential();
+    }
+
+
+    @After
+    @Override
+    public void tearDown() throws Exception {
+        tearDownLockCredentials();
+        super.tearDown();
+    }
+
+    /**
+     * Test that virtual display content is hidden when device is locked.
+     */
+    @Test
+    public void testVirtualDisplayHidesContentWhenLocked() throws Exception {
+        try (final VirtualDisplaySession virtualDisplaySession = new VirtualDisplaySession()) {
+            // Create new usual virtual display.
+            final ActivityDisplay newDisplay = virtualDisplaySession.createDisplay();
+            mAmWmState.assertVisibility(VIRTUAL_DISPLAY_ACTIVITY, true /* visible */);
+
+            // Launch activity on new secondary display.
+            launchActivityOnDisplay(TEST_ACTIVITY_NAME, newDisplay.mId);
+            mAmWmState.assertVisibility(TEST_ACTIVITY_NAME, true /* visible */);
+
+            // Lock the device.
+            gotoKeyguard();
+            mAmWmState.waitForKeyguardShowingAndNotOccluded();
+            mAmWmState.waitForActivityState(TEST_ACTIVITY_NAME, STATE_STOPPED);
+            mAmWmState.assertVisibility(TEST_ACTIVITY_NAME, false /* visible */);
+
+            // Unlock and check if visibility is back.
+            unlockDeviceWithCredential();
+            mAmWmState.waitForKeyguardGone();
+            mAmWmState.waitForActivityState(TEST_ACTIVITY_NAME, STATE_RESUMED);
+            mAmWmState.assertVisibility(TEST_ACTIVITY_NAME, true /* visible */);
+        }
+    }
+
+    /**
+     * Tests whether a FLAG_DISMISS_KEYGUARD activity on a secondary display dismisses the keyguard.
+     */
+    @Test
+    public void testDismissKeyguard_secondaryDisplay() throws Exception {
+        try (final VirtualDisplaySession virtualDisplaySession = new VirtualDisplaySession()) {
+            final ActivityDisplay newDisplay = virtualDisplaySession.createDisplay();
+
+            gotoKeyguard();
+            mAmWmState.waitForKeyguardShowingAndNotOccluded();
+            mAmWmState.assertKeyguardShowingAndNotOccluded();
+            launchActivityOnDisplay(DISMISS_KEYGUARD_ACTIVITY, newDisplay.mId);
+            enterAndConfirmLockCredential();
+            mAmWmState.waitForKeyguardGone();
+            mAmWmState.assertKeyguardGone();
+            mAmWmState.assertVisibility(DISMISS_KEYGUARD_ACTIVITY, true);
+        }
+    }
+
+    @Test
+    public void testDismissKeyguard_whileOccluded_secondaryDisplay() throws Exception {
+        try (final VirtualDisplaySession virtualDisplaySession = new VirtualDisplaySession()) {
+            final ActivityDisplay newDisplay = virtualDisplaySession.createDisplay();
+
+            gotoKeyguard();
+            mAmWmState.waitForKeyguardShowingAndNotOccluded();
+            mAmWmState.assertKeyguardShowingAndNotOccluded();
+            launchActivity(SHOW_WHEN_LOCKED_ACTIVITY);
+            mAmWmState.computeState(new WaitForValidActivityState(SHOW_WHEN_LOCKED_ACTIVITY));
+            mAmWmState.assertVisibility(SHOW_WHEN_LOCKED_ACTIVITY, true);
+            launchActivityOnDisplay(DISMISS_KEYGUARD_ACTIVITY, newDisplay.mId);
+            enterAndConfirmLockCredential();
+            mAmWmState.waitForKeyguardGone();
+            mAmWmState.assertKeyguardGone();
+            mAmWmState.assertVisibility(DISMISS_KEYGUARD_ACTIVITY, true);
+            mAmWmState.assertVisibility(SHOW_WHEN_LOCKED_ACTIVITY, true);
+        }
+    }
+}
diff --git a/tests/framework/base/activitymanager/src/android/server/am/ActivityManagerDisplayTestBase.java b/tests/framework/base/activitymanager/src/android/server/am/ActivityManagerDisplayTestBase.java
new file mode 100644
index 0000000..09d793f
--- /dev/null
+++ b/tests/framework/base/activitymanager/src/android/server/am/ActivityManagerDisplayTestBase.java
@@ -0,0 +1,429 @@
+/*
+ * 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.server.am;
+
+import static android.content.pm.PackageManager.FEATURE_ACTIVITIES_ON_SECONDARY_DISPLAYS;
+import static android.server.am.ActivityAndWindowManagersState.DEFAULT_DISPLAY_ID;
+import static android.server.am.StateLogger.log;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertTrue;
+
+import android.content.res.Configuration;
+import android.provider.Settings;
+import android.server.am.ActivityManagerState.ActivityDisplay;
+import android.server.am.settings.SettingsSession;
+import android.support.annotation.NonNull;
+import android.support.annotation.Nullable;
+import android.util.Size;
+
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.List;
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
+
+/**
+ * Base class for ActivityManager display tests.
+ *
+ * @see ActivityManagerDisplayTests
+ * @see ActivityManagerDisplayLockedKeyguardTests
+ */
+public class ActivityManagerDisplayTestBase extends ActivityManagerTestBase {
+    private static final String WM_SIZE = "wm size";
+    private static final String WM_DENSITY = "wm density";
+
+    static final int CUSTOM_DENSITY_DPI = 222;
+
+    private static final String VIRTUAL_DISPLAY_ACTIVITY = "VirtualDisplayActivity";
+    private static final int INVALID_DENSITY_DPI = -1;
+
+    ActivityDisplay getDisplayState(List<ActivityDisplay> displays, int displayId) {
+        for (ActivityDisplay display : displays) {
+            if (display.mId == displayId) {
+                return display;
+            }
+        }
+        return null;
+    }
+
+    /** Return the display state with width, height, dpi. Always not default display. */
+    ActivityDisplay getDisplayState(List<ActivityDisplay> displays, int width, int height,
+            int dpi) {
+        for (ActivityDisplay display : displays) {
+            if (display.mId == DEFAULT_DISPLAY_ID) {
+                continue;
+            }
+            final Configuration config = display.mFullConfiguration;
+            if (config.densityDpi == dpi && config.screenWidthDp == width
+                    && config.screenHeightDp == height) {
+                return display;
+            }
+        }
+        return null;
+    }
+
+    List<ActivityDisplay> getDisplaysStates() {
+        mAmWmState.getAmState().computeState();
+        return mAmWmState.getAmState().getDisplays();
+    }
+
+    /** Find the display that was not originally reported in oldDisplays and added in newDisplays */
+    List<ActivityDisplay> findNewDisplayStates(List<ActivityDisplay> oldDisplays,
+            List<ActivityDisplay> newDisplays) {
+        final ArrayList<ActivityDisplay> result = new ArrayList<>();
+
+        for (ActivityDisplay newDisplay : newDisplays) {
+            if (oldDisplays.stream().noneMatch(d -> d.mId == newDisplay.mId)) {
+                result.add(newDisplay);
+            }
+        }
+
+        return result;
+    }
+
+    static class ReportedDisplayMetrics {
+        private static final String WM_SIZE = "wm size";
+        private static final String WM_DENSITY = "wm density";
+        private static final Pattern PHYSICAL_SIZE =
+                Pattern.compile("Physical size: (\\d+)x(\\d+)");
+        private static final Pattern OVERRIDE_SIZE =
+                Pattern.compile("Override size: (\\d+)x(\\d+)");
+        private static final Pattern PHYSICAL_DENSITY =
+                Pattern.compile("Physical density: (\\d+)");
+        private static final Pattern OVERRIDE_DENSITY =
+                Pattern.compile("Override density: (\\d+)");
+
+        @NonNull
+        final Size physicalSize;
+        final int physicalDensity;
+
+        @Nullable
+        final Size overrideSize;
+        @Nullable
+        final Integer overrideDensity;
+
+        /** Get physical and override display metrics from WM. */
+        static ReportedDisplayMetrics getDisplayMetrics() throws Exception {
+            return new ReportedDisplayMetrics(
+                    executeShellCommand(WM_SIZE) + executeShellCommand(WM_DENSITY));
+        }
+
+        void setDisplayMetrics(final Size size, final int density) {
+            setSize(size);
+            setDensity(density);
+        }
+
+        void restoreDisplayMetrics() {
+            if (overrideSize != null) {
+                setSize(overrideSize);
+            } else {
+                executeShellCommand(WM_SIZE + " reset");
+            }
+            if (overrideDensity != null) {
+                setDensity(overrideDensity);
+            } else {
+                executeShellCommand(WM_DENSITY + " reset");
+            }
+        }
+
+        private void setSize(final Size size) {
+            executeShellCommand(WM_SIZE + " " + size.getWidth() + "x" + size.getHeight());
+        }
+
+        private void setDensity(final int density) {
+            executeShellCommand(WM_DENSITY + " " + density);
+        }
+
+        /** Get display size that WM operates with. */
+        Size getSize() {
+            return overrideSize != null ? overrideSize : physicalSize;
+        }
+
+        /** Get density that WM operates with. */
+        int getDensity() {
+            return overrideDensity != null ? overrideDensity : physicalDensity;
+        }
+
+        private ReportedDisplayMetrics(final String lines) throws Exception {
+            Matcher matcher = PHYSICAL_SIZE.matcher(lines);
+            assertTrue("Physical display size must be reported", matcher.find());
+            log(matcher.group());
+            physicalSize = new Size(
+                    Integer.parseInt(matcher.group(1)), Integer.parseInt(matcher.group(2)));
+
+            matcher = PHYSICAL_DENSITY.matcher(lines);
+            assertTrue("Physical display density must be reported", matcher.find());
+            log(matcher.group());
+            physicalDensity = Integer.parseInt(matcher.group(1));
+
+            matcher = OVERRIDE_SIZE.matcher(lines);
+            if (matcher.find()) {
+                log(matcher.group());
+                overrideSize = new Size(
+                        Integer.parseInt(matcher.group(1)), Integer.parseInt(matcher.group(2)));
+            } else {
+                overrideSize = null;
+            }
+
+            matcher = OVERRIDE_DENSITY.matcher(lines);
+            if (matcher.find()) {
+                log(matcher.group());
+                overrideDensity = Integer.parseInt(matcher.group(1));
+            } else {
+                overrideDensity = null;
+            }
+        }
+    }
+
+    protected class VirtualDisplaySession implements AutoCloseable {
+        private int mDensityDpi = CUSTOM_DENSITY_DPI;
+        private boolean mLaunchInSplitScreen = false;
+        private boolean mCanShowWithInsecureKeyguard = false;
+        private boolean mPublicDisplay = false;
+        private boolean mResizeDisplay = true;
+        private String mLaunchActivity = null;
+        private boolean mSimulateDisplay = false;
+        private boolean mMustBeCreated = true;
+
+        private boolean mVirtualDisplayCreated = false;
+        private final OverlayDisplayDevicesSession mOverlayDisplayDeviceSession =
+                new OverlayDisplayDevicesSession();
+
+        public VirtualDisplaySession setDensityDpi(int densityDpi) {
+            mDensityDpi = densityDpi;
+            return this;
+        }
+
+        public VirtualDisplaySession setLaunchInSplitScreen(boolean launchInSplitScreen) {
+            mLaunchInSplitScreen = launchInSplitScreen;
+            return this;
+        }
+
+        public VirtualDisplaySession setCanShowWithInsecureKeyguard(
+                boolean canShowWithInsecureKeyguard) {
+            mCanShowWithInsecureKeyguard = canShowWithInsecureKeyguard;
+            return this;
+        }
+
+        public VirtualDisplaySession setPublicDisplay(boolean publicDisplay) {
+            mPublicDisplay = publicDisplay;
+            return this;
+        }
+
+        public VirtualDisplaySession setResizeDisplay(boolean resizeDisplay) {
+            mResizeDisplay = resizeDisplay;
+            return this;
+        }
+
+        public VirtualDisplaySession setLaunchActivity(String launchActivity) {
+            mLaunchActivity = launchActivity;
+            return this;
+        }
+
+        public VirtualDisplaySession setSimulateDisplay(boolean simulateDisplay) {
+            mSimulateDisplay = simulateDisplay;
+            return this;
+        }
+
+        public VirtualDisplaySession setMustBeCreated(boolean mustBeCreated) {
+            mMustBeCreated = mustBeCreated;
+            return this;
+        }
+
+        @Nullable
+        public ActivityDisplay createDisplay() throws Exception {
+            return createDisplays(1).stream().findFirst().orElse(null);
+        }
+
+        @NonNull
+        public List<ActivityDisplay> createDisplays(int count) throws Exception {
+            if (mSimulateDisplay) {
+                return simulateDisplay();
+            } else {
+                return createVirtualDisplays(count);
+            }
+        }
+
+        @Override
+        public void close() throws Exception {
+            mOverlayDisplayDeviceSession.close();
+            if (mVirtualDisplayCreated) {
+                destroyVirtualDisplays();
+                mVirtualDisplayCreated = false;
+            }
+        }
+
+        /**
+         * Simulate new display.
+         * <pre>
+         * <code>mDensityDpi</code> provide custom density for the display.
+         * </pre>
+         * @return {@link ActivityDisplay} of newly created display.
+         */
+        private List<ActivityDisplay> simulateDisplay() throws Exception {
+            final List<ActivityDisplay> originalDs = getDisplaysStates();
+
+            // Create virtual display with custom density dpi.
+            mOverlayDisplayDeviceSession.set("1024x768/" + mDensityDpi);
+
+            return assertAndGetNewDisplays(1, originalDs);
+        }
+
+        /**
+         * Create new virtual display.
+         * <pre>
+         * <code>mDensityDpi</code> provide custom density for the display.
+         * <code>mLaunchInSplitScreen</code> start {@link VirtualDisplayActivity} to side from
+         *     {@link LaunchingActivity} on primary display.
+         * <code>mCanShowWithInsecureKeyguard</code>  allow showing content when device is
+         *     showing an insecure keyguard.
+         * <code>mMustBeCreated</code> should assert if the display was or wasn't created.
+         * <code>mPublicDisplay</code> make display public.
+         * <code>mResizeDisplay</code> should resize display when surface size changes.
+         * <code>LaunchActivity</code> should launch test activity immediately after display
+         *     creation.
+         * </pre>
+         * @param displayCount number of displays to be created.
+         * @return A list of {@link ActivityDisplay} that represent newly created displays.
+         * @throws Exception
+         */
+        private List<ActivityDisplay> createVirtualDisplays(int displayCount) throws Exception {
+            // Start an activity that is able to create virtual displays.
+            if (mLaunchInSplitScreen) {
+                getLaunchActivityBuilder()
+                        .setToSide(true)
+                        .setTargetActivityName(VIRTUAL_DISPLAY_ACTIVITY)
+                        .execute();
+            } else {
+                launchActivity(VIRTUAL_DISPLAY_ACTIVITY);
+            }
+            mAmWmState.computeState(false /* compareTaskAndStackBounds */,
+                    new WaitForValidActivityState(VIRTUAL_DISPLAY_ACTIVITY));
+            final List<ActivityDisplay> originalDS = getDisplaysStates();
+
+            // Create virtual display with custom density dpi.
+            final StringBuilder createVirtualDisplayCommand = new StringBuilder(
+                    getAmStartCmd(VIRTUAL_DISPLAY_ACTIVITY))
+                    .append(" -f 0x20000000")
+                    .append(" --es command create_display");
+            if (mDensityDpi != INVALID_DENSITY_DPI) {
+                createVirtualDisplayCommand
+                        .append(" --ei density_dpi ")
+                        .append(mDensityDpi);
+            }
+            createVirtualDisplayCommand.append(" --ei count ").append(displayCount)
+                    .append(" --ez can_show_with_insecure_keyguard ")
+                    .append(mCanShowWithInsecureKeyguard)
+                    .append(" --ez public_display ").append(mPublicDisplay)
+                    .append(" --ez resize_display ").append(mResizeDisplay);
+            if (mLaunchActivity != null) {
+                createVirtualDisplayCommand
+                        .append(" --es launch_target_activity ")
+                        .append(mLaunchActivity);
+            }
+            executeShellCommand(createVirtualDisplayCommand.toString());
+            mVirtualDisplayCreated = true;
+
+            return assertAndGetNewDisplays(mMustBeCreated ? displayCount : -1, originalDS);
+        }
+
+        /**
+         * Destroy existing virtual display.
+         */
+        void destroyVirtualDisplays() throws Exception {
+            final String destroyVirtualDisplayCommand = getAmStartCmd(VIRTUAL_DISPLAY_ACTIVITY)
+                    + " -f 0x20000000"
+                    + " --es command destroy_display";
+            executeShellCommand(destroyVirtualDisplayCommand);
+        }
+
+        /**
+         * Wait for desired number of displays to be created and get their properties.
+         * @param newDisplayCount expected display count, -1 if display should not be created.
+         * @param originalDS display states before creation of new display(s).
+         * @return list of new displays, empty list if no new display is created.
+         */
+        private List<ActivityDisplay> assertAndGetNewDisplays(int newDisplayCount,
+                List<ActivityDisplay> originalDS) throws Exception {
+            final int originalDisplayCount = originalDS.size();
+
+            // Wait for the display(s) to be created and get configurations.
+            final List<ActivityDisplay> ds = getDisplayStateAfterChange(
+                    originalDisplayCount + newDisplayCount);
+            if (newDisplayCount != -1) {
+                assertEquals("New virtual display(s) must be created",
+                        originalDisplayCount + newDisplayCount, ds.size());
+            } else {
+                assertEquals("New virtual display must not be created",
+                        originalDisplayCount, ds.size());
+                return Collections.emptyList();
+            }
+
+            // Find the newly added display(s).
+            final List<ActivityDisplay> newDisplays = findNewDisplayStates(originalDS, ds);
+            assertTrue("New virtual display must be created",
+                    newDisplayCount == newDisplays.size());
+
+            return newDisplays;
+        }
+    }
+
+    /** Helper class to save, set, and restore overlay_display_devices preference. */
+    private static class OverlayDisplayDevicesSession extends SettingsSession<String> {
+        OverlayDisplayDevicesSession() {
+            super(Settings.Global.getUriFor(Settings.Global.OVERLAY_DISPLAY_DEVICES),
+                    Settings.Global::getString,
+                    Settings.Global::putString);
+        }
+    }
+
+    /** Wait for provided number of displays and report their configurations. */
+    List<ActivityDisplay> getDisplayStateAfterChange(int expectedDisplayCount) {
+        List<ActivityDisplay> ds = getDisplaysStates();
+
+        int retriesLeft = 5;
+        while (!areDisplaysValid(ds, expectedDisplayCount) && retriesLeft-- > 0) {
+            log("***Waiting for the correct number of displays...");
+            try {
+                Thread.sleep(1000);
+            } catch (InterruptedException e) {
+                log(e.toString());
+            }
+            ds = getDisplaysStates();
+        }
+
+        return ds;
+    }
+
+    private boolean areDisplaysValid(List<ActivityDisplay> displays, int expectedDisplayCount) {
+        if (displays.size() != expectedDisplayCount) {
+            return false;
+        }
+        for (ActivityDisplay display : displays) {
+            if (display.mOverrideConfiguration.densityDpi == 0) {
+                return false;
+            }
+        }
+        return true;
+    }
+
+    /** Checks if the device supports multi-display. */
+    boolean supportsMultiDisplay() {
+        return hasDeviceFeature(FEATURE_ACTIVITIES_ON_SECONDARY_DISPLAYS);
+    }
+}
diff --git a/tests/framework/base/activitymanager/src/android/server/am/ActivityManagerDisplayTests.java b/tests/framework/base/activitymanager/src/android/server/am/ActivityManagerDisplayTests.java
new file mode 100644
index 0000000..d82c7c5
--- /dev/null
+++ b/tests/framework/base/activitymanager/src/android/server/am/ActivityManagerDisplayTests.java
@@ -0,0 +1,179 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License
+ */
+
+package android.server.am;
+
+import static android.server.am.ActivityAndWindowManagersState.DEFAULT_DISPLAY_ID;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assume.assumeFalse;
+
+import android.platform.test.annotations.Presubmit;
+import android.server.am.ActivityManagerState.ActivityDisplay;
+import android.util.Size;
+
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Test;
+
+import java.util.List;
+
+/**
+ * Build/Install/Run:
+ *     atest CtsActivityManagerDeviceTestCases:ActivityManagerDisplayTests
+ */
+public class ActivityManagerDisplayTests extends ActivityManagerDisplayTestBase {
+    private static final String TEST_ACTIVITY_NAME = "TestActivity";
+
+    /**
+     * Tests that the global configuration is equal to the default display's override configuration.
+     */
+    @Test
+    public void testDefaultDisplayOverrideConfiguration() throws Exception {
+        final List<ActivityDisplay> reportedDisplays = getDisplaysStates();
+        final ActivityDisplay primaryDisplay = getDisplayState(reportedDisplays,
+                DEFAULT_DISPLAY_ID);
+        assertEquals("Primary display's configuration should be equal to global configuration.",
+                primaryDisplay.mOverrideConfiguration, primaryDisplay.mFullConfiguration);
+        assertEquals("Primary display's configuration should be equal to global configuration.",
+                primaryDisplay.mOverrideConfiguration, primaryDisplay.mMergedOverrideConfiguration);
+    }
+
+    /**
+     * Tests that secondary display has override configuration set.
+     */
+    @Test
+    public void testCreateVirtualDisplayWithCustomConfig() throws Exception {
+        try (final VirtualDisplaySession virtualDisplaySession = new VirtualDisplaySession()) {
+            final ActivityDisplay newDisplay = virtualDisplaySession.createDisplay();
+
+            // Find the density of created display.
+            final int newDensityDpi = newDisplay.mFullConfiguration.densityDpi;
+            assertEquals(CUSTOM_DENSITY_DPI, newDensityDpi);
+        }
+    }
+
+    /**
+     * Tests that launch on secondary display is not permitted if device has the feature disabled.
+     * Activities requested to be launched on a secondary display in this case should land on the
+     * default display.
+     */
+    @Test
+    public void testMultiDisplayDisabled() throws Exception {
+        // Only check devices with the feature disabled.
+        assumeFalse(supportsMultiDisplay());
+
+        try (final VirtualDisplaySession virtualDisplaySession = new VirtualDisplaySession()) {
+            // Create new virtual display.
+            final ActivityDisplay newDisplay = virtualDisplaySession.createDisplay();
+
+            // Launch activity on new secondary display.
+            launchActivityOnDisplay(TEST_ACTIVITY_NAME, newDisplay.mId);
+            mAmWmState.computeState(new WaitForValidActivityState(TEST_ACTIVITY_NAME));
+
+            mAmWmState.assertFocusedActivity("Launched activity must be focused",
+                    TEST_ACTIVITY_NAME);
+
+            // Check that activity is on the right display.
+            final int frontStackId = mAmWmState.getAmState().getFrontStackId(DEFAULT_DISPLAY_ID);
+            final ActivityManagerState.ActivityStack frontStack =
+                    mAmWmState.getAmState().getStackById(frontStackId);
+            assertEquals("Launched activity must be resumed",
+                    getActivityComponentName(TEST_ACTIVITY_NAME), frontStack.mResumedActivity);
+            assertEquals("Front stack must be on the default display", DEFAULT_DISPLAY_ID,
+                    frontStack.mDisplayId);
+            mAmWmState.assertFocusedStack("Focus must be on the default display", frontStackId);
+        }
+    }
+
+    @Test
+    public void testCreateMultipleVirtualDisplays() throws Exception {
+        final List<ActivityDisplay> originalDs = getDisplaysStates();
+        try (final VirtualDisplaySession virtualDisplaySession = new VirtualDisplaySession()) {
+            // Create new virtual displays
+            virtualDisplaySession.createDisplays(3);
+            getDisplayStateAfterChange(originalDs.size() + 3);
+        }
+        getDisplayStateAfterChange(originalDs.size());
+    }
+
+    /**
+     * Test that display overrides apply correctly and won't be affected by display changes.
+     * This sets overrides to display size and density, initiates a display changed event by locking
+     * and unlocking the phone and verifies that overrides are kept.
+     */
+    @Presubmit
+    @Test
+    public void testForceDisplayMetrics() throws Exception {
+        launchHomeActivity();
+
+        try (final DisplayMetricsSession displayMetricsSession = new DisplayMetricsSession()) {
+            // Read initial sizes.
+            final ReportedDisplayMetrics originalDisplayMetrics =
+                    displayMetricsSession.getInitialDisplayMetrics();
+
+            // Apply new override values that don't match the physical metrics.
+            final Size overrideSize = new Size(
+                    (int) (originalDisplayMetrics.physicalSize.getWidth() * 1.5),
+                    (int) (originalDisplayMetrics.physicalSize.getHeight() * 1.5));
+            final Integer overrideDensity = (int) (originalDisplayMetrics.physicalDensity * 1.1);
+            displayMetricsSession.overrideDisplayMetrics(overrideSize, overrideDensity);
+
+            // Check if overrides applied correctly.
+            ReportedDisplayMetrics displayMetrics = displayMetricsSession.getDisplayMetrics();
+            assertEquals(overrideSize, displayMetrics.overrideSize);
+            assertEquals(overrideDensity, displayMetrics.overrideDensity);
+
+            // Lock and unlock device. This will cause a DISPLAY_CHANGED event to be triggered and
+            // might update the metrics.
+            sleepDevice();
+
+            wakeUpAndUnlockDevice();
+            mAmWmState.waitForHomeActivityVisible();
+
+            // Check if overrides are still applied.
+            displayMetrics = displayMetricsSession.getDisplayMetrics();
+            assertEquals(overrideSize, displayMetrics.overrideSize);
+            assertEquals(overrideDensity, displayMetrics.overrideDensity);
+        }
+    }
+
+    private static class DisplayMetricsSession implements AutoCloseable {
+
+        private final ReportedDisplayMetrics mInitialDisplayMetrics;
+
+        DisplayMetricsSession() throws Exception {
+            mInitialDisplayMetrics = ReportedDisplayMetrics.getDisplayMetrics();
+        }
+
+        ReportedDisplayMetrics getInitialDisplayMetrics() {
+            return mInitialDisplayMetrics;
+        }
+
+        ReportedDisplayMetrics getDisplayMetrics() throws Exception {
+            return ReportedDisplayMetrics.getDisplayMetrics();
+        }
+
+        void overrideDisplayMetrics(final Size size, final int density) {
+            mInitialDisplayMetrics.setDisplayMetrics(size, density);
+        }
+
+        @Override
+        public void close() throws Exception {
+            mInitialDisplayMetrics.restoreDisplayMetrics();
+        }
+    }
+}
diff --git a/tests/framework/base/activitymanager/src/android/server/am/ActivityManagerFreeformStackTests.java b/tests/framework/base/activitymanager/src/android/server/am/ActivityManagerFreeformStackTests.java
new file mode 100644
index 0000000..22d0934
--- /dev/null
+++ b/tests/framework/base/activitymanager/src/android/server/am/ActivityManagerFreeformStackTests.java
@@ -0,0 +1,136 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License
+ */
+
+package android.server.am;
+
+import static android.app.WindowConfiguration.ACTIVITY_TYPE_STANDARD;
+import static android.app.WindowConfiguration.WINDOWING_MODE_FREEFORM;
+import static org.junit.Assert.assertEquals;
+
+import android.graphics.Rect;
+import android.server.am.ActivityManagerState.ActivityStack;
+import android.server.am.ActivityManagerState.ActivityTask;
+
+import org.junit.Test;
+
+/**
+ * Build/Install/Run:
+ *     atest CtsActivityManagerDeviceTestCases:ActivityManagerFreeformStackTests
+ */
+public class ActivityManagerFreeformStackTests extends ActivityManagerTestBase {
+
+    private static final String TEST_ACTIVITY = "TestActivity";
+    private static final int TEST_TASK_OFFSET = 20;
+    private static final int TEST_TASK_OFFSET_2 = 100;
+    private static final int TEST_TASK_SIZE_1 = 900;
+    private static final int TEST_TASK_SIZE_2 = TEST_TASK_SIZE_1 * 2;
+    private static final int TEST_TASK_SIZE_DP_1 = 220;
+    private static final int TEST_TASK_SIZE_DP_2 = TEST_TASK_SIZE_DP_1 * 2;
+
+    // NOTE: Launching the FreeformActivity will automatically launch the TestActivity
+    // with bounds (0, 0, 900, 900)
+    private static final String FREEFORM_ACTIVITY = "FreeformActivity";
+    private static final String NON_RESIZEABLE_ACTIVITY = "NonResizeableActivity";
+    private static final String NO_RELAUNCH_ACTIVITY = "NoRelaunchActivity";
+
+    @Test
+    public void testFreeformWindowManagementSupport() throws Exception {
+
+        launchActivity(FREEFORM_ACTIVITY, WINDOWING_MODE_FREEFORM);
+
+        mAmWmState.computeState(FREEFORM_ACTIVITY, TEST_ACTIVITY);
+
+        if (!supportsFreeform()) {
+            mAmWmState.assertDoesNotContainStack("Must not contain freeform stack.",
+                    WINDOWING_MODE_FREEFORM, ACTIVITY_TYPE_STANDARD);
+            return;
+        }
+
+        mAmWmState.assertFrontStack("Freeform stack must be the front stack.",
+                WINDOWING_MODE_FREEFORM, ACTIVITY_TYPE_STANDARD);
+        mAmWmState.assertVisibility(FREEFORM_ACTIVITY, true);
+        mAmWmState.assertVisibility(TEST_ACTIVITY, true);
+        mAmWmState.assertFocusedActivity(
+                TEST_ACTIVITY + " must be focused Activity", TEST_ACTIVITY);
+        assertEquals(new Rect(0, 0, TEST_TASK_SIZE_1, TEST_TASK_SIZE_1),
+                mAmWmState.getAmState().getTaskByActivityName(TEST_ACTIVITY).getBounds());
+    }
+
+    @Test
+    public void testNonResizeableActivityHasFullDisplayBounds() throws Exception {
+        launchActivity(NON_RESIZEABLE_ACTIVITY, WINDOWING_MODE_FREEFORM);
+
+        mAmWmState.computeState(new WaitForValidActivityState.Builder(NON_RESIZEABLE_ACTIVITY).build());
+
+        final ActivityTask task =
+                mAmWmState.getAmState().getTaskByActivityName(NON_RESIZEABLE_ACTIVITY);
+        final ActivityStack stack = mAmWmState.getAmState().getStackById(task.mStackId);
+
+        if (task.isFullscreen()) {
+            // If the task is on the fullscreen stack, then we know that it will have bounds that
+            // fill the entire display.
+            return;
+        }
+
+        // If the task is not on the fullscreen stack, then compare the task bounds to the display
+        // bounds.
+        assertEquals(mAmWmState.getWmState().getDisplay(stack.mDisplayId).getDisplayRect(),
+                task.getBounds());
+    }
+
+    @Test
+    public void testActivityLifeCycleOnResizeFreeformTask() throws Exception {
+        launchActivity(TEST_ACTIVITY, WINDOWING_MODE_FREEFORM);
+        launchActivity(NO_RELAUNCH_ACTIVITY, WINDOWING_MODE_FREEFORM);
+
+        mAmWmState.computeState(new WaitForValidActivityState.Builder(TEST_ACTIVITY).build(),
+                new WaitForValidActivityState.Builder(NO_RELAUNCH_ACTIVITY).build());
+
+        if (!supportsFreeform()) {
+            mAmWmState.assertDoesNotContainStack("Must not contain freeform stack.",
+                    WINDOWING_MODE_FREEFORM, ACTIVITY_TYPE_STANDARD);
+            return;
+        }
+
+        final int displayId = mAmWmState.getAmState().getStandardStackByWindowingMode(
+                WINDOWING_MODE_FREEFORM).mDisplayId;
+        final int densityDpi =
+                mAmWmState.getWmState().getDisplay(displayId).getDpi();
+        final int testTaskSize1 =
+                ActivityAndWindowManagersState.dpToPx(TEST_TASK_SIZE_DP_1, densityDpi);
+        final int testTaskSize2 =
+                ActivityAndWindowManagersState.dpToPx(TEST_TASK_SIZE_DP_2, densityDpi);
+
+        resizeActivityTask(TEST_ACTIVITY,
+                TEST_TASK_OFFSET, TEST_TASK_OFFSET, testTaskSize1, testTaskSize2);
+        resizeActivityTask(NO_RELAUNCH_ACTIVITY,
+                TEST_TASK_OFFSET_2, TEST_TASK_OFFSET_2, testTaskSize1, testTaskSize2);
+
+        mAmWmState.computeState(new WaitForValidActivityState.Builder(TEST_ACTIVITY).build(),
+                new WaitForValidActivityState.Builder(NO_RELAUNCH_ACTIVITY).build());
+
+        final String logSeparator = clearLogcat();
+        resizeActivityTask(TEST_ACTIVITY,
+                TEST_TASK_OFFSET, TEST_TASK_OFFSET, testTaskSize2, testTaskSize1);
+        resizeActivityTask(NO_RELAUNCH_ACTIVITY,
+                TEST_TASK_OFFSET_2, TEST_TASK_OFFSET_2, testTaskSize2, testTaskSize1);
+        mAmWmState.computeState(new WaitForValidActivityState.Builder(TEST_ACTIVITY).build(),
+                new WaitForValidActivityState.Builder(NO_RELAUNCH_ACTIVITY).build());
+
+        assertActivityLifecycle(TEST_ACTIVITY, true /* relaunched */, logSeparator);
+        assertActivityLifecycle(NO_RELAUNCH_ACTIVITY, false /* relaunched */, logSeparator);
+    }
+}
diff --git a/tests/framework/base/activitymanager/src/android/server/am/ActivityManagerManifestLayoutTests.java b/tests/framework/base/activitymanager/src/android/server/am/ActivityManagerManifestLayoutTests.java
new file mode 100644
index 0000000..cb9be66
--- /dev/null
+++ b/tests/framework/base/activitymanager/src/android/server/am/ActivityManagerManifestLayoutTests.java
@@ -0,0 +1,192 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.server.am;
+
+import static android.app.WindowConfiguration.WINDOWING_MODE_FREEFORM;
+import static android.app.WindowConfiguration.WINDOWING_MODE_SPLIT_SCREEN_PRIMARY;
+import static android.server.am.ActivityAndWindowManagersState.dpToPx;
+
+import static org.junit.Assume.assumeTrue;
+
+import android.graphics.Rect;
+import android.server.am.WindowManagerState.Display;
+import android.server.am.WindowManagerState.WindowState;
+
+import junit.framework.Assert;
+
+import org.junit.Test;
+
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * Build/Install/Run:
+ *     atest CtsActivityManagerDeviceTestCases:ActivityManagerManifestLayoutTests
+ */
+public class ActivityManagerManifestLayoutTests extends ActivityManagerTestBase {
+
+    // Test parameters
+    private static final int DEFAULT_WIDTH_DP = 240;
+    private static final int DEFAULT_HEIGHT_DP = 160;
+    private static final float DEFAULT_WIDTH_FRACTION = 0.25f;
+    private static final float DEFAULT_HEIGHT_FRACTION = 0.35f;
+    private static final int MIN_WIDTH_DP = 100;
+    private static final int MIN_HEIGHT_DP = 80;
+
+    private static final int GRAVITY_VER_CENTER = 0x01;
+    private static final int GRAVITY_VER_TOP    = 0x02;
+    private static final int GRAVITY_VER_BOTTOM = 0x04;
+    private static final int GRAVITY_HOR_CENTER = 0x10;
+    private static final int GRAVITY_HOR_LEFT   = 0x20;
+    private static final int GRAVITY_HOR_RIGHT  = 0x40;
+
+    private List<WindowState> mTempWindowList = new ArrayList();
+    private Display mDisplay;
+    private WindowState mWindowState;
+
+    @Test
+    public void testGravityAndDefaultSizeTopLeft() throws Exception {
+        testLayout(GRAVITY_VER_TOP, GRAVITY_HOR_LEFT, false /*fraction*/);
+    }
+
+    @Test
+    public void testGravityAndDefaultSizeTopRight() throws Exception {
+        testLayout(GRAVITY_VER_TOP, GRAVITY_HOR_RIGHT, true /*fraction*/);
+    }
+
+    @Test
+    public void testGravityAndDefaultSizeBottomLeft() throws Exception {
+        testLayout(GRAVITY_VER_BOTTOM, GRAVITY_HOR_LEFT, true /*fraction*/);
+    }
+
+    @Test
+    public void testGravityAndDefaultSizeBottomRight() throws Exception {
+        testLayout(GRAVITY_VER_BOTTOM, GRAVITY_HOR_RIGHT, false /*fraction*/);
+    }
+
+    @Test
+    public void testMinimalSizeFreeform() throws Exception {
+        assumeTrue("Skipping test: no freeform support", supportsFreeform());
+
+        testMinimalSize(WINDOWING_MODE_FREEFORM);
+    }
+
+    @Test
+    public void testMinimalSizeDocked() throws Exception {
+        assumeTrue("Skipping test: no multi-window support", supportsSplitScreenMultiWindow());
+
+        testMinimalSize(WINDOWING_MODE_SPLIT_SCREEN_PRIMARY);
+    }
+
+    private void testMinimalSize(int windowingMode) throws Exception {
+        final String activityName = "BottomRightLayoutActivity";
+
+        // Issue command to resize to <0,0,1,1>. We expect the size to be floored at
+        // MIN_WIDTH_DPxMIN_HEIGHT_DP.
+        if (windowingMode == WINDOWING_MODE_FREEFORM) {
+            launchActivity(activityName, WINDOWING_MODE_FREEFORM);
+            resizeActivityTask(activityName, 0, 0, 1, 1);
+        } else { // stackId == DOCKED_STACK_ID
+            launchActivityInSplitScreenWithRecents(activityName);
+            resizeDockedStack(1, 1, 1, 1);
+        }
+        getDisplayAndWindowState(activityName, false);
+
+        final int minWidth = dpToPx(MIN_WIDTH_DP, mDisplay.getDpi());
+        final int minHeight = dpToPx(MIN_HEIGHT_DP, mDisplay.getDpi());
+        final Rect containingRect = mWindowState.getContainingFrame();
+
+        Assert.assertEquals("Min width is incorrect", minWidth, containingRect.width());
+        Assert.assertEquals("Min height is incorrect", minHeight, containingRect.height());
+    }
+
+    private void testLayout(
+            int vGravity, int hGravity, boolean fraction) throws Exception {
+        assumeTrue("Skipping test: no freeform support", supportsFreeform());
+
+        final String activityName = (vGravity == GRAVITY_VER_TOP ? "Top" : "Bottom")
+                + (hGravity == GRAVITY_HOR_LEFT ? "Left" : "Right") + "LayoutActivity";
+
+        // Launch in freeform stack
+        launchActivity(activityName, WINDOWING_MODE_FREEFORM);
+
+        getDisplayAndWindowState(activityName, true);
+
+        final Rect containingRect = mWindowState.getContainingFrame();
+        final Rect appRect = mDisplay.getAppRect();
+        final int expectedWidthPx, expectedHeightPx;
+        // Evaluate the expected window size in px. If we're using fraction dimensions,
+        // calculate the size based on the app rect size. Otherwise, convert the expected
+        // size in dp to px.
+        if (fraction) {
+            expectedWidthPx = (int) (appRect.width() * DEFAULT_WIDTH_FRACTION);
+            expectedHeightPx = (int) (appRect.height() * DEFAULT_HEIGHT_FRACTION);
+        } else {
+            final int densityDpi = mDisplay.getDpi();
+            expectedWidthPx = dpToPx(DEFAULT_WIDTH_DP, densityDpi);
+            expectedHeightPx = dpToPx(DEFAULT_HEIGHT_DP, densityDpi);
+        }
+
+        verifyFrameSizeAndPosition(
+                vGravity, hGravity, expectedWidthPx, expectedHeightPx, containingRect, appRect);
+    }
+
+    private void getDisplayAndWindowState(String activityName, boolean checkFocus)
+            throws Exception {
+        final String windowName = getWindowName(activityName);
+
+        mAmWmState.computeState(new WaitForValidActivityState.Builder(activityName).build());
+
+        if (checkFocus) {
+            mAmWmState.assertFocusedWindow("Test window must be the front window.", windowName);
+        } else {
+            mAmWmState.assertVisibility(activityName, true);
+        }
+
+        mAmWmState.getWmState().getMatchingVisibleWindowState(windowName, mTempWindowList);
+
+        Assert.assertEquals("Should have exactly one window state for the activity.",
+                1, mTempWindowList.size());
+
+        mWindowState = mTempWindowList.get(0);
+        Assert.assertNotNull("Should have a valid window", mWindowState);
+
+        mDisplay = mAmWmState.getWmState().getDisplay(mWindowState.getDisplayId());
+        Assert.assertNotNull("Should be on a display", mDisplay);
+    }
+
+    private void verifyFrameSizeAndPosition(
+            int vGravity, int hGravity, int expectedWidthPx, int expectedHeightPx,
+            Rect containingFrame, Rect parentFrame) {
+        Assert.assertEquals("Width is incorrect", expectedWidthPx, containingFrame.width());
+        Assert.assertEquals("Height is incorrect", expectedHeightPx, containingFrame.height());
+
+        if (vGravity == GRAVITY_VER_TOP) {
+            Assert.assertEquals("Should be on the top", parentFrame.top, containingFrame.top);
+        } else if (vGravity == GRAVITY_VER_BOTTOM) {
+            Assert.assertEquals("Should be on the bottom",
+                    parentFrame.bottom, containingFrame.bottom);
+        }
+
+        if (hGravity == GRAVITY_HOR_LEFT) {
+            Assert.assertEquals("Should be on the left", parentFrame.left, containingFrame.left);
+        } else if (hGravity == GRAVITY_HOR_RIGHT){
+            Assert.assertEquals("Should be on the right",
+                    parentFrame.right, containingFrame.right);
+        }
+    }
+}
diff --git a/tests/framework/base/activitymanager/src/android/server/am/ActivityManagerMultiDisplayTests.java b/tests/framework/base/activitymanager/src/android/server/am/ActivityManagerMultiDisplayTests.java
new file mode 100644
index 0000000..b79d4ce
--- /dev/null
+++ b/tests/framework/base/activitymanager/src/android/server/am/ActivityManagerMultiDisplayTests.java
@@ -0,0 +1,1800 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License
+ */
+
+package android.server.am;
+
+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.server.am.ActivityAndWindowManagersState.DEFAULT_DISPLAY_ID;
+import static android.server.am.ActivityManagerDisplayTestBase.ReportedDisplayMetrics
+        .getDisplayMetrics;
+import static android.server.am.ActivityManagerState.STATE_RESUMED;
+import static android.server.am.ActivityManagerState.STATE_STOPPED;
+import static android.server.am.StateLogger.logE;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertNull;
+import static org.junit.Assert.assertTrue;
+import static org.junit.Assert.fail;
+import static org.junit.Assume.assumeTrue;
+
+import android.content.ComponentName;
+import android.platform.test.annotations.Presubmit;
+import android.server.am.ActivityManagerState.ActivityDisplay;
+import android.server.am.displayservice.DisplayHelper;
+import android.support.annotation.Nullable;
+import android.support.test.filters.FlakyTest;
+
+import org.junit.Before;
+import org.junit.Test;
+
+import java.util.List;
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
+
+/**
+ * Build/Install/Run:
+ *     atest CtsActivityManagerDeviceTestCases:ActivityManagerMultiDisplayTests
+ */
+public class ActivityManagerMultiDisplayTests extends ActivityManagerDisplayTestBase {
+    private static final String TEST_ACTIVITY_NAME = "TestActivity";
+    private static final String VIRTUAL_DISPLAY_ACTIVITY = "VirtualDisplayActivity";
+    private static final String RESIZEABLE_ACTIVITY_NAME = "ResizeableActivity";
+    private static final String NON_RESIZEABLE_ACTIVITY_NAME = "NonResizeableActivity";
+    private static final String SHOW_WHEN_LOCKED_ATTR_ACTIVITY_NAME = "ShowWhenLockedAttrActivity";
+    private static final String SECOND_PACKAGE = "android.server.am.second";
+    private static final String THIRD_PACKAGE = "android.server.am.third";
+    private static final ComponentName SECOND_ACTIVITY = ComponentName.createRelative(
+            SECOND_PACKAGE, ".SecondActivity");
+    private static final ComponentName SECOND_NO_EMBEDDING_ACTIVITY = ComponentName.createRelative(
+            SECOND_PACKAGE, ".SecondActivityNoEmbedding");
+    private static final ComponentName LAUNCH_BROADCAST_RECEIVER = ComponentName.createRelative(
+            SECOND_PACKAGE, ".LaunchBroadcastReceiver");
+    /** See AndroidManifest.xml of appSecondUid. */
+    private static final String LAUNCH_BROADCAST_ACTION =
+            SECOND_PACKAGE + ".LAUNCH_BROADCAST_ACTION";
+    private static final ComponentName THIRD_ACTIVITY = ComponentName.createRelative(
+            THIRD_PACKAGE, ".ThirdActivity");
+
+    @Before
+    @Override
+    public void setUp() throws Exception {
+        super.setUp();
+
+        assumeTrue(supportsMultiDisplay());
+    }
+
+    /**
+     * Tests launching an activity on virtual display.
+     */
+    @Presubmit
+    @Test
+    public void testLaunchActivityOnSecondaryDisplay() throws Exception {
+        try (final VirtualDisplaySession virtualDisplaySession = new VirtualDisplaySession()) {
+            // Create new virtual display.
+            final ActivityDisplay newDisplay = virtualDisplaySession.createDisplay();
+
+            // Launch activity on new secondary display.
+            final String logSeparator = clearLogcat();
+            launchActivityOnDisplay(TEST_ACTIVITY_NAME, newDisplay.mId);
+            mAmWmState.computeState(new WaitForValidActivityState(TEST_ACTIVITY_NAME));
+
+            mAmWmState.assertFocusedActivity(
+                    "Activity launched on secondary display must be focused",
+                    TEST_ACTIVITY_NAME);
+
+            // 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",
+                    getActivityComponentName(TEST_ACTIVITY_NAME), frontStack.mResumedActivity);
+            mAmWmState.assertFocusedStack("Focus must be on secondary display", frontStackId);
+
+            // Check that activity config corresponds to display config.
+            final ReportedSizes reportedSizes = getLastReportedSizesForActivity(TEST_ACTIVITY_NAME,
+                    logSeparator);
+            assertEquals("Activity launched on secondary display must have proper configuration",
+                    CUSTOM_DENSITY_DPI, reportedSizes.densityDpi);
+        }
+    }
+
+    /**
+     * Tests launching a non-resizeable activity on virtual display. It should land on the
+     * default display.
+     */
+    @Test
+    public void testLaunchNonResizeableActivityOnSecondaryDisplay() throws Exception {
+        try (final VirtualDisplaySession virtualDisplaySession = new VirtualDisplaySession()) {
+            // Create new virtual display.
+            final ActivityDisplay newDisplay = virtualDisplaySession.createDisplay();
+
+            // Launch activity on new secondary display.
+            launchActivityOnDisplay(NON_RESIZEABLE_ACTIVITY_NAME, newDisplay.mId);
+            mAmWmState.computeState(new WaitForValidActivityState(NON_RESIZEABLE_ACTIVITY_NAME));
+
+            mAmWmState.assertFocusedActivity(
+                    "Activity launched on secondary display must be focused",
+                    NON_RESIZEABLE_ACTIVITY_NAME);
+
+            // Check that activity is on the right display.
+            final int frontStackId = mAmWmState.getAmState().getFrontStackId(DEFAULT_DISPLAY_ID);
+            final ActivityManagerState.ActivityStack frontStack =
+                    mAmWmState.getAmState().getStackById(frontStackId);
+            assertEquals("Launched activity must be on the primary display and resumed",
+                    getActivityComponentName(NON_RESIZEABLE_ACTIVITY_NAME),
+                    frontStack.mResumedActivity);
+            mAmWmState.assertFocusedStack("Focus must be on the primary display", frontStackId);
+        }
+    }
+
+    /**
+     * Tests launching a non-resizeable activity on virtual display while split-screen is active
+     * on the primary display. It should land on the primary display and dismiss docked stack.
+     */
+    @Test
+    public void testLaunchNonResizeableActivityWithSplitScreen() throws Exception {
+        assumeTrue(supportsSplitScreenMultiWindow());
+
+        // Start launching activity.
+        launchActivityInSplitScreenWithRecents(LAUNCHING_ACTIVITY);
+
+        try (final VirtualDisplaySession virtualDisplaySession = new VirtualDisplaySession()) {
+            // Create new virtual display.
+            final ActivityDisplay newDisplay = virtualDisplaySession.setLaunchInSplitScreen(true)
+                    .createDisplay();
+
+            // Launch activity on new secondary display.
+            launchActivityOnDisplay(NON_RESIZEABLE_ACTIVITY_NAME, newDisplay.mId);
+            mAmWmState.computeState(new WaitForValidActivityState(NON_RESIZEABLE_ACTIVITY_NAME));
+
+            mAmWmState.assertFocusedActivity(
+                    "Activity launched on secondary display must be focused",
+                    NON_RESIZEABLE_ACTIVITY_NAME);
+
+            // Check that activity is on the right display.
+            final int frontStackId = mAmWmState.getAmState().getFrontStackId(DEFAULT_DISPLAY_ID);
+            final ActivityManagerState.ActivityStack frontStack =
+                    mAmWmState.getAmState().getStackById(frontStackId);
+            assertEquals("Launched activity must be on the primary display and resumed",
+                    getActivityComponentName(NON_RESIZEABLE_ACTIVITY_NAME),
+                    frontStack.mResumedActivity);
+            mAmWmState.assertFocusedStack("Focus must be on the primary display", frontStackId);
+            mAmWmState.assertDoesNotContainStack("Must not contain docked stack.",
+                    WINDOWING_MODE_SPLIT_SCREEN_PRIMARY, ACTIVITY_TYPE_STANDARD);
+        }
+    }
+
+    /**
+     * Tests moving a non-resizeable activity to a virtual display. It should stay on the default
+     * display with no action performed.
+     */
+    @Test
+    public void testMoveNonResizeableActivityToSecondaryDisplay() throws Exception {
+        try (final VirtualDisplaySession virtualDisplaySession = new VirtualDisplaySession()) {
+            // Create new virtual display.
+            final ActivityDisplay newDisplay = virtualDisplaySession.createDisplay();
+            // Launch a non-resizeable activity on a primary display.
+            launchActivityInNewTask(NON_RESIZEABLE_ACTIVITY_NAME);
+            // Launch a resizeable activity on new secondary display to create a new stack there.
+            launchActivityOnDisplay(RESIZEABLE_ACTIVITY_NAME, newDisplay.mId);
+            final int externalFrontStackId = mAmWmState.getAmState()
+                    .getFrontStackId(newDisplay.mId);
+
+            // Try to move the non-resizeable activity to new secondary display.
+            moveActivityToStack(NON_RESIZEABLE_ACTIVITY_NAME, externalFrontStackId);
+            mAmWmState.computeState(new WaitForValidActivityState(NON_RESIZEABLE_ACTIVITY_NAME));
+
+            mAmWmState.assertFocusedActivity(
+                    "Activity launched on secondary display must be focused",
+                    RESIZEABLE_ACTIVITY_NAME);
+
+            // Check that activity is in the same stack
+            final int defaultFrontStackId = mAmWmState.getAmState().getFrontStackId(
+                    DEFAULT_DISPLAY_ID);
+            final ActivityManagerState.ActivityStack defaultFrontStack =
+                    mAmWmState.getAmState().getStackById(defaultFrontStackId);
+            assertEquals("Launched activity must be on the primary display and resumed",
+                    getActivityComponentName(NON_RESIZEABLE_ACTIVITY_NAME),
+                    defaultFrontStack.getTopTask().mRealActivity);
+            mAmWmState.assertFocusedStack("Focus must remain on the secondary display",
+                    externalFrontStackId);
+        }
+    }
+
+    /**
+     * Tests launching a non-resizeable activity on virtual display from activity there. It should
+     * land on the secondary display based on the resizeability of the root activity of the task.
+     */
+    @Test
+    public void testLaunchNonResizeableActivityFromSecondaryDisplaySameTask() throws Exception {
+        try (final VirtualDisplaySession virtualDisplaySession = new VirtualDisplaySession()) {
+            // Create new simulated display.
+            final ActivityDisplay newDisplay = virtualDisplaySession.setSimulateDisplay(true)
+                    .createDisplay();
+
+            // 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",
+                    getActivityComponentName(BROADCAST_RECEIVER_ACTIVITY),
+                    frontStack.mResumedActivity);
+            mAmWmState.assertFocusedStack("Focus must be on the secondary display", frontStackId);
+
+            // Launch non-resizeable activity from secondary display.
+            executeShellCommand("am broadcast -a trigger_broadcast --ez launch_activity true "
+                    + "--ez new_task true --es target_activity " + NON_RESIZEABLE_ACTIVITY_NAME);
+            mAmWmState.computeState(new WaitForValidActivityState(NON_RESIZEABLE_ACTIVITY_NAME));
+
+            // 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",
+                    getActivityComponentName(NON_RESIZEABLE_ACTIVITY_NAME),
+                    frontStack.mResumedActivity);
+            mAmWmState.assertFocusedStack("Focus must be on the primary display", frontStackId);
+        }
+    }
+
+    /**
+     * 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).
+     */
+    @Test
+    public void testLaunchNonResizeableActivityFromSecondaryDisplayNewTask() throws Exception {
+        try (final VirtualDisplaySession virtualDisplaySession = new VirtualDisplaySession()) {
+            // Create new virtual display.
+            final ActivityDisplay newDisplay = virtualDisplaySession.createDisplay();
+
+            // Launch activity on new secondary display.
+            launchActivityOnDisplay(LAUNCHING_ACTIVITY, newDisplay.mId);
+            mAmWmState.assertFocusedActivity(
+                    "Activity launched on secondary display must be focused",
+                    LAUNCHING_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",
+                    getActivityComponentName(LAUNCHING_ACTIVITY),
+                    frontStack.mResumedActivity);
+            mAmWmState.assertFocusedStack("Focus must be on the secondary display", frontStackId);
+
+            // Launch non-resizeable activity from secondary display.
+            getLaunchActivityBuilder().setTargetActivityName(NON_RESIZEABLE_ACTIVITY_NAME)
+                    .setNewTask(true).setMultipleTask(true).execute();
+
+            // Check that non-resizeable activity is on the primary display.
+            frontStackId = mAmWmState.getAmState().getFocusedStackId();
+            frontStack = mAmWmState.getAmState().getStackById(frontStackId);
+            assertFalse("Launched activity must be on a different display",
+                    newDisplay.mId == frontStack.mDisplayId);
+            assertEquals("Launched activity must be resumed",
+                    getActivityComponentName(NON_RESIZEABLE_ACTIVITY_NAME),
+                    frontStack.mResumedActivity);
+            mAmWmState.assertFocusedStack("Focus must be on a just launched activity",
+                    frontStackId);
+        }
+    }
+
+    /**
+     * Tests launching an activity on a virtual display without special permission must not be
+     * allowed.
+     */
+    @Test
+    public void testLaunchWithoutPermissionOnVirtualDisplay() throws Exception {
+        try (final VirtualDisplaySession virtualDisplaySession = new VirtualDisplaySession()) {
+            // Create new virtual display.
+            final ActivityDisplay newDisplay = virtualDisplaySession.createDisplay();
+
+            final String logSeparator = clearLogcat();
+
+            // Try to launch an activity and check it security exception was triggered.
+            final String broadcastTarget = "-a " + LAUNCH_BROADCAST_ACTION
+                    + " -p " + LAUNCH_BROADCAST_RECEIVER.getPackageName();
+            final String includeStoppedPackagesFlag = " -f 0x00000020";
+            executeShellCommand("am broadcast " + broadcastTarget
+                    + " --ez launch_activity true --es target_activity " + TEST_ACTIVITY_NAME
+                    + " --es package_name " + componentName
+                    + " --ei display_id " + newDisplay.mId
+                    + includeStoppedPackagesFlag);
+
+            assertSecurityException("LaunchBroadcastReceiver", logSeparator);
+
+            mAmWmState.computeState(new WaitForValidActivityState(TEST_ACTIVITY_NAME));
+            assertFalse("Restricted activity must not be launched",
+                    mAmWmState.getAmState().containsActivity(TEST_ACTIVITY_NAME));
+        }
+    }
+
+    /**
+     * Tests launching an activity on a virtual display without special permission must be allowed
+     * for activities with same UID.
+     */
+    @Test
+    public void testLaunchWithoutPermissionOnVirtualDisplayByOwner() throws Exception {
+        try (final VirtualDisplaySession virtualDisplaySession = new VirtualDisplaySession()) {
+            // Create new virtual display.
+            final ActivityDisplay newDisplay = virtualDisplaySession.createDisplay();
+
+            // Try to launch an activity and check it security exception was triggered.
+            final String broadcastTarget = "-a " + componentName + ".LAUNCH_BROADCAST_ACTION"
+                    + " -p " + componentName;
+            executeShellCommand("am broadcast " + broadcastTarget
+                    + " --ez launch_activity true --es target_activity " + TEST_ACTIVITY_NAME
+                    + " --es package_name " + componentName
+                    + " --ei display_id " + newDisplay.mId);
+
+            mAmWmState.waitForValidState(TEST_ACTIVITY_NAME);
+
+            final int externalFocusedStackId = mAmWmState.getAmState().getFocusedStackId();
+            final ActivityManagerState.ActivityStack focusedStack =
+                    mAmWmState.getAmState().getStackById(externalFocusedStackId);
+            assertEquals("Focused stack must be on secondary display", newDisplay.mId,
+                    focusedStack.mDisplayId);
+
+            mAmWmState.assertFocusedActivity("Focus must be on newly launched app",
+                    TEST_ACTIVITY_NAME);
+            assertEquals("Activity launched by owner must be on external display",
+                    externalFocusedStackId, mAmWmState.getAmState().getFocusedStackId());
+        }
+    }
+
+    /**
+     * Tests launching an activity on virtual display and then launching another activity via shell
+     * command and without specifying the display id - the second activity must appear on the
+     * primary display.
+     */
+    @Presubmit
+    @Test
+    public void testConsequentLaunchActivity() throws Exception {
+        try (final VirtualDisplaySession virtualDisplaySession = new VirtualDisplaySession()) {
+            // Create new virtual display.
+            final ActivityDisplay newDisplay = virtualDisplaySession.createDisplay();
+
+            // Launch activity on new secondary display.
+            launchActivityOnDisplay(TEST_ACTIVITY_NAME, newDisplay.mId);
+            mAmWmState.computeState(new WaitForValidActivityState(TEST_ACTIVITY_NAME));
+
+            mAmWmState.assertFocusedActivity(
+                    "Activity launched on secondary display must be focused",
+                    TEST_ACTIVITY_NAME);
+
+            // Launch second activity without specifying display.
+            launchActivity(LAUNCHING_ACTIVITY);
+            mAmWmState.computeState(new WaitForValidActivityState(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_ID);
+            final ActivityManagerState.ActivityStack frontStack
+                    = mAmWmState.getAmState().getStackById(frontStackId);
+            assertEquals("Launched activity must be resumed in front stack",
+                    getActivityComponentName(LAUNCHING_ACTIVITY), frontStack.mResumedActivity);
+            assertEquals("Front stack must be on primary display",
+                    DEFAULT_DISPLAY_ID, frontStack.mDisplayId);
+        }
+    }
+
+    /**
+     * Tests launching an activity on simulated display and then launching another activity from the
+     * first one - it must appear on the secondary display, because it was launched from there.
+     */
+    @FlakyTest(bugId = 71564456)
+    @Presubmit
+    @Test
+    public void testConsequentLaunchActivityFromSecondaryDisplay() throws Exception {
+        try (final VirtualDisplaySession virtualDisplaySession = new VirtualDisplaySession()) {
+            // Create new simulated display.
+            final ActivityDisplay newDisplay = virtualDisplaySession.setSimulateDisplay(true)
+                    .createDisplay();
+
+            // 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);
+
+            // Launch second activity from app on secondary display without specifying display id.
+            getLaunchActivityBuilder().setTargetActivityName(TEST_ACTIVITY_NAME).execute();
+            mAmWmState.computeState(new WaitForValidActivityState(TEST_ACTIVITY_NAME));
+
+            // Check that activity is launched in focused stack on external display.
+            mAmWmState.assertFocusedActivity("Launched activity must be focused",
+                    TEST_ACTIVITY_NAME);
+            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",
+                    getActivityComponentName(TEST_ACTIVITY_NAME), frontStack.mResumedActivity);
+        }
+    }
+
+    /**
+     * Tests launching an activity on virtual display and then launching another activity from the
+     * first one - it must appear on the secondary display, because it was launched from there.
+     */
+    @Test
+    public void testConsequentLaunchActivityFromVirtualDisplay() throws Exception {
+        try (final VirtualDisplaySession virtualDisplaySession = new VirtualDisplaySession()) {
+            // Create new virtual display.
+            final ActivityDisplay newDisplay = virtualDisplaySession.createDisplay();
+
+            // 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);
+
+            // Launch second activity from app on secondary display without specifying display id.
+            getLaunchActivityBuilder().setTargetActivityName(TEST_ACTIVITY_NAME).execute();
+            mAmWmState.computeState(TEST_ACTIVITY_NAME);
+
+            // Check that activity is launched in focused stack on external display.
+            mAmWmState.assertFocusedActivity("Launched activity must be focused",
+                    TEST_ACTIVITY_NAME);
+            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",
+                    getActivityComponentName(TEST_ACTIVITY_NAME), frontStack.mResumedActivity);
+        }
+    }
+
+    /**
+     * Tests launching an activity on virtual display and then launching another activity from the
+     * first one with specifying the target display - it must appear on the secondary display.
+     */
+    @Test
+    public void testConsequentLaunchActivityFromVirtualDisplayToTargetDisplay() throws Exception {
+        try (final VirtualDisplaySession virtualDisplaySession = new VirtualDisplaySession()) {
+            // Create new virtual display.
+            final ActivityDisplay newDisplay = virtualDisplaySession.createDisplay();
+
+            // 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);
+
+            // Launch second activity from app on secondary display specifying same display id.
+            getLaunchActivityBuilder()
+                    .setTargetActivity(SECOND_ACTIVITY)
+                    .setDisplayId(newDisplay.mId)
+                    .execute();
+            mAmWmState.computeState(TEST_ACTIVITY_NAME);
+
+            // 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",
+                    SECOND_ACTIVITY.flattenToShortString(), frontStack.mResumedActivity);
+
+            // Launch other activity with different uid and check if it has launched successfully.
+            getLaunchActivityBuilder()
+                    .setUseBroadcastReceiver(LAUNCH_BROADCAST_RECEIVER, LAUNCH_BROADCAST_ACTION)
+                    .setDisplayId(newDisplay.mId)
+                    .setTargetActivity(THIRD_ACTIVITY)
+                    .execute();
+            mAmWmState.waitForValidState(new WaitForValidActivityState(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",
+                    THIRD_ACTIVITY.flattenToShortString(), frontStack.mResumedActivity);
+        }
+    }
+
+    /**
+     * Tests launching an activity on virtual display and then launching another activity that
+     * doesn't allow embedding - it should fail with security exception.
+     */
+    @Test
+    public void testConsequentLaunchActivityFromVirtualDisplayNoEmbedding() throws Exception {
+        try (final VirtualDisplaySession virtualDisplaySession = new VirtualDisplaySession()) {
+            // Create new virtual display.
+            final ActivityDisplay newDisplay = virtualDisplaySession.createDisplay();
+
+            // 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);
+
+            final String logSeparator = clearLogcat();
+
+            // Launch second activity from app on secondary display specifying same display id.
+            getLaunchActivityBuilder()
+                    .setTargetActivity(SECOND_NO_EMBEDDING_ACTIVITY)
+                    .setDisplayId(newDisplay.mId)
+                    .execute();
+
+            assertSecurityException("ActivityLauncher", logSeparator);
+        }
+    }
+
+    /**
+     * Tests launching an activity to secondary display from activity on primary display.
+     */
+    @Test
+    public void testLaunchActivityFromAppToSecondaryDisplay() throws Exception {
+        // Start launching activity.
+        launchActivity(LAUNCHING_ACTIVITY);
+
+        try (final VirtualDisplaySession virtualDisplaySession = new VirtualDisplaySession()) {
+            // Create new simulated display.
+            final ActivityDisplay newDisplay = virtualDisplaySession.setSimulateDisplay(true)
+                    .createDisplay();
+
+            // Launch activity on secondary display from the app on primary display.
+            getLaunchActivityBuilder().setTargetActivityName(TEST_ACTIVITY_NAME)
+                    .setDisplayId(newDisplay.mId).execute();
+
+            // Check that activity is launched on external display.
+            mAmWmState.computeState(new WaitForValidActivityState(TEST_ACTIVITY_NAME));
+            mAmWmState.assertFocusedActivity(
+                    "Activity launched on secondary display must be focused",
+                    TEST_ACTIVITY_NAME);
+            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",
+                    getActivityComponentName(TEST_ACTIVITY_NAME), frontStack.mResumedActivity);
+        }
+    }
+
+    /**
+     * Tests launching activities on secondary and then on primary display to see if the stack
+     * visibility is not affected.
+     */
+    @Presubmit
+    @Test
+    public void testLaunchActivitiesAffectsVisibility() throws Exception {
+        // Start launching activity.
+        launchActivity(LAUNCHING_ACTIVITY);
+
+        try (final VirtualDisplaySession virtualDisplaySession = new VirtualDisplaySession()) {
+            // Create new virtual display.
+            final ActivityDisplay newDisplay = virtualDisplaySession.createDisplay();
+            mAmWmState.assertVisibility(VIRTUAL_DISPLAY_ACTIVITY, true /* visible */);
+
+            // Launch activity on new secondary display.
+            launchActivityOnDisplay(TEST_ACTIVITY_NAME, newDisplay.mId);
+            mAmWmState.assertVisibility(TEST_ACTIVITY_NAME, true /* visible */);
+            mAmWmState.assertVisibility(VIRTUAL_DISPLAY_ACTIVITY, true /* visible */);
+
+            // Launch activity on primary display and check if it doesn't affect activity on
+            // secondary display.
+            getLaunchActivityBuilder().setTargetActivityName(RESIZEABLE_ACTIVITY_NAME).execute();
+            mAmWmState.waitForValidState(RESIZEABLE_ACTIVITY_NAME);
+            mAmWmState.assertVisibility(TEST_ACTIVITY_NAME, true /* visible */);
+            mAmWmState.assertVisibility(RESIZEABLE_ACTIVITY_NAME, true /* visible */);
+        }
+    }
+
+    /**
+     * Test that move-task works when moving between displays.
+     */
+    @Presubmit
+    @Test
+    public void testMoveTaskBetweenDisplays() throws Exception {
+        try (final VirtualDisplaySession virtualDisplaySession = new VirtualDisplaySession()) {
+            // Create new virtual display.
+            final ActivityDisplay newDisplay = virtualDisplaySession.createDisplay();
+            mAmWmState.assertVisibility(VIRTUAL_DISPLAY_ACTIVITY, true /* visible */);
+            mAmWmState.assertFocusedActivity("Virtual display activity must be focused",
+                    VIRTUAL_DISPLAY_ACTIVITY);
+            final int defaultDisplayStackId = mAmWmState.getAmState().getFocusedStackId();
+            ActivityManagerState.ActivityStack focusedStack = mAmWmState.getAmState().getStackById(
+                    defaultDisplayStackId);
+            assertEquals("Focus must remain on primary display", DEFAULT_DISPLAY_ID,
+                    focusedStack.mDisplayId);
+
+            // Launch activity on new secondary display.
+            launchActivityOnDisplay(TEST_ACTIVITY_NAME, newDisplay.mId);
+            mAmWmState.assertFocusedActivity("Focus must be on secondary display",
+                    TEST_ACTIVITY_NAME);
+            int focusedStackId = mAmWmState.getAmState().getFocusedStackId();
+            focusedStack = mAmWmState.getAmState().getStackById(focusedStackId);
+            assertEquals("Focused stack must be on secondary display",
+                    newDisplay.mId, focusedStack.mDisplayId);
+
+            // Move activity from secondary display to primary.
+            moveActivityToStack(TEST_ACTIVITY_NAME, defaultDisplayStackId);
+            mAmWmState.waitForFocusedStack(defaultDisplayStackId);
+            mAmWmState.assertFocusedActivity("Focus must be on moved activity", TEST_ACTIVITY_NAME);
+            focusedStackId = mAmWmState.getAmState().getFocusedStackId();
+            focusedStack = mAmWmState.getAmState().getStackById(focusedStackId);
+            assertEquals("Focus must return to primary display", DEFAULT_DISPLAY_ID,
+                    focusedStack.mDisplayId);
+        }
+    }
+
+    /**
+     * Tests launching activities on secondary display and then removing it to see if stack focus
+     * is moved correctly.
+     * This version launches virtual display creator to fullscreen stack in split-screen.
+     */
+    @FlakyTest(bugId = 69573940)
+    @Presubmit
+    @Test
+    public void testStackFocusSwitchOnDisplayRemoved() throws Exception {
+        assumeTrue(supportsSplitScreenMultiWindow());
+
+        // Start launching activity into docked stack.
+        launchActivityInSplitScreenWithRecents(LAUNCHING_ACTIVITY);
+        mAmWmState.assertVisibility(LAUNCHING_ACTIVITY, true /* visible */);
+
+        tryCreatingAndRemovingDisplayWithActivity(true /* splitScreen */,
+                WINDOWING_MODE_SPLIT_SCREEN_SECONDARY);
+    }
+
+    /**
+     * Tests launching activities on secondary display and then removing it to see if stack focus
+     * is moved correctly.
+     * This version launches virtual display creator to docked stack in split-screen.
+     */
+    @Test
+    public void testStackFocusSwitchOnDisplayRemoved2() throws Exception {
+        assumeTrue(supportsSplitScreenMultiWindow());
+
+        // Setup split-screen.
+        launchActivitiesInSplitScreen(RESIZEABLE_ACTIVITY_NAME, LAUNCHING_ACTIVITY);
+        mAmWmState.assertVisibility(LAUNCHING_ACTIVITY, true /* visible */);
+
+        tryCreatingAndRemovingDisplayWithActivity(true /* splitScreen */,
+                WINDOWING_MODE_SPLIT_SCREEN_SECONDARY);
+    }
+
+    /**
+     * Tests launching activities on secondary display and then removing it to see if stack focus
+     * is moved correctly.
+     * This version works without split-screen.
+     */
+    @Test
+    public void testStackFocusSwitchOnDisplayRemoved3() throws Exception {
+        // Start an activity on default display to determine default stack.
+        launchActivity(BROADCAST_RECEIVER_ACTIVITY);
+        final int focusedStackWindowingMode = mAmWmState.getAmState().getFrontStackWindowingMode(
+                DEFAULT_DISPLAY_ID);
+        // Finish probing activity.
+        executeShellCommand(FINISH_ACTIVITY_BROADCAST);
+
+        tryCreatingAndRemovingDisplayWithActivity(false /* splitScreen */,
+                focusedStackWindowingMode);
+    }
+
+    /**
+     * Create a virtual display, launch a test activity there, destroy the display and check if test
+     * activity is moved to a stack on the default display.
+     */
+    private void tryCreatingAndRemovingDisplayWithActivity(boolean splitScreen, int windowingMode)
+            throws Exception {
+        String logSeparator;
+        try (final VirtualDisplaySession virtualDisplaySession = new VirtualDisplaySession()) {
+            // Create new virtual display.
+            final ActivityDisplay newDisplay = virtualDisplaySession
+                    .setPublicDisplay(true)
+                    .setLaunchInSplitScreen(splitScreen)
+                    .createDisplay();
+            mAmWmState.assertVisibility(VIRTUAL_DISPLAY_ACTIVITY, true /* visible */);
+            if (splitScreen) {
+                mAmWmState.assertVisibility(LAUNCHING_ACTIVITY, true /* visible */);
+            }
+
+            // Launch activity on new secondary display.
+            launchActivityOnDisplay(TEST_ACTIVITY_NAME, newDisplay.mId);
+            mAmWmState.assertFocusedActivity("Focus must be on secondary display",
+                    TEST_ACTIVITY_NAME);
+            final int frontStackId = mAmWmState.getAmState().getFrontStackId(newDisplay.mId);
+            mAmWmState.assertFocusedStack("Focus must be on secondary display", frontStackId);
+
+            // Destroy virtual display.
+            logSeparator = clearLogcat();
+        }
+
+        assertActivityLifecycle(TEST_ACTIVITY_NAME, false /* relaunched */, logSeparator);
+        mAmWmState.waitForValidState(TEST_ACTIVITY_NAME, windowingMode, ACTIVITY_TYPE_STANDARD);
+        mAmWmState.assertSanity();
+        mAmWmState.assertValidBounds(true /* compareTaskAndStackBounds */);
+
+        // Check if the focus is switched back to primary display.
+        mAmWmState.assertVisibility(TEST_ACTIVITY_NAME, true /* visible */);
+        mAmWmState.assertFocusedStack(
+                "Default stack on primary display must be focused after display removed",
+                windowingMode, ACTIVITY_TYPE_STANDARD);
+        mAmWmState.assertFocusedActivity(
+                "Focus must be switched back to activity on primary display",
+                TEST_ACTIVITY_NAME);
+    }
+
+    /**
+     * Tests launching activities on secondary display and then removing it to see if stack focus
+     * is moved correctly.
+     */
+    @Test
+    public void testStackFocusSwitchOnStackEmptied() throws Exception {
+        try (final VirtualDisplaySession virtualDisplaySession = new VirtualDisplaySession()) {
+            // Create new virtual display.
+            final ActivityDisplay newDisplay = virtualDisplaySession.createDisplay();
+            mAmWmState.assertVisibility(VIRTUAL_DISPLAY_ACTIVITY, true /* visible */);
+            final int focusedStackId = mAmWmState.getAmState().getFrontStackId(DEFAULT_DISPLAY_ID);
+
+            // Launch activity on new secondary display.
+            launchActivityOnDisplay(BROADCAST_RECEIVER_ACTIVITY, newDisplay.mId);
+            mAmWmState.assertFocusedActivity("Focus must be on secondary display",
+                    BROADCAST_RECEIVER_ACTIVITY);
+
+            // Lock the device, so that activity containers will be detached.
+            sleepDevice();
+
+            // Finish activity on secondary display.
+            executeShellCommand(FINISH_ACTIVITY_BROADCAST);
+
+            // Unlock and check if the focus is switched back to primary display.
+            wakeUpAndUnlockDevice();
+            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);
+        }
+    }
+
+    /**
+     * Tests that input events on the primary display take focus from the virtual display.
+     */
+    @Test
+    public void testStackFocusSwitchOnTouchEvent() throws Exception {
+        try (final VirtualDisplaySession virtualDisplaySession = new VirtualDisplaySession()) {
+            // Create new virtual display.
+            final ActivityDisplay newDisplay = virtualDisplaySession.createDisplay();
+
+            mAmWmState.computeState(new WaitForValidActivityState(VIRTUAL_DISPLAY_ACTIVITY));
+            mAmWmState.assertFocusedActivity("Focus must be switched back to primary display",
+                    VIRTUAL_DISPLAY_ACTIVITY);
+
+            launchActivityOnDisplay(TEST_ACTIVITY_NAME, newDisplay.mId);
+
+            mAmWmState.computeState(new WaitForValidActivityState(TEST_ACTIVITY_NAME));
+            mAmWmState.assertFocusedActivity(
+                    "Activity launched on secondary display must be focused",
+                    TEST_ACTIVITY_NAME);
+
+            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(new WaitForValidActivityState(VIRTUAL_DISPLAY_ACTIVITY));
+            mAmWmState.assertFocusedActivity("Focus must be switched back to primary display",
+                    VIRTUAL_DISPLAY_ACTIVITY);
+        }
+    }
+
+    /** Test that shell is allowed to launch on secondary displays. */
+    @Test
+    public void testPermissionLaunchFromShell() throws Exception {
+        try (final VirtualDisplaySession virtualDisplaySession = new VirtualDisplaySession()) {
+            // Create new virtual display.
+            final ActivityDisplay newDisplay = virtualDisplaySession.createDisplay();
+            mAmWmState.assertVisibility(VIRTUAL_DISPLAY_ACTIVITY, true /* visible */);
+            mAmWmState.assertFocusedActivity("Virtual display activity must be focused",
+                    VIRTUAL_DISPLAY_ACTIVITY);
+            final int defaultDisplayFocusedStackId = mAmWmState.getAmState().getFocusedStackId();
+            ActivityManagerState.ActivityStack focusedStack = mAmWmState.getAmState().getStackById(
+                    defaultDisplayFocusedStackId);
+            assertEquals("Focus must remain on primary display", DEFAULT_DISPLAY_ID,
+                    focusedStack.mDisplayId);
+
+            // Launch activity on new secondary display.
+            launchActivityOnDisplay(TEST_ACTIVITY_NAME, newDisplay.mId);
+            mAmWmState.assertFocusedActivity("Focus must be on secondary display",
+                    TEST_ACTIVITY_NAME);
+            final int externalFocusedStackId = mAmWmState.getAmState().getFocusedStackId();
+            focusedStack = mAmWmState.getAmState().getStackById(externalFocusedStackId);
+            assertEquals("Focused stack must be on secondary display", newDisplay.mId,
+                    focusedStack.mDisplayId);
+
+            // Launch other activity with different uid and check it is launched on dynamic stack on
+            // secondary display.
+            final String startCmd = "am start -n " + SECOND_ACTIVITY.flattenToShortString()
+                            + " --display " + newDisplay.mId;
+            executeShellCommand(startCmd);
+
+            mAmWmState.waitForValidState(new WaitForValidActivityState(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());
+        }
+    }
+
+    /** Test that launching from app that is on external display is allowed. */
+    @Test
+    public void testPermissionLaunchFromAppOnSecondary() throws Exception {
+        try (final VirtualDisplaySession virtualDisplaySession = new VirtualDisplaySession()) {
+            // Create new simulated display.
+            final ActivityDisplay newDisplay = virtualDisplaySession.setSimulateDisplay(true)
+                    .createDisplay();
+
+            // Launch activity with different uid on secondary display.
+            final String startCmd = "am start -n " + SECOND_ACTIVITY.flattenToShortString()
+                    + " --display " + newDisplay.mId;
+            executeShellCommand(startCmd);
+
+            mAmWmState.waitForValidState(new WaitForValidActivityState(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);
+
+            // Launch another activity with third different uid from app on secondary display and
+            // check it is launched on secondary display.
+            final String targetActivity =
+                    " --es target_activity " + THIRD_ACTIVITY.getShortClassName()
+                    + " --es package_name " + THIRD_ACTIVITY.getPackageName()
+                    + " --ei display_id " + newDisplay.mId;
+            final String includeStoppedPackagesFlag = " -f 0x00000020";
+            executeShellCommand("am broadcast -a " + LAUNCH_BROADCAST_ACTION
+                    + " -p " + LAUNCH_BROADCAST_RECEIVER.getPackageName()
+                    + targetActivity + includeStoppedPackagesFlag);
+
+            mAmWmState.waitForValidState(new WaitForValidActivityState(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());
+        }
+    }
+
+    /** Tests that an activity can launch an activity from a different UID into its own task. */
+    @Test
+    public void testPermissionLaunchMultiUidTask() throws Exception {
+        try (final VirtualDisplaySession virtualDisplaySession = new VirtualDisplaySession()) {
+            final ActivityDisplay newDisplay = virtualDisplaySession.setSimulateDisplay(true)
+                    .createDisplay();
+
+            launchActivityOnDisplay(LAUNCHING_ACTIVITY, newDisplay.mId);
+            mAmWmState.computeState(new WaitForValidActivityState(LAUNCHING_ACTIVITY));
+
+            // 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(
+                    frontStackId);
+            assertEquals("Activity launched on secondary display must be resumed",
+                    getActivityComponentName(LAUNCHING_ACTIVITY),
+                    frontStack.mResumedActivity);
+            mAmWmState.assertFocusedStack("Focus 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);
+            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());
+        }
+    }
+
+    /**
+     * Test that launching from display owner is allowed even when the the display owner
+     * doesn't have anything on the display.
+     */
+    @Test
+    public void testPermissionLaunchFromOwner() throws Exception {
+        try (final VirtualDisplaySession virtualDisplaySession = new VirtualDisplaySession()) {
+            // Create new virtual display.
+            final ActivityDisplay newDisplay = virtualDisplaySession.createDisplay();
+            mAmWmState.assertVisibility(VIRTUAL_DISPLAY_ACTIVITY, true /* visible */);
+            mAmWmState.assertFocusedActivity("Virtual display activity must be focused",
+                    VIRTUAL_DISPLAY_ACTIVITY);
+            final int defaultDisplayFocusedStackId = mAmWmState.getAmState().getFocusedStackId();
+            ActivityManagerState.ActivityStack focusedStack
+                    = mAmWmState.getAmState().getStackById(defaultDisplayFocusedStackId);
+            assertEquals("Focus must remain on primary display", DEFAULT_DISPLAY_ID,
+                    focusedStack.mDisplayId);
+
+            // Launch other activity with different uid on secondary display.
+            final String startCmd = "am start -n " + SECOND_ACTIVITY.flattenToShortString()
+                    + " --display " + newDisplay.mId;
+            executeShellCommand(startCmd);
+
+            mAmWmState.waitForValidState(new WaitForValidActivityState(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);
+
+            // Check that owner uid can launch its own activity on secondary display.
+            final String broadcastAction = componentName + ".LAUNCH_BROADCAST_ACTION";
+            executeShellCommand("am broadcast -a " + broadcastAction + " -p " + componentName
+                    + " --ez launch_activity true --ez new_task true --ez multiple_task true"
+                    + " --ei display_id " + newDisplay.mId);
+
+            mAmWmState.waitForValidState(TEST_ACTIVITY_NAME);
+            mAmWmState.assertFocusedActivity("Focus must be on newly launched app",
+                    TEST_ACTIVITY_NAME);
+            assertEquals("Activity launched by owner must be on external display",
+                    externalFocusedStackId, mAmWmState.getAmState().getFocusedStackId());
+        }
+    }
+
+    /**
+     * Test that launching from app that is not present on external display and doesn't own it to
+     * that external display is not allowed.
+     */
+    @Test
+    public void testPermissionLaunchFromDifferentApp() throws Exception {
+        try (final VirtualDisplaySession virtualDisplaySession = new VirtualDisplaySession()) {
+            // Create new virtual display.
+            final ActivityDisplay newDisplay = virtualDisplaySession.createDisplay();
+            mAmWmState.assertVisibility(VIRTUAL_DISPLAY_ACTIVITY, true /* visible */);
+            mAmWmState.assertFocusedActivity("Virtual display activity must be focused",
+                    VIRTUAL_DISPLAY_ACTIVITY);
+            final int defaultDisplayFocusedStackId = mAmWmState.getAmState().getFocusedStackId();
+            ActivityManagerState.ActivityStack focusedStack = mAmWmState.getAmState().getStackById(
+                    defaultDisplayFocusedStackId);
+            assertEquals("Focus must remain on primary display", DEFAULT_DISPLAY_ID,
+                    focusedStack.mDisplayId);
+
+            // Launch activity on new secondary display.
+            launchActivityOnDisplay(TEST_ACTIVITY_NAME, newDisplay.mId);
+            mAmWmState.assertFocusedActivity("Focus must be on secondary display",
+                    TEST_ACTIVITY_NAME);
+            final int externalFocusedStackId = mAmWmState.getAmState().getFocusedStackId();
+            focusedStack = mAmWmState.getAmState().getStackById(externalFocusedStackId);
+            assertEquals("Focused stack must be on secondary display", newDisplay.mId,
+                    focusedStack.mDisplayId);
+
+            final String logSeparator = clearLogcat();
+
+            // Launch other activity with different uid and check security exception is triggered.
+            final String includeStoppedPackagesFlag = " -f 0x00000020";
+            executeShellCommand("am broadcast -a " + LAUNCH_BROADCAST_ACTION
+                    + " -p " + LAUNCH_BROADCAST_RECEIVER.getPackageName()
+                    + " --ei display_id " + newDisplay.mId
+                    + includeStoppedPackagesFlag);
+
+            assertSecurityException("LaunchBroadcastReceiver", logSeparator);
+
+            mAmWmState.waitForValidState(false /* compareTaskAndStackBounds */, componentName,
+                    new WaitForValidActivityState(TEST_ACTIVITY_NAME));
+            mAmWmState.assertFocusedActivity("Focus must be on first activity", TEST_ACTIVITY_NAME);
+            assertEquals("Focused stack must be on secondary display's stack",
+                    externalFocusedStackId, mAmWmState.getAmState().getFocusedStackId());
+        }
+    }
+
+    private void assertSecurityException(String component, String logSeparator) throws Exception {
+        int tries = 0;
+        boolean match = false;
+        final Pattern pattern = Pattern.compile(".*SecurityException launching activity.*");
+        while (tries < 5 && !match) {
+            String[] logs = getDeviceLogsForComponent(component, logSeparator);
+            for (String line : logs) {
+                Matcher m = pattern.matcher(line);
+                if (m.matches()) {
+                    match = true;
+                    break;
+                }
+            }
+            tries++;
+            try {
+                Thread.sleep(500);
+            } catch (InterruptedException e) {
+            }
+        }
+
+        assertTrue("Expected exception not found", match);
+    }
+
+    /**
+     * Test that only private virtual display can show content with insecure keyguard.
+     */
+    @Test
+    public void testFlagShowWithInsecureKeyguardOnPublicVirtualDisplay() throws Exception {
+        try (final VirtualDisplaySession virtualDisplaySession = new VirtualDisplaySession()) {
+            // Try to create new show-with-insecure-keyguard public virtual display.
+            final ActivityDisplay newDisplay = virtualDisplaySession
+                    .setPublicDisplay(true)
+                    .setCanShowWithInsecureKeyguard(true)
+                    .setMustBeCreated(false)
+                    .createDisplay();
+
+            // Check that the display is not created.
+            assertNull(newDisplay);
+        }
+    }
+
+    /**
+     * Test that all activities that were on the private display are destroyed on display removal.
+     */
+    @FlakyTest(bugId = 63404575)
+    @Presubmit
+    @Test
+    public void testContentDestroyOnDisplayRemoved() throws Exception {
+        String logSeparator;
+        try (final VirtualDisplaySession virtualDisplaySession = new VirtualDisplaySession()) {
+            // Create new private virtual display.
+            final ActivityDisplay newDisplay = virtualDisplaySession.createDisplay();
+            mAmWmState.assertVisibility(VIRTUAL_DISPLAY_ACTIVITY, true /* visible */);
+
+            // Launch activities on new secondary display.
+            launchActivityOnDisplay(TEST_ACTIVITY_NAME, newDisplay.mId);
+            mAmWmState.assertVisibility(TEST_ACTIVITY_NAME, true /* visible */);
+            mAmWmState.assertFocusedActivity("Launched activity must be focused",
+                    TEST_ACTIVITY_NAME);
+            launchActivityOnDisplay(RESIZEABLE_ACTIVITY_NAME, newDisplay.mId);
+            mAmWmState.assertVisibility(RESIZEABLE_ACTIVITY_NAME, true /* visible */);
+            mAmWmState.assertFocusedActivity("Launched activity must be focused",
+                    RESIZEABLE_ACTIVITY_NAME);
+
+            // Destroy the display and check if activities are removed from system.
+            logSeparator = clearLogcat();
+        }
+
+        final String activityName1 = ActivityManagerTestBase.getActivityComponentName(
+                TEST_ACTIVITY_NAME);
+        final String activityName2 = ActivityManagerTestBase.getActivityComponentName(
+                RESIZEABLE_ACTIVITY_NAME);
+        final String windowName1 = ActivityManagerTestBase.getWindowName(TEST_ACTIVITY_NAME);
+        final String windowName2 = ActivityManagerTestBase.getWindowName(RESIZEABLE_ACTIVITY_NAME);
+        mAmWmState.waitForWithAmState(
+                (state) -> !state.containsActivity(activityName1)
+                        && !state.containsActivity(activityName2),
+                "Waiting for activity to be removed");
+        mAmWmState.waitForWithWmState(
+                (state) -> !state.containsWindow(windowName1)
+                        && !state.containsWindow(windowName2),
+                "Waiting for activity window to be gone");
+
+        // Check AM state.
+        assertFalse("Activity from removed display must be destroyed",
+                mAmWmState.getAmState().containsActivity(activityName1));
+        assertFalse("Activity from removed display must be destroyed",
+                mAmWmState.getAmState().containsActivity(activityName2));
+        // Check WM state.
+        assertFalse("Activity windows from removed display must be destroyed",
+                mAmWmState.getWmState().containsWindow(windowName1));
+        assertFalse("Activity windows from removed display must be destroyed",
+                mAmWmState.getWmState().containsWindow(windowName2));
+        // Check activity logs.
+        assertActivityDestroyed(TEST_ACTIVITY_NAME, logSeparator);
+        assertActivityDestroyed(RESIZEABLE_ACTIVITY_NAME, logSeparator);
+    }
+
+    /**
+     * Test that the update of display metrics updates all its content.
+     */
+    @Presubmit
+    @Test
+    public void testDisplayResize() throws Exception {
+        try (final VirtualDisplaySession virtualDisplaySession = new VirtualDisplaySession()) {
+            // Create new virtual display.
+            final ActivityDisplay newDisplay = virtualDisplaySession.createDisplay();
+            mAmWmState.assertVisibility(VIRTUAL_DISPLAY_ACTIVITY, true /* visible */);
+
+            // Launch a resizeable activity on new secondary display.
+            final String initialLogSeparator = clearLogcat();
+            launchActivityOnDisplay(RESIZEABLE_ACTIVITY_NAME, newDisplay.mId);
+            mAmWmState.assertVisibility(RESIZEABLE_ACTIVITY_NAME, true /* visible */);
+            mAmWmState.assertFocusedActivity("Launched activity must be focused",
+                    RESIZEABLE_ACTIVITY_NAME);
+
+            // Grab reported sizes and compute new with slight size change.
+            final ReportedSizes initialSize = getLastReportedSizesForActivity(
+                    RESIZEABLE_ACTIVITY_NAME,
+                    initialLogSeparator);
+
+            // Resize the docked stack, so that activity with virtual display will also be resized.
+            final String logSeparator = clearLogcat();
+            executeShellCommand(getResizeVirtualDisplayCommand());
+
+            mAmWmState.waitForWithAmState(amState -> {
+                try {
+                    return readConfigChangeNumber(RESIZEABLE_ACTIVITY_NAME, logSeparator) == 1
+                            && amState.hasActivityState(RESIZEABLE_ACTIVITY_NAME, STATE_RESUMED);
+                } catch (Exception e) {
+                    logE("Error waiting for valid state: " + e.getMessage());
+                    return false;
+                }
+            }, "Wait for the configuration change to happen and for activity to be resumed.");
+
+            mAmWmState.computeState(false /* compareTaskAndStackBounds */,
+                    new WaitForValidActivityState(RESIZEABLE_ACTIVITY_NAME),
+                    new WaitForValidActivityState(VIRTUAL_DISPLAY_ACTIVITY));
+            mAmWmState.assertVisibility(VIRTUAL_DISPLAY_ACTIVITY, true);
+            mAmWmState.assertVisibility(RESIZEABLE_ACTIVITY_NAME, true);
+
+            // Check if activity in virtual display was resized properly.
+            assertRelaunchOrConfigChanged(RESIZEABLE_ACTIVITY_NAME, 0 /* numRelaunch */,
+                    1 /* numConfigChange */, logSeparator);
+
+            final ReportedSizes updatedSize = getLastReportedSizesForActivity(
+                    RESIZEABLE_ACTIVITY_NAME,
+                    logSeparator);
+            assertTrue(updatedSize.widthDp <= initialSize.widthDp);
+            assertTrue(updatedSize.heightDp <= initialSize.heightDp);
+            assertTrue(updatedSize.displayWidth == initialSize.displayWidth / 2);
+            assertTrue(updatedSize.displayHeight == initialSize.displayHeight / 2);
+        }
+    }
+
+    /** Read the number of configuration changes sent to activity from logs. */
+    private int readConfigChangeNumber(String activityName, String logSeparator) throws Exception {
+        return (new ActivityLifecycleCounts(activityName, logSeparator)).mConfigurationChangedCount;
+    }
+
+    /**
+     * Tests that when an activity is launched with displayId specified and there is an existing
+     * matching task on some other display - that task will moved to the target display.
+     */
+    @Test
+    public void testMoveToDisplayOnLaunch() throws Exception {
+        // Launch activity with unique affinity, so it will the only one in its task.
+        launchActivity(LAUNCHING_ACTIVITY);
+
+        try (final VirtualDisplaySession virtualDisplaySession = new VirtualDisplaySession()) {
+            // Create new virtual display.
+            final ActivityDisplay newDisplay = virtualDisplaySession.createDisplay();
+            mAmWmState.assertVisibility(VIRTUAL_DISPLAY_ACTIVITY, true /* visible */);
+            // Launch something to that display so that a new stack is created. We need this to be
+            // able to compare task numbers in stacks later.
+            launchActivityOnDisplay(RESIZEABLE_ACTIVITY_NAME, newDisplay.mId);
+            mAmWmState.assertVisibility(RESIZEABLE_ACTIVITY_NAME, true /* visible */);
+
+            final int stackNum = mAmWmState.getAmState().getDisplay(DEFAULT_DISPLAY_ID)
+                    .mStacks.size();
+            final int frontStackId = mAmWmState.getAmState().getFrontStackId(newDisplay.mId);
+            final int taskNumOnSecondary = mAmWmState.getAmState().getStackById(frontStackId)
+                    .getTasks().size();
+
+            // Launch activity on new secondary display.
+            // Using custom command here, because normally we add flags
+            // {@link Intent#FLAG_ACTIVITY_NEW_TASK} and {@link Intent#FLAG_ACTIVITY_MULTIPLE_TASK}
+            // when launching on some specific display. We don't do it here as we want an existing
+            // task to be used.
+            final String launchCommand = "am start -n " + getActivityComponentName(
+                    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",
+                    getActivityComponentName(LAUNCHING_ACTIVITY), firstFrontStack.mResumedActivity);
+            mAmWmState.assertFocusedStack("Focus must be on secondary display", frontStackId);
+
+            // Check that task has moved from primary display to secondary.
+            final int stackNumFinal = mAmWmState.getAmState().getDisplay(DEFAULT_DISPLAY_ID)
+                    .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);
+        }
+    }
+
+    /**
+     * Tests that when an activity is launched with displayId specified and there is an existing
+     * matching task on some other display - that task will moved to the target display.
+     */
+    @Test
+    public void testMoveToEmptyDisplayOnLaunch() throws Exception {
+        // Launch activity with unique affinity, so it will the only one in its task.
+        launchActivity(LAUNCHING_ACTIVITY);
+
+        try (final VirtualDisplaySession virtualDisplaySession = new VirtualDisplaySession()) {
+            // Create new virtual display.
+            final ActivityDisplay newDisplay = virtualDisplaySession.createDisplay();
+            mAmWmState.assertVisibility(VIRTUAL_DISPLAY_ACTIVITY, true /* visible */);
+
+            final int stackNum = mAmWmState.getAmState().getDisplay(DEFAULT_DISPLAY_ID)
+                    .mStacks.size();
+
+            // Launch activity on new secondary display.
+            // Using custom command here, because normally we add flags
+            // {@link Intent#FLAG_ACTIVITY_NEW_TASK} and {@link Intent#FLAG_ACTIVITY_MULTIPLE_TASK}
+            // when launching on some specific display. We don't do it here as we want an existing
+            // task to be used.
+            final String launchCommand = "am start -n " + getActivityComponentName(
+                    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",
+                    getActivityComponentName(LAUNCHING_ACTIVITY), firstFrontStack.mResumedActivity);
+            mAmWmState.assertFocusedStack("Focus must be on secondary display", frontStackId);
+
+            // Check that task has moved from primary display to secondary.
+            final int stackNumFinal = mAmWmState.getAmState().getDisplay(DEFAULT_DISPLAY_ID)
+                    .mStacks.size();
+            assertEquals("Stack number in default stack must be decremented.", stackNum - 1,
+                    stackNumFinal);
+        }
+    }
+
+    /**
+     * Tests that when primary display is rotated secondary displays are not affected.
+     */
+    @Test
+    public void testRotationNotAffectingSecondaryScreen() throws Exception {
+        try (final VirtualDisplaySession virtualDisplaySession = new VirtualDisplaySession()) {
+            // Create new virtual display.
+            final ActivityDisplay newDisplay = virtualDisplaySession.setResizeDisplay(false)
+                    .createDisplay();
+            mAmWmState.assertVisibility(VIRTUAL_DISPLAY_ACTIVITY, true /* visible */);
+
+            // Launch activity on new secondary display.
+            String logSeparator = clearLogcat();
+            launchActivityOnDisplay(RESIZEABLE_ACTIVITY_NAME, newDisplay.mId);
+            mAmWmState.assertFocusedActivity("Focus must be on secondary display",
+                    RESIZEABLE_ACTIVITY_NAME);
+            final ReportedSizes initialSizes = getLastReportedSizesForActivity(
+                    RESIZEABLE_ACTIVITY_NAME, logSeparator);
+            assertNotNull("Test activity must have reported initial sizes on launch", initialSizes);
+
+            try (final RotationSession rotationSession = new RotationSession()) {
+                // Rotate primary display and check that activity on secondary display is not
+                // affected.
+
+                rotateAndCheckSameSizes(rotationSession, RESIZEABLE_ACTIVITY_NAME);
+
+                // Launch activity to secondary display when primary one is rotated.
+                final int initialRotation = mAmWmState.getWmState().getRotation();
+                rotationSession.set((initialRotation + 1) % 4);
+
+                logSeparator = clearLogcat();
+                launchActivityOnDisplay(TEST_ACTIVITY_NAME, newDisplay.mId);
+                mAmWmState.waitForActivityState(TEST_ACTIVITY_NAME, STATE_RESUMED);
+                mAmWmState.assertFocusedActivity("Focus must be on secondary display",
+                        TEST_ACTIVITY_NAME);
+                final ReportedSizes testActivitySizes = getLastReportedSizesForActivity(
+                        TEST_ACTIVITY_NAME, logSeparator);
+                assertEquals(
+                        "Sizes of secondary display must not change after rotation of primary "
+                                + "display",
+                        initialSizes, testActivitySizes);
+            }
+        }
+    }
+
+    private void rotateAndCheckSameSizes(RotationSession rotationSession, String activityName)
+            throws Exception {
+        for (int rotation = 3; rotation >= 0; --rotation) {
+            final String logSeparator = clearLogcat();
+            rotationSession.set(rotation);
+            final ReportedSizes rotatedSizes = getLastReportedSizesForActivity(activityName,
+                    logSeparator);
+            assertNull("Sizes must not change after rotation", rotatedSizes);
+        }
+    }
+
+    /**
+     * Tests that task affinity does affect what display an activity is launched on but that
+     * matching the task component root does.
+     */
+    @Test
+    public void testTaskMatchAcrossDisplays() throws Exception {
+        try (final VirtualDisplaySession virtualDisplaySession = new VirtualDisplaySession()) {
+            final ActivityDisplay newDisplay = virtualDisplaySession.createDisplay();
+
+            launchActivityOnDisplay(LAUNCHING_ACTIVITY, newDisplay.mId);
+            mAmWmState.computeState(new WaitForValidActivityState(LAUNCHING_ACTIVITY));
+
+            // Check that activity is on the secondary display.
+            final int frontStackId = mAmWmState.getAmState().getFrontStackId(newDisplay.mId);
+            final ActivityManagerState.ActivityStack firstFrontStack =
+                    mAmWmState.getAmState().getStackById(frontStackId);
+            assertEquals("Activity launched on secondary display must be resumed",
+                    getActivityComponentName(LAUNCHING_ACTIVITY), firstFrontStack.mResumedActivity);
+            mAmWmState.assertFocusedStack("Focus must be on secondary display", frontStackId);
+
+            executeShellCommand("am start -n " + getActivityComponentName(ALT_LAUNCHING_ACTIVITY));
+            mAmWmState.waitForValidState(false /* compareTaskAndStackBounds */, componentName,
+                    new WaitForValidActivityState(ALT_LAUNCHING_ACTIVITY));
+
+            // Check that second activity gets launched on the default display despite
+            // the affinity match on the secondary display.
+            final int defaultDisplayFrontStackId = mAmWmState.getAmState().getFrontStackId(
+                    DEFAULT_DISPLAY_ID);
+            final ActivityManagerState.ActivityStack defaultDisplayFrontStack =
+                    mAmWmState.getAmState().getStackById(defaultDisplayFrontStackId);
+            assertEquals("Activity launched on default display must be resumed",
+                    getActivityComponentName(ALT_LAUNCHING_ACTIVITY),
+                    defaultDisplayFrontStack.mResumedActivity);
+            mAmWmState.assertFocusedStack("Focus must be on primary display",
+                    defaultDisplayFrontStackId);
+
+            executeShellCommand("am start -n " + getActivityComponentName(LAUNCHING_ACTIVITY));
+            mAmWmState.waitForFocusedStack(frontStackId);
+
+            // 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
+                    = mAmWmState.getAmState().getStackById(frontStackId);
+            assertEquals("Activity launched on secondary display must be resumed",
+                    getActivityComponentName(LAUNCHING_ACTIVITY),
+                    secondFrontStack.mResumedActivity);
+            mAmWmState.assertFocusedStack("Focus must be on primary display", frontStackId);
+            assertEquals("Focused stack must only contain 1 task",
+                    1, secondFrontStack.getTasks().size());
+            assertEquals("Focused task must only contain 1 activity",
+                    1, secondFrontStack.getTasks().get(0).mActivities.size());
+        }
+    }
+
+    /**
+     * Tests that the task affinity search respects the launch display id.
+     */
+    @Test
+    public void testLaunchDisplayAffinityMatch() throws Exception {
+        try (final VirtualDisplaySession virtualDisplaySession = new VirtualDisplaySession()) {
+            final ActivityDisplay newDisplay = virtualDisplaySession.createDisplay();
+
+            launchActivityOnDisplay(LAUNCHING_ACTIVITY, newDisplay.mId);
+
+            // Check that activity is on the secondary display.
+            final int frontStackId = mAmWmState.getAmState().getFrontStackId(newDisplay.mId);
+            final ActivityManagerState.ActivityStack firstFrontStack =
+                    mAmWmState.getAmState().getStackById(frontStackId);
+            assertEquals("Activity launched on secondary display must be resumed",
+                    getActivityComponentName(LAUNCHING_ACTIVITY), firstFrontStack.mResumedActivity);
+            mAmWmState.assertFocusedStack("Focus must be on secondary display", frontStackId);
+
+            // We don't want FLAG_ACTIVITY_MULTIPLE_TASK, so we can't use launchActivityOnDisplay
+            executeShellCommand("am start -n "
+                    + getActivityComponentName(ALT_LAUNCHING_ACTIVITY)
+                    + " -f 0x10000000" // FLAG_ACTIVITY_NEW_TASK
+                    + " --display " + newDisplay.mId);
+            mAmWmState.computeState(new WaitForValidActivityState(ALT_LAUNCHING_ACTIVITY));
+
+            // Check that second activity gets launched into the affinity matching
+            // task on the secondary display
+            final int secondFrontStackId =
+                    mAmWmState.getAmState().getFrontStackId(newDisplay.mId);
+            final ActivityManagerState.ActivityStack secondFrontStack =
+                    mAmWmState.getAmState().getStackById(secondFrontStackId);
+            assertEquals("Activity launched on secondary display must be resumed",
+                    getActivityComponentName(ALT_LAUNCHING_ACTIVITY),
+                    secondFrontStack.mResumedActivity);
+            mAmWmState.assertFocusedStack("Focus must be on secondary display",
+                    secondFrontStackId);
+            assertEquals("Focused stack must only contain 1 task",
+                    1, secondFrontStack.getTasks().size());
+            assertEquals("Focused 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
+     * even if the focused stack is not on that activity's display.
+     */
+    @Test
+    public void testNewTaskSameDisplay() throws Exception {
+        try (final VirtualDisplaySession virtualDisplaySession = new VirtualDisplaySession()) {
+            final ActivityDisplay newDisplay = virtualDisplaySession.setSimulateDisplay(true)
+                    .createDisplay();
+
+            launchActivityOnDisplay(BROADCAST_RECEIVER_ACTIVITY, newDisplay.mId);
+            mAmWmState.computeState(new WaitForValidActivityState(BROADCAST_RECEIVER_ACTIVITY));
+
+            // Check that the first activity is launched onto the secondary display
+            final int frontStackId = mAmWmState.getAmState().getFrontStackId(newDisplay.mId);
+            final ActivityManagerState.ActivityStack firstFrontStack =
+                    mAmWmState.getAmState().getStackById(frontStackId);
+            assertEquals("Activity launched on secondary display must be resumed",
+                    getActivityComponentName(BROADCAST_RECEIVER_ACTIVITY),
+                    firstFrontStack.mResumedActivity);
+            mAmWmState.assertFocusedStack("Focus must be on secondary display", frontStackId);
+
+            executeShellCommand("am start -n " + getActivityComponentName(TEST_ACTIVITY_NAME));
+            mAmWmState.waitForValidState(false /* compareTaskAndStackBounds */, componentName,
+                    new WaitForValidActivityState(TEST_ACTIVITY_NAME));
+
+            // Check that the second activity is launched on the default display
+            final int focusedStackId = mAmWmState.getAmState().getFocusedStackId();
+            final ActivityManagerState.ActivityStack focusedStack
+                    = mAmWmState.getAmState().getStackById(focusedStackId);
+            assertEquals("Activity launched on default display must be resumed",
+                    getActivityComponentName(TEST_ACTIVITY_NAME), focusedStack.mResumedActivity);
+            assertEquals("Focus must be on primary display", DEFAULT_DISPLAY_ID,
+                    focusedStack.mDisplayId);
+
+            executeShellCommand("am broadcast -a trigger_broadcast --ez launch_activity true "
+                    + "--ez new_task true --es target_activity " + LAUNCHING_ACTIVITY);
+
+            // Check that the third activity ends up in a new task in the same stack as the
+            // first activity
+            mAmWmState.waitForValidState(false /* compareTaskAndStackBounds */, componentName,
+                    new WaitForValidActivityState(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",
+                    getActivityComponentName(LAUNCHING_ACTIVITY),
+                    secondFrontStack.mResumedActivity);
+            assertEquals("Secondary display must contain 2 tasks",
+                    2, secondFrontStack.getTasks().size());
+        }
+    }
+
+    /**
+     * Tests than an immediate launch after new display creation is handled correctly.
+     */
+    @Test
+    public void testImmediateLaunchOnNewDisplay() throws Exception {
+        try (final VirtualDisplaySession virtualDisplaySession = new VirtualDisplaySession()) {
+            // Create new virtual display and immediately launch an activity on it.
+            final ActivityDisplay newDisplay = virtualDisplaySession
+                    .setLaunchActivity(TEST_ACTIVITY_NAME)
+                    .createDisplay();
+
+            // Check that activity is launched and placed correctly.
+            mAmWmState.waitForActivityState(TEST_ACTIVITY_NAME, STATE_RESUMED);
+            mAmWmState.assertResumedActivity("Test activity must be launched on a new display",
+                    TEST_ACTIVITY_NAME);
+            final int frontStackId = mAmWmState.getAmState().getFrontStackId(newDisplay.mId);
+            final ActivityManagerState.ActivityStack firstFrontStack =
+                    mAmWmState.getAmState().getStackById(frontStackId);
+            assertEquals("Activity launched on secondary display must be resumed",
+                    getActivityComponentName(TEST_ACTIVITY_NAME), firstFrontStack.mResumedActivity);
+            mAmWmState.assertFocusedStack("Focus must be on secondary display", frontStackId);
+        }
+    }
+
+    /**
+     * Tests that turning the primary display off does not affect the activity running
+     * on an external secondary display.
+     */
+    @Test
+    public void testExternalDisplayActivityTurnPrimaryOff() throws Exception {
+        // Launch something on the primary display so we know there is a resumed activity there
+        launchActivity(RESIZEABLE_ACTIVITY_NAME);
+        waitAndAssertActivityResumed(RESIZEABLE_ACTIVITY_NAME, DEFAULT_DISPLAY_ID,
+                "Activity launched on primary display must be resumed");
+
+        try (final ExternalDisplaySession externalDisplaySession = new ExternalDisplaySession();
+             final PrimaryDisplayStateSession displayStateSession =
+                     new PrimaryDisplayStateSession()) {
+            final ActivityDisplay newDisplay =
+                    externalDisplaySession.createVirtualDisplay(true /* showContentWhenLocked */);
+
+            launchActivityOnDisplay(TEST_ACTIVITY_NAME, newDisplay.mId);
+
+            // Check that the activity is launched onto the external display
+            waitAndAssertActivityResumed(TEST_ACTIVITY_NAME, newDisplay.mId,
+                    "Activity launched on external display must be resumed");
+
+            displayStateSession.turnScreenOff();
+
+            // Wait for the fullscreen stack to start sleeping, and then make sure the
+            // test activity is still resumed.
+            waitAndAssertActivityStopped(RESIZEABLE_ACTIVITY_NAME,
+                    "Activity launched on primary display must be stopped after turning off");
+            waitAndAssertActivityResumed(TEST_ACTIVITY_NAME, newDisplay.mId,
+                    "Activity launched on external display must be resumed");
+        }
+    }
+
+    /**
+     * Tests that an activity can be launched on a secondary display while the primary
+     * display is off.
+     */
+    @Test
+    public void testLaunchExternalDisplayActivityWhilePrimaryOff() throws Exception {
+        // Launch something on the primary display so we know there is a resumed activity there
+        launchActivity(RESIZEABLE_ACTIVITY_NAME);
+        waitAndAssertActivityResumed(RESIZEABLE_ACTIVITY_NAME, DEFAULT_DISPLAY_ID,
+                "Activity launched on primary display must be resumed");
+
+        try (final PrimaryDisplayStateSession displayStateSession =
+                     new PrimaryDisplayStateSession();
+             final ExternalDisplaySession externalDisplaySession = new ExternalDisplaySession()) {
+            displayStateSession.turnScreenOff();
+
+            // Make sure there is no resumed activity when the primary display is off
+            waitAndAssertActivityStopped(RESIZEABLE_ACTIVITY_NAME,
+                    "Activity launched on primary display must be stopped after turning off");
+            assertEquals("Unexpected resumed activity",
+                    0, mAmWmState.getAmState().getResumedActivitiesCount());
+
+            final ActivityDisplay newDisplay =
+                    externalDisplaySession.createVirtualDisplay(true /* showContentWhenLocked */);
+
+            launchActivityOnDisplay(TEST_ACTIVITY_NAME, newDisplay.mId);
+
+            // Check that the test activity is resumed on the external display
+            waitAndAssertActivityResumed(TEST_ACTIVITY_NAME, newDisplay.mId,
+                    "Activity launched on external display must be resumed");
+        }
+    }
+
+    /**
+     * Tests that turning the secondary display off stops activities running on that display.
+     */
+    @Test
+    public void testExternalDisplayToggleState() throws Exception {
+        try (final ExternalDisplaySession externalDisplaySession = new ExternalDisplaySession()) {
+            final ActivityDisplay newDisplay =
+                    externalDisplaySession.createVirtualDisplay(false /* showContentWhenLocked */);
+
+            launchActivityOnDisplay(TEST_ACTIVITY_NAME, newDisplay.mId);
+
+            // Check that the test activity is resumed on the external display
+            waitAndAssertActivityResumed(TEST_ACTIVITY_NAME, newDisplay.mId,
+                    "Activity launched on external display must be resumed");
+
+            externalDisplaySession.turnDisplayOff();
+
+            // Check that turning off the external display stops the activity
+            waitAndAssertActivityStopped(TEST_ACTIVITY_NAME,
+                    "Activity launched on external display must be stopped after turning off");
+
+            externalDisplaySession.turnDisplayOn();
+
+            // Check that turning on the external display resumes the activity
+            waitAndAssertActivityResumed(TEST_ACTIVITY_NAME, newDisplay.mId,
+                    "Activity launched on external display must be resumed");
+        }
+    }
+
+    /**
+     * Tests that tapping on the primary display after showing the keyguard resumes the
+     * activity on the primary display.
+     */
+    @Test
+    public void testStackFocusSwitchOnTouchEventAfterKeyguard() throws Exception {
+        // Launch something on the primary display so we know there is a resumed activity there
+        launchActivity(RESIZEABLE_ACTIVITY_NAME);
+        waitAndAssertActivityResumed(RESIZEABLE_ACTIVITY_NAME, DEFAULT_DISPLAY_ID,
+                "Activity launched on primary display must be resumed");
+
+        sleepDevice();
+
+        // Make sure there is no resumed activity when the primary display is off
+        waitAndAssertActivityStopped(RESIZEABLE_ACTIVITY_NAME,
+                "Activity launched on primary display must be stopped after turning off");
+        assertEquals("Unexpected resumed activity",
+                0, mAmWmState.getAmState().getResumedActivitiesCount());
+
+        try (final ExternalDisplaySession externalDisplaySession = new ExternalDisplaySession()) {
+            final ActivityDisplay newDisplay =
+                    externalDisplaySession.createVirtualDisplay(true /* showContentWhenLocked */);
+
+            launchActivityOnDisplay(TEST_ACTIVITY_NAME, newDisplay.mId);
+
+            // Check that the test activity is resumed on the external display
+            waitAndAssertActivityResumed(TEST_ACTIVITY_NAME, newDisplay.mId,
+                    "Activity launched on external display must be resumed");
+
+            // Unlock the device and tap on the middle of the primary display
+            wakeUpDevice();
+            executeShellCommand("wm dismiss-keyguard");
+            mAmWmState.waitForKeyguardGone();
+            mAmWmState.waitForValidState(new WaitForValidActivityState(TEST_ACTIVITY_NAME));
+            final ReportedDisplayMetrics displayMetrics = getDisplayMetrics();
+            final int width = displayMetrics.getSize().getWidth();
+            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_NAME, DEFAULT_DISPLAY_ID,
+                    "Activity launched on primary display must be resumed");
+            assertEquals("Unexpected resumed activity",
+                    1, mAmWmState.getAmState().getResumedActivitiesCount());
+        }
+    }
+
+    private void waitAndAssertActivityResumed(String activityName, int displayId, String message)
+            throws Exception {
+        mAmWmState.waitForActivityState(activityName, STATE_RESUMED);
+
+        final String fullActivityName = getActivityComponentName(activityName);
+        assertEquals(message, fullActivityName, mAmWmState.getAmState().getResumedActivity());
+        final int frontStackId = mAmWmState.getAmState().getFrontStackId(displayId);
+        ActivityManagerState.ActivityStack firstFrontStack =
+                mAmWmState.getAmState().getStackById(frontStackId);
+        assertEquals(message, fullActivityName, firstFrontStack.mResumedActivity);
+        assertTrue(message,
+                mAmWmState.getAmState().hasActivityState(activityName, STATE_RESUMED));
+        mAmWmState.assertFocusedStack("Focus must be on external display", frontStackId);
+        mAmWmState.assertVisibility(activityName, true /* visible */);
+    }
+
+    private void waitAndAssertActivityStopped(String activityName, String message)
+            throws Exception {
+        mAmWmState.waitForActivityState(activityName, STATE_STOPPED);
+
+        assertTrue(message, mAmWmState.getAmState().hasActivityState(activityName,
+                STATE_STOPPED));
+    }
+
+    /**
+     * Tests that showWhenLocked works on a secondary display.
+     */
+    public void testSecondaryDisplayShowWhenLocked() throws Exception {
+        try (final ExternalDisplaySession externalDisplaySession = new ExternalDisplaySession()) {
+            setLockCredential();
+
+            launchActivity(TEST_ACTIVITY_NAME);
+
+            final ActivityDisplay newDisplay =
+                    externalDisplaySession.createVirtualDisplay(false /* showContentWhenLocked */);
+            launchActivityOnDisplay(SHOW_WHEN_LOCKED_ATTR_ACTIVITY_NAME, newDisplay.mId);
+
+            gotoKeyguard();
+            mAmWmState.waitForKeyguardShowingAndNotOccluded();
+
+            mAmWmState.waitForActivityState(TEST_ACTIVITY_NAME, STATE_STOPPED);
+            mAmWmState.waitForActivityState(SHOW_WHEN_LOCKED_ATTR_ACTIVITY_NAME, STATE_RESUMED);
+
+            mAmWmState.computeState(SHOW_WHEN_LOCKED_ATTR_ACTIVITY_NAME);
+            assertTrue("Expected resumed activity on secondary display", mAmWmState.getAmState()
+                    .hasActivityState(SHOW_WHEN_LOCKED_ATTR_ACTIVITY_NAME, STATE_RESUMED));
+        } finally {
+            tearDownLockCredentials();
+        }
+    }
+
+    /** Assert that component received onMovedToDisplay and onConfigurationChanged callbacks. */
+    private void assertMovedToDisplay(String componentName, String 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 static String getResizeVirtualDisplayCommand() {
+        return getAmStartCmd(VIRTUAL_DISPLAY_ACTIVITY) + " -f 0x20000000" +
+                " --es command resize_display";
+    }
+
+    private class ExternalDisplaySession implements AutoCloseable {
+
+        @Nullable
+        private DisplayHelper mExternalDisplayHelper;
+
+        /**
+         * Creates a private virtual display with the external and show with insecure
+         * keyguard flags set.
+         */
+        ActivityDisplay createVirtualDisplay(boolean showContentWhenLocked)
+                throws Exception {
+            final List<ActivityDisplay> originalDS = getDisplaysStates();
+            final int originalDisplayCount = originalDS.size();
+
+            mExternalDisplayHelper = new DisplayHelper();
+            mExternalDisplayHelper.createAndWaitForDisplay(true /* external */,
+                    showContentWhenLocked);
+
+            // Wait for the virtual display to be created and get configurations.
+            final List<ActivityDisplay> ds = getDisplayStateAfterChange(originalDisplayCount + 1);
+            assertEquals("New virtual display must be created", originalDisplayCount + 1,
+                    ds.size());
+
+            // Find the newly added display.
+            final List<ActivityDisplay> newDisplays = findNewDisplayStates(originalDS, ds);
+            return newDisplays.get(0);
+        }
+
+        void turnDisplayOff() {
+            if (mExternalDisplayHelper == null) {
+                new RuntimeException("No external display created");
+            }
+            mExternalDisplayHelper.turnDisplayOff();
+        }
+
+        void turnDisplayOn() {
+            if (mExternalDisplayHelper == null) {
+                new RuntimeException("No external display created");
+            }
+            mExternalDisplayHelper.turnDisplayOn();
+        }
+
+        @Override
+        public void close() throws Exception {
+            if (mExternalDisplayHelper != null) {
+                mExternalDisplayHelper.releaseDisplay();
+                mExternalDisplayHelper = null;
+            }
+        }
+    }
+
+    private static class PrimaryDisplayStateSession implements AutoCloseable {
+
+        void turnScreenOff() {
+            setPrimaryDisplayState(false);
+        }
+
+        @Override
+        public void close() throws Exception {
+            setPrimaryDisplayState(true);
+        }
+
+        /** Turns the primary display on/off by pressing the power key */
+        private void setPrimaryDisplayState(boolean wantOn) {
+            // Either KeyEvent.KEYCODE_WAKEUP or KeyEvent.KEYCODE_SLEEP
+            int keycode = wantOn ? 224 : 223;
+            executeShellCommand("input keyevent " + keycode);
+            DisplayHelper.waitForDefaultDisplayState(wantOn);
+        }
+    }
+}
diff --git a/tests/framework/base/activitymanager/src/android/server/am/ActivityManagerPinnedStackTests.java b/tests/framework/base/activitymanager/src/android/server/am/ActivityManagerPinnedStackTests.java
new file mode 100644
index 0000000..f5a6a22
--- /dev/null
+++ b/tests/framework/base/activitymanager/src/android/server/am/ActivityManagerPinnedStackTests.java
@@ -0,0 +1,1553 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License
+ */
+
+package android.server.am;
+
+import static android.app.ActivityManager.StackId.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;
+import static android.app.WindowConfiguration.WINDOWING_MODE_PINNED;
+import static android.app.WindowConfiguration.WINDOWING_MODE_UNDEFINED;
+import static android.server.am.ActivityAndWindowManagersState.DEFAULT_DISPLAY_ID;
+import static android.server.am.ActivityManagerState.STATE_DESTROYED;
+import static android.server.am.ActivityManagerState.STATE_RESUMED;
+import static android.server.am.ActivityManagerState.STATE_STOPPED;
+import static android.view.KeyEvent.KEYCODE_WINDOW;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertNotEquals;
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertTrue;
+import static org.junit.Assert.fail;
+import static org.junit.Assume.assumeTrue;
+
+import android.graphics.Rect;
+import android.platform.test.annotations.Presubmit;
+import android.provider.Settings;
+import android.server.am.ActivityManagerState.ActivityStack;
+import android.server.am.ActivityManagerState.ActivityTask;
+import android.server.am.settings.SettingsSession;
+import android.support.test.filters.FlakyTest;
+
+import org.junit.Test;
+
+import java.util.ArrayList;
+import java.util.List;
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
+
+/**
+ * Build/Install/Run:
+ *     atest CtsActivityManagerDeviceTestCases:ActivityManagerPinnedStackTests
+ */
+public class ActivityManagerPinnedStackTests extends ActivityManagerTestBase {
+    private static final String TEST_ACTIVITY = "TestActivity";
+    private static final String TEST_ACTIVITY_WITH_SAME_AFFINITY = "TestActivityWithSameAffinity";
+    private static final String TRANSLUCENT_TEST_ACTIVITY = "TranslucentTestActivity";
+    private static final String NON_RESIZEABLE_ACTIVITY = "NonResizeableActivity";
+    private static final String RESUME_WHILE_PAUSING_ACTIVITY = "ResumeWhilePausingActivity";
+    private static final String PIP_ACTIVITY = "PipActivity";
+    private static final String PIP_ACTIVITY2 = "PipActivity2";
+    private static final String PIP_ACTIVITY_WITH_SAME_AFFINITY = "PipActivityWithSameAffinity";
+    private static final String ALWAYS_FOCUSABLE_PIP_ACTIVITY = "AlwaysFocusablePipActivity";
+    private static final String LAUNCH_INTO_PINNED_STACK_PIP_ACTIVITY =
+            "LaunchIntoPinnedStackPipActivity";
+    private static final String LAUNCH_ENTER_PIP_ACTIVITY = "LaunchEnterPipActivity";
+    private static final String PIP_ON_STOP_ACTIVITY = "PipOnStopActivity";
+
+    private static final String EXTRA_FIXED_ORIENTATION = "fixed_orientation";
+    private static final String EXTRA_ENTER_PIP = "enter_pip";
+    private static final String EXTRA_ENTER_PIP_ASPECT_RATIO_NUMERATOR =
+            "enter_pip_aspect_ratio_numerator";
+    private static final String EXTRA_ENTER_PIP_ASPECT_RATIO_DENOMINATOR =
+            "enter_pip_aspect_ratio_denominator";
+    private static final String EXTRA_SET_ASPECT_RATIO_NUMERATOR = "set_aspect_ratio_numerator";
+    private static final String EXTRA_SET_ASPECT_RATIO_DENOMINATOR = "set_aspect_ratio_denominator";
+    private static final String EXTRA_SET_ASPECT_RATIO_WITH_DELAY_NUMERATOR =
+            "set_aspect_ratio_with_delay_numerator";
+    private static final String EXTRA_SET_ASPECT_RATIO_WITH_DELAY_DENOMINATOR =
+            "set_aspect_ratio_with_delay_denominator";
+    private static final String EXTRA_ENTER_PIP_ON_PAUSE = "enter_pip_on_pause";
+    private static final String EXTRA_TAP_TO_FINISH = "tap_to_finish";
+    private static final String EXTRA_START_ACTIVITY = "start_activity";
+    private static final String EXTRA_FINISH_SELF_ON_RESUME = "finish_self_on_resume";
+    private static final String EXTRA_REENTER_PIP_ON_EXIT = "reenter_pip_on_exit";
+    private static final String EXTRA_ASSERT_NO_ON_STOP_BEFORE_PIP = "assert_no_on_stop_before_pip";
+    private static final String EXTRA_ON_PAUSE_DELAY = "on_pause_delay";
+
+    private static final String PIP_ACTIVITY_ACTION_ENTER_PIP =
+            "android.server.am.PipActivity.enter_pip";
+    private static final String PIP_ACTIVITY_ACTION_MOVE_TO_BACK =
+            "android.server.am.PipActivity.move_to_back";
+    private static final String PIP_ACTIVITY_ACTION_EXPAND_PIP =
+            "android.server.am.PipActivity.expand_pip";
+    private static final String PIP_ACTIVITY_ACTION_SET_REQUESTED_ORIENTATION =
+            "android.server.am.PipActivity.set_requested_orientation";
+    private static final String PIP_ACTIVITY_ACTION_FINISH =
+            "android.server.am.PipActivity.finish";
+    private static final String TEST_ACTIVITY_ACTION_FINISH =
+            "android.server.am.TestActivity.finish_self";
+
+    private static final String APP_OPS_OP_ENTER_PICTURE_IN_PICTURE = "PICTURE_IN_PICTURE";
+    private static final int APP_OPS_MODE_ALLOWED = 0;
+    private static final int APP_OPS_MODE_IGNORED = 1;
+    private static final int APP_OPS_MODE_ERRORED = 2;
+
+    private static final int ROTATION_0 = 0;
+    private static final int ROTATION_90 = 1;
+    private static final int ROTATION_180 = 2;
+    private static final int ROTATION_270 = 3;
+
+    // Corresponds to ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE
+    private static final int ORIENTATION_LANDSCAPE = 0;
+    // Corresponds to ActivityInfo.SCREEN_ORIENTATION_PORTRAIT
+    private static final int ORIENTATION_PORTRAIT = 1;
+
+    private static final float FLOAT_COMPARE_EPSILON = 0.005f;
+
+    // Corresponds to com.android.internal.R.dimen.config_pictureInPictureMinAspectRatio
+    private static final int MIN_ASPECT_RATIO_NUMERATOR = 100;
+    private static final int MIN_ASPECT_RATIO_DENOMINATOR = 239;
+    private static final int BELOW_MIN_ASPECT_RATIO_DENOMINATOR = MIN_ASPECT_RATIO_DENOMINATOR + 1;
+    // Corresponds to com.android.internal.R.dimen.config_pictureInPictureMaxAspectRatio
+    private static final int MAX_ASPECT_RATIO_NUMERATOR = 239;
+    private static final int MAX_ASPECT_RATIO_DENOMINATOR = 100;
+    private static final int ABOVE_MAX_ASPECT_RATIO_NUMERATOR = MAX_ASPECT_RATIO_NUMERATOR + 1;
+
+    @Test
+    public void testMinimumDeviceSize() throws Exception {
+        assumeTrue(supportsPip());
+
+        mAmWmState.assertDeviceDefaultDisplaySize(
+                "Devices supporting picture-in-picture must be larger than the default minimum"
+                        + " task size");
+    }
+
+    @Presubmit
+    @Test
+    public void testEnterPictureInPictureMode() throws Exception {
+        pinnedStackTester(getAmStartCmd(PIP_ACTIVITY, EXTRA_ENTER_PIP, "true"),
+                PIP_ACTIVITY, PIP_ACTIVITY, false /* moveTopToPinnedStack */,
+                false /* isFocusable */);
+    }
+
+    @FlakyTest(bugId = 71444628)
+    @Presubmit
+    @Test
+    public void testMoveTopActivityToPinnedStack() throws Exception {
+        pinnedStackTester(getAmStartCmd(PIP_ACTIVITY), PIP_ACTIVITY, PIP_ACTIVITY,
+                true /* moveTopToPinnedStack */, false /* isFocusable */);
+    }
+
+    // This test is back-listed in cts-known-failures.xml.
+    @Test
+    public void testAlwaysFocusablePipActivity() throws Exception {
+        pinnedStackTester(getAmStartCmd(ALWAYS_FOCUSABLE_PIP_ACTIVITY),
+                ALWAYS_FOCUSABLE_PIP_ACTIVITY, ALWAYS_FOCUSABLE_PIP_ACTIVITY,
+                false /* moveTopToPinnedStack */, true /* isFocusable */);
+    }
+
+    // This test is back-listed in cts-known-failures.xml.
+    @Presubmit
+    @Test
+    public void testLaunchIntoPinnedStack() throws Exception {
+        pinnedStackTester(getAmStartCmd(LAUNCH_INTO_PINNED_STACK_PIP_ACTIVITY),
+                LAUNCH_INTO_PINNED_STACK_PIP_ACTIVITY, ALWAYS_FOCUSABLE_PIP_ACTIVITY,
+                false /* moveTopToPinnedStack */, true /* isFocusable */);
+    }
+
+    @Test
+    public void testNonTappablePipActivity() throws Exception {
+        assumeTrue(supportsPip());
+
+        // Launch the tap-to-finish activity at a specific place
+        launchActivity(PIP_ACTIVITY,
+                EXTRA_ENTER_PIP, "true",
+                EXTRA_TAP_TO_FINISH, "true");
+        mAmWmState.waitForValidState(PIP_ACTIVITY, WINDOWING_MODE_PINNED, ACTIVITY_TYPE_STANDARD);
+        mAmWmState.waitForAppTransitionIdle();
+        assertPinnedStackExists();
+
+        // Tap the screen at a known location in the pinned stack bounds, and ensure that it is
+        // not passed down to the top task
+        tapToFinishPip();
+        mAmWmState.computeState(false /* compareTaskAndStackBounds */,
+                new WaitForValidActivityState.Builder(PIP_ACTIVITY).build());
+        mAmWmState.assertVisibility(PIP_ACTIVITY, true);
+    }
+
+    @Test
+    public void testPinnedStackDefaultBounds() throws Exception {
+        assumeTrue(supportsPip());
+
+        // Launch a PIP activity
+        launchActivity(PIP_ACTIVITY, EXTRA_ENTER_PIP, "true");
+
+        try (final RotationSession rotationSession = new RotationSession()) {
+            rotationSession.set(ROTATION_0);
+
+            WindowManagerState wmState = mAmWmState.getWmState();
+            wmState.computeState();
+            Rect defaultPipBounds = wmState.getDefaultPinnedStackBounds();
+            Rect stableBounds = wmState.getStableBounds();
+            assertTrue(defaultPipBounds.width() > 0 && defaultPipBounds.height() > 0);
+            assertTrue(stableBounds.contains(defaultPipBounds));
+
+            rotationSession.set(ROTATION_90);
+            wmState = mAmWmState.getWmState();
+            wmState.computeState();
+            defaultPipBounds = wmState.getDefaultPinnedStackBounds();
+            stableBounds = wmState.getStableBounds();
+            assertTrue(defaultPipBounds.width() > 0 && defaultPipBounds.height() > 0);
+            assertTrue(stableBounds.contains(defaultPipBounds));
+        }
+    }
+
+    @Test
+    public void testPinnedStackMovementBounds() throws Exception {
+        assumeTrue(supportsPip());
+
+        // Launch a PIP activity
+        launchActivity(PIP_ACTIVITY, EXTRA_ENTER_PIP, "true");
+
+        try (final RotationSession rotationSession = new RotationSession()) {
+            rotationSession.set(ROTATION_0);
+            WindowManagerState wmState = mAmWmState.getWmState();
+            wmState.computeState();
+            Rect pipMovementBounds = wmState.getPinnedStackMomentBounds();
+            Rect stableBounds = wmState.getStableBounds();
+            assertTrue(pipMovementBounds.width() > 0 && pipMovementBounds.height() > 0);
+            assertTrue(stableBounds.contains(pipMovementBounds));
+
+            rotationSession.set(ROTATION_90);
+            wmState = mAmWmState.getWmState();
+            wmState.computeState();
+            pipMovementBounds = wmState.getPinnedStackMomentBounds();
+            stableBounds = wmState.getStableBounds();
+            assertTrue(pipMovementBounds.width() > 0 && pipMovementBounds.height() > 0);
+            assertTrue(stableBounds.contains(pipMovementBounds));
+        }
+    }
+
+    @Test
+    @FlakyTest // TODO: Reintroduce to presubmit once b/71508234 is resolved.
+    public void testPinnedStackOutOfBoundsInsetsNonNegative() throws Exception {
+        assumeTrue(supportsPip());
+
+        final WindowManagerState wmState = mAmWmState.getWmState();
+
+        // Launch an activity into the pinned stack
+        launchActivity(PIP_ACTIVITY, EXTRA_ENTER_PIP, "true", EXTRA_TAP_TO_FINISH, "true");
+        mAmWmState.waitForValidState(PIP_ACTIVITY, WINDOWING_MODE_PINNED, ACTIVITY_TYPE_STANDARD);
+        mAmWmState.waitForAppTransitionIdle();
+
+        // Get the display dimensions
+        WindowManagerState.WindowState windowState = getWindowState(PIP_ACTIVITY);
+        WindowManagerState.Display display = wmState.getDisplay(windowState.getDisplayId());
+        Rect displayRect = display.getDisplayRect();
+
+        // Move the pinned stack offscreen
+        final int stackId = getPinnedStack().mStackId;
+        final int top = 0;
+        final int left = displayRect.width() - 200;
+        resizeStack(stackId, left, top, left + 500, top + 500);
+
+        // Ensure that the surface insets are not negative
+        windowState = getWindowState(PIP_ACTIVITY);
+        Rect contentInsets = windowState.getContentInsets();
+        if (contentInsets != null) {
+            assertTrue(contentInsets.left >= 0 && contentInsets.top >= 0
+                    && contentInsets.width() >= 0 && contentInsets.height() >= 0);
+        }
+    }
+
+    @Test
+    public void testPinnedStackInBoundsAfterRotation() throws Exception {
+        assumeTrue(supportsPip());
+
+        // Launch an activity into the pinned stack
+        launchActivity(PIP_ACTIVITY,
+                EXTRA_ENTER_PIP, "true",
+                EXTRA_TAP_TO_FINISH, "true");
+        mAmWmState.waitForValidState(PIP_ACTIVITY, WINDOWING_MODE_PINNED, ACTIVITY_TYPE_STANDARD);
+
+        // Ensure that the PIP stack is fully visible in each orientation
+        try (final RotationSession rotationSession = new RotationSession()) {
+            rotationSession.set(ROTATION_0);
+            assertPinnedStackActivityIsInDisplayBounds(PIP_ACTIVITY);
+            rotationSession.set(ROTATION_90);
+            assertPinnedStackActivityIsInDisplayBounds(PIP_ACTIVITY);
+            rotationSession.set(ROTATION_180);
+            assertPinnedStackActivityIsInDisplayBounds(PIP_ACTIVITY);
+            rotationSession.set(ROTATION_270);
+            assertPinnedStackActivityIsInDisplayBounds(PIP_ACTIVITY);
+        }
+    }
+
+    @Test
+    public void testEnterPipToOtherOrientation() throws Exception {
+        assumeTrue(supportsPip());
+
+        // Launch a portrait only app on the fullscreen stack
+        launchActivity(TEST_ACTIVITY,
+                EXTRA_FIXED_ORIENTATION, String.valueOf(ORIENTATION_PORTRAIT));
+        // Launch the PiP activity fixed as landscape
+        launchActivity(PIP_ACTIVITY,
+                EXTRA_FIXED_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 " + PIP_ACTIVITY_ACTION_ENTER_PIP);
+        mAmWmState.waitForValidState(PIP_ACTIVITY, WINDOWING_MODE_PINNED, ACTIVITY_TYPE_STANDARD);
+        assertPinnedStackExists();
+        assertPinnedStackActivityIsInDisplayBounds(PIP_ACTIVITY);
+    }
+
+    @Test
+    public void testEnterPipAspectRatioMin() throws Exception {
+        testEnterPipAspectRatio(MIN_ASPECT_RATIO_NUMERATOR, MIN_ASPECT_RATIO_DENOMINATOR);
+    }
+
+    @Test
+    public void testEnterPipAspectRatioMax() throws Exception {
+        testEnterPipAspectRatio(MAX_ASPECT_RATIO_NUMERATOR, MAX_ASPECT_RATIO_DENOMINATOR);
+    }
+
+    private void testEnterPipAspectRatio(int num, int denom) throws Exception {
+        assumeTrue(supportsPip());
+
+        launchActivity(PIP_ACTIVITY,
+                EXTRA_ENTER_PIP, "true",
+                EXTRA_ENTER_PIP_ASPECT_RATIO_NUMERATOR, Integer.toString(num),
+                EXTRA_ENTER_PIP_ASPECT_RATIO_DENOMINATOR, Integer.toString(denom));
+        assertPinnedStackExists();
+
+        // Assert that we have entered PIP and that the aspect ratio is correct
+        Rect pinnedStackBounds = getPinnedStackBounds();
+        assertFloatEquals((float) pinnedStackBounds.width() / pinnedStackBounds.height(),
+                (float) num / denom);
+    }
+
+    @Test
+    public void testResizePipAspectRatioMin() throws Exception {
+        testResizePipAspectRatio(MIN_ASPECT_RATIO_NUMERATOR, MIN_ASPECT_RATIO_DENOMINATOR);
+    }
+
+    @Test
+    public void testResizePipAspectRatioMax() throws Exception {
+        testResizePipAspectRatio(MAX_ASPECT_RATIO_NUMERATOR, MAX_ASPECT_RATIO_DENOMINATOR);
+    }
+
+    private void testResizePipAspectRatio(int num, int denom) throws Exception {
+        assumeTrue(supportsPip());
+
+        launchActivity(PIP_ACTIVITY,
+                EXTRA_ENTER_PIP, "true",
+                EXTRA_SET_ASPECT_RATIO_NUMERATOR, Integer.toString(num),
+                EXTRA_SET_ASPECT_RATIO_DENOMINATOR, Integer.toString(denom));
+        assertPinnedStackExists();
+        waitForValidAspectRatio(num, denom);
+        Rect bounds = getPinnedStackBounds();
+        assertFloatEquals((float) bounds.width() / bounds.height(), (float) num / denom);
+    }
+
+    @Test
+    public void testEnterPipExtremeAspectRatioMin() throws Exception {
+        testEnterPipExtremeAspectRatio(MIN_ASPECT_RATIO_NUMERATOR,
+                BELOW_MIN_ASPECT_RATIO_DENOMINATOR);
+    }
+
+    @Test
+    public void testEnterPipExtremeAspectRatioMax() throws Exception {
+        testEnterPipExtremeAspectRatio(ABOVE_MAX_ASPECT_RATIO_NUMERATOR,
+                MAX_ASPECT_RATIO_DENOMINATOR);
+    }
+
+    private void testEnterPipExtremeAspectRatio(int num, int denom) throws Exception {
+        assumeTrue(supportsPip());
+
+        // Assert that we could not create a pinned stack with an extreme aspect ratio
+        launchActivity(PIP_ACTIVITY,
+                EXTRA_ENTER_PIP, "true",
+                EXTRA_ENTER_PIP_ASPECT_RATIO_NUMERATOR, Integer.toString(num),
+                EXTRA_ENTER_PIP_ASPECT_RATIO_DENOMINATOR, Integer.toString(denom));
+        assertPinnedStackDoesNotExist();
+    }
+
+    @Test
+    public void testSetPipExtremeAspectRatioMin() throws Exception {
+        testSetPipExtremeAspectRatio(MIN_ASPECT_RATIO_NUMERATOR,
+                BELOW_MIN_ASPECT_RATIO_DENOMINATOR);
+    }
+
+    @Test
+    public void testSetPipExtremeAspectRatioMax() throws Exception {
+        testSetPipExtremeAspectRatio(ABOVE_MAX_ASPECT_RATIO_NUMERATOR,
+                MAX_ASPECT_RATIO_DENOMINATOR);
+    }
+
+    private void testSetPipExtremeAspectRatio(int num, int denom) throws Exception {
+        assumeTrue(supportsPip());
+
+        // Try to resize the a normal pinned stack to an extreme aspect ratio and ensure that
+        // fails (the aspect ratio remains the same)
+        launchActivity(PIP_ACTIVITY,
+                EXTRA_ENTER_PIP, "true",
+                EXTRA_ENTER_PIP_ASPECT_RATIO_NUMERATOR,
+                        Integer.toString(MAX_ASPECT_RATIO_NUMERATOR),
+                EXTRA_ENTER_PIP_ASPECT_RATIO_DENOMINATOR,
+                        Integer.toString(MAX_ASPECT_RATIO_DENOMINATOR),
+                EXTRA_SET_ASPECT_RATIO_NUMERATOR, Integer.toString(num),
+                EXTRA_SET_ASPECT_RATIO_DENOMINATOR, Integer.toString(denom));
+        assertPinnedStackExists();
+        Rect pinnedStackBounds = getPinnedStackBounds();
+        assertFloatEquals((float) pinnedStackBounds.width() / pinnedStackBounds.height(),
+                (float) MAX_ASPECT_RATIO_NUMERATOR / MAX_ASPECT_RATIO_DENOMINATOR);
+    }
+
+    @Test
+    public void testDisallowPipLaunchFromStoppedActivity() throws Exception {
+        assumeTrue(supportsPip());
+
+        // Launch the bottom pip activity
+        launchActivity(PIP_ON_STOP_ACTIVITY);
+        mAmWmState.waitForValidState(PIP_ACTIVITY, WINDOWING_MODE_PINNED, ACTIVITY_TYPE_STANDARD);
+
+        // Wait for the bottom pip activity to be stopped
+        mAmWmState.waitForActivityState(PIP_ON_STOP_ACTIVITY, STATE_STOPPED);
+
+        // Assert that there is no pinned stack (that enterPictureInPicture() failed)
+        assertPinnedStackDoesNotExist();
+    }
+
+    @Test
+    public void testAutoEnterPictureInPicture() throws Exception {
+        assumeTrue(supportsPip());
+
+        // Launch a test activity so that we're not over home
+        launchActivity(TEST_ACTIVITY);
+
+        // Launch the PIP activity on pause
+        launchActivity(PIP_ACTIVITY, EXTRA_ENTER_PIP_ON_PAUSE, "true");
+        assertPinnedStackDoesNotExist();
+
+        // Go home and ensure that there is a pinned stack
+        launchHomeActivity();
+        assertPinnedStackExists();
+    }
+
+    @Test
+    public void testAutoEnterPictureInPictureLaunchActivity() throws Exception {
+        assumeTrue(supportsPip());
+
+        // Launch a test activity so that we're not over home
+        launchActivity(TEST_ACTIVITY);
+
+        // Launch the PIP activity on pause, and have it start another activity on
+        // top of itself.  Wait for the new activity to be visible and ensure that the pinned stack
+        // was not created in the process
+        launchActivity(PIP_ACTIVITY,
+                EXTRA_ENTER_PIP_ON_PAUSE, "true",
+                EXTRA_START_ACTIVITY, getActivityComponentName(NON_RESIZEABLE_ACTIVITY));
+        mAmWmState.computeState(false /* compareTaskAndStackBounds */,
+                new WaitForValidActivityState.Builder(NON_RESIZEABLE_ACTIVITY).build());
+        assertPinnedStackDoesNotExist();
+
+        // Go home while the pip activity is open and ensure the previous activity is not PIPed
+        launchHomeActivity();
+        assertPinnedStackDoesNotExist();
+    }
+
+    @Test
+    public void testAutoEnterPictureInPictureFinish() throws Exception {
+        assumeTrue(supportsPip());
+
+        // Launch a test activity so that we're not over home
+        launchActivity(TEST_ACTIVITY);
+
+        // Launch the PIP activity on pause, and set it to finish itself after
+        // some period.  Wait for the previous activity to be visible, and ensure that the pinned
+        // stack was not created in the process
+        launchActivity(PIP_ACTIVITY,
+                EXTRA_ENTER_PIP_ON_PAUSE, "true",
+                EXTRA_FINISH_SELF_ON_RESUME, "true");
+        assertPinnedStackDoesNotExist();
+    }
+
+    @Presubmit
+    @Test
+    public void testAutoEnterPictureInPictureAspectRatio() throws Exception {
+        assumeTrue(supportsPip());
+
+        // Launch the PIP activity on pause, and set the aspect ratio
+        launchActivity(PIP_ACTIVITY,
+                EXTRA_ENTER_PIP_ON_PAUSE, "true",
+                EXTRA_SET_ASPECT_RATIO_NUMERATOR, Integer.toString(MAX_ASPECT_RATIO_NUMERATOR),
+                EXTRA_SET_ASPECT_RATIO_DENOMINATOR, Integer.toString(MAX_ASPECT_RATIO_DENOMINATOR));
+
+        // Go home while the pip activity is open to trigger auto-PIP
+        launchHomeActivity();
+        assertPinnedStackExists();
+
+        waitForValidAspectRatio(MAX_ASPECT_RATIO_NUMERATOR, MAX_ASPECT_RATIO_DENOMINATOR);
+        Rect bounds = getPinnedStackBounds();
+        assertFloatEquals((float) bounds.width() / bounds.height(),
+                (float) MAX_ASPECT_RATIO_NUMERATOR / MAX_ASPECT_RATIO_DENOMINATOR);
+    }
+
+    @Presubmit
+    @Test
+    public void testAutoEnterPictureInPictureOverPip() throws Exception {
+        assumeTrue(supportsPip());
+
+        // Launch another PIP activity
+        launchActivity(LAUNCH_INTO_PINNED_STACK_PIP_ACTIVITY);
+        mAmWmState.waitForValidState(PIP_ACTIVITY, WINDOWING_MODE_PINNED, ACTIVITY_TYPE_STANDARD);
+        assertPinnedStackExists();
+
+        // Launch the PIP activity on pause
+        launchActivity(PIP_ACTIVITY, EXTRA_ENTER_PIP_ON_PAUSE, "true");
+
+        // Go home while the PIP activity is open to trigger auto-enter PIP
+        launchHomeActivity();
+        assertPinnedStackExists();
+
+        // Ensure that auto-enter pip failed and that the resumed activity in the pinned stack is
+        // still the first activity
+        final ActivityStack pinnedStack = getPinnedStack();
+        assertTrue(pinnedStack.getTasks().size() == 1);
+        assertTrue(pinnedStack.getTasks().get(0).mRealActivity.equals(getActivityComponentName(
+                ALWAYS_FOCUSABLE_PIP_ACTIVITY)));
+    }
+
+    @Presubmit
+    @Test
+    public void testDisallowMultipleTasksInPinnedStack() throws Exception {
+        assumeTrue(supportsPip());
+
+        // Launch a test activity so that we have multiple fullscreen tasks
+        launchActivity(TEST_ACTIVITY);
+
+        // Launch first PIP activity
+        launchActivity(PIP_ACTIVITY, EXTRA_ENTER_PIP, "true");
+
+        // Launch second PIP activity
+        launchActivity(PIP_ACTIVITY2, EXTRA_ENTER_PIP, "true");
+
+        final ActivityStack pinnedStack = getPinnedStack();
+        assertEquals(1, pinnedStack.getTasks().size());
+        assertTrue(mAmWmState.getAmState().containsActivityInWindowingMode(
+                PIP_ACTIVITY2, WINDOWING_MODE_PINNED));
+        assertTrue(mAmWmState.getAmState().containsActivityInWindowingMode(
+                PIP_ACTIVITY, WINDOWING_MODE_FULLSCREEN));
+    }
+
+    @Test
+    public void testPipUnPipOverHome() throws Exception {
+        assumeTrue(supportsPip());
+
+        // Go home
+        launchHomeActivity();
+        // Launch an auto pip activity
+        launchActivity(PIP_ACTIVITY, EXTRA_ENTER_PIP, "true", EXTRA_REENTER_PIP_ON_EXIT, "true");
+        assertPinnedStackExists();
+
+        // Relaunch the activity to fullscreen to trigger the activity to exit and re-enter pip
+        launchActivity(PIP_ACTIVITY);
+        mAmWmState.waitForWithAmState((amState) -> {
+            return amState.getFrontStackWindowingMode(DEFAULT_DISPLAY_ID)
+                    == WINDOWING_MODE_FULLSCREEN;
+        }, "Waiting for PIP to exit to fullscreen");
+        mAmWmState.waitForWithAmState((amState) -> {
+            return amState.getFrontStackWindowingMode(DEFAULT_DISPLAY_ID) == WINDOWING_MODE_PINNED;
+        }, "Waiting to re-enter PIP");
+        mAmWmState.assertHomeActivityVisible(true);
+    }
+
+    @Test
+    public void testPipUnPipOverApp() throws Exception {
+        assumeTrue(supportsPip());
+
+        // Launch a test activity so that we're not over home
+        launchActivity(TEST_ACTIVITY);
+
+        // Launch an auto pip activity
+        launchActivity(PIP_ACTIVITY,
+                EXTRA_ENTER_PIP, "true",
+                EXTRA_REENTER_PIP_ON_EXIT, "true");
+        assertPinnedStackExists();
+
+        // Relaunch the activity to fullscreen to trigger the activity to exit and re-enter pip
+        launchActivity(PIP_ACTIVITY);
+        mAmWmState.waitForWithAmState((amState) -> {
+            return amState.getFrontStackWindowingMode(DEFAULT_DISPLAY_ID)
+                    == WINDOWING_MODE_FULLSCREEN;
+        }, "Waiting for PIP to exit to fullscreen");
+        mAmWmState.waitForWithAmState((amState) -> {
+            return amState.getFrontStackWindowingMode(DEFAULT_DISPLAY_ID) == WINDOWING_MODE_PINNED;
+        }, "Waiting to re-enter PIP");
+        mAmWmState.assertVisibility(TEST_ACTIVITY, true);
+    }
+
+    @Presubmit
+    @Test
+    public void testRemovePipWithNoFullscreenStack() throws Exception {
+        assumeTrue(supportsPip());
+
+        // Start with a clean slate, remove all the stacks but home
+        removeStacksWithActivityTypes(ALL_ACTIVITY_TYPE_BUT_HOME);
+
+        // Launch a pip activity
+        launchActivity(PIP_ACTIVITY, EXTRA_ENTER_PIP, "true");
+        assertPinnedStackExists();
+
+        // Remove the stack and ensure that the task is now in the fullscreen stack (when no
+        // fullscreen stack existed before)
+        removeStacksInWindowingModes(WINDOWING_MODE_PINNED);
+        assertPinnedStackStateOnMoveToFullscreen(PIP_ACTIVITY,
+                WINDOWING_MODE_UNDEFINED, ACTIVITY_TYPE_HOME);
+    }
+
+    @Presubmit
+    @Test
+    public void testRemovePipWithVisibleFullscreenStack() throws Exception {
+        assumeTrue(supportsPip());
+
+        // Launch a fullscreen activity, and a pip activity over that
+        launchActivity(TEST_ACTIVITY);
+        launchActivity(PIP_ACTIVITY, EXTRA_ENTER_PIP, "true");
+        assertPinnedStackExists();
+
+        // Remove the stack and ensure that the task is placed in the fullscreen stack, behind the
+        // top fullscreen activity
+        removeStacksInWindowingModes(WINDOWING_MODE_PINNED);
+        assertPinnedStackStateOnMoveToFullscreen(PIP_ACTIVITY,
+                WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_STANDARD);
+    }
+
+    @FlakyTest(bugId = 70746098)
+    @Presubmit
+    @Test
+    public void testRemovePipWithHiddenFullscreenStack() throws Exception {
+        assumeTrue(supportsPip());
+
+        // Launch a fullscreen activity, return home and while the fullscreen stack is hidden,
+        // launch a pip activity over home
+        launchActivity(TEST_ACTIVITY);
+        launchHomeActivity();
+        launchActivity(PIP_ACTIVITY, EXTRA_ENTER_PIP, "true");
+        assertPinnedStackExists();
+
+        // 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
+        removeStacksInWindowingModes(WINDOWING_MODE_PINNED);
+        assertPinnedStackStateOnMoveToFullscreen(PIP_ACTIVITY,
+                WINDOWING_MODE_UNDEFINED, ACTIVITY_TYPE_HOME);
+    }
+
+    @Test
+    public void testMovePipToBackWithNoFullscreenStack() throws Exception {
+        assumeTrue(supportsPip());
+
+        // Start with a clean slate, remove all the stacks but home
+        removeStacksWithActivityTypes(ALL_ACTIVITY_TYPE_BUT_HOME);
+
+        // Launch a pip activity
+        launchActivity(PIP_ACTIVITY, EXTRA_ENTER_PIP, "true");
+        assertPinnedStackExists();
+
+        // Remove the stack and ensure that the task is now in the fullscreen stack (when no
+        // fullscreen stack existed before)
+        executeShellCommand("am broadcast -a " + PIP_ACTIVITY_ACTION_MOVE_TO_BACK);
+        assertPinnedStackStateOnMoveToFullscreen(PIP_ACTIVITY,
+                WINDOWING_MODE_UNDEFINED, ACTIVITY_TYPE_HOME);
+    }
+
+    @FlakyTest(bugId = 70906499)
+    @Presubmit
+    @Test
+    public void testMovePipToBackWithVisibleFullscreenStack() throws Exception {
+        assumeTrue(supportsPip());
+
+        // Launch a fullscreen activity, and a pip activity over that
+        launchActivity(TEST_ACTIVITY);
+        launchActivity(PIP_ACTIVITY, EXTRA_ENTER_PIP, "true");
+        assertPinnedStackExists();
+
+        // Remove the stack and ensure that the task is placed in the fullscreen stack, behind the
+        // top fullscreen activity
+        executeShellCommand("am broadcast -a " + PIP_ACTIVITY_ACTION_MOVE_TO_BACK);
+        assertPinnedStackStateOnMoveToFullscreen(PIP_ACTIVITY,
+                WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_STANDARD);
+    }
+
+    @FlakyTest(bugId = 70906499)
+    @Presubmit
+    @Test
+    public void testMovePipToBackWithHiddenFullscreenStack() throws Exception {
+        assumeTrue(supportsPip());
+
+        // Launch a fullscreen activity, return home and while the fullscreen stack is hidden,
+        // launch a pip activity over home
+        launchActivity(TEST_ACTIVITY);
+        launchHomeActivity();
+        launchActivity(PIP_ACTIVITY, EXTRA_ENTER_PIP, "true");
+        assertPinnedStackExists();
+
+        // 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 " + PIP_ACTIVITY_ACTION_MOVE_TO_BACK);
+        assertPinnedStackStateOnMoveToFullscreen(
+                PIP_ACTIVITY, WINDOWING_MODE_UNDEFINED, ACTIVITY_TYPE_HOME);
+    }
+
+    @Test
+    public void testPinnedStackAlwaysOnTop() throws Exception {
+        assumeTrue(supportsPip());
+
+        // Launch activity into pinned stack and assert it's on top.
+        launchActivity(PIP_ACTIVITY, EXTRA_ENTER_PIP, "true");
+        assertPinnedStackExists();
+        assertPinnedStackIsOnTop();
+
+        // Launch another activity in fullscreen stack and check that pinned stack is still on top.
+        launchActivity(TEST_ACTIVITY);
+        assertPinnedStackExists();
+        assertPinnedStackIsOnTop();
+
+        // Launch home and check that pinned stack is still on top.
+        launchHomeActivity();
+        assertPinnedStackExists();
+        assertPinnedStackIsOnTop();
+    }
+
+    @Test
+    public void testAppOpsDenyPipOnPause() throws Exception {
+        assumeTrue(supportsPip());
+
+        // Disable enter-pip and try to enter pip
+        setAppOpsOpToMode(ActivityManagerTestBase.componentName,
+                APP_OPS_OP_ENTER_PICTURE_IN_PICTURE, APP_OPS_MODE_IGNORED);
+
+        // Launch the PIP activity on pause
+        launchActivity(PIP_ACTIVITY, EXTRA_ENTER_PIP, "true");
+        assertPinnedStackDoesNotExist();
+
+        // Go home and ensure that there is no pinned stack
+        launchHomeActivity();
+        assertPinnedStackDoesNotExist();
+
+        // Re-enable enter-pip-on-hide
+        setAppOpsOpToMode(ActivityManagerTestBase.componentName,
+                APP_OPS_OP_ENTER_PICTURE_IN_PICTURE, APP_OPS_MODE_ALLOWED);
+    }
+
+    @Test
+    public void testEnterPipFromTaskWithMultipleActivities() throws Exception {
+        assumeTrue(supportsPip());
+
+        // Try to enter picture-in-picture from an activity that has more than one activity in the
+        // task and ensure that it works
+        launchActivity(LAUNCH_ENTER_PIP_ACTIVITY);
+        mAmWmState.waitForValidState(PIP_ACTIVITY, WINDOWING_MODE_PINNED, ACTIVITY_TYPE_STANDARD);
+        assertPinnedStackExists();
+    }
+
+    @Test
+    public void testEnterPipWithResumeWhilePausingActivityNoStop() throws Exception {
+        assumeTrue(supportsPip());
+
+        /*
+         * Launch the resumeWhilePausing activity and ensure that the PiP activity did not get
+         * stopped and actually went into the pinned stack.
+         *
+         * Note that this is a workaround because to trigger the path that we want to happen in
+         * activity manager, we need to add the leaving activity to the stopping state, which only
+         * happens when a hidden stack is brought forward. Normally, this happens when you go home,
+         * 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
+         *    fullscreen stack
+         * 4) Bring the activity in the dynamic stack forward to trigger PiP
+         */
+        int stackId = launchActivityInNewDynamicStack(RESUME_WHILE_PAUSING_ACTIVITY);
+        resizeStack(stackId, 0, 0, 500, 500);
+        // 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)
+        launchActivity(PIP_ACTIVITY, WINDOWING_MODE_FULLSCREEN,
+                EXTRA_ENTER_PIP_ON_PAUSE, "true",
+                EXTRA_ON_PAUSE_DELAY, "350",
+                EXTRA_ASSERT_NO_ON_STOP_BEFORE_PIP, "true");
+        launchActivity(RESUME_WHILE_PAUSING_ACTIVITY);
+        assertPinnedStackExists();
+    }
+
+    @Test
+    public void testDisallowEnterPipActivityLocked() throws Exception {
+        assumeTrue(supportsPip());
+
+        launchActivity(PIP_ACTIVITY, EXTRA_ENTER_PIP_ON_PAUSE, "true");
+        ActivityTask task = mAmWmState.getAmState().getStandardStackByWindowingMode(
+                WINDOWING_MODE_FULLSCREEN).getTopTask();
+
+        // 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 " + PIP_ACTIVITY_ACTION_ENTER_PIP);
+        mAmWmState.waitForValidState(PIP_ACTIVITY, WINDOWING_MODE_PINNED, ACTIVITY_TYPE_STANDARD);
+        assertPinnedStackDoesNotExist();
+        launchHomeActivity();
+        assertPinnedStackDoesNotExist();
+        executeShellCommand("am task lock stop");
+    }
+
+    @FlakyTest(bugId = 70328524)
+    @Presubmit
+    @Test
+    public void testConfigurationChangeOrderDuringTransition() throws Exception {
+        assumeTrue(supportsPip());
+
+        // Launch a PiP activity and ensure configuration change only happened once, and that the
+        // configuration change happened after the picture-in-picture and multi-window callbacks
+        launchActivity(PIP_ACTIVITY);
+        String logSeparator = clearLogcat();
+        executeShellCommand("am broadcast -a " + PIP_ACTIVITY_ACTION_ENTER_PIP);
+        mAmWmState.waitForValidState(PIP_ACTIVITY, WINDOWING_MODE_PINNED, ACTIVITY_TYPE_STANDARD);
+        assertPinnedStackExists();
+        waitForValidPictureInPictureCallbacks(PIP_ACTIVITY, logSeparator);
+        assertValidPictureInPictureCallbackOrder(PIP_ACTIVITY, logSeparator);
+
+        // Trigger it to go back to fullscreen and ensure that only triggered one configuration
+        // change as well
+        logSeparator = clearLogcat();
+        launchActivity(PIP_ACTIVITY);
+        waitForValidPictureInPictureCallbacks(PIP_ACTIVITY, logSeparator);
+        assertValidPictureInPictureCallbackOrder(PIP_ACTIVITY, logSeparator);
+    }
+
+    /** Helper class to save, set, and restore transition_animation_scale preferences. */
+    private static class TransitionAnimationScaleSession extends SettingsSession<Float> {
+        TransitionAnimationScaleSession() {
+            super(Settings.Global.getUriFor(Settings.Global.TRANSITION_ANIMATION_SCALE),
+                    Settings.Global::getFloat,
+                    Settings.Global::putFloat);
+        }
+    }
+
+    @Test
+    public void testEnterPipInterruptedCallbacks() throws Exception {
+        assumeTrue(supportsPip());
+
+        try (final TransitionAnimationScaleSession transitionAnimationScaleSession =
+                new TransitionAnimationScaleSession()) {
+            // Slow down the transition animations for this test
+            transitionAnimationScaleSession.set(20f);
+
+            // Launch a PiP activity
+            launchActivity(PIP_ACTIVITY, EXTRA_ENTER_PIP, "true");
+            // Wait until the PiP activity has moved into the pinned stack (happens before the
+            // transition has started)
+            mAmWmState.waitForValidState(PIP_ACTIVITY, WINDOWING_MODE_PINNED,
+                    ACTIVITY_TYPE_STANDARD);
+            assertPinnedStackExists();
+
+            // Relaunch the PiP activity back into fullscreen
+            String logSeparator = clearLogcat();
+            launchActivity(PIP_ACTIVITY);
+            // Wait until the PiP activity is reparented into the fullscreen stack (happens after
+            // the transition has finished)
+            mAmWmState.waitForValidState(
+                    PIP_ACTIVITY, WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_STANDARD);
+
+            // Ensure that we get the callbacks indicating that PiP/MW mode was cancelled, but no
+            // configuration change (since none was sent)
+            final ActivityLifecycleCounts lifecycleCounts = new ActivityLifecycleCounts(
+                    PIP_ACTIVITY, logSeparator);
+            assertTrue(lifecycleCounts.mConfigurationChangedCount == 0);
+            assertTrue(lifecycleCounts.mPictureInPictureModeChangedCount == 1);
+            assertTrue(lifecycleCounts.mMultiWindowModeChangedCount == 1);
+        }
+    }
+
+    @FlakyTest(bugId = 71564769)
+    @Presubmit
+    @Test
+    public void testStopBeforeMultiWindowCallbacksOnDismiss() throws Exception {
+        assumeTrue(supportsPip());
+
+        // Launch a PiP activity
+        launchActivity(PIP_ACTIVITY, EXTRA_ENTER_PIP, "true");
+        assertPinnedStackExists();
+
+        // Dismiss it
+        String logSeparator = clearLogcat();
+        removeStacksInWindowingModes(WINDOWING_MODE_PINNED);
+        mAmWmState.waitForValidState(
+                PIP_ACTIVITY, WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_STANDARD);
+
+        // Confirm that we get stop before the multi-window and picture-in-picture mode change
+        // callbacks
+        final ActivityLifecycleCounts lifecycleCounts = new ActivityLifecycleCounts(PIP_ACTIVITY,
+                logSeparator);
+        if (lifecycleCounts.mStopCount != 1) {
+            fail(PIP_ACTIVITY + " has received " + lifecycleCounts.mStopCount
+                    + " onStop() calls, expecting 1");
+        } else if (lifecycleCounts.mPictureInPictureModeChangedCount != 1) {
+            fail(PIP_ACTIVITY + " has received " + lifecycleCounts.mPictureInPictureModeChangedCount
+                    + " onPictureInPictureModeChanged() calls, expecting 1");
+        } else if (lifecycleCounts.mMultiWindowModeChangedCount != 1) {
+            fail(PIP_ACTIVITY + " has received " + lifecycleCounts.mMultiWindowModeChangedCount
+                    + " onMultiWindowModeChanged() calls, expecting 1");
+        } else {
+            int lastStopLine = lifecycleCounts.mLastStopLineIndex;
+            int lastPipLine = lifecycleCounts.mLastPictureInPictureModeChangedLineIndex;
+            int lastMwLine = lifecycleCounts.mLastMultiWindowModeChangedLineIndex;
+            if (!(lastStopLine < lastPipLine && lastPipLine < lastMwLine)) {
+                fail(PIP_ACTIVITY + " has received callbacks in unexpected order.  Expected:"
+                        + " stop < pip < mw, but got line indices: " + lastStopLine + ", "
+                        + lastPipLine + ", " + lastMwLine + " respectively");
+            }
+        }
+    }
+
+    @Test
+    public void testPreventSetAspectRatioWhileExpanding() throws Exception {
+        assumeTrue(supportsPip());
+
+        // Launch the PiP activity
+        launchActivity(PIP_ACTIVITY, EXTRA_ENTER_PIP, "true");
+
+        // 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 " + PIP_ACTIVITY_ACTION_EXPAND_PIP
+                + " -e " + EXTRA_SET_ASPECT_RATIO_WITH_DELAY_NUMERATOR + " 123456789"
+                + " -e " + EXTRA_SET_ASPECT_RATIO_WITH_DELAY_DENOMINATOR + " 100000000");
+        mAmWmState.waitForValidState(
+                PIP_ACTIVITY, WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_STANDARD);
+        assertPinnedStackDoesNotExist();
+    }
+
+    @Test
+    public void testSetRequestedOrientationWhilePinned() throws Exception {
+        assumeTrue(supportsPip());
+
+        // Launch the PiP activity fixed as portrait, and enter picture-in-picture
+        launchActivity(PIP_ACTIVITY,
+                EXTRA_FIXED_ORIENTATION, String.valueOf(ORIENTATION_PORTRAIT),
+                EXTRA_ENTER_PIP, "true");
+        assertPinnedStackExists();
+
+        // Request that the orientation is set to landscape
+        executeShellCommand("am broadcast -a "
+                + PIP_ACTIVITY_ACTION_SET_REQUESTED_ORIENTATION + " -e "
+                + EXTRA_FIXED_ORIENTATION + " " + String.valueOf(ORIENTATION_LANDSCAPE));
+
+        // Launch the activity back into fullscreen and ensure that it is now in landscape
+        launchActivity(PIP_ACTIVITY);
+        mAmWmState.waitForValidState(
+                PIP_ACTIVITY, WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_STANDARD);
+        assertPinnedStackDoesNotExist();
+        assertTrue(mAmWmState.getWmState().getLastOrientation() == ORIENTATION_LANDSCAPE);
+    }
+
+    @Test
+    public void testWindowButtonEntersPip() throws Exception {
+        assumeTrue(supportsPip());
+
+        // Launch the PiP activity trigger the window button, ensure that we have entered PiP
+        launchActivity(PIP_ACTIVITY);
+        pressWindowButton();
+        mAmWmState.waitForValidState(PIP_ACTIVITY, WINDOWING_MODE_PINNED, ACTIVITY_TYPE_STANDARD);
+        assertPinnedStackExists();
+    }
+
+    @Test
+    public void testFinishPipActivityWithTaskOverlay() throws Exception {
+        assumeTrue(supportsPip());
+
+        // Launch PiP activity
+        launchActivity(PIP_ACTIVITY, EXTRA_ENTER_PIP, "true");
+        assertPinnedStackExists();
+        int taskId = mAmWmState.getAmState().getStandardStackByWindowingMode(
+                WINDOWING_MODE_PINNED).getTopTask().mTaskId;
+
+        // Ensure that we don't any any other overlays as a result of launching into PIP
+        launchHomeActivity();
+
+        // Launch task overlay activity into PiP activity task
+        launchPinnedActivityAsTaskOverlay(TRANSLUCENT_TEST_ACTIVITY, taskId);
+
+        // Finish the PiP activity and ensure that there is no pinned stack
+        executeShellCommand("am broadcast -a " + PIP_ACTIVITY_ACTION_FINISH);
+        mAmWmState.waitForWithAmState((amState) -> {
+            return getPinnedStack() == null;
+        }, "Waiting for pinned stack to be removed...");
+        assertPinnedStackDoesNotExist();
+    }
+
+    @Test
+    public void testNoResumeAfterTaskOverlayFinishes() throws Exception {
+        assumeTrue(supportsPip());
+
+        // Launch PiP activity
+        launchActivity(PIP_ACTIVITY, EXTRA_ENTER_PIP, "true");
+        assertPinnedStackExists();
+        int taskId = mAmWmState.getAmState().getStandardStackByWindowingMode(
+                WINDOWING_MODE_PINNED).getTopTask().mTaskId;
+
+        // Launch task overlay activity into PiP activity task
+        launchPinnedActivityAsTaskOverlay(TRANSLUCENT_TEST_ACTIVITY, taskId);
+
+        // Finish the task overlay activity while animating and ensure that the PiP activity never
+        // got resumed
+        String logSeparator = clearLogcat();
+        executeShellCommand("am stack resize-animated 4 20 20 500 500");
+        executeShellCommand("am broadcast -a " + TEST_ACTIVITY_ACTION_FINISH);
+        mAmWmState.waitFor((amState, wmState) -> !amState.containsActivity(
+                TRANSLUCENT_TEST_ACTIVITY), "Waiting for test activity to finish...");
+        final ActivityLifecycleCounts lifecycleCounts = new ActivityLifecycleCounts(PIP_ACTIVITY,
+                logSeparator);
+        assertTrue(lifecycleCounts.mResumeCount == 0);
+        assertTrue(lifecycleCounts.mPauseCount == 0);
+    }
+
+    @Test
+    public void testPinnedStackWithDockedStack() throws Exception {
+        assumeTrue(supportsPip());
+        assumeTrue(supportsSplitScreenMultiWindow());
+
+        launchActivity(PIP_ACTIVITY, EXTRA_ENTER_PIP, "true");
+        launchActivitiesInSplitScreen(
+                getLaunchActivityBuilder().setTargetActivityName(LAUNCHING_ACTIVITY),
+                getLaunchActivityBuilder().setTargetActivityName(TEST_ACTIVITY).setRandomData(true)
+                        .setMultipleTask(false)
+        );
+        mAmWmState.assertVisibility(PIP_ACTIVITY, true);
+        mAmWmState.assertVisibility(LAUNCHING_ACTIVITY, true);
+        mAmWmState.assertVisibility(TEST_ACTIVITY, true);
+
+        // Launch the activities again to take focus and make sure nothing is hidden
+        launchActivitiesInSplitScreen(
+                getLaunchActivityBuilder().setTargetActivityName(LAUNCHING_ACTIVITY),
+                getLaunchActivityBuilder().setTargetActivityName(TEST_ACTIVITY).setRandomData(true)
+                        .setMultipleTask(false)
+        );
+        mAmWmState.assertVisibility(LAUNCHING_ACTIVITY, true);
+        mAmWmState.assertVisibility(TEST_ACTIVITY, true);
+
+        // Go to recents to make sure that fullscreen stack is invisible
+        // Some devices do not support recents or implement it differently (instead of using a
+        // separate stack id or as an activity), for those cases the visibility asserts will be
+        // ignored
+        pressAppSwitchButton();
+        mAmWmState.assertVisibility(LAUNCHING_ACTIVITY, true);
+        mAmWmState.assertVisibility(TEST_ACTIVITY, false);
+    }
+
+    @Test
+    public void testLaunchTaskByComponentMatchMultipleTasks() throws Exception {
+        assumeTrue(supportsPip());
+
+        // Launch a fullscreen activity which will launch a PiP activity in a new task with the same
+        // affinity
+        launchActivity(TEST_ACTIVITY_WITH_SAME_AFFINITY);
+        launchActivity(PIP_ACTIVITY_WITH_SAME_AFFINITY);
+        assertPinnedStackExists();
+
+        // Launch the root activity again...
+        int rootActivityTaskId = mAmWmState.getAmState().getTaskByActivityName(
+                TEST_ACTIVITY_WITH_SAME_AFFINITY).mTaskId;
+        launchHomeActivity();
+        launchActivity(TEST_ACTIVITY_WITH_SAME_AFFINITY);
+
+        // ...and ensure that the root activity task is found and reused, and that the pinned stack
+        // is unaffected
+        assertPinnedStackExists();
+        mAmWmState.assertFocusedActivity("Expected root activity focused",
+                TEST_ACTIVITY_WITH_SAME_AFFINITY);
+        assertTrue(rootActivityTaskId == mAmWmState.getAmState().getTaskByActivityName(
+                TEST_ACTIVITY_WITH_SAME_AFFINITY).mTaskId);
+    }
+
+    @Test
+    public void testLaunchTaskByAffinityMatchMultipleTasks() throws Exception {
+        assumeTrue(supportsPip());
+
+        // Launch a fullscreen activity which will launch a PiP activity in a new task with the same
+        // affinity, and also launch another activity in the same task, while finishing itself. As
+        // a result, the task will not have a component matching the same activity as what it was
+        // started with
+        launchActivity(TEST_ACTIVITY_WITH_SAME_AFFINITY,
+                EXTRA_START_ACTIVITY, getActivityComponentName(TEST_ACTIVITY),
+                EXTRA_FINISH_SELF_ON_RESUME, "true");
+        mAmWmState.waitForValidState(
+                TEST_ACTIVITY, WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_STANDARD);
+        launchActivity(PIP_ACTIVITY_WITH_SAME_AFFINITY);
+        mAmWmState.waitForValidState(
+                PIP_ACTIVITY_WITH_SAME_AFFINITY, WINDOWING_MODE_PINNED, ACTIVITY_TYPE_STANDARD);
+        assertPinnedStackExists();
+
+        // Launch the root activity again...
+        int rootActivityTaskId = mAmWmState.getAmState().getTaskByActivityName(
+                TEST_ACTIVITY).mTaskId;
+        launchHomeActivity();
+        launchActivity(TEST_ACTIVITY_WITH_SAME_AFFINITY);
+
+        // ...and ensure that even while matching purely by task affinity, the root activity task is
+        // found and reused, and that the pinned stack is unaffected
+        assertPinnedStackExists();
+        mAmWmState.assertFocusedActivity("Expected root activity focused", TEST_ACTIVITY);
+        assertTrue(rootActivityTaskId == mAmWmState.getAmState().getTaskByActivityName(
+                TEST_ACTIVITY).mTaskId);
+    }
+
+    @Test
+    public void testLaunchTaskByAffinityMatchSingleTask() throws Exception {
+        assumeTrue(supportsPip());
+
+        // Launch an activity into the pinned stack with a fixed affinity
+        launchActivity(TEST_ACTIVITY_WITH_SAME_AFFINITY,
+                EXTRA_ENTER_PIP, "true",
+                EXTRA_START_ACTIVITY, getActivityComponentName(PIP_ACTIVITY),
+                EXTRA_FINISH_SELF_ON_RESUME, "true");
+        mAmWmState.waitForValidState(PIP_ACTIVITY, WINDOWING_MODE_PINNED, ACTIVITY_TYPE_STANDARD);
+        assertPinnedStackExists();
+
+        // Launch the root activity again, of the matching task and ensure that we expand to
+        // fullscreen
+        int activityTaskId = mAmWmState.getAmState().getTaskByActivityName(
+                PIP_ACTIVITY).mTaskId;
+        launchHomeActivity();
+        launchActivity(TEST_ACTIVITY_WITH_SAME_AFFINITY);
+        mAmWmState.waitForValidState(
+                PIP_ACTIVITY, WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_STANDARD);
+        assertPinnedStackDoesNotExist();
+        assertTrue(activityTaskId == mAmWmState.getAmState().getTaskByActivityName(
+                PIP_ACTIVITY).mTaskId);
+    }
+
+    /** Test that reported display size corresponds to fullscreen after exiting PiP. */
+    @FlakyTest
+    @Presubmit
+    @Test
+    public void testDisplayMetricsPinUnpin() throws Exception {
+        assumeTrue(supportsPip());
+
+        String logSeparator = clearLogcat();
+        launchActivity(TEST_ACTIVITY);
+        final int defaultWindowingMode = mAmWmState.getAmState()
+                .getTaskByActivityName(TEST_ACTIVITY).getWindowingMode();
+        final ReportedSizes initialSizes = getLastReportedSizesForActivity(TEST_ACTIVITY,
+                logSeparator);
+        final Rect initialAppBounds = readAppBounds(TEST_ACTIVITY, logSeparator);
+        assertNotNull("Must report display dimensions", initialSizes);
+        assertNotNull("Must report app bounds", initialAppBounds);
+
+        logSeparator = clearLogcat();
+        launchActivity(PIP_ACTIVITY, EXTRA_ENTER_PIP, "true");
+        mAmWmState.waitForValidState(PIP_ACTIVITY, WINDOWING_MODE_PINNED, ACTIVITY_TYPE_STANDARD);
+        mAmWmState.waitForAppTransitionIdle();
+        final ReportedSizes pinnedSizes = getLastReportedSizesForActivity(PIP_ACTIVITY,
+                logSeparator);
+        final Rect pinnedAppBounds = readAppBounds(PIP_ACTIVITY, logSeparator);
+        assertFalse("Reported display size when pinned must be different from default",
+                initialSizes.equals(pinnedSizes));
+        assertFalse("Reported app bounds when pinned must be different from default",
+                initialAppBounds.width() == pinnedAppBounds.width()
+                        && initialAppBounds.height() == pinnedAppBounds.height());
+
+        logSeparator = clearLogcat();
+        launchActivity(PIP_ACTIVITY, defaultWindowingMode);
+        final ReportedSizes finalSizes = getLastReportedSizesForActivity(PIP_ACTIVITY,
+                logSeparator);
+        final Rect finalAppBounds = readAppBounds(PIP_ACTIVITY, logSeparator);
+        assertEquals("Must report default size after exiting PiP", initialSizes, finalSizes);
+        assertEquals("Must report default app width() after exiting PiP", initialAppBounds.width(),
+                finalAppBounds.width());
+        assertEquals("Must report default app height() after exiting PiP", initialAppBounds.height(),
+                finalAppBounds.height());
+    }
+
+    @Presubmit
+    @Test
+    public void testEnterPictureInPictureSavePosition() throws Exception {
+        if (!supportsPip()) return;
+
+        // Launch PiP activity with auto-enter PiP, save the default position of the PiP
+        // (while the PiP is still animating sleep)
+        launchActivity(PIP_ACTIVITY, EXTRA_ENTER_PIP, "true");
+        mAmWmState.waitForAppTransitionIdle();
+        assertPinnedStackExists();
+
+        // Move the PiP to a new position on screen
+        final int stackId = getPinnedStack().mStackId;
+        final Rect initialDefaultBounds = mAmWmState.getWmState().getDefaultPinnedStackBounds();
+        final Rect offsetStackBounds = getPinnedStackBounds();
+        offsetStackBounds.offset(0, -100);
+        resizeStack(stackId, offsetStackBounds.left, offsetStackBounds.top, offsetStackBounds.right,
+                offsetStackBounds.bottom);
+
+        // 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 " + PIP_ACTIVITY_ACTION_EXPAND_PIP);
+        mAmWmState.waitForValidState(PIP_ACTIVITY, WINDOWING_MODE_FULLSCREEN,
+                ACTIVITY_TYPE_STANDARD);
+        executeShellCommand("am broadcast -a " + PIP_ACTIVITY_ACTION_ENTER_PIP);
+        mAmWmState.waitForValidState(PIP_ACTIVITY, WINDOWING_MODE_PINNED, ACTIVITY_TYPE_STANDARD);
+        mAmWmState.waitForAppTransitionIdle();
+        mAmWmState.computeState(true);
+        // Due to rounding in how we save and apply the snap fraction we may be a pixel off, so just
+        // account for that in this check
+        offsetStackBounds.inset(-1, -1);
+        assertTrue("Expected offsetBounds=" + offsetStackBounds + " to contain bounds="
+                + getPinnedStackBounds(), offsetStackBounds.contains(getPinnedStackBounds()));
+
+        // 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 " + PIP_ACTIVITY_ACTION_EXPAND_PIP);
+        mAmWmState.waitForValidState(PIP_ACTIVITY, WINDOWING_MODE_FULLSCREEN,
+                ACTIVITY_TYPE_STANDARD);
+        launchActivity(TEST_ACTIVITY);
+        executeShellCommand("am broadcast -a " + TEST_ACTIVITY_ACTION_FINISH);
+        mAmWmState.waitForActivityState(PIP_ACTIVITY, STATE_RESUMED);
+        executeShellCommand("am broadcast -a " + PIP_ACTIVITY_ACTION_ENTER_PIP);
+        mAmWmState.waitForValidState(PIP_ACTIVITY, WINDOWING_MODE_PINNED, ACTIVITY_TYPE_STANDARD);
+        mAmWmState.waitForAppTransitionIdle();
+        mAmWmState.computeState(true);
+        assertTrue("Expected initialBounds=" + initialDefaultBounds + " to equal bounds="
+                + getPinnedStackBounds(), initialDefaultBounds.equals(getPinnedStackBounds()));
+    }
+
+    @Presubmit
+    @Test
+    @FlakyTest(bugId = 71792368)
+    public void testEnterPictureInPictureDiscardSavedPositionOnFinish() throws Exception {
+        if (!supportsPip()) return;
+
+        // Launch PiP activity with auto-enter PiP, save the default position of the PiP
+        // (while the PiP is still animating sleep)
+        launchActivity(PIP_ACTIVITY, EXTRA_ENTER_PIP, "true");
+        assertPinnedStackExists();
+        mAmWmState.waitForAppTransitionIdle();
+
+        // Move the PiP to a new position on screen
+        final int stackId = getPinnedStack().mStackId;
+        final Rect initialDefaultBounds = mAmWmState.getWmState().getDefaultPinnedStackBounds();
+        final Rect offsetStackBounds = getPinnedStackBounds();
+        offsetStackBounds.offset(0, -100);
+        resizeStack(stackId, offsetStackBounds.left, offsetStackBounds.top, offsetStackBounds.right,
+                offsetStackBounds.bottom);
+
+        // Finish the activity
+        executeShellCommand("am broadcast -a " + PIP_ACTIVITY_ACTION_FINISH);
+        mAmWmState.waitForActivityState(PIP_ACTIVITY, STATE_DESTROYED);
+        mAmWmState.waitForWithAmState((amState) -> {
+            return getPinnedStack() == null;
+        }, "Waiting for pinned stack to be removed...");
+        assertPinnedStackDoesNotExist();
+
+        // Ensure that starting the same PiP activity after it finished will go to the default
+        // bounds
+        launchActivity(PIP_ACTIVITY, EXTRA_ENTER_PIP, "true");
+        assertPinnedStackExists();
+        mAmWmState.waitForAppTransitionIdle();
+        mAmWmState.computeState(true);
+        assertTrue("Expected initialBounds=" + initialDefaultBounds + " to equal bounds="
+                + getPinnedStackBounds(), initialDefaultBounds.equals(getPinnedStackBounds()));
+    }
+
+    private static final Pattern sAppBoundsPattern = Pattern.compile(
+            "(.+)mAppBounds=Rect\\((\\d+), (\\d+) - (\\d+), (\\d+)\\)(.*)");
+
+    /** Read app bounds in last applied configuration from logs. */
+    private Rect readAppBounds(String activityName, String logSeparator) throws Exception {
+        final String[] lines = getDeviceLogsForComponent(activityName, logSeparator);
+        for (int i = lines.length - 1; i >= 0; i--) {
+            final String line = lines[i].trim();
+            final Matcher matcher = sAppBoundsPattern.matcher(line);
+            if (matcher.matches()) {
+                final int left = Integer.parseInt(matcher.group(2));
+                final int top = Integer.parseInt(matcher.group(3));
+                final int right = Integer.parseInt(matcher.group(4));
+                final int bottom = Integer.parseInt(matcher.group(5));
+                return new Rect(left, top, right - left, bottom - top);
+            }
+        }
+        return null;
+    }
+
+    /**
+     * Called after the given {@param activityName} has been moved to the fullscreen stack. Ensures
+     * that the stack matching the {@param windowingMode} and {@param activityType} is focused, and
+     * checks the top and/or bottom tasks in the fullscreen stack if
+     * {@param expectTopTaskHasActivity} or {@param expectBottomTaskHasActivity} are set respectively.
+     */
+    private void assertPinnedStackStateOnMoveToFullscreen(String activityName, int windowingMode,
+            int activityType) throws Exception {
+        mAmWmState.waitForFocusedStack(windowingMode, activityType);
+        mAmWmState.assertFocusedStack("Wrong focused stack", windowingMode, activityType);
+        mAmWmState.waitForActivityState(activityName, STATE_STOPPED);
+        assertTrue(mAmWmState.getAmState().hasActivityState(activityName, STATE_STOPPED));
+        assertTrue(mAmWmState.getAmState().containsActivityInWindowingMode(
+                activityName, WINDOWING_MODE_FULLSCREEN));
+        assertPinnedStackDoesNotExist();
+    }
+
+    /**
+     * Asserts that the pinned stack bounds does not intersect with the IME bounds.
+     */
+    private void assertPinnedStackDoesNotIntersectIME() throws Exception {
+        // Ensure that the IME is visible
+        WindowManagerState wmState = mAmWmState.getWmState();
+        wmState.computeState();
+        WindowManagerState.WindowState imeWinState = wmState.getInputMethodWindowState();
+        assertTrue(imeWinState != null);
+
+        // Ensure that the PIP movement is constrained by the display bounds intersecting the
+        // non-IME bounds
+        Rect imeContentFrame = imeWinState.getContentFrame();
+        Rect imeContentInsets = imeWinState.getGivenContentInsets();
+        Rect imeBounds = new Rect(imeContentFrame.left + imeContentInsets.left,
+                imeContentFrame.top + imeContentInsets.top,
+                imeContentFrame.right - imeContentInsets.width(),
+                imeContentFrame.bottom - imeContentInsets.height());
+        wmState.computeState();
+        Rect pipMovementBounds = wmState.getPinnedStackMomentBounds();
+        assertTrue(!Rect.intersects(pipMovementBounds, imeBounds));
+    }
+
+    /**
+     * Asserts that the pinned stack bounds is contained in the display bounds.
+     */
+    private void assertPinnedStackActivityIsInDisplayBounds(String activity) throws Exception {
+        final WindowManagerState.WindowState windowState = getWindowState(activity);
+        final WindowManagerState.Display display = mAmWmState.getWmState().getDisplay(
+                windowState.getDisplayId());
+        final Rect displayRect = display.getDisplayRect();
+        final Rect pinnedStackBounds = getPinnedStackBounds();
+        assertTrue(displayRect.contains(pinnedStackBounds));
+    }
+
+    /**
+     * Asserts that the pinned stack exists.
+     */
+    private void assertPinnedStackExists() throws Exception {
+        mAmWmState.assertContainsStack("Must contain pinned stack.", WINDOWING_MODE_PINNED,
+                ACTIVITY_TYPE_STANDARD);
+    }
+
+    /**
+     * Asserts that the pinned stack does not exist.
+     */
+    private void assertPinnedStackDoesNotExist() throws Exception {
+        mAmWmState.assertDoesNotContainStack("Must not contain pinned stack.",
+                WINDOWING_MODE_PINNED, ACTIVITY_TYPE_STANDARD);
+    }
+
+    /**
+     * Asserts that the pinned stack is the front stack.
+     */
+    private void assertPinnedStackIsOnTop() throws Exception {
+        mAmWmState.assertFrontStack("Pinned stack must always be on top.",
+                WINDOWING_MODE_PINNED, ACTIVITY_TYPE_STANDARD);
+    }
+
+    /**
+     * Asserts that the activity received exactly one of each of the callbacks when entering and
+     * exiting picture-in-picture.
+     */
+    private void assertValidPictureInPictureCallbackOrder(String activityName, String logSeparator)
+            throws Exception {
+        final ActivityLifecycleCounts lifecycleCounts = new ActivityLifecycleCounts(activityName,
+                logSeparator);
+
+        if (lifecycleCounts.mConfigurationChangedCount != 1) {
+            fail(activityName + " has received " + lifecycleCounts.mConfigurationChangedCount
+                    + " onConfigurationChanged() calls, expecting 1");
+        } else if (lifecycleCounts.mPictureInPictureModeChangedCount != 1) {
+            fail(activityName + " has received " + lifecycleCounts.mPictureInPictureModeChangedCount
+                    + " onPictureInPictureModeChanged() calls, expecting 1");
+        } else if (lifecycleCounts.mMultiWindowModeChangedCount != 1) {
+            fail(activityName + " has received " + lifecycleCounts.mMultiWindowModeChangedCount
+                    + " onMultiWindowModeChanged() calls, expecting 1");
+        } else {
+            int lastPipLine = lifecycleCounts.mLastPictureInPictureModeChangedLineIndex;
+            int lastMwLine = lifecycleCounts.mLastMultiWindowModeChangedLineIndex;
+            int lastConfigLine = lifecycleCounts.mLastConfigurationChangedLineIndex;
+            if (!(lastPipLine < lastMwLine && lastMwLine < lastConfigLine)) {
+                fail(activityName + " has received callbacks in unexpected order.  Expected:"
+                        + " pip < mw < config change, but got line indices: " + lastPipLine + ", "
+                        + lastMwLine + ", " + lastConfigLine + " respectively");
+            }
+        }
+    }
+
+    /**
+     * Waits until the expected picture-in-picture callbacks have been made.
+     */
+    private void waitForValidPictureInPictureCallbacks(String activityName, String logSeparator)
+            throws Exception {
+        mAmWmState.waitFor((amState, wmState) -> {
+            try {
+                final ActivityLifecycleCounts lifecycleCounts = new ActivityLifecycleCounts(
+                        activityName, logSeparator);
+                return lifecycleCounts.mConfigurationChangedCount == 1 &&
+                        lifecycleCounts.mPictureInPictureModeChangedCount == 1 &&
+                        lifecycleCounts.mMultiWindowModeChangedCount == 1;
+            } catch (Exception e) {
+                return false;
+            }
+        }, "Waiting for picture-in-picture activity callbacks...");
+    }
+
+    private void waitForValidAspectRatio(int num, int denom) throws Exception {
+        // Hacky, but we need to wait for the auto-enter picture-in-picture animation to complete
+        // and before we can check the pinned stack bounds
+        mAmWmState.waitForWithAmState((state) -> {
+            Rect bounds = state.getStandardStackByWindowingMode(WINDOWING_MODE_PINNED).getBounds();
+            return floatEquals((float) bounds.width() / bounds.height(), (float) num / denom);
+        }, "waitForValidAspectRatio");
+    }
+
+    /**
+     * @return the window state for the given {@param activity}'s window.
+     */
+    private WindowManagerState.WindowState getWindowState(String activity) throws Exception {
+        String windowName = getWindowName(activity);
+        mAmWmState.computeState(new WaitForValidActivityState.Builder(activity).build());
+        final List<WindowManagerState.WindowState> tempWindowList = new ArrayList<>();
+        mAmWmState.getWmState().getMatchingVisibleWindowState(windowName, tempWindowList);
+        return tempWindowList.get(0);
+    }
+
+    /**
+     * @return the current pinned stack.
+     */
+    private ActivityStack getPinnedStack() {
+        return mAmWmState.getAmState().getStandardStackByWindowingMode(WINDOWING_MODE_PINNED);
+    }
+
+    /**
+     * @return the current pinned stack bounds.
+     */
+    private Rect getPinnedStackBounds() {
+        return getPinnedStack().getBounds();
+    }
+
+    /**
+     * Compares two floats with a common epsilon.
+     */
+    private void assertFloatEquals(float actual, float expected) {
+        if (!floatEquals(actual, expected)) {
+            fail(expected + " not equal to " + actual);
+        }
+    }
+
+    private boolean floatEquals(float a, float b) {
+        return Math.abs(a - b) < FLOAT_COMPARE_EPSILON;
+    }
+
+    /**
+     * Triggers a tap over the pinned stack bounds to trigger the PIP to close.
+     */
+    private void tapToFinishPip() throws Exception {
+        Rect pinnedStackBounds = getPinnedStackBounds();
+        int tapX = pinnedStackBounds.left + pinnedStackBounds.width() - 100;
+        int tapY = pinnedStackBounds.top + pinnedStackBounds.height() - 100;
+        executeShellCommand(String.format("input tap %d %d", tapX, tapY));
+    }
+
+    /**
+     * Launches the given {@param activityName} into the {@param taskId} as a task overlay.
+     */
+    private void launchPinnedActivityAsTaskOverlay(String activityName, int taskId)
+            throws Exception {
+        executeShellCommand(getAmStartCmd(activityName) + " --task " + taskId + " --task-overlay");
+
+        mAmWmState.waitForValidState(activityName, WINDOWING_MODE_PINNED, ACTIVITY_TYPE_STANDARD);
+    }
+
+    /**
+     * Sets an app-ops op for a given package to a given mode.
+     */
+    private void setAppOpsOpToMode(String packageName, String op, int mode) throws Exception {
+        executeShellCommand(String.format("appops set %s %s %d", packageName, op, mode));
+    }
+
+    /**
+     * Triggers the window keycode.
+     */
+    private void pressWindowButton() throws Exception {
+        mDevice.pressKeyCode(KEYCODE_WINDOW);
+    }
+
+    /**
+     * TODO: Improve tests check to actually check that apps are not interactive instead of checking
+     *       if the stack is focused.
+     */
+    private void pinnedStackTester(String startActivityCmd, String startActivity,
+            String topActivityName, boolean moveTopToPinnedStack, boolean isFocusable)
+            throws Exception {
+        executeShellCommand(startActivityCmd);
+        mAmWmState.waitForValidState(new WaitForValidActivityState.Builder(startActivity).build());
+
+        if (moveTopToPinnedStack) {
+            final int stackId = mAmWmState.getAmState().getStackIdByActivityName(topActivityName);
+
+            assertNotEquals(stackId, INVALID_STACK_ID);
+            executeShellCommand(getMoveToPinnedStackCommand(stackId));
+        }
+
+        mAmWmState.waitForValidState(topActivityName,
+                WINDOWING_MODE_PINNED, ACTIVITY_TYPE_STANDARD);
+        mAmWmState.computeState();
+
+        if (supportsPip()) {
+            final String windowName = getWindowName(topActivityName);
+            assertPinnedStackExists();
+            mAmWmState.assertFrontStack("Pinned stack must be the front stack.",
+                    WINDOWING_MODE_PINNED, ACTIVITY_TYPE_STANDARD);
+            mAmWmState.assertVisibility(topActivityName, true);
+
+            if (isFocusable) {
+                mAmWmState.assertFocusedStack("Pinned stack must be the focused stack.",
+                        WINDOWING_MODE_PINNED, ACTIVITY_TYPE_STANDARD);
+                mAmWmState.assertFocusedActivity(
+                        "Pinned activity must be focused activity.", topActivityName);
+                mAmWmState.assertFocusedWindow(
+                        "Pinned window must be focused window.", windowName);
+                // Not checking for resumed state here because PiP overlay can be launched on top
+                // in different task by SystemUI.
+            } else {
+                // Don't assert that the stack is not focused as a focusable PiP overlay can be
+                // launched on top as a task overlay by SystemUI.
+                mAmWmState.assertNotFocusedActivity(
+                        "Pinned activity can't be the focused activity.", topActivityName);
+                mAmWmState.assertNotResumedActivity(
+                        "Pinned activity can't be the resumed activity.", topActivityName);
+                mAmWmState.assertNotFocusedWindow(
+                        "Pinned window can't be focused window.", windowName);
+            }
+        } else {
+            mAmWmState.assertDoesNotContainStack("Must not contain pinned stack.",
+                    WINDOWING_MODE_PINNED, ACTIVITY_TYPE_STANDARD);
+        }
+    }
+}
diff --git a/tests/framework/base/activitymanager/src/android/server/am/ActivityManagerReplaceWindowTests.java b/tests/framework/base/activitymanager/src/android/server/am/ActivityManagerReplaceWindowTests.java
new file mode 100644
index 0000000..faa72a9
--- /dev/null
+++ b/tests/framework/base/activitymanager/src/android/server/am/ActivityManagerReplaceWindowTests.java
@@ -0,0 +1,107 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.server.am;
+
+import static android.app.WindowConfiguration.WINDOWING_MODE_SPLIT_SCREEN_PRIMARY;
+import static android.server.am.StateLogger.log;
+
+import static org.junit.Assume.assumeTrue;
+
+import junit.framework.Assert;
+
+import org.junit.Before;
+import org.junit.Test;
+
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * Build/Install/Run:
+ *     atest CtsActivityManagerDeviceTestCases:ActivityManagerReplaceWindowTests
+ */
+public class ActivityManagerReplaceWindowTests extends ActivityManagerTestBase {
+
+    private static final String SLOW_CREATE_ACTIVITY_NAME = "SlowCreateActivity";
+    private static final String NO_RELAUNCH_ACTIVITY_NAME = "NoRelaunchActivity";
+
+    private List<String> mTempWindowTokens = new ArrayList();
+
+    @Before
+    @Override
+    public void setUp() throws Exception {
+        super.setUp();
+
+        assumeTrue("Skipping test: no multi-window support", supportsSplitScreenMultiWindow());
+    }
+
+    @Test
+    public void testReplaceWindow_Dock_Relaunch() throws Exception {
+        testReplaceWindow_Dock(true);
+    }
+
+    @Test
+    public void testReplaceWindow_Dock_NoRelaunch() throws Exception {
+        testReplaceWindow_Dock(false);
+    }
+
+    private void testReplaceWindow_Dock(boolean relaunch) throws Exception {
+        final String activityName =
+                relaunch ? SLOW_CREATE_ACTIVITY_NAME : NO_RELAUNCH_ACTIVITY_NAME;
+        final String windowName = getWindowName(activityName);
+        final String amStartCmd = getAmStartCmd(activityName);
+
+        executeShellCommand(amStartCmd);
+
+        // Sleep 2 seconds, then check if the window is started properly.
+        // SlowCreateActivity will do a sleep inside its onCreate() to simulate a
+        // slow-starting app. So instead of relying on WindowManagerState's
+        // retrying mechanism, we do an explicit sleep to avoid excess spews
+        // from WindowManagerState.
+        if (SLOW_CREATE_ACTIVITY_NAME.equals(activityName)) {
+            Thread.sleep(2000);
+        }
+
+        log("==========Before Docking========");
+        final String oldToken = getWindowToken(windowName, activityName);
+
+        // Move to docked stack
+        setActivityTaskWindowingMode(activityName, WINDOWING_MODE_SPLIT_SCREEN_PRIMARY);
+
+        // Sleep 5 seconds, then check if the window is replaced properly.
+        Thread.sleep(5000);
+
+        log("==========After Docking========");
+        final String newToken = getWindowToken(windowName, activityName);
+
+        // For both relaunch and not relaunch case, we'd like the window to be kept.
+        Assert.assertEquals("Window replaced while docking.", oldToken, newToken);
+    }
+
+    private String getWindowToken(String windowName, String activityName)
+            throws Exception {
+        mAmWmState.computeState(new WaitForValidActivityState.Builder(activityName).build());
+
+        mAmWmState.assertVisibility(activityName, true);
+
+        mAmWmState.getWmState().getMatchingWindowTokens(windowName, mTempWindowTokens);
+
+        Assert.assertEquals("Should have exactly one window for the activity.",
+                1, mTempWindowTokens.size());
+
+        return mTempWindowTokens.get(0);
+    }
+}
diff --git a/tests/framework/base/activitymanager/src/android/server/am/ActivityManagerSplitScreenTests.java b/tests/framework/base/activitymanager/src/android/server/am/ActivityManagerSplitScreenTests.java
new file mode 100644
index 0000000..8c793b9
--- /dev/null
+++ b/tests/framework/base/activitymanager/src/android/server/am/ActivityManagerSplitScreenTests.java
@@ -0,0 +1,654 @@
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package 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.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_FULLSCREEN;
+import static android.app.WindowConfiguration.WINDOWING_MODE_FULLSCREEN_OR_SPLIT_SCREEN_SECONDARY;
+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.WindowManagerState.TRANSIT_WALLPAPER_OPEN;
+import static android.view.Surface.ROTATION_0;
+import static android.view.Surface.ROTATION_180;
+import static android.view.Surface.ROTATION_270;
+import static android.view.Surface.ROTATION_90;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertNotSame;
+import static org.junit.Assert.assertTrue;
+import static org.junit.Assume.assumeTrue;
+
+import android.graphics.Rect;
+import android.platform.test.annotations.Presubmit;
+
+import android.support.test.filters.FlakyTest;
+import org.junit.Before;
+import org.junit.Test;
+
+/**
+ * Build/Install/Run:
+ *     atest CtsActivityManagerDeviceTestCases:ActivityManagerSplitScreenTests
+ */
+public class ActivityManagerSplitScreenTests extends ActivityManagerTestBase {
+
+    private static final String TEST_ACTIVITY_NAME = "TestActivity";
+    private static final String NON_RESIZEABLE_ACTIVITY_NAME = "NonResizeableActivity";
+    private static final String DOCKED_ACTIVITY_NAME = "DockedActivity";
+    private static final String NO_RELAUNCH_ACTIVITY_NAME = "NoRelaunchActivity";
+    private static final String SINGLE_INSTANCE_ACTIVITY_NAME = "SingleInstanceActivity";
+    private static final String SINGLE_TASK_ACTIVITY_NAME = "SingleTaskActivity";
+
+    private static final String TEST_ACTIVITY_ACTION_FINISH =
+        "android.server.am.TestActivity.finish_self";
+
+    private static final int TASK_SIZE = 600;
+    private static final int STACK_SIZE = 300;
+
+    @Before
+    @Override
+    public void setUp() throws Exception {
+        super.setUp();
+
+        assumeTrue("Skipping test: no split multi-window support",
+                supportsSplitScreenMultiWindow());
+    }
+
+    @Test
+    public void testMinimumDeviceSize() throws Exception {
+        mAmWmState.assertDeviceDefaultDisplaySize(
+                "Devices supporting multi-window must be larger than the default minimum"
+                        + " task size");
+    }
+
+    @Test
+    @Presubmit
+    public void testStackList() throws Exception {
+        launchActivity(TEST_ACTIVITY_NAME);
+        mAmWmState.computeState(new String[] {TEST_ACTIVITY_NAME});
+        mAmWmState.assertContainsStack("Must contain home stack.",
+                WINDOWING_MODE_UNDEFINED, ACTIVITY_TYPE_HOME);
+        mAmWmState.assertContainsStack("Must contain fullscreen stack.",
+                WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_STANDARD);
+        mAmWmState.assertDoesNotContainStack("Must not contain docked stack.",
+                WINDOWING_MODE_SPLIT_SCREEN_PRIMARY, ACTIVITY_TYPE_STANDARD);
+    }
+
+    @Test
+    @Presubmit
+    public void testDockActivity() throws Exception {
+        launchActivityInSplitScreenWithRecents(TEST_ACTIVITY_NAME);
+        mAmWmState.assertContainsStack("Must contain home stack.",
+                WINDOWING_MODE_UNDEFINED, ACTIVITY_TYPE_HOME);
+        mAmWmState.assertContainsStack("Must contain docked stack.",
+                WINDOWING_MODE_SPLIT_SCREEN_PRIMARY, ACTIVITY_TYPE_STANDARD);
+    }
+
+    @Test
+    @Presubmit
+    public void testNonResizeableNotDocked() throws Exception {
+        launchActivityInSplitScreenWithRecents(NON_RESIZEABLE_ACTIVITY_NAME);
+
+        mAmWmState.assertContainsStack("Must contain home stack.",
+                WINDOWING_MODE_UNDEFINED, ACTIVITY_TYPE_HOME);
+        mAmWmState.assertDoesNotContainStack("Must not contain docked stack.",
+                WINDOWING_MODE_SPLIT_SCREEN_PRIMARY, ACTIVITY_TYPE_STANDARD);
+        mAmWmState.assertFrontStack("Fullscreen stack must be front stack.",
+                WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_STANDARD);
+    }
+
+    @Test
+    @Presubmit
+    public void testLaunchToSide() throws Exception {
+        launchActivitiesInSplitScreen(LAUNCHING_ACTIVITY, TEST_ACTIVITY_NAME);
+        mAmWmState.assertContainsStack("Must contain fullscreen stack.",
+                WINDOWING_MODE_SPLIT_SCREEN_SECONDARY, ACTIVITY_TYPE_STANDARD);
+        mAmWmState.assertContainsStack("Must contain docked stack.",
+                WINDOWING_MODE_SPLIT_SCREEN_PRIMARY, ACTIVITY_TYPE_STANDARD);
+    }
+
+    @Test
+    @Presubmit
+    public void testLaunchToSideMultiWindowCallbacks() throws Exception {
+        // Launch two activities in split-screen mode.
+        launchActivitiesInSplitScreen(LAUNCHING_ACTIVITY, TEST_ACTIVITY_NAME);
+        mAmWmState.assertContainsStack("Must contain fullscreen stack.",
+                WINDOWING_MODE_SPLIT_SCREEN_SECONDARY, ACTIVITY_TYPE_STANDARD);
+        mAmWmState.assertContainsStack("Must contain docked stack.",
+                WINDOWING_MODE_SPLIT_SCREEN_PRIMARY, ACTIVITY_TYPE_STANDARD);
+
+        // Exit split-screen mode and ensure we only get 1 multi-window mode changed callback.
+        final String logSeparator = clearLogcat();
+        removeStacksInWindowingModes(WINDOWING_MODE_SPLIT_SCREEN_PRIMARY);
+        final ActivityLifecycleCounts lifecycleCounts = waitForOnMultiWindowModeChanged(
+                TEST_ACTIVITY_NAME, logSeparator);
+        assertEquals(1, lifecycleCounts.mMultiWindowModeChangedCount);
+    }
+
+    @Test
+    @Presubmit
+    public void testNoUserLeaveHintOnMultiWindowModeChanged() throws Exception {
+        launchActivity(TEST_ACTIVITY_NAME, WINDOWING_MODE_FULLSCREEN);
+
+        // Move to docked stack.
+        String logSeparator = clearLogcat();
+        setActivityTaskWindowingMode(TEST_ACTIVITY_NAME, WINDOWING_MODE_SPLIT_SCREEN_PRIMARY);
+        ActivityLifecycleCounts lifecycleCounts = waitForOnMultiWindowModeChanged(
+                TEST_ACTIVITY_NAME, logSeparator);
+        assertEquals("mMultiWindowModeChangedCount",
+                1, lifecycleCounts.mMultiWindowModeChangedCount);
+        assertEquals("mUserLeaveHintCount", 0, lifecycleCounts.mUserLeaveHintCount);
+
+        // Make sure docked stack is focused. This way when we dismiss it later fullscreen stack
+        // will come up.
+        launchActivity(TEST_ACTIVITY_NAME, WINDOWING_MODE_SPLIT_SCREEN_PRIMARY);
+
+        // Move activity back to fullscreen stack.
+        logSeparator = clearLogcat();
+        setActivityTaskWindowingMode(TEST_ACTIVITY_NAME, WINDOWING_MODE_FULLSCREEN);
+        lifecycleCounts = waitForOnMultiWindowModeChanged(TEST_ACTIVITY_NAME, logSeparator);
+        assertEquals("mMultiWindowModeChangedCount",
+                1, lifecycleCounts.mMultiWindowModeChangedCount);
+        assertEquals("mUserLeaveHintCount", 0, lifecycleCounts.mUserLeaveHintCount);
+    }
+
+    @Test
+    @Presubmit
+    public void testLaunchToSideAndBringToFront() throws Exception {
+        launchActivitiesInSplitScreen(LAUNCHING_ACTIVITY, TEST_ACTIVITY_NAME);
+
+        int taskNumberInitial = mAmWmState.getAmState().getStandardTaskCountByWindowingMode(
+                WINDOWING_MODE_SPLIT_SCREEN_SECONDARY);
+        mAmWmState.assertFocusedActivity("Launched to side activity must be in front.",
+                TEST_ACTIVITY_NAME);
+
+        // Launch another activity to side to cover first one.
+        launchActivity(
+                NO_RELAUNCH_ACTIVITY_NAME, WINDOWING_MODE_FULLSCREEN_OR_SPLIT_SCREEN_SECONDARY);
+        int taskNumberCovered = mAmWmState.getAmState().getStandardTaskCountByWindowingMode(
+                WINDOWING_MODE_SPLIT_SCREEN_SECONDARY);
+        assertEquals("Fullscreen stack must have one task added.",
+                taskNumberInitial + 1, taskNumberCovered);
+        mAmWmState.assertFocusedActivity("Launched to side covering activity must be in front.",
+                NO_RELAUNCH_ACTIVITY_NAME);
+
+        // Launch activity that was first launched to side. It should be brought to front.
+        getLaunchActivityBuilder()
+                .setTargetActivityName(TEST_ACTIVITY_NAME)
+                .setToSide(true)
+                .setWaitForLaunched(true)
+                .execute();
+        int taskNumberFinal = mAmWmState.getAmState().getStandardTaskCountByWindowingMode(
+                WINDOWING_MODE_SPLIT_SCREEN_SECONDARY);
+        assertEquals("Task number in fullscreen stack must remain the same.",
+                taskNumberCovered, taskNumberFinal);
+        mAmWmState.assertFocusedActivity("Launched to side covering activity must be in front.",
+                TEST_ACTIVITY_NAME);
+    }
+
+    @Test
+    @Presubmit
+    public void testLaunchToSideMultiple() throws Exception {
+        launchActivitiesInSplitScreen(LAUNCHING_ACTIVITY, TEST_ACTIVITY_NAME);
+
+        int taskNumberInitial = mAmWmState.getAmState().getStandardTaskCountByWindowingMode(
+                WINDOWING_MODE_SPLIT_SCREEN_SECONDARY);
+        assertNotNull("Launched to side activity must be in fullscreen stack.",
+                mAmWmState.getAmState().getTaskByActivityName(
+                        TEST_ACTIVITY_NAME, WINDOWING_MODE_SPLIT_SCREEN_SECONDARY));
+
+        // Try to launch to side same activity again.
+        getLaunchActivityBuilder().setToSide(true).execute();
+        final String[] waitForActivitiesVisible =
+                new String[] {TEST_ACTIVITY_NAME, LAUNCHING_ACTIVITY};
+        mAmWmState.computeState(waitForActivitiesVisible);
+        int taskNumberFinal = mAmWmState.getAmState().getStandardTaskCountByWindowingMode(
+                WINDOWING_MODE_SPLIT_SCREEN_SECONDARY);
+        assertEquals("Task number mustn't change.", taskNumberInitial, taskNumberFinal);
+        mAmWmState.assertFocusedActivity("Launched to side activity must remain in front.",
+                TEST_ACTIVITY_NAME);
+        assertNotNull("Launched to side activity must remain in fullscreen stack.",
+                mAmWmState.getAmState().getTaskByActivityName(
+                        TEST_ACTIVITY_NAME, WINDOWING_MODE_SPLIT_SCREEN_SECONDARY));
+    }
+
+    @Test
+    @Presubmit
+    public void testLaunchToSideSingleInstance() throws Exception {
+        launchTargetToSide(SINGLE_INSTANCE_ACTIVITY_NAME, false);
+    }
+
+    @Test
+    public void testLaunchToSideSingleTask() throws Exception {
+        launchTargetToSide(SINGLE_TASK_ACTIVITY_NAME, false);
+    }
+
+    @FlakyTest
+    @Presubmit
+    @Test
+    public void testLaunchToSideMultipleWithDifferentIntent() throws Exception {
+        launchTargetToSide(TEST_ACTIVITY_NAME, true);
+    }
+
+    private void launchTargetToSide(String targetActivityName, boolean taskCountMustIncrement)
+            throws Exception {
+        final LaunchActivityBuilder targetActivityLauncher = getLaunchActivityBuilder()
+                .setTargetActivityName(targetActivityName)
+                .setToSide(true)
+                .setRandomData(true)
+                .setMultipleTask(false);
+
+        launchActivitiesInSplitScreen(
+                getLaunchActivityBuilder().setTargetActivityName(LAUNCHING_ACTIVITY),
+                targetActivityLauncher);
+
+        final WaitForValidActivityState[] waitForActivitiesVisible =
+                new WaitForValidActivityState[] {
+                    new WaitForValidActivityState.Builder(targetActivityName).build(),
+                    new WaitForValidActivityState.Builder(LAUNCHING_ACTIVITY).build()
+                };
+
+        mAmWmState.computeState(waitForActivitiesVisible);
+        mAmWmState.assertContainsStack("Must contain fullscreen stack.",
+                WINDOWING_MODE_SPLIT_SCREEN_SECONDARY, ACTIVITY_TYPE_STANDARD);
+        int taskNumberInitial = mAmWmState.getAmState().getStandardTaskCountByWindowingMode(
+                WINDOWING_MODE_SPLIT_SCREEN_SECONDARY);
+        assertNotNull("Launched to side activity must be in fullscreen stack.",
+                mAmWmState.getAmState().getTaskByActivityName(
+                        targetActivityName, WINDOWING_MODE_SPLIT_SCREEN_SECONDARY));
+
+        // Try to launch to side same activity again with different data.
+        targetActivityLauncher.execute();
+        mAmWmState.computeState(waitForActivitiesVisible);
+        int taskNumberSecondLaunch = mAmWmState.getAmState().getStandardTaskCountByWindowingMode(
+                WINDOWING_MODE_SPLIT_SCREEN_SECONDARY);
+        if (taskCountMustIncrement) {
+            assertEquals("Task number must be incremented.", taskNumberInitial + 1,
+                    taskNumberSecondLaunch);
+        } else {
+            assertEquals("Task number must not change.", taskNumberInitial,
+                    taskNumberSecondLaunch);
+        }
+        mAmWmState.assertFocusedActivity("Launched to side activity must be in front.",
+                targetActivityName);
+        assertNotNull("Launched to side activity must be launched in fullscreen stack.",
+                mAmWmState.getAmState().getTaskByActivityName(
+                        targetActivityName, WINDOWING_MODE_SPLIT_SCREEN_SECONDARY));
+
+        // Try to launch to side same activity again with no data.
+        targetActivityLauncher.setRandomData(false).execute();
+        mAmWmState.computeState(waitForActivitiesVisible);
+        int taskNumberFinal = mAmWmState.getAmState().getStandardTaskCountByWindowingMode(
+                WINDOWING_MODE_SPLIT_SCREEN_SECONDARY);
+        if (taskCountMustIncrement) {
+            assertEquals("Task number must be incremented.", taskNumberSecondLaunch + 1,
+                    taskNumberFinal);
+        } else {
+            assertEquals("Task number must not change.", taskNumberSecondLaunch,
+                    taskNumberFinal);
+        }
+        mAmWmState.assertFocusedActivity("Launched to side activity must be in front.",
+                targetActivityName);
+        assertNotNull("Launched to side activity must be launched in fullscreen stack.",
+                mAmWmState.getAmState().getTaskByActivityName(
+                        targetActivityName, WINDOWING_MODE_SPLIT_SCREEN_SECONDARY));
+    }
+
+    @Presubmit
+    @Test
+    public void testLaunchToSideMultipleWithFlag() throws Exception {
+        launchActivitiesInSplitScreen(LAUNCHING_ACTIVITY, TEST_ACTIVITY_NAME);
+        int taskNumberInitial = mAmWmState.getAmState().getStandardTaskCountByWindowingMode(
+                WINDOWING_MODE_SPLIT_SCREEN_SECONDARY);
+        assertNotNull("Launched to side activity must be in fullscreen stack.",
+                mAmWmState.getAmState().getTaskByActivityName(
+                        TEST_ACTIVITY_NAME, WINDOWING_MODE_SPLIT_SCREEN_SECONDARY));
+
+        // Try to launch to side same activity again, but with Intent#FLAG_ACTIVITY_MULTIPLE_TASK.
+        getLaunchActivityBuilder().setToSide(true).setMultipleTask(true).execute();
+        final String[] waitForActivitiesVisible =
+                new String[] {LAUNCHING_ACTIVITY, TEST_ACTIVITY_NAME};
+        mAmWmState.computeState(waitForActivitiesVisible);
+        int taskNumberFinal = mAmWmState.getAmState().getStandardTaskCountByWindowingMode(
+                WINDOWING_MODE_SPLIT_SCREEN_SECONDARY);
+        assertEquals("Task number must be incremented.", taskNumberInitial + 1,
+                taskNumberFinal);
+        mAmWmState.assertFocusedActivity("Launched to side activity must be in front.",
+                TEST_ACTIVITY_NAME);
+        assertNotNull("Launched to side activity must remain in fullscreen stack.",
+                mAmWmState.getAmState().getTaskByActivityName(
+                        TEST_ACTIVITY_NAME, WINDOWING_MODE_SPLIT_SCREEN_SECONDARY));
+    }
+
+    @Test
+    public void testRotationWhenDocked() throws Exception {
+        launchActivitiesInSplitScreen(LAUNCHING_ACTIVITY, TEST_ACTIVITY_NAME);
+        mAmWmState.assertContainsStack("Must contain fullscreen stack.",
+                WINDOWING_MODE_SPLIT_SCREEN_SECONDARY, ACTIVITY_TYPE_STANDARD);
+        mAmWmState.assertContainsStack("Must contain docked stack.",
+                WINDOWING_MODE_SPLIT_SCREEN_PRIMARY, ACTIVITY_TYPE_STANDARD);
+
+        // Rotate device single steps (90°) 0-1-2-3.
+        // Each time we compute the state we implicitly assert valid bounds.
+        String[] waitForActivitiesVisible =
+            new String[] {LAUNCHING_ACTIVITY, TEST_ACTIVITY_NAME};
+        try (final RotationSession rotationSession = new RotationSession()) {
+            for (int i = 0; i < 4; i++) {
+                rotationSession.set(i);
+                mAmWmState.computeState(waitForActivitiesVisible);
+            }
+            // Double steps (180°) We ended the single step at 3. So, we jump directly to 1 for
+            // double step. So, we are testing 3-1-3 for one side and 0-2-0 for the other side.
+            rotationSession.set(ROTATION_90);
+            mAmWmState.computeState(waitForActivitiesVisible);
+            rotationSession.set(ROTATION_270);
+            mAmWmState.computeState(waitForActivitiesVisible);
+            rotationSession.set(ROTATION_0);
+            mAmWmState.computeState(waitForActivitiesVisible);
+            rotationSession.set(ROTATION_180);
+            mAmWmState.computeState(waitForActivitiesVisible);
+            rotationSession.set(ROTATION_0);
+            mAmWmState.computeState(waitForActivitiesVisible);
+        }
+    }
+
+    @Test
+    @Presubmit
+    public void testRotationWhenDockedWhileLocked() throws Exception {
+        launchActivitiesInSplitScreen(LAUNCHING_ACTIVITY, TEST_ACTIVITY_NAME);
+        mAmWmState.assertSanity();
+        mAmWmState.assertContainsStack("Must contain fullscreen stack.",
+                WINDOWING_MODE_SPLIT_SCREEN_SECONDARY, ACTIVITY_TYPE_STANDARD);
+        mAmWmState.assertContainsStack("Must contain docked stack.",
+                WINDOWING_MODE_SPLIT_SCREEN_PRIMARY, ACTIVITY_TYPE_STANDARD);
+
+        String[] waitForActivitiesVisible =
+                new String[] {LAUNCHING_ACTIVITY, TEST_ACTIVITY_NAME};
+        try (final RotationSession rotationSession = new RotationSession()) {
+            for (int i = 0; i < 4; i++) {
+                sleepDevice();
+                rotationSession.set(i);
+                wakeUpAndUnlockDevice();
+                mAmWmState.computeState(waitForActivitiesVisible);
+            }
+        }
+    }
+
+    @Test
+    public void testMinimizedFromEachDockedSide() throws Exception {
+        try (final RotationSession rotationSession = new RotationSession()) {
+            for (int i = 0; i < 2; i++) {
+                rotationSession.set(i);
+                launchActivityInDockStackAndMinimize(TEST_ACTIVITY_NAME);
+                if (!mAmWmState.isScreenPortrait() && isTablet()) {
+                    // Test minimize to the right only on tablets in landscape
+                    removeStacksWithActivityTypes(ALL_ACTIVITY_TYPE_BUT_HOME);
+                    launchActivityInDockStackAndMinimize(TEST_ACTIVITY_NAME,
+                            SPLIT_SCREEN_CREATE_MODE_BOTTOM_OR_RIGHT);
+                }
+                removeStacksWithActivityTypes(ALL_ACTIVITY_TYPE_BUT_HOME);
+            }
+        }
+    }
+
+    @Test
+    @Presubmit
+    public void testRotationWhileDockMinimized() throws Exception {
+        launchActivityInDockStackAndMinimize(TEST_ACTIVITY_NAME);
+
+        // Rotate device single steps (90°) 0-1-2-3.
+        // Each time we compute the state we implicitly assert valid bounds in minimized mode.
+        String[] waitForActivitiesVisible = new String[] {TEST_ACTIVITY_NAME};
+        try (final RotationSession rotationSession = new RotationSession()) {
+            for (int i = 0; i < 4; i++) {
+                rotationSession.set(i);
+                mAmWmState.computeState(waitForActivitiesVisible);
+            }
+
+            // Double steps (180°) We ended the single step at 3. So, we jump directly to 1 for
+            // double step. So, we are testing 3-1-3 for one side and 0-2-0 for the other side in
+            // minimized mode.
+            rotationSession.set(ROTATION_90);
+            mAmWmState.computeState(waitForActivitiesVisible);
+            rotationSession.set(ROTATION_270);
+            mAmWmState.computeState(waitForActivitiesVisible);
+            rotationSession.set(ROTATION_0);
+            mAmWmState.computeState(waitForActivitiesVisible);
+            rotationSession.set(ROTATION_180);
+            mAmWmState.computeState(waitForActivitiesVisible);
+            rotationSession.set(ROTATION_0);
+            mAmWmState.computeState(waitForActivitiesVisible);
+        }
+    }
+
+    @Test
+    public void testMinimizeAndUnminimizeThenGoingHome() throws Exception {
+        // Rotate the screen to check that minimize, unminimize, dismiss the docked stack and then
+        // going home has the correct app transition
+        try (final RotationSession rotationSession = new RotationSession()) {
+            for (int i = 0; i < 4; i++) {
+                rotationSession.set(i);
+                launchActivityInDockStackAndMinimize(DOCKED_ACTIVITY_NAME);
+
+                // Unminimize the docked stack
+                pressAppSwitchButton();
+                waitForDockNotMinimized();
+                assertDockNotMinimized();
+
+                // Dismiss the dock stack
+                launchActivity(TEST_ACTIVITY_NAME,
+                        WINDOWING_MODE_FULLSCREEN_OR_SPLIT_SCREEN_SECONDARY);
+                setActivityTaskWindowingMode(DOCKED_ACTIVITY_NAME,
+                        WINDOWING_MODE_FULLSCREEN_OR_SPLIT_SCREEN_SECONDARY);
+                mAmWmState.computeState(new String[]{DOCKED_ACTIVITY_NAME});
+
+                // Go home and check the app transition
+                assertNotSame(TRANSIT_WALLPAPER_OPEN, mAmWmState.getWmState().getLastTransition());
+                pressHomeButton();
+                mAmWmState.computeState();
+                assertEquals(TRANSIT_WALLPAPER_OPEN, mAmWmState.getWmState().getLastTransition());
+            }
+        }
+    }
+
+    @Test
+    @Presubmit
+    public void testFinishDockActivityWhileMinimized() throws Exception {
+        launchActivityInDockStackAndMinimize(TEST_ACTIVITY_NAME);
+
+        executeShellCommand("am broadcast -a " + TEST_ACTIVITY_ACTION_FINISH);
+        waitForDockNotMinimized();
+        mAmWmState.assertVisibility(TEST_ACTIVITY_NAME, false);
+        assertDockNotMinimized();
+    }
+
+    @Test
+    @Presubmit
+    public void testDockedStackToMinimizeWhenUnlocked() throws Exception {
+        launchActivityInSplitScreenWithRecents(TEST_ACTIVITY_NAME);
+        mAmWmState.computeState(new WaitForValidActivityState.Builder(TEST_ACTIVITY_NAME).build());
+        sleepDevice();
+        wakeUpAndUnlockDevice();
+        mAmWmState.computeState(new WaitForValidActivityState.Builder(TEST_ACTIVITY_NAME).build());
+        assertDockMinimized();
+    }
+
+    @Test
+    public void testMinimizedStateWhenUnlockedAndUnMinimized() throws Exception {
+        launchActivityInDockStackAndMinimize(TEST_ACTIVITY_NAME);
+
+        sleepDevice();
+        wakeUpAndUnlockDevice();
+        mAmWmState.computeState(new WaitForValidActivityState.Builder(TEST_ACTIVITY_NAME).build());
+
+        // Unminimized back to splitscreen
+        pressAppSwitchButton();
+        mAmWmState.computeState(new WaitForValidActivityState.Builder(TEST_ACTIVITY_NAME).build());
+    }
+
+    @Test
+    @Presubmit
+    public void testResizeDockedStack() throws Exception {
+        launchActivitiesInSplitScreen(DOCKED_ACTIVITY_NAME, TEST_ACTIVITY_NAME);
+        resizeDockedStack(STACK_SIZE, STACK_SIZE, TASK_SIZE, TASK_SIZE);
+        mAmWmState.computeState(false /* compareTaskAndStackBounds */,
+                new WaitForValidActivityState.Builder(TEST_ACTIVITY_NAME).build(),
+                new WaitForValidActivityState.Builder(DOCKED_ACTIVITY_NAME).build());
+        mAmWmState.assertContainsStack("Must contain secondary split-screen stack.",
+                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_NAME);
+        mAmWmState.assertVisibility(DOCKED_ACTIVITY_NAME, true);
+        mAmWmState.assertVisibility(TEST_ACTIVITY_NAME, true);
+    }
+
+    @Test
+    public void testActivityLifeCycleOnResizeDockedStack() throws Exception {
+        final WaitForValidActivityState[] waitTestActivityName =
+                new WaitForValidActivityState[] {new WaitForValidActivityState.Builder(
+                        TEST_ACTIVITY_NAME).build()};
+        launchActivity(TEST_ACTIVITY_NAME);
+        mAmWmState.computeState(waitTestActivityName);
+        final Rect fullScreenBounds = mAmWmState.getWmState().getStandardStackByWindowingMode(
+                WINDOWING_MODE_FULLSCREEN).getBounds();
+
+        setActivityTaskWindowingMode(TEST_ACTIVITY_NAME, WINDOWING_MODE_SPLIT_SCREEN_PRIMARY);
+        mAmWmState.computeState(waitTestActivityName);
+        launchActivity(NO_RELAUNCH_ACTIVITY_NAME,
+                WINDOWING_MODE_FULLSCREEN_OR_SPLIT_SCREEN_SECONDARY);
+
+        mAmWmState.computeState(new WaitForValidActivityState.Builder(TEST_ACTIVITY_NAME).build(),
+                new WaitForValidActivityState.Builder(NO_RELAUNCH_ACTIVITY_NAME).build());
+        final Rect initialDockBounds = mAmWmState.getWmState().getStandardStackByWindowingMode(
+                WINDOWING_MODE_SPLIT_SCREEN_PRIMARY) .getBounds();
+
+        final String logSeparator = clearLogcat();
+
+        Rect newBounds = computeNewDockBounds(fullScreenBounds, initialDockBounds, true);
+        resizeDockedStack(newBounds.width(), newBounds.height(), newBounds.width(), newBounds.height());
+        mAmWmState.computeState(new WaitForValidActivityState.Builder(TEST_ACTIVITY_NAME).build(),
+                new WaitForValidActivityState.Builder(NO_RELAUNCH_ACTIVITY_NAME).build());
+
+        // We resize twice to make sure we cross an orientation change threshold for both
+        // activities.
+        newBounds = computeNewDockBounds(fullScreenBounds, initialDockBounds, false);
+        resizeDockedStack(newBounds.width(), newBounds.height(), newBounds.width(), newBounds.height());
+        mAmWmState.computeState(new WaitForValidActivityState.Builder(TEST_ACTIVITY_NAME).build(),
+                new WaitForValidActivityState.Builder(NO_RELAUNCH_ACTIVITY_NAME).build());
+        assertActivityLifecycle(TEST_ACTIVITY_NAME, true /* relaunched */, logSeparator);
+        assertActivityLifecycle(NO_RELAUNCH_ACTIVITY_NAME, false /* relaunched */, logSeparator);
+    }
+
+    private Rect computeNewDockBounds(
+            Rect fullscreenBounds, Rect dockBounds, boolean reduceSize) {
+        final boolean inLandscape = fullscreenBounds.width() > dockBounds.width();
+        // We are either increasing size or reducing it.
+        final float sizeChangeFactor = reduceSize ? 0.5f : 1.5f;
+        final Rect newBounds = new Rect(dockBounds);
+        if (inLandscape) {
+            // In landscape we change the width.
+            newBounds.right = (int) (newBounds.left + (newBounds.width() * sizeChangeFactor));
+        } else {
+            // In portrait we change the height
+            newBounds.bottom = (int) (newBounds.top + (newBounds.height() * sizeChangeFactor));
+        }
+
+        return newBounds;
+    }
+
+    @Test
+    @Presubmit
+    public void testStackListOrderLaunchDockedActivity() throws Exception {
+        launchActivityInSplitScreenWithRecents(TEST_ACTIVITY_NAME);
+
+        final int homeStackIndex = mAmWmState.getStackIndexByActivityType(ACTIVITY_TYPE_HOME);
+        final int recentsStackIndex = mAmWmState.getStackIndexByActivityType(ACTIVITY_TYPE_RECENTS);
+        assertTrue("Recents stack should be on top of home stack",
+                recentsStackIndex < homeStackIndex);
+    }
+
+    @Test
+    @Presubmit
+    public void testStackListOrderOnSplitScreenDismissed() throws Exception {
+        launchActivitiesInSplitScreen(DOCKED_ACTIVITY_NAME, TEST_ACTIVITY_NAME);
+
+        setActivityTaskWindowingMode(DOCKED_ACTIVITY_NAME, WINDOWING_MODE_FULLSCREEN);
+        mAmWmState.computeState(new WaitForValidActivityState.Builder(
+                DOCKED_ACTIVITY_NAME).setWindowingMode(WINDOWING_MODE_FULLSCREEN).build());
+
+        final int homeStackIndex = mAmWmState.getStackIndexByActivityType(ACTIVITY_TYPE_HOME);
+        final int prevSplitScreenPrimaryIndex =
+                mAmWmState.getAmState().getStackIndexByActivityName(DOCKED_ACTIVITY_NAME);
+        final int prevSplitScreenSecondaryIndex =
+                mAmWmState.getAmState().getStackIndexByActivityName(TEST_ACTIVITY_NAME);
+
+        final int expectedHomeStackIndex =
+                (prevSplitScreenPrimaryIndex > prevSplitScreenSecondaryIndex
+                        ? prevSplitScreenPrimaryIndex : prevSplitScreenSecondaryIndex) - 1;
+        assertTrue("Home stack needs to be directly behind the top stack",
+                expectedHomeStackIndex == homeStackIndex);
+    }
+
+    private void launchActivityInDockStackAndMinimize(String activityName) throws Exception {
+        launchActivityInDockStackAndMinimize(activityName, SPLIT_SCREEN_CREATE_MODE_TOP_OR_LEFT);
+    }
+
+    private void launchActivityInDockStackAndMinimize(String activityName, int createMode)
+            throws Exception {
+        launchActivityInSplitScreenWithRecents(activityName, createMode);
+        pressHomeButton();
+        waitForAndAssertDockMinimized();
+    }
+
+    private void assertDockMinimized() {
+        assertTrue(mAmWmState.getWmState().isDockedStackMinimized());
+    }
+
+    private void waitForAndAssertDockMinimized() throws Exception {
+        waitForDockMinimized();
+        assertDockMinimized();
+        mAmWmState.computeState(new WaitForValidActivityState.Builder(TEST_ACTIVITY_NAME).build());
+        mAmWmState.assertContainsStack("Must contain docked stack.",
+                WINDOWING_MODE_SPLIT_SCREEN_PRIMARY, ACTIVITY_TYPE_STANDARD);
+        mAmWmState.assertFocusedStack("Home activity should be focused in minimized mode",
+                WINDOWING_MODE_UNDEFINED, ACTIVITY_TYPE_HOME);
+    }
+
+    private void assertDockNotMinimized() {
+        assertFalse(mAmWmState.getWmState().isDockedStackMinimized());
+    }
+
+    private void waitForDockMinimized() throws Exception {
+        mAmWmState.waitForWithWmState(state -> state.isDockedStackMinimized(),
+                "***Waiting for Dock stack to be minimized");
+    }
+
+    private void waitForDockNotMinimized() throws Exception {
+        mAmWmState.waitForWithWmState(state -> !state.isDockedStackMinimized(),
+                "***Waiting for Dock stack to not be minimized");
+    }
+}
diff --git a/tests/framework/base/activitymanager/src/android/server/am/ActivityManagerTransitionSelectionTests.java b/tests/framework/base/activitymanager/src/android/server/am/ActivityManagerTransitionSelectionTests.java
new file mode 100644
index 0000000..6f22893
--- /dev/null
+++ b/tests/framework/base/activitymanager/src/android/server/am/ActivityManagerTransitionSelectionTests.java
@@ -0,0 +1,295 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.server.am;
+
+import static android.server.am.WindowManagerState.TRANSIT_ACTIVITY_CLOSE;
+import static android.server.am.WindowManagerState.TRANSIT_ACTIVITY_OPEN;
+import static android.server.am.WindowManagerState.TRANSIT_TASK_CLOSE;
+import static android.server.am.WindowManagerState.TRANSIT_TASK_OPEN;
+import static android.server.am.WindowManagerState.TRANSIT_WALLPAPER_CLOSE;
+import static android.server.am.WindowManagerState.TRANSIT_WALLPAPER_INTRA_CLOSE;
+import static android.server.am.WindowManagerState.TRANSIT_WALLPAPER_INTRA_OPEN;
+import static android.server.am.WindowManagerState.TRANSIT_WALLPAPER_OPEN;
+
+import static org.junit.Assert.assertEquals;
+
+import android.platform.test.annotations.Presubmit;
+import android.support.test.filters.FlakyTest;
+
+import org.junit.Test;
+
+/**
+ * This test tests the transition type selection logic in ActivityManager/WindowManager.
+ * BottomActivity is started first, then TopActivity, and we check the transition type that the
+ * system selects when TopActivity enters or exits under various setups.
+ *
+ * Note that we only require the correct transition type to be reported (eg. TRANSIT_ACTIVITY_OPEN,
+ * TRANSIT_TASK_CLOSE, TRANSIT_WALLPAPER_OPEN, etc.). The exact animation is unspecified and can be
+ * overridden.
+ *
+ * <p>Build/Install/Run:
+ *     atest CtsActivityManagerDeviceTestCases:ActivityManagerTransitionSelectionTests
+ */
+@Presubmit
+public class ActivityManagerTransitionSelectionTests extends ActivityManagerTestBase {
+
+    private static final String BOTTOM_ACTIVITY_NAME = "BottomActivity";
+    private static final String TOP_ACTIVITY_NAME = "TopActivity";
+    private static final String TRANSLUCENT_TOP_ACTIVITY_NAME = "TranslucentTopActivity";
+
+    //------------------------------------------------------------------------//
+
+    // Test activity open/close under normal timing
+    @Test
+    public void testOpenActivity_NeitherWallpaper() throws Exception {
+        testOpenActivity(false /*bottomWallpaper*/, false /*topWallpaper*/,
+                false /*slowStop*/, TRANSIT_ACTIVITY_OPEN);
+    }
+
+    @Test
+    @FlakyTest(bugId = 71792333)
+    public void testCloseActivity_NeitherWallpaper() throws Exception {
+        testCloseActivity(false /*bottomWallpaper*/, false /*topWallpaper*/,
+                false /*slowStop*/, TRANSIT_ACTIVITY_CLOSE);
+    }
+
+    @Test
+    public void testOpenActivity_BottomWallpaper() throws Exception {
+        testOpenActivity(true /*bottomWallpaper*/, false /*topWallpaper*/,
+                false /*slowStop*/, TRANSIT_WALLPAPER_CLOSE);
+    }
+
+    @Test
+    public void testCloseActivity_BottomWallpaper() throws Exception {
+        testCloseActivity(true /*bottomWallpaper*/, false /*topWallpaper*/,
+                false /*slowStop*/, TRANSIT_WALLPAPER_OPEN);
+    }
+
+    @Test
+    public void testOpenActivity_BothWallpaper() throws Exception {
+        testOpenActivity(true /*bottomWallpaper*/, true /*topWallpaper*/,
+                false /*slowStop*/, TRANSIT_WALLPAPER_INTRA_OPEN);
+    }
+
+    @Test
+    public void testCloseActivity_BothWallpaper() throws Exception {
+        testCloseActivity(true /*bottomWallpaper*/, true /*topWallpaper*/,
+                false /*slowStop*/, TRANSIT_WALLPAPER_INTRA_CLOSE);
+    }
+
+    //------------------------------------------------------------------------//
+
+    // Test task open/close under normal timing
+    @Test
+    public void testOpenTask_NeitherWallpaper() throws Exception {
+        testOpenTask(false /*bottomWallpaper*/, false /*topWallpaper*/,
+                false /*slowStop*/, TRANSIT_TASK_OPEN);
+    }
+
+    @Test
+    public void testCloseTask_NeitherWallpaper() throws Exception {
+        testCloseTask(false /*bottomWallpaper*/, false /*topWallpaper*/,
+                false /*slowStop*/, TRANSIT_TASK_CLOSE);
+    }
+
+    @Test
+    public void testOpenTask_BottomWallpaper() throws Exception {
+        testOpenTask(true /*bottomWallpaper*/, false /*topWallpaper*/,
+                false /*slowStop*/, TRANSIT_WALLPAPER_CLOSE);
+    }
+
+    @Test
+    public void testCloseTask_BottomWallpaper() throws Exception {
+        testCloseTask(true /*bottomWallpaper*/, false /*topWallpaper*/,
+                false /*slowStop*/, TRANSIT_WALLPAPER_OPEN);
+    }
+
+    @Test
+    public void testOpenTask_BothWallpaper() throws Exception {
+        testOpenTask(true /*bottomWallpaper*/, true /*topWallpaper*/,
+                false /*slowStop*/, TRANSIT_WALLPAPER_INTRA_OPEN);
+    }
+
+    @Test
+    public void testCloseTask_BothWallpaper() throws Exception {
+        testCloseTask(true /*bottomWallpaper*/, true /*topWallpaper*/,
+                false /*slowStop*/, TRANSIT_WALLPAPER_INTRA_CLOSE);
+    }
+
+    //------------------------------------------------------------------------//
+
+    // Test activity close -- bottom activity slow in stopping
+    // These simulate the case where the bottom activity is resumed
+    // before AM receives its activitiyStopped
+    @Test
+    @FlakyTest(bugId = 71792333)
+    public void testCloseActivity_NeitherWallpaper_SlowStop() throws Exception {
+        testCloseActivity(false /*bottomWallpaper*/, false /*topWallpaper*/,
+                true /*slowStop*/, TRANSIT_ACTIVITY_CLOSE);
+    }
+
+    @Test
+    public void testCloseActivity_BottomWallpaper_SlowStop() throws Exception {
+        testCloseActivity(true /*bottomWallpaper*/, false /*topWallpaper*/,
+                true /*slowStop*/, TRANSIT_WALLPAPER_OPEN);
+    }
+
+    @Test
+    public void testCloseActivity_BothWallpaper_SlowStop() throws Exception {
+        testCloseActivity(true /*bottomWallpaper*/, true /*topWallpaper*/,
+                true /*slowStop*/, TRANSIT_WALLPAPER_INTRA_CLOSE);
+    }
+
+    //------------------------------------------------------------------------//
+
+    // Test task close -- bottom task top activity slow in stopping
+    // These simulate the case where the bottom activity is resumed
+    // before AM receives its activitiyStopped
+    @Test
+    public void testCloseTask_NeitherWallpaper_SlowStop() throws Exception {
+        testCloseTask(false /*bottomWallpaper*/, false /*topWallpaper*/,
+                true /*slowStop*/, TRANSIT_TASK_CLOSE);
+    }
+
+    @Test
+    public void testCloseTask_BottomWallpaper_SlowStop() throws Exception {
+        testCloseTask(true /*bottomWallpaper*/, false /*topWallpaper*/,
+                true /*slowStop*/, TRANSIT_WALLPAPER_OPEN);
+    }
+
+    @Test
+    public void testCloseTask_BothWallpaper_SlowStop() throws Exception {
+        testCloseTask(true /*bottomWallpaper*/, true /*topWallpaper*/,
+                true /*slowStop*/, TRANSIT_WALLPAPER_INTRA_CLOSE);
+    }
+
+    //------------------------------------------------------------------------//
+
+    /// Test closing of translucent activity/task
+    @Test
+    public void testCloseActivity_NeitherWallpaper_Translucent() throws Exception {
+        testCloseActivityTranslucent(false /*bottomWallpaper*/, false /*topWallpaper*/,
+                TRANSIT_ACTIVITY_CLOSE);
+    }
+
+    @Test
+    @FlakyTest(bugId = 71792333)
+    public void testCloseActivity_BottomWallpaper_Translucent() throws Exception {
+        testCloseActivityTranslucent(true /*bottomWallpaper*/, false /*topWallpaper*/,
+                TRANSIT_WALLPAPER_OPEN);
+    }
+
+    @Test
+    public void testCloseActivity_BothWallpaper_Translucent() throws Exception {
+        testCloseActivityTranslucent(true /*bottomWallpaper*/, true /*topWallpaper*/,
+                TRANSIT_WALLPAPER_INTRA_CLOSE);
+    }
+
+    @Test
+    public void testCloseTask_NeitherWallpaper_Translucent() throws Exception {
+        testCloseTaskTranslucent(false /*bottomWallpaper*/, false /*topWallpaper*/,
+                TRANSIT_TASK_CLOSE);
+    }
+
+    @Test
+    public void testCloseTask_BottomWallpaper_Translucent() throws Exception {
+        testCloseTaskTranslucent(true /*bottomWallpaper*/, false /*topWallpaper*/,
+                TRANSIT_WALLPAPER_OPEN);
+    }
+
+    @Test
+    public void testCloseTask_BothWallpaper_Translucent() throws Exception {
+        testCloseTaskTranslucent(true /*bottomWallpaper*/, true /*topWallpaper*/,
+                TRANSIT_WALLPAPER_INTRA_CLOSE);
+    }
+
+    //------------------------------------------------------------------------//
+
+    private void testOpenActivity(boolean bottomWallpaper,
+            boolean topWallpaper, boolean slowStop, String expectedTransit) throws Exception {
+        testTransitionSelection(true /*testOpen*/, false /*testNewTask*/,
+                bottomWallpaper, topWallpaper, false /*topTranslucent*/, slowStop, expectedTransit);
+    }
+    private void testCloseActivity(boolean bottomWallpaper,
+            boolean topWallpaper, boolean slowStop, String expectedTransit) throws Exception {
+        testTransitionSelection(false /*testOpen*/, false /*testNewTask*/,
+                bottomWallpaper, topWallpaper, false /*topTranslucent*/, slowStop, expectedTransit);
+    }
+    private void testOpenTask(boolean bottomWallpaper,
+            boolean topWallpaper, boolean slowStop, String expectedTransit) throws Exception {
+        testTransitionSelection(true /*testOpen*/, true /*testNewTask*/,
+                bottomWallpaper, topWallpaper, false /*topTranslucent*/, slowStop, expectedTransit);
+    }
+    private void testCloseTask(boolean bottomWallpaper,
+            boolean topWallpaper, boolean slowStop, String expectedTransit) throws Exception {
+        testTransitionSelection(false /*testOpen*/, true /*testNewTask*/,
+                bottomWallpaper, topWallpaper, false /*topTranslucent*/, slowStop, expectedTransit);
+    }
+    private void testCloseActivityTranslucent(boolean bottomWallpaper,
+            boolean topWallpaper, String expectedTransit) throws Exception {
+        testTransitionSelection(false /*testOpen*/, false /*testNewTask*/,
+                bottomWallpaper, topWallpaper, true /*topTranslucent*/,
+                false /*slowStop*/, expectedTransit);
+    }
+    private void testCloseTaskTranslucent(boolean bottomWallpaper,
+            boolean topWallpaper, String expectedTransit) throws Exception {
+        testTransitionSelection(false /*testOpen*/, true /*testNewTask*/,
+                bottomWallpaper, topWallpaper, true /*topTranslucent*/,
+                false /*slowStop*/, expectedTransit);
+    }
+    //------------------------------------------------------------------------//
+
+    private void testTransitionSelection(
+            boolean testOpen, boolean testNewTask,
+            boolean bottomWallpaper, boolean topWallpaper, boolean topTranslucent,
+            boolean testSlowStop, String expectedTransit) throws Exception {
+        String bottomStartCmd = getAmStartCmd(BOTTOM_ACTIVITY_NAME);
+        if (bottomWallpaper) {
+            bottomStartCmd += " --ez USE_WALLPAPER true";
+        }
+        if (testSlowStop) {
+            bottomStartCmd += " --ei STOP_DELAY 3000";
+        }
+        executeShellCommand(bottomStartCmd);
+
+        mAmWmState.computeState(new WaitForValidActivityState.Builder(BOTTOM_ACTIVITY_NAME).build());
+
+        final String topActivityName = topTranslucent ?
+                TRANSLUCENT_TOP_ACTIVITY_NAME : TOP_ACTIVITY_NAME;
+        String topStartCmd = getAmStartCmd(topActivityName);
+        if (testNewTask) {
+            topStartCmd += " -f 0x18000000";
+        }
+        if (topWallpaper) {
+            topStartCmd += " --ez USE_WALLPAPER true";
+        }
+        if (!testOpen) {
+            topStartCmd += " --ei FINISH_DELAY 1000";
+        }
+        executeShellCommand(topStartCmd);
+
+        Thread.sleep(5000);
+        if (testOpen) {
+            mAmWmState.computeState(new WaitForValidActivityState.Builder(topActivityName).build());
+        } else {
+            mAmWmState.computeState(new WaitForValidActivityState.Builder(BOTTOM_ACTIVITY_NAME).build());
+        }
+
+        assertEquals("Picked wrong transition", expectedTransit,
+                mAmWmState.getWmState().getLastTransition());
+    }
+}
diff --git a/tests/framework/base/activitymanager/src/android/server/am/ActivityManagerVrDisplayTests.java b/tests/framework/base/activitymanager/src/android/server/am/ActivityManagerVrDisplayTests.java
new file mode 100644
index 0000000..3c0a584
--- /dev/null
+++ b/tests/framework/base/activitymanager/src/android/server/am/ActivityManagerVrDisplayTests.java
@@ -0,0 +1,229 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License
+ */
+
+package android.server.am;
+
+import static android.server.am.ActivityAndWindowManagersState.DEFAULT_DISPLAY_ID;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assume.assumeTrue;
+
+import android.server.am.ActivityManagerState.ActivityDisplay;
+
+import org.junit.Before;
+import org.junit.Test;
+
+import java.util.List;
+
+/**
+ * Build/Install/Run:
+ *     atest CtsActivityManagerDeviceTestCases:ActivityManagerVrDisplayTests
+ */
+public class ActivityManagerVrDisplayTests extends ActivityManagerDisplayTestBase {
+    private static final String RESIZEABLE_ACTIVITY_NAME = "ResizeableActivity";
+    private static final String VR_TEST_ACTIVITY_NAME = "VrTestActivity";
+    private static final int VR_VIRTUAL_DISPLAY_WIDTH = 700;
+    private static final int VR_VIRTUAL_DISPLAY_HEIGHT = 900;
+    private static final int VR_VIRTUAL_DISPLAY_DPI = 320;
+
+    @Before
+    @Override
+    public void setUp() throws Exception {
+        super.setUp();
+
+        assumeTrue(supportsVrMode());
+    }
+
+    private static class VrModeSession implements AutoCloseable {
+
+        void enablePersistentVrMode() throws Exception {
+            executeShellCommand("setprop vr_virtualdisplay true");
+            executeShellCommand("vr set-persistent-vr-mode-enabled true");
+        }
+
+        @Override
+        public void close() throws Exception {
+            executeShellCommand("vr set-persistent-vr-mode-enabled false");
+            executeShellCommand("setprop vr_virtualdisplay false");
+        }
+    }
+
+    /**
+     * Tests that any new activity launch in Vr mode is in Vr display.
+     */
+    @Test
+    public void testVrActivityLaunch() throws Exception {
+        assumeTrue(supportsMultiDisplay());
+
+        try (final VrModeSession vrModeSession = new VrModeSession()) {
+            // Put the device in persistent vr mode.
+            vrModeSession.enablePersistentVrMode();
+
+            // Launch the VR activity.
+            launchActivity(VR_TEST_ACTIVITY_NAME);
+            mAmWmState.computeState(new WaitForValidActivityState(VR_TEST_ACTIVITY_NAME));
+            mAmWmState.assertVisibility(VR_TEST_ACTIVITY_NAME, true /* visible */);
+
+            // Launch the non-VR 2D activity and check where it ends up.
+            launchActivity(LAUNCHING_ACTIVITY);
+            mAmWmState.computeState(new WaitForValidActivityState(LAUNCHING_ACTIVITY));
+
+            // Ensure that the subsequent activity is visible
+            mAmWmState.assertVisibility(LAUNCHING_ACTIVITY, true /* visible */);
+
+            // Check that activity is launched in focused stack on primary display.
+            mAmWmState.assertFocusedActivity("Launched activity must be focused",
+                    LAUNCHING_ACTIVITY);
+            final int focusedStackId = mAmWmState.getAmState().getFocusedStackId();
+            final ActivityManagerState.ActivityStack focusedStack
+                    = mAmWmState.getAmState().getStackById(focusedStackId);
+            assertEquals("Launched activity must be resumed in focused stack",
+                    getActivityComponentName(LAUNCHING_ACTIVITY), focusedStack.mResumedActivity);
+
+            // Check if the launch activity is in Vr virtual display id.
+            final List<ActivityDisplay> reportedDisplays = getDisplaysStates();
+            final ActivityDisplay vrDisplay = getDisplayState(reportedDisplays,
+                    VR_VIRTUAL_DISPLAY_WIDTH, VR_VIRTUAL_DISPLAY_HEIGHT, VR_VIRTUAL_DISPLAY_DPI);
+            assertNotNull("Vr mode should have a virtual display", vrDisplay);
+
+            // Check if the focused activity is on this virtual stack.
+            assertEquals("Launch in Vr mode should be in virtual stack", vrDisplay.mId,
+                    focusedStack.mDisplayId);
+        }
+    }
+
+    /**
+     * Tests that any activity already present is re-launched in Vr display in vr mode.
+     */
+    @Test
+    public void testVrActivityReLaunch() throws Exception {
+        assumeTrue(supportsMultiDisplay());
+
+        // Launch a 2D activity.
+        launchActivity(LAUNCHING_ACTIVITY);
+
+        try (final VrModeSession vrModeSession = new VrModeSession()) {
+            // Put the device in persistent vr mode.
+            vrModeSession.enablePersistentVrMode();
+
+            // Launch the VR activity.
+            launchActivity(VR_TEST_ACTIVITY_NAME);
+            mAmWmState.computeState(new WaitForValidActivityState(VR_TEST_ACTIVITY_NAME));
+            mAmWmState.assertVisibility(VR_TEST_ACTIVITY_NAME, true /* visible */);
+
+            // Re-launch the non-VR 2D activity and check where it ends up.
+            launchActivity(LAUNCHING_ACTIVITY);
+            mAmWmState.computeState(new WaitForValidActivityState(LAUNCHING_ACTIVITY));
+
+            // Ensure that the subsequent activity is visible
+            mAmWmState.assertVisibility(LAUNCHING_ACTIVITY, true /* visible */);
+
+            // Check that activity is launched in focused stack on primary display.
+            mAmWmState.assertFocusedActivity("Launched activity must be focused",
+                    LAUNCHING_ACTIVITY);
+            final int focusedStackId = mAmWmState.getAmState().getFocusedStackId();
+            final ActivityManagerState.ActivityStack focusedStack
+                    = mAmWmState.getAmState().getStackById(focusedStackId);
+            assertEquals("Launched activity must be resumed in focused stack",
+                    getActivityComponentName(LAUNCHING_ACTIVITY), focusedStack.mResumedActivity);
+
+            // Check if the launch activity is in Vr virtual display id.
+            final List<ActivityDisplay> reportedDisplays = getDisplaysStates();
+            final ActivityDisplay vrDisplay = getDisplayState(reportedDisplays,
+                    VR_VIRTUAL_DISPLAY_WIDTH, VR_VIRTUAL_DISPLAY_HEIGHT, VR_VIRTUAL_DISPLAY_DPI);
+            assertNotNull("Vr mode should have a virtual display", vrDisplay);
+
+            // Check if the focused activity is on this virtual stack.
+            assertEquals("Launch in Vr mode should be in virtual stack", vrDisplay.mId,
+                    focusedStack.mDisplayId);
+        }
+    }
+
+    /**
+     * Tests that any new activity launch post Vr mode is in the main display.
+     */
+    @Test
+    public void testActivityLaunchPostVr() throws Exception {
+        assumeTrue(supportsMultiDisplay());
+
+        try (final VrModeSession vrModeSession = new VrModeSession()) {
+            // Put the device in persistent vr mode.
+            vrModeSession.enablePersistentVrMode();
+
+            // Launch the VR activity.
+            launchActivity(VR_TEST_ACTIVITY_NAME);
+            mAmWmState.computeState(new WaitForValidActivityState(VR_TEST_ACTIVITY_NAME));
+            mAmWmState.assertVisibility(VR_TEST_ACTIVITY_NAME, true /* visible */);
+
+            // Launch the non-VR 2D activity and check where it ends up.
+            launchActivity(ALT_LAUNCHING_ACTIVITY);
+            mAmWmState.computeState(new WaitForValidActivityState(ALT_LAUNCHING_ACTIVITY));
+
+            // Ensure that the subsequent activity is visible
+            mAmWmState.assertVisibility(ALT_LAUNCHING_ACTIVITY, true /* visible */);
+
+            // Check that activity is launched in focused stack on primary display.
+            mAmWmState.assertFocusedActivity("Launched activity must be focused",
+                    ALT_LAUNCHING_ACTIVITY);
+            final int focusedStackId = mAmWmState.getAmState().getFocusedStackId();
+            final ActivityManagerState.ActivityStack focusedStack
+                    = mAmWmState.getAmState().getStackById(focusedStackId);
+            assertEquals("Launched activity must be resumed in focused stack",
+                    getActivityComponentName(ALT_LAUNCHING_ACTIVITY),
+                    focusedStack.mResumedActivity);
+
+            // Check if the launch activity is in Vr virtual display id.
+            final List<ActivityDisplay> reportedDisplays = getDisplaysStates();
+            final ActivityDisplay vrDisplay = getDisplayState(reportedDisplays,
+                    VR_VIRTUAL_DISPLAY_WIDTH, VR_VIRTUAL_DISPLAY_HEIGHT,
+                    VR_VIRTUAL_DISPLAY_DPI);
+            assertNotNull("Vr mode should have a virtual display", vrDisplay);
+
+            // Check if the focused activity is on this virtual stack.
+            assertEquals("Launch in Vr mode should be in virtual stack", vrDisplay.mId,
+                    focusedStack.mDisplayId);
+
+        }
+
+        // There isn't a direct launch of activity which can take an user out of persistent VR mode.
+        // This sleep is to account for that delay and let device settle once it comes out of VR
+        // mode.
+        try {
+            Thread.sleep(2000);
+        } catch (Exception e) {
+            e.printStackTrace();
+        }
+
+        // Launch the non-VR 2D activity and check where it ends up.
+        launchActivity(RESIZEABLE_ACTIVITY_NAME);
+        mAmWmState.computeState(new WaitForValidActivityState(RESIZEABLE_ACTIVITY_NAME));
+
+        // Ensure that the subsequent activity is visible
+        mAmWmState.assertVisibility(RESIZEABLE_ACTIVITY_NAME, true /* visible */);
+
+        // Check that activity is launched in focused stack on primary display.
+        mAmWmState.assertFocusedActivity("Launched activity must be focused",
+                RESIZEABLE_ACTIVITY_NAME);
+        final int frontStackId = mAmWmState.getAmState().getFrontStackId(DEFAULT_DISPLAY_ID);
+        final ActivityManagerState.ActivityStack frontStack
+                = mAmWmState.getAmState().getStackById(frontStackId);
+        assertEquals("Launched activity must be resumed in front stack",
+                getActivityComponentName(RESIZEABLE_ACTIVITY_NAME), frontStack.mResumedActivity);
+        assertEquals("Front stack must be on primary display",
+                DEFAULT_DISPLAY_ID, frontStack.mDisplayId);
+    }
+}
diff --git a/tests/framework/base/activitymanager/src/android/server/am/AnimationBackgroundTests.java b/tests/framework/base/activitymanager/src/android/server/am/AnimationBackgroundTests.java
new file mode 100644
index 0000000..fd8ed2f
--- /dev/null
+++ b/tests/framework/base/activitymanager/src/android/server/am/AnimationBackgroundTests.java
@@ -0,0 +1,61 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.server.am;
+
+
+import static android.app.WindowConfiguration.WINDOWING_MODE_FULLSCREEN;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertTrue;
+
+import org.junit.Test;
+
+import static android.server.am.ActivityAndWindowManagersState.DEFAULT_DISPLAY_ID;
+
+/**
+ * Build/Install/Run:
+ *     atest CtsActivityManagerDeviceTestCases:AnimationBackgroundTests
+ */
+public class AnimationBackgroundTests extends ActivityManagerTestBase {
+
+    @Test
+    public void testAnimationBackground_duringAnimation() throws Exception {
+        launchActivityOnDisplay(LAUNCHING_ACTIVITY, DEFAULT_DISPLAY_ID);
+        getLaunchActivityBuilder()
+                .setTargetActivityName("AnimationTestActivity")
+                .setWaitForLaunched(false)
+                .execute();
+
+        // Make sure we are in the middle of the animation.
+        mAmWmState.waitForWithWmState(state -> state
+                .getStandardStackByWindowingMode(WINDOWING_MODE_FULLSCREEN)
+                        .isWindowAnimationBackgroundSurfaceShowing(),
+                "***Waiting for animation background showing");
+        assertTrue("window animation background needs to be showing", mAmWmState.getWmState()
+                .getStandardStackByWindowingMode(WINDOWING_MODE_FULLSCREEN)
+                .isWindowAnimationBackgroundSurfaceShowing());
+    }
+
+    @Test
+    public void testAnimationBackground_gone() throws Exception {
+        launchActivityOnDisplay(LAUNCHING_ACTIVITY, DEFAULT_DISPLAY_ID);
+        getLaunchActivityBuilder().setTargetActivityName("AnimationTestActivity").execute();
+        mAmWmState.computeState(new WaitForValidActivityState.Builder("AnimationTestActivity").build());
+        assertFalse("window animation background needs to be gone", mAmWmState.getWmState()
+                .getStandardStackByWindowingMode(WINDOWING_MODE_FULLSCREEN)
+                .isWindowAnimationBackgroundSurfaceShowing());
+    }
+}
diff --git a/tests/framework/base/activitymanager/src/android/server/am/AspectRatioTests.java b/tests/framework/base/activitymanager/src/android/server/am/AspectRatioTests.java
new file mode 100644
index 0000000..0cb300a
--- /dev/null
+++ b/tests/framework/base/activitymanager/src/android/server/am/AspectRatioTests.java
@@ -0,0 +1,165 @@
+/*
+ * 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.server.am;
+
+import static android.content.Context.WINDOW_SERVICE;
+import static android.content.pm.PackageManager.FEATURE_WATCH;
+
+import static org.junit.Assert.fail;
+
+import android.app.Activity;
+import android.content.Context;
+import android.platform.test.annotations.Presubmit;
+import android.support.test.InstrumentationRegistry;
+import android.support.test.rule.ActivityTestRule;
+import android.support.test.runner.AndroidJUnit4;
+import android.util.DisplayMetrics;
+import android.view.Display;
+import android.view.WindowManager;
+
+import com.android.compatibility.common.util.PollingCheck;
+
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+/**
+ * Build/Install/Run:
+ *     atest CtsActivityManagerDeviceTestCases:AspectRatioTests
+ */
+@RunWith(AndroidJUnit4.class)
+@Presubmit
+public class AspectRatioTests extends AspectRatioTestsBase {
+
+    // The max. aspect ratio the test activities are using.
+    private static final float MAX_ASPECT_RATIO = 1.0f;
+
+    // The minimum supported device aspect ratio.
+    private static final float MIN_DEVICE_ASPECT_RATIO = 1.333f;
+
+    // The minimum supported device aspect ratio for watches.
+    private static final float MIN_WATCH_DEVICE_ASPECT_RATIO = 1.0f;
+
+    // Test target activity that has maxAspectRatio="true" and resizeableActivity="false".
+    public static class MaxAspectRatioActivity extends Activity {
+    }
+
+    // Test target activity that has maxAspectRatio="1.0" and resizeableActivity="true".
+    public static class MaxAspectRatioResizeableActivity extends Activity {
+    }
+
+    // Test target activity that has no maxAspectRatio defined and resizeableActivity="false".
+    public static class MaxAspectRatioUnsetActivity extends Activity {
+    }
+
+    // Test target activity that has maxAspectRatio defined as
+    //   <meta-data android:name="android.max_aspect" android:value="1.0" />
+    // and resizeableActivity="false".
+    public static class MetaDataMaxAspectRatioActivity extends Activity {
+    }
+
+    @Rule
+    public ActivityTestRule<MaxAspectRatioActivity> mMaxAspectRatioActivity =
+            new ActivityTestRule<>(MaxAspectRatioActivity.class,
+                    false /* initialTouchMode */, false /* launchActivity */);
+
+    @Rule
+    public ActivityTestRule<MaxAspectRatioResizeableActivity> mMaxAspectRatioResizeableActivity =
+            new ActivityTestRule<>(MaxAspectRatioResizeableActivity.class,
+                    false /* initialTouchMode */, false /* launchActivity */);
+
+    @Rule
+    public ActivityTestRule<MetaDataMaxAspectRatioActivity> mMetaDataMaxAspectRatioActivity =
+            new ActivityTestRule<>(MetaDataMaxAspectRatioActivity.class,
+                    false /* initialTouchMode */, false /* launchActivity */);
+
+    @Rule
+    public ActivityTestRule<MaxAspectRatioUnsetActivity> mMaxAspectRatioUnsetActivity =
+            new ActivityTestRule<>(MaxAspectRatioUnsetActivity.class,
+                    false /* initialTouchMode */, false /* launchActivity */);
+
+    @Test
+    public void testDeviceAspectRatio() throws Exception {
+        final Context context = InstrumentationRegistry.getInstrumentation().getContext();
+        final WindowManager wm = (WindowManager) context.getSystemService(WINDOW_SERVICE);
+        final Display display = wm.getDefaultDisplay();
+        final DisplayMetrics metrics = new DisplayMetrics();
+        display.getRealMetrics(metrics);
+
+        float longSide = Math.max(metrics.widthPixels, metrics.heightPixels);
+        float shortSide = Math.min(metrics.widthPixels, metrics.heightPixels);
+        float deviceAspectRatio = longSide / shortSide;
+        float expectedMinAspectRatio = context.getPackageManager().hasSystemFeature(FEATURE_WATCH)
+                ? MIN_WATCH_DEVICE_ASPECT_RATIO : MIN_DEVICE_ASPECT_RATIO;
+
+        if (deviceAspectRatio < expectedMinAspectRatio) {
+            fail("deviceAspectRatio=" + deviceAspectRatio
+                    + " is less than expectedMinAspectRatio=" + expectedMinAspectRatio);
+        }
+    }
+
+    @Test
+    public void testMaxAspectRatio() throws Exception {
+        runAspectRatioTest(mMaxAspectRatioActivity, actual -> {
+            if (MAX_ASPECT_RATIO >= actual) return;
+            fail("actual=" + actual + " is greater than expected=" + MAX_ASPECT_RATIO);
+        });
+    }
+
+    @Test
+    public void testMetaDataMaxAspectRatio() throws Exception {
+        runAspectRatioTest(mMetaDataMaxAspectRatioActivity, actual -> {
+            if (MAX_ASPECT_RATIO >= actual) return;
+            fail("actual=" + actual + " is greater than expected=" + MAX_ASPECT_RATIO);
+        });
+    }
+
+    @Test
+    public void testMaxAspectRatioResizeableActivity() throws Exception {
+        final Context context = InstrumentationRegistry.getInstrumentation().getContext();
+        final float expected = getAspectRatio(context);
+        final Activity testActivity = launchActivity(mMaxAspectRatioResizeableActivity);
+        PollingCheck.waitFor(testActivity::hasWindowFocus);
+
+        Display testDisplay = testActivity.findViewById(android.R.id.content).getDisplay();
+
+        // TODO(b/69982434): Fix DisplayManager NPE when getting display from Instrumentation
+        // context, then can use DisplayManager to get the aspect ratio of the correct display.
+        if (testDisplay.getDisplayId() != Display.DEFAULT_DISPLAY) {
+            return;
+        }
+
+        // Since this activity is resizeable, its aspect ratio shouldn't be less than the device's
+        runTest(testActivity, actual -> {
+            if (aspectRatioEqual(expected, actual) || expected < actual) return;
+            fail("actual=" + actual + " is less than expected=" + expected);
+        });
+    }
+
+    @Test
+    public void testMaxAspectRatioUnsetActivity() throws Exception {
+        final Context context = InstrumentationRegistry.getInstrumentation().getContext();
+        final float expected = getAspectRatio(context);
+
+        // Since this activity didn't set an aspect ratio, its aspect ratio shouldn't be less than
+        // the device's
+        runAspectRatioTest(mMaxAspectRatioUnsetActivity, actual -> {
+            if (aspectRatioEqual(expected, actual) || expected < actual) return;
+            fail("actual=" + actual + " is less than expected=" + expected);
+        });
+    }
+}
diff --git a/tests/framework/base/activitymanager/src/android/server/am/AspectRatioTestsBase.java b/tests/framework/base/activitymanager/src/android/server/am/AspectRatioTestsBase.java
new file mode 100644
index 0000000..6d1dc53
--- /dev/null
+++ b/tests/framework/base/activitymanager/src/android/server/am/AspectRatioTestsBase.java
@@ -0,0 +1,87 @@
+/*
+ * 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.server.am;
+
+import android.app.Activity;
+import android.content.Context;
+import android.graphics.Point;
+import android.support.test.InstrumentationRegistry;
+import android.support.test.rule.ActivityTestRule;
+import android.view.Display;
+import android.view.WindowManager;
+
+class AspectRatioTestsBase {
+
+    interface AssertAspectRatioCallback {
+        void assertAspectRatio(float actual);
+    }
+
+    void runAspectRatioTest(final ActivityTestRule activityRule,
+            final AssertAspectRatioCallback callback) {
+        final Activity activity = launchActivity(activityRule);
+        runTest(activity, callback);
+        finishActivity(activityRule);
+
+        // TODO(b/35810513): All this rotation stuff doesn't really work yet. Need to make sure
+        // context is updated correctly here. Also, what does it mean to be holding a reference to
+        // this activity if changing the orientation will cause a relaunch?
+//        activity.setRequestedOrientation(SCREEN_ORIENTATION_LANDSCAPE);
+//        waitForIdle();
+//        callback.assertAspectRatio(getAspectRatio(activity));
+//
+//        activity.setRequestedOrientation(SCREEN_ORIENTATION_PORTRAIT);
+//        waitForIdle();
+//        callback.assertAspectRatio(getAspectRatio(activity));
+    }
+
+    protected void runTest(Activity activity, AssertAspectRatioCallback callback) {
+        callback.assertAspectRatio(getAspectRatio(activity));
+    }
+
+     static float getAspectRatio(Context context) {
+        final Display display =
+                context.getSystemService(WindowManager.class).getDefaultDisplay();
+        final Point size = new Point();
+        display.getSize(size);
+        final float longSide = Math.max(size.x, size.y);
+        final float shortSide = Math.min(size.x, size.y);
+        return longSide / shortSide;
+    }
+
+    protected Activity launchActivity(final ActivityTestRule activityRule) {
+        final Activity activity = activityRule.launchActivity(null);
+        waitForIdle();
+        return activity;
+    }
+
+    private void finishActivity(ActivityTestRule activityRule) {
+        final Activity activity = activityRule.getActivity();
+        if (activity != null) {
+            activity.finish();
+        }
+    }
+
+    private static void waitForIdle() {
+        InstrumentationRegistry.getInstrumentation().waitForIdleSync();
+    }
+
+    static boolean aspectRatioEqual(float a, float b) {
+        // Aspect ratios are considered equal if they ware within to significant digits.
+        float diff = Math.abs(a - b);
+        return diff < 0.01f;
+    }
+}
diff --git a/tests/framework/base/activitymanager/src/android/server/am/DisplaySizeTest.java b/tests/framework/base/activitymanager/src/android/server/am/DisplaySizeTest.java
new file mode 100644
index 0000000..478c0b6
--- /dev/null
+++ b/tests/framework/base/activitymanager/src/android/server/am/DisplaySizeTest.java
@@ -0,0 +1,147 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.server.am;
+
+import static android.content.Intent.FLAG_ACTIVITY_SINGLE_TOP;
+
+import static org.junit.Assert.assertTrue;
+
+import android.content.ComponentName;
+import android.os.Build;
+
+import org.junit.After;
+import org.junit.Test;
+
+/**
+ * Ensure that compatibility dialog is shown when launching an application with
+ * an unsupported smallest width.
+ *
+ * <p>Build/Install/Run:
+ *     atest CtsActivityManagerDeviceTestCases:DisplaySizeTest
+ */
+public class DisplaySizeTest extends ActivityManagerTestBase {
+    private static final String DENSITY_PROP_DEVICE = "ro.sf.lcd_density";
+    private static final String DENSITY_PROP_EMULATOR = "qemu.sf.lcd_density";
+
+    private static final ComponentName TEST_ACTIVITY = ComponentName.createRelative(
+            "android.server.am", ".TestActivity");
+    private static final ComponentName SMALLEST_WIDTH_ACTIVITY = ComponentName.createRelative(
+            "android.server.am.displaysize", ".SmallestWidthActivity");
+    /** @see android.server.am.displaysize.SmallestWidthActivity#EXTRA_LAUNCH_ANOTHER_ACTIVITY */
+    private static final String EXTRA_LAUNCH_ANOTHER_ACTIVITY = "launch_another_activity";
+
+    /** @see com.android.server.am.UnsupportedDisplaySizeDialog */
+    private static final String UNSUPPORTED_DISPLAY_SIZE_DIALOG_NAME =
+            "UnsupportedDisplaySizeDialog";
+
+    @After
+    @Override
+    public void tearDown() throws Exception {
+        super.tearDown();
+        resetDensity();
+
+        // Ensure app process is stopped.
+        stopTestPackage(SMALLEST_WIDTH_ACTIVITY);
+        stopTestPackage(TEST_ACTIVITY);
+    }
+
+    @Test
+    public void testCompatibilityDialog() throws Exception {
+        // Launch some other app (not to perform density change on launcher).
+        executeShellCommand(getAmStartCmd(TEST_ACTIVITY));
+        assertActivityDisplayed(TEST_ACTIVITY);
+
+        setUnsupportedDensity();
+
+        // Launch target app.
+        executeShellCommand(getAmStartCmd(SMALLEST_WIDTH_ACTIVITY));
+        assertActivityDisplayed(SMALLEST_WIDTH_ACTIVITY);
+        assertWindowDisplayed(UNSUPPORTED_DISPLAY_SIZE_DIALOG_NAME);
+    }
+
+    @Test
+    public void testCompatibilityDialogWhenFocused() throws Exception {
+        executeShellCommand(getAmStartCmd(SMALLEST_WIDTH_ACTIVITY));
+        assertActivityDisplayed(SMALLEST_WIDTH_ACTIVITY);
+
+        setUnsupportedDensity();
+
+        assertWindowDisplayed(UNSUPPORTED_DISPLAY_SIZE_DIALOG_NAME);
+    }
+
+    @Test
+    public void testCompatibilityDialogAfterReturn() throws Exception {
+        // Launch target app.
+        executeShellCommand(getAmStartCmd(SMALLEST_WIDTH_ACTIVITY));
+        assertActivityDisplayed(SMALLEST_WIDTH_ACTIVITY);
+        // Launch another activity.
+        final String startActivityOnTop = String.format("%s -f 0x%x --es %s %s",
+                getAmStartCmd(SMALLEST_WIDTH_ACTIVITY), FLAG_ACTIVITY_SINGLE_TOP,
+                EXTRA_LAUNCH_ANOTHER_ACTIVITY, TEST_ACTIVITY.flattenToShortString());
+        executeShellCommand(startActivityOnTop);
+        assertActivityDisplayed(TEST_ACTIVITY);
+
+        setUnsupportedDensity();
+
+        pressBackButton();
+
+        assertActivityDisplayed(SMALLEST_WIDTH_ACTIVITY);
+        assertWindowDisplayed(UNSUPPORTED_DISPLAY_SIZE_DIALOG_NAME);
+    }
+
+    private void setUnsupportedDensity() {
+        // Set device to 0.85 zoom. It doesn't matter that we're zooming out
+        // since the feature verifies that we're in a non-default density.
+        final int stableDensity = getStableDensity();
+        final int targetDensity = (int) (stableDensity * 0.85);
+        setDensity(targetDensity);
+    }
+
+    private int getStableDensity() {
+        final String densityProp;
+        if (Build.getSerial().startsWith("emulator-")) {
+            densityProp = DENSITY_PROP_EMULATOR;
+        } else {
+            densityProp = DENSITY_PROP_DEVICE;
+        }
+
+        return Integer.parseInt(executeShellCommand("getprop " + densityProp).trim());
+    }
+
+    private void setDensity(int targetDensity) {
+        executeShellCommand("wm density " + targetDensity);
+
+        // Verify that the density is changed.
+        final String output = executeShellCommand("wm density");
+        final boolean success = output.contains("Override density: " + targetDensity);
+
+        assertTrue("Failed to set density to " + targetDensity, success);
+    }
+
+    private void resetDensity() {
+        executeShellCommand("wm density reset");
+    }
+
+    private void assertActivityDisplayed(final ComponentName activityName) throws Exception {
+        assertWindowDisplayed(activityName.flattenToString());
+    }
+
+    private void assertWindowDisplayed(final String windowName) throws Exception {
+        mAmWmState.waitForValidState(WaitForValidActivityState.forWindow(windowName));
+        assertTrue(windowName + "is visible", mAmWmState.getWmState().isWindowVisible(windowName));
+    }
+}
diff --git a/tests/framework/base/activitymanager/src/android/server/am/KeyguardLockedTests.java b/tests/framework/base/activitymanager/src/android/server/am/KeyguardLockedTests.java
new file mode 100644
index 0000000..55c8ebf
--- /dev/null
+++ b/tests/framework/base/activitymanager/src/android/server/am/KeyguardLockedTests.java
@@ -0,0 +1,207 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License
+ */
+
+package android.server.am;
+
+import static android.app.WindowConfiguration.ACTIVITY_TYPE_STANDARD;
+import static android.app.WindowConfiguration.WINDOWING_MODE_PINNED;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertTrue;
+import static org.junit.Assume.assumeTrue;
+
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Test;
+
+/**
+ * Build/Install/Run:
+ *     atest CtsActivityManagerDeviceTestCases:KeyguardLockedTests
+ */
+public class KeyguardLockedTests extends KeyguardTestBase {
+
+    private static final String SHOW_WHEN_LOCKED_ACTIVITY = "ShowWhenLockedActivity";
+    private static final String PIP_ACTIVITY = "PipActivity";
+    private static final String PIP_ACTIVITY_ACTION_ENTER_PIP =
+            "android.server.am.PipActivity.enter_pip";
+    private static final String EXTRA_SHOW_OVER_KEYGUARD = "show_over_keyguard";
+
+    @Before
+    @Override
+    public void setUp() throws Exception {
+        super.setUp();
+
+        assumeTrue(isHandheld());
+
+        setLockCredential();
+    }
+
+    @After
+    @Override
+    public void tearDown() throws Exception {
+        super.tearDown();
+        tearDownLockCredentials();
+    }
+
+    @Test
+    public void testLockAndUnlock() throws Exception {
+        gotoKeyguard();
+        mAmWmState.waitForKeyguardShowingAndNotOccluded();
+        mAmWmState.assertKeyguardShowingAndNotOccluded();
+        unlockDeviceWithCredential();
+        mAmWmState.waitForKeyguardGone();
+        mAmWmState.assertKeyguardGone();
+    }
+
+    @Test
+    public void testDismissKeyguard() throws Exception {
+        gotoKeyguard();
+        mAmWmState.waitForKeyguardShowingAndNotOccluded();
+        mAmWmState.assertKeyguardShowingAndNotOccluded();
+        launchActivity("DismissKeyguardActivity");
+        enterAndConfirmLockCredential();
+        mAmWmState.waitForKeyguardGone();
+        mAmWmState.assertKeyguardGone();
+        mAmWmState.assertVisibility("DismissKeyguardActivity", true);
+    }
+
+    @Test
+    public void testDismissKeyguard_whileOccluded() throws Exception {
+        gotoKeyguard();
+        mAmWmState.waitForKeyguardShowingAndNotOccluded();
+        mAmWmState.assertKeyguardShowingAndNotOccluded();
+        launchActivity(SHOW_WHEN_LOCKED_ACTIVITY);
+        mAmWmState.computeState(new WaitForValidActivityState.Builder( SHOW_WHEN_LOCKED_ACTIVITY ).build());
+        mAmWmState.assertVisibility(SHOW_WHEN_LOCKED_ACTIVITY, true);
+        launchActivity("DismissKeyguardActivity");
+        enterAndConfirmLockCredential();
+        mAmWmState.waitForKeyguardGone();
+        mAmWmState.assertKeyguardGone();
+        mAmWmState.assertVisibility("DismissKeyguardActivity", true);
+        mAmWmState.assertVisibility(SHOW_WHEN_LOCKED_ACTIVITY, false);
+    }
+
+    @Test
+    public void testDismissKeyguard_fromShowWhenLocked_notAllowed() throws Exception {
+        gotoKeyguard();
+        mAmWmState.waitForKeyguardShowingAndNotOccluded();
+        mAmWmState.assertKeyguardShowingAndNotOccluded();
+        launchActivity(SHOW_WHEN_LOCKED_ACTIVITY);
+        mAmWmState.computeState(new WaitForValidActivityState.Builder( SHOW_WHEN_LOCKED_ACTIVITY ).build());
+        mAmWmState.assertVisibility(SHOW_WHEN_LOCKED_ACTIVITY, true);
+        executeShellCommand("am broadcast -a trigger_broadcast --ez dismissKeyguard true");
+        enterAndConfirmLockCredential();
+
+        // Make sure we stay on Keyguard.
+        mAmWmState.assertKeyguardShowingAndOccluded();
+        mAmWmState.assertVisibility(SHOW_WHEN_LOCKED_ACTIVITY, true);
+    }
+
+    @Test
+    public void testDismissKeyguardActivity_method() throws Exception {
+        final String logSeparator = clearLogcat();
+        gotoKeyguard();
+        mAmWmState.computeState();
+        assertTrue(mAmWmState.getAmState().getKeyguardControllerState().keyguardShowing);
+        launchActivity("DismissKeyguardMethodActivity");
+        enterAndConfirmLockCredential();
+        mAmWmState.waitForKeyguardGone();
+        mAmWmState.computeState(new WaitForValidActivityState.Builder( "DismissKeyguardMethodActivity").build());
+        mAmWmState.assertVisibility("DismissKeyguardMethodActivity", true);
+        assertFalse(mAmWmState.getAmState().getKeyguardControllerState().keyguardShowing);
+        assertOnDismissSucceededInLogcat(logSeparator);
+    }
+
+    @Test
+    public void testDismissKeyguardActivity_method_cancelled() throws Exception {
+        final String logSeparator = clearLogcat();
+        gotoKeyguard();
+        mAmWmState.computeState();
+        assertTrue(mAmWmState.getAmState().getKeyguardControllerState().keyguardShowing);
+        launchActivity("DismissKeyguardMethodActivity");
+        pressBackButton();
+        assertOnDismissCancelledInLogcat(logSeparator);
+        mAmWmState.computeState();
+        mAmWmState.assertVisibility("DismissKeyguardMethodActivity", false);
+        assertTrue(mAmWmState.getAmState().getKeyguardControllerState().keyguardShowing);
+        unlockDeviceWithCredential();
+    }
+
+    @Test
+    public void testEnterPipOverKeyguard() throws Exception {
+        assumeTrue(supportsPip());
+
+        // Go to the keyguard
+        gotoKeyguard();
+        mAmWmState.waitForKeyguardShowingAndNotOccluded();
+        assertTrue(mAmWmState.getAmState().getKeyguardControllerState().keyguardShowing);
+
+        // Enter PiP on an activity on top of the keyguard, and ensure that it prompts the user for
+        // their credentials and does not enter picture-in-picture yet
+        launchActivity(PIP_ACTIVITY, EXTRA_SHOW_OVER_KEYGUARD, "true");
+        executeShellCommand("am broadcast -a " + PIP_ACTIVITY_ACTION_ENTER_PIP);
+        mAmWmState.waitForKeyguardShowingAndOccluded();
+        mAmWmState.assertKeyguardShowingAndOccluded();
+        mAmWmState.assertDoesNotContainStack("Must not contain pinned stack.",
+                WINDOWING_MODE_PINNED, ACTIVITY_TYPE_STANDARD);
+
+        // Enter the credentials and ensure that the activity actually entered picture-in-picture
+        enterAndConfirmLockCredential();
+        mAmWmState.waitForKeyguardGone();
+        mAmWmState.assertKeyguardGone();
+        mAmWmState.assertContainsStack("Must contain pinned stack.", WINDOWING_MODE_PINNED,
+                ACTIVITY_TYPE_STANDARD);
+    }
+
+    @Test
+    public void testShowWhenLockedActivityAndPipActivity() throws Exception {
+        assumeTrue(supportsPip());
+
+        launchActivity(PIP_ACTIVITY);
+        executeShellCommand("am broadcast -a " + PIP_ACTIVITY_ACTION_ENTER_PIP);
+        mAmWmState.computeState(new WaitForValidActivityState.Builder(PIP_ACTIVITY).build());
+        mAmWmState.assertContainsStack("Must contain pinned stack.", WINDOWING_MODE_PINNED,
+                ACTIVITY_TYPE_STANDARD);
+        mAmWmState.assertVisibility(PIP_ACTIVITY, true);
+
+        launchActivity(SHOW_WHEN_LOCKED_ACTIVITY);
+        mAmWmState.computeState(
+                new WaitForValidActivityState.Builder(SHOW_WHEN_LOCKED_ACTIVITY).build());
+        mAmWmState.assertVisibility(SHOW_WHEN_LOCKED_ACTIVITY, true);
+
+        gotoKeyguard();
+        mAmWmState.waitForKeyguardShowingAndOccluded();
+        mAmWmState.assertKeyguardShowingAndOccluded();
+        mAmWmState.assertVisibility(SHOW_WHEN_LOCKED_ACTIVITY, true);
+        mAmWmState.assertVisibility(PIP_ACTIVITY, false);
+    }
+
+    @Test
+    public void testShowWhenLockedPipActivity() throws Exception {
+        assumeTrue(supportsPip());
+
+        launchActivity(PIP_ACTIVITY, EXTRA_SHOW_OVER_KEYGUARD, "true");
+        executeShellCommand("am broadcast -a " + PIP_ACTIVITY_ACTION_ENTER_PIP);
+        mAmWmState.computeState(new WaitForValidActivityState.Builder(PIP_ACTIVITY).build());
+        mAmWmState.assertContainsStack("Must contain pinned stack.", WINDOWING_MODE_PINNED,
+                ACTIVITY_TYPE_STANDARD);
+        mAmWmState.assertVisibility(PIP_ACTIVITY, true);
+
+        gotoKeyguard();
+        mAmWmState.waitForKeyguardShowingAndNotOccluded();
+        mAmWmState.assertKeyguardShowingAndNotOccluded();
+        mAmWmState.assertVisibility(PIP_ACTIVITY, false);
+    }
+}
diff --git a/tests/framework/base/activitymanager/src/android/server/am/KeyguardTestBase.java b/tests/framework/base/activitymanager/src/android/server/am/KeyguardTestBase.java
new file mode 100644
index 0000000..9a63396
--- /dev/null
+++ b/tests/framework/base/activitymanager/src/android/server/am/KeyguardTestBase.java
@@ -0,0 +1,62 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License
+ */
+
+package android.server.am;
+
+import static android.server.am.StateLogger.log;
+
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertTrue;
+import static org.junit.Assert.fail;
+
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
+
+public class KeyguardTestBase extends ActivityManagerTestBase {
+
+    protected void assertOnDismissSucceededInLogcat(String logSeparator) throws Exception {
+        assertInLogcat("KeyguardDismissLoggerCallback", "onDismissSucceeded", logSeparator);
+    }
+
+    protected void assertOnDismissCancelledInLogcat(String logSeparator) throws Exception {
+        assertInLogcat("KeyguardDismissLoggerCallback", "onDismissCancelled", logSeparator);
+    }
+
+    protected void assertOnDismissErrorInLogcat(String logSeparator) throws Exception {
+        assertInLogcat("KeyguardDismissLoggerCallback", "onDismissError", logSeparator);
+    }
+
+    private void assertInLogcat(String activityName, String entry, String logSeparator)
+            throws Exception {
+        final Pattern pattern = Pattern.compile("(.+)" + entry);
+        int tries = 0;
+        while (tries < 5) {
+            final String[] lines = getDeviceLogsForComponent(activityName, logSeparator);
+            log("Looking at logcat");
+            for (int i = lines.length - 1; i >= 0; i--) {
+                final String line = lines[i].trim();
+                log(line);
+                Matcher matcher = pattern.matcher(line);
+                if (matcher.matches()) {
+                    return;
+                }
+            }
+            tries++;
+            Thread.sleep(500);
+        }
+        fail("Not in logcat: " + entry);
+    }
+}
diff --git a/tests/framework/base/activitymanager/src/android/server/am/KeyguardTests.java b/tests/framework/base/activitymanager/src/android/server/am/KeyguardTests.java
new file mode 100644
index 0000000..015c926
--- /dev/null
+++ b/tests/framework/base/activitymanager/src/android/server/am/KeyguardTests.java
@@ -0,0 +1,366 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License
+ */
+
+package android.server.am;
+
+import static android.app.WindowConfiguration.ACTIVITY_TYPE_STANDARD;
+import static android.app.WindowConfiguration.WINDOWING_MODE_SPLIT_SCREEN_PRIMARY;
+import static android.view.Surface.ROTATION_90;
+import static android.view.WindowManager.LayoutParams.TYPE_WALLPAPER;
+
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertTrue;
+import static org.junit.Assume.assumeTrue;
+
+import android.platform.test.annotations.Presubmit;
+import android.server.am.WindowManagerState.WindowState;
+
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Test;
+
+/**
+ * Build/Install/Run:
+ *     atest CtsActivityManagerDeviceTestCases:KeyguardTests
+ */
+public class KeyguardTests extends KeyguardTestBase {
+
+    @Before
+    @Override
+    public void setUp() throws Exception {
+        super.setUp();
+
+        assumeTrue(isHandheld());
+        assertFalse(isUiModeLockedToVrHeadset());
+
+        // Set screen lock (swipe)
+        setLockDisabled(false);
+    }
+
+    @After
+    @Override
+    public void tearDown() throws Exception {
+        super.tearDown();
+
+        tearDownLockCredentials();
+    }
+
+    @Test
+    public void testKeyguardHidesActivity() throws Exception {
+        launchActivity("TestActivity");
+        mAmWmState.computeState(new WaitForValidActivityState.Builder( "TestActivity").build());
+        mAmWmState.assertVisibility("TestActivity", true);
+        gotoKeyguard();
+        mAmWmState.computeState();
+        mAmWmState.assertKeyguardShowingAndNotOccluded();
+        mAmWmState.assertVisibility("TestActivity", false);
+        unlockDevice();
+    }
+
+    @Test
+    public void testShowWhenLockedActivity() throws Exception {
+        launchActivity("ShowWhenLockedActivity");
+        mAmWmState.computeState(new WaitForValidActivityState.Builder( "ShowWhenLockedActivity").build());
+        mAmWmState.assertVisibility("ShowWhenLockedActivity", true);
+        gotoKeyguard();
+        mAmWmState.computeState();
+        mAmWmState.assertVisibility("ShowWhenLockedActivity", true);
+        mAmWmState.assertKeyguardShowingAndOccluded();
+        pressHomeButton();
+        unlockDevice();
+    }
+
+    /**
+     * Tests whether dialogs from SHOW_WHEN_LOCKED activities are also visible if Keyguard is
+     * showing.
+     */
+    @Test
+    public void testShowWhenLockedActivity_withDialog() throws Exception {
+        launchActivity("ShowWhenLockedWithDialogActivity");
+        mAmWmState.computeState(new WaitForValidActivityState.Builder("ShowWhenLockedWithDialogActivity").build());
+        mAmWmState.assertVisibility("ShowWhenLockedWithDialogActivity", true);
+        gotoKeyguard();
+        mAmWmState.computeState();
+        mAmWmState.assertVisibility("ShowWhenLockedWithDialogActivity", true);
+        assertTrue(mAmWmState.getWmState().allWindowsVisible(
+                getWindowName("ShowWhenLockedWithDialogActivity")));
+        mAmWmState.assertKeyguardShowingAndOccluded();
+        pressHomeButton();
+        unlockDevice();
+    }
+
+    /**
+     * Tests whether multiple SHOW_WHEN_LOCKED activities are shown if the topmost is translucent.
+     */
+    @Test
+    public void testMultipleShowWhenLockedActivities() throws Exception {
+        launchActivity("ShowWhenLockedActivity");
+        launchActivity("ShowWhenLockedTranslucentActivity");
+        mAmWmState.computeState(new WaitForValidActivityState.Builder("ShowWhenLockedActivity").build(),
+                new WaitForValidActivityState.Builder("ShowWhenLockedTranslucentActivity").build());
+        mAmWmState.assertVisibility("ShowWhenLockedActivity", true);
+        mAmWmState.assertVisibility("ShowWhenLockedTranslucentActivity", true);
+        gotoKeyguard();
+        mAmWmState.computeState();
+        mAmWmState.assertVisibility("ShowWhenLockedActivity", true);
+        mAmWmState.assertVisibility("ShowWhenLockedTranslucentActivity", true);
+        mAmWmState.assertKeyguardShowingAndOccluded();
+        pressHomeButton();
+        unlockDevice();
+    }
+
+    /**
+     * If we have a translucent SHOW_WHEN_LOCKED_ACTIVITY, the wallpaper should also be showing.
+     */
+    @Test
+    public void testTranslucentShowWhenLockedActivity() throws Exception {
+        launchActivity("ShowWhenLockedTranslucentActivity");
+        mAmWmState.computeState(new WaitForValidActivityState.Builder("ShowWhenLockedTranslucentActivity").build());
+        mAmWmState.assertVisibility("ShowWhenLockedTranslucentActivity", true);
+        gotoKeyguard();
+        mAmWmState.computeState();
+        mAmWmState.assertVisibility("ShowWhenLockedTranslucentActivity", true);
+        assertWallpaperShowing();
+        mAmWmState.assertKeyguardShowingAndOccluded();
+        pressHomeButton();
+        unlockDevice();
+    }
+
+    /**
+     * If we have a translucent SHOW_WHEN_LOCKED activity, the activity behind should not be shown.
+     */
+    @Test
+    public void testTranslucentDoesntRevealBehind() throws Exception {
+        launchActivity("TestActivity");
+        launchActivity("ShowWhenLockedTranslucentActivity");
+        mAmWmState.computeState(new WaitForValidActivityState.Builder("TestActivity").build(),
+                new WaitForValidActivityState.Builder("ShowWhenLockedTranslucentActivity").build());
+        mAmWmState.assertVisibility("TestActivity", true);
+        mAmWmState.assertVisibility("ShowWhenLockedTranslucentActivity", true);
+        gotoKeyguard();
+        mAmWmState.computeState();
+        mAmWmState.assertVisibility("ShowWhenLockedTranslucentActivity", true);
+        mAmWmState.assertVisibility("TestActivity", false);
+        mAmWmState.assertKeyguardShowingAndOccluded();
+        pressHomeButton();
+        unlockDevice();
+    }
+
+    @Test
+    public void testDialogShowWhenLockedActivity() throws Exception {
+        launchActivity("ShowWhenLockedDialogActivity");
+        mAmWmState.computeState(new WaitForValidActivityState.Builder( "ShowWhenLockedDialogActivity").build());
+        mAmWmState.assertVisibility("ShowWhenLockedDialogActivity", true);
+        gotoKeyguard();
+        mAmWmState.computeState();
+        mAmWmState.assertVisibility("ShowWhenLockedDialogActivity", true);
+        assertWallpaperShowing();
+        mAmWmState.assertKeyguardShowingAndOccluded();
+        pressHomeButton();
+        unlockDevice();
+    }
+
+    /**
+     * Test that showWhenLocked activity is fullscreen when shown over keyguard
+     */
+    @Test
+    @Presubmit
+    public void testShowWhenLockedActivityWhileSplit() throws Exception {
+        assumeTrue(supportsSplitScreenMultiWindow());
+
+        launchActivitiesInSplitScreen(
+                getLaunchActivityBuilder().setTargetActivityName(LAUNCHING_ACTIVITY),
+                getLaunchActivityBuilder().setTargetActivityName("ShowWhenLockedActivity")
+                        .setRandomData(true)
+                        .setMultipleTask(false)
+        );
+        mAmWmState.assertVisibility("ShowWhenLockedActivity", true);
+        gotoKeyguard();
+        mAmWmState.computeState(
+                new WaitForValidActivityState.Builder("ShowWhenLockedActivity").build());
+        mAmWmState.assertVisibility("ShowWhenLockedActivity", true);
+        mAmWmState.assertKeyguardShowingAndOccluded();
+        mAmWmState.assertDoesNotContainStack("Activity must be full screen.",
+                WINDOWING_MODE_SPLIT_SCREEN_PRIMARY, ACTIVITY_TYPE_STANDARD);
+        pressHomeButton();
+        unlockDevice();
+    }
+
+    /**
+     * Tests whether a FLAG_DISMISS_KEYGUARD activity occludes Keyguard.
+     */
+    @Test
+    public void testDismissKeyguardActivity() throws Exception {
+        gotoKeyguard();
+        mAmWmState.computeState();
+        assertTrue(mAmWmState.getAmState().getKeyguardControllerState().keyguardShowing);
+        launchActivity("DismissKeyguardActivity");
+        mAmWmState.waitForKeyguardShowingAndOccluded();
+        mAmWmState.computeState(new WaitForValidActivityState.Builder( "DismissKeyguardActivity").build());
+        mAmWmState.assertVisibility("DismissKeyguardActivity", true);
+        mAmWmState.assertKeyguardShowingAndOccluded();
+    }
+
+    @Test
+    public void testDismissKeyguardActivity_method() throws Exception {
+        final String logSeparator = clearLogcat();
+        gotoKeyguard();
+        mAmWmState.computeState();
+        assertTrue(mAmWmState.getAmState().getKeyguardControllerState().keyguardShowing);
+        launchActivity("DismissKeyguardMethodActivity");
+        mAmWmState.waitForKeyguardGone();
+        mAmWmState.computeState(new WaitForValidActivityState.Builder( "DismissKeyguardMethodActivity").build());
+        mAmWmState.assertVisibility("DismissKeyguardMethodActivity", true);
+        assertFalse(mAmWmState.getAmState().getKeyguardControllerState().keyguardShowing);
+        assertOnDismissSucceededInLogcat(logSeparator);
+    }
+
+    @Test
+    public void testDismissKeyguardActivity_method_notTop() throws Exception {
+        final String logSeparator = clearLogcat();
+        gotoKeyguard();
+        mAmWmState.computeState();
+        assertTrue(mAmWmState.getAmState().getKeyguardControllerState().keyguardShowing);
+        launchActivity("BroadcastReceiverActivity");
+        launchActivity("TestActivity");
+        executeShellCommand("am broadcast -a trigger_broadcast --ez dismissKeyguardMethod true");
+        assertOnDismissErrorInLogcat(logSeparator);
+    }
+
+    @Test
+    public void testDismissKeyguardActivity_method_turnScreenOn() throws Exception {
+        final String logSeparator = clearLogcat();
+        sleepDevice();
+        mAmWmState.computeState();
+        assertTrue(mAmWmState.getAmState().getKeyguardControllerState().keyguardShowing);
+        launchActivity("TurnScreenOnDismissKeyguardActivity");
+        mAmWmState.waitForKeyguardGone();
+        mAmWmState.computeState(new WaitForValidActivityState.Builder( "TurnScreenOnDismissKeyguardActivity").build());
+        mAmWmState.assertVisibility("TurnScreenOnDismissKeyguardActivity", true);
+        assertFalse(mAmWmState.getAmState().getKeyguardControllerState().keyguardShowing);
+        assertOnDismissSucceededInLogcat(logSeparator);
+    }
+
+    @Test
+    public void testDismissKeyguard_fromShowWhenLocked_notAllowed() throws Exception {
+        gotoKeyguard();
+        mAmWmState.waitForKeyguardShowingAndNotOccluded();
+        mAmWmState.assertKeyguardShowingAndNotOccluded();
+        launchActivity("ShowWhenLockedActivity");
+        mAmWmState.computeState(new WaitForValidActivityState.Builder( "ShowWhenLockedActivity" ).build());
+        mAmWmState.assertVisibility("ShowWhenLockedActivity", true);
+        mAmWmState.assertKeyguardShowingAndOccluded();
+        executeShellCommand("am broadcast -a trigger_broadcast --ez dismissKeyguard true");
+        mAmWmState.assertKeyguardShowingAndOccluded();
+        mAmWmState.assertVisibility("ShowWhenLockedActivity", true);
+    }
+
+    @Test
+    public void testKeyguardLock() throws Exception {
+        gotoKeyguard();
+        mAmWmState.waitForKeyguardShowingAndNotOccluded();
+        mAmWmState.assertKeyguardShowingAndNotOccluded();
+        launchActivity("KeyguardLockActivity");
+        mAmWmState.computeState(new WaitForValidActivityState.Builder( "KeyguardLockActivity" ).build());
+        mAmWmState.assertVisibility("KeyguardLockActivity", true);
+        executeShellCommand(FINISH_ACTIVITY_BROADCAST);
+        mAmWmState.waitForKeyguardShowingAndNotOccluded();
+        mAmWmState.assertKeyguardShowingAndNotOccluded();
+    }
+
+    @Test
+    public void testUnoccludeRotationChange() throws Exception {
+        gotoKeyguard();
+        mAmWmState.waitForKeyguardShowingAndNotOccluded();
+        mAmWmState.assertKeyguardShowingAndNotOccluded();
+        executeShellCommand(getAmStartCmd("ShowWhenLockedActivity"));
+        mAmWmState.computeState(new WaitForValidActivityState("ShowWhenLockedActivity"));
+        mAmWmState.assertVisibility("ShowWhenLockedActivity", true);
+        try (final RotationSession rotationSession = new RotationSession()) {
+            rotationSession.set(ROTATION_90);
+            pressHomeButton();
+            mAmWmState.waitForKeyguardShowingAndNotOccluded();
+            mAmWmState.waitForDisplayUnfrozen();
+            mAmWmState.assertSanity();
+            mAmWmState.assertHomeActivityVisible(false);
+            mAmWmState.assertKeyguardShowingAndNotOccluded();
+            mAmWmState.assertVisibility("ShowWhenLockedActivity", false);
+        }
+    }
+
+    private void assertWallpaperShowing() {
+        WindowState wallpaper =
+                mAmWmState.getWmState().findFirstWindowWithType(TYPE_WALLPAPER);
+        assertNotNull(wallpaper);
+        assertTrue(wallpaper.isShown());
+    }
+
+    @Test
+    public void testDismissKeyguardAttrActivity_method_turnScreenOn() throws Exception {
+        final String activityName = "TurnScreenOnAttrDismissKeyguardActivity";
+        sleepDevice();
+
+        final String logSeparator = clearLogcat();
+        mAmWmState.computeState();
+        assertTrue(mAmWmState.getAmState().getKeyguardControllerState().keyguardShowing);
+        launchActivity(activityName);
+        mAmWmState.waitForKeyguardGone();
+        mAmWmState.assertVisibility(activityName, true);
+        assertFalse(mAmWmState.getAmState().getKeyguardControllerState().keyguardShowing);
+        assertOnDismissSucceededInLogcat(logSeparator);
+        assertTrue(isDisplayOn());
+    }
+
+    @Test
+    public void testDismissKeyguardAttrActivity_method_turnScreenOn_withSecureKeyguard() throws Exception {
+        final String activityName = "TurnScreenOnAttrDismissKeyguardActivity";
+
+        setLockCredential();
+        sleepDevice();
+
+        mAmWmState.computeState();
+        assertTrue(mAmWmState.getAmState().getKeyguardControllerState().keyguardShowing);
+        launchActivity(activityName);
+        mAmWmState.waitForKeyguardShowingAndNotOccluded();
+        mAmWmState.assertVisibility(activityName, false);
+        assertTrue(mAmWmState.getAmState().getKeyguardControllerState().keyguardShowing);
+        assertTrue(isDisplayOn());
+    }
+
+    @Test
+    public void testScreenOffWhileOccludedStopsActivity() throws Exception {
+        final String logSeparator = clearLogcat();
+        gotoKeyguard();
+        mAmWmState.waitForKeyguardShowingAndNotOccluded();
+        mAmWmState.assertKeyguardShowingAndNotOccluded();
+        launchActivity("ShowWhenLockedAttrActivity");
+        mAmWmState.computeState(new WaitForValidActivityState.Builder( "ShowWhenLockedAttrActivity" ).build());
+        mAmWmState.assertVisibility("ShowWhenLockedAttrActivity", true);
+        mAmWmState.assertKeyguardShowingAndOccluded();
+        sleepDevice();
+        assertSingleLaunchAndStop("ShowWhenLockedAttrActivity", logSeparator);
+    }
+
+    @Test
+    public void testScreenOffCausesSingleStop() throws Exception {
+        final String logSeparator = clearLogcat();
+        launchActivity("TestActivity");
+        mAmWmState.assertVisibility("TestActivity", true);
+        sleepDevice();
+        assertSingleLaunchAndStop("TestActivity", logSeparator);
+    }
+}
diff --git a/tests/framework/base/activitymanager/src/android/server/am/KeyguardTransitionTests.java b/tests/framework/base/activitymanager/src/android/server/am/KeyguardTransitionTests.java
new file mode 100644
index 0000000..cb0d5b5e
--- /dev/null
+++ b/tests/framework/base/activitymanager/src/android/server/am/KeyguardTransitionTests.java
@@ -0,0 +1,137 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License
+ */
+
+package android.server.am;
+
+import static android.server.am.WindowManagerState.TRANSIT_ACTIVITY_OPEN;
+import static android.server.am.WindowManagerState.TRANSIT_KEYGUARD_GOING_AWAY;
+import static android.server.am.WindowManagerState.TRANSIT_KEYGUARD_GOING_AWAY_ON_WALLPAPER;
+import static android.server.am.WindowManagerState.TRANSIT_KEYGUARD_OCCLUDE;
+import static android.server.am.WindowManagerState.TRANSIT_KEYGUARD_UNOCCLUDE;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assume.assumeFalse;
+import static org.junit.Assume.assumeTrue;
+
+import org.junit.Before;
+import org.junit.Test;
+
+/**
+ * Build/Install/Run:
+ *     atest CtsActivityManagerDeviceTestCases:KeyguardTransitionTests
+ */
+public class KeyguardTransitionTests extends ActivityManagerTestBase {
+
+    @Before
+    @Override
+    public void setUp() throws Exception {
+        super.setUp();
+
+        assumeTrue(isHandheld());
+        assumeFalse(isUiModeLockedToVrHeadset());
+
+        // Set screen lock (swipe)
+        setLockDisabled(false);
+    }
+
+    @Test
+    public void testUnlock() throws Exception {
+        launchActivity("TestActivity");
+        gotoKeyguard();
+        unlockDevice();
+        mAmWmState.computeState(new WaitForValidActivityState("TestActivity"));
+        assertEquals("Picked wrong transition", TRANSIT_KEYGUARD_GOING_AWAY,
+                mAmWmState.getWmState().getLastTransition());
+    }
+    @Test
+    public void testUnlockWallpaper() throws Exception {
+        launchActivity("WallpaperActivity");
+        gotoKeyguard();
+        unlockDevice();
+        mAmWmState.computeState(new WaitForValidActivityState("WallpaperActivity"));
+        assertEquals("Picked wrong transition", TRANSIT_KEYGUARD_GOING_AWAY_ON_WALLPAPER,
+                mAmWmState.getWmState().getLastTransition());
+    }
+    @Test
+    public void testOcclude() throws Exception {
+        gotoKeyguard();
+        launchActivity("ShowWhenLockedActivity");
+        mAmWmState.computeState(new WaitForValidActivityState("ShowWhenLockedActivity"));
+        assertEquals("Picked wrong transition", TRANSIT_KEYGUARD_OCCLUDE,
+                mAmWmState.getWmState().getLastTransition());
+    }
+    @Test
+    public void testUnocclude() throws Exception {
+        gotoKeyguard();
+        launchActivity("ShowWhenLockedActivity");
+        launchActivity("TestActivity");
+        mAmWmState.waitForKeyguardShowingAndNotOccluded();
+        mAmWmState.computeState();
+        assertEquals("Picked wrong transition", TRANSIT_KEYGUARD_UNOCCLUDE,
+                mAmWmState.getWmState().getLastTransition());
+    }
+    @Test
+    public void testNewActivityDuringOccluded() throws Exception {
+        launchActivity("ShowWhenLockedActivity");
+        gotoKeyguard();
+        launchActivity("ShowWhenLockedWithDialogActivity");
+        mAmWmState.computeState(new WaitForValidActivityState.Builder("ShowWhenLockedWithDialogActivity").build());
+        assertEquals("Picked wrong transition", TRANSIT_ACTIVITY_OPEN,
+                mAmWmState.getWmState().getLastTransition());
+    }
+    @Test
+    public void testOccludeManifestAttr() throws Exception {
+         String activityName = "ShowWhenLockedAttrActivity";
+
+         gotoKeyguard();
+         final String logSeparator = clearLogcat();
+         launchActivity(activityName);
+         mAmWmState.computeState(new WaitForValidActivityState.Builder(activityName).build());
+         assertEquals("Picked wrong transition", TRANSIT_KEYGUARD_OCCLUDE,
+                 mAmWmState.getWmState().getLastTransition());
+         assertSingleLaunch(activityName, logSeparator);
+    }
+    @Test
+    public void testOccludeAttrRemove() throws Exception {
+        String activityName = "ShowWhenLockedAttrRemoveAttrActivity";
+
+        gotoKeyguard();
+        String logSeparator = clearLogcat();
+        launchActivity(activityName);
+        mAmWmState.computeState(new WaitForValidActivityState.Builder(activityName).build());
+        assertEquals("Picked wrong transition", TRANSIT_KEYGUARD_OCCLUDE,
+                mAmWmState.getWmState().getLastTransition());
+        assertSingleLaunch(activityName, logSeparator);
+
+        gotoKeyguard();
+        logSeparator = clearLogcat();
+        launchActivity(activityName);
+        mAmWmState.computeState(new WaitForValidActivityState.Builder(activityName).build());
+        assertSingleStartAndStop(activityName, logSeparator);
+    }
+    @Test
+    public void testNewActivityDuringOccludedWithAttr() throws Exception {
+        String activityName1 = "ShowWhenLockedAttrActivity";
+        String activityName2 = "ShowWhenLockedAttrWithDialogActivity";
+
+        launchActivity(activityName1);
+        gotoKeyguard();
+        launchActivity(activityName2);
+        mAmWmState.computeState(new WaitForValidActivityState.Builder(activityName2).build());
+        assertEquals("Picked wrong transition", TRANSIT_ACTIVITY_OPEN,
+                mAmWmState.getWmState().getLastTransition());
+    }
+}
diff --git a/tests/framework/base/activitymanager/src/android/server/am/PrereleaseSdkTest.java b/tests/framework/base/activitymanager/src/android/server/am/PrereleaseSdkTest.java
new file mode 100644
index 0000000..6e98083
--- /dev/null
+++ b/tests/framework/base/activitymanager/src/android/server/am/PrereleaseSdkTest.java
@@ -0,0 +1,88 @@
+/*
+ * 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.server.am;
+
+import static org.junit.Assert.assertTrue;
+
+import org.junit.After;
+import org.junit.Test;
+
+/**
+ * Ensure that compatibility dialog is shown when launching an application built
+ * against a prerelease SDK.
+ * <p>Build/Install/Run:
+ *     atest CtsActivityManagerDeviceTestCases:PrereleaseSdkTest
+ */
+public class PrereleaseSdkTest extends ActivityManagerTestBase {
+    private static final String AM_START_COMMAND = "am start -n %s/%s.%s";
+    private static final String AM_FORCE_STOP = "am force-stop %s";
+
+    private static final int ACTIVITY_TIMEOUT_MILLIS = 1000;
+    private static final int WINDOW_TIMEOUT_MILLIS = 1000;
+
+    @After
+    @Override
+    public void tearDown() throws Exception {
+        super.tearDown();
+
+        // Ensure app process is stopped.
+        forceStopPackage("android.server.am.prerelease");
+        forceStopPackage("android.server.am");
+    }
+
+    @Test
+    public void testCompatibilityDialog() throws Exception {
+        // Launch target app.
+        startActivity("android.server.am.prerelease", "MainActivity");
+        verifyWindowDisplayed("MainActivity", ACTIVITY_TIMEOUT_MILLIS);
+        verifyWindowDisplayed("UnsupportedCompileSdkDialog", WINDOW_TIMEOUT_MILLIS);
+
+        // Go back to dismiss the warning dialog.
+        executeShellCommand("input keyevent 4");
+
+        // Go back again to formally stop the app. If we just kill the process, it'll attempt to
+        // resume rather than starting from scratch (as far as ActivityStack is concerned) and it
+        // won't invoke the warning dialog.
+        executeShellCommand("input keyevent 4");
+    }
+
+    private void forceStopPackage(String packageName) {
+        final String forceStopCmd = String.format(AM_FORCE_STOP, packageName);
+        executeShellCommand(forceStopCmd);
+    }
+
+    private void startActivity(String packageName, String activityName){
+        executeShellCommand(getStartCommand(packageName, activityName));
+    }
+
+    private String getStartCommand(String packageName, String activityName) {
+        return String.format(AM_START_COMMAND, packageName, packageName, activityName);
+    }
+
+    private void verifyWindowDisplayed(String windowName, long timeoutMillis) {
+        boolean success = false;
+
+        // Verify that compatibility dialog is shown within 1000ms.
+        final long timeoutTimeMillis = System.currentTimeMillis() + timeoutMillis;
+        while (!success && System.currentTimeMillis() < timeoutTimeMillis) {
+            final String output = executeShellCommand("dumpsys window");
+            success = output.contains(windowName);
+        }
+
+        assertTrue(windowName + " was not displayed", success);
+    }
+}
diff --git a/tests/framework/base/activitymanager/src/android/server/am/SplashscreenTests.java b/tests/framework/base/activitymanager/src/android/server/am/SplashscreenTests.java
new file mode 100644
index 0000000..f4edf0f
--- /dev/null
+++ b/tests/framework/base/activitymanager/src/android/server/am/SplashscreenTests.java
@@ -0,0 +1,83 @@
+/*
+ * 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.server.am;
+
+import static org.junit.Assert.assertTrue;
+import static org.junit.Assert.fail;
+
+import android.graphics.Bitmap;
+import android.graphics.Color;
+import android.graphics.Rect;
+
+import org.junit.Test;
+
+/**
+ * Build/Install/Run:
+ *     atest CtsActivityManagerDeviceTestCases:SplashscreenTests
+ */
+public class SplashscreenTests extends ActivityManagerTestBase {
+
+    @Test
+    public void testSplashscreenContent() throws Exception {
+        launchActivityNoWait("SplashscreenActivity");
+        mAmWmState.waitForAppTransitionIdle();
+        mAmWmState.getWmState().getStableBounds();
+        final Bitmap image = takeScreenshot();
+        // Use ratios to flexibly accomodate circular or not quite rectangular displays
+        // Note: Color.BLACK is the pixel color outside of the display region
+        assertColors(image, mAmWmState.getWmState().getStableBounds(),
+            Color.RED, 0.50f, Color.BLACK, 0.01f);
+    }
+
+    private void assertColors(Bitmap img, Rect bounds, int primaryColor,
+        float expectedPrimaryRatio, int secondaryColor, float acceptableWrongRatio) {
+
+        int primaryPixels = 0;
+        int secondaryPixels = 0;
+        int wrongPixels = 0;
+        for (int x = bounds.left; x < bounds.right; x++) {
+            for (int y = bounds.top; y < bounds.bottom; y++) {
+                assertTrue(x < img.getWidth());
+                assertTrue(y < img.getHeight());
+                final int color = img.getPixel(x, y);
+                if (primaryColor == color) {
+                    primaryPixels++;
+                } else if (secondaryColor == color) {
+                    secondaryPixels++;
+                } else {
+                    wrongPixels++;
+                }
+            }
+        }
+
+        final int totalPixels = bounds.width() * bounds.height();
+        final float primaryRatio = (float) primaryPixels / totalPixels;
+        if (primaryRatio < expectedPrimaryRatio) {
+            fail("Less than " + (expectedPrimaryRatio * 100.0f)
+                    + "% of pixels have non-primary color primaryPixels=" + primaryPixels
+                    + " secondaryPixels=" + secondaryPixels + " wrongPixels=" + wrongPixels);
+        }
+        // Some pixels might be covered by screen shape decorations, like rounded corners.
+        // On circular displays, there is an antialiased edge.
+        final float wrongRatio = (float) wrongPixels / totalPixels;
+        if (wrongRatio > acceptableWrongRatio) {
+            fail("More than " + (acceptableWrongRatio * 100.0f)
+                    + "% of pixels have wrong color primaryPixels=" + primaryPixels
+                    + " secondaryPixels=" + secondaryPixels + " wrongPixels=" + wrongPixels);
+        }
+    }
+}
diff --git a/tests/framework/base/activitymanager/src/android/server/am/lifecycle/ActivityLifecycleClientTestBase.java b/tests/framework/base/activitymanager/src/android/server/am/lifecycle/ActivityLifecycleClientTestBase.java
new file mode 100644
index 0000000..a5673a6
--- /dev/null
+++ b/tests/framework/base/activitymanager/src/android/server/am/lifecycle/ActivityLifecycleClientTestBase.java
@@ -0,0 +1,83 @@
+package android.server.am.lifecycle;
+
+import static android.server.am.StateLogger.log;
+
+import android.app.Activity;
+import android.content.Intent;
+import android.server.am.ActivityManagerTestBase;
+import android.support.test.InstrumentationRegistry;
+import android.support.test.rule.ActivityTestRule;
+import android.support.test.runner.lifecycle.ActivityLifecycleMonitor;
+import android.support.test.runner.lifecycle.ActivityLifecycleMonitorRegistry;
+import android.support.test.runner.lifecycle.Stage;
+import android.util.Pair;
+
+import org.junit.After;
+import org.junit.Before;
+
+/** Base class for device-side tests that verify correct activity lifecycle transitions. */
+public class ActivityLifecycleClientTestBase extends ActivityManagerTestBase {
+
+    final ActivityTestRule mFirstActivityTestRule = new ActivityTestRule(FirstActivity.class,
+            true /* initialTouchMode */, false /* launchActivity */);
+
+    final ActivityTestRule mSecondActivityTestRule = new ActivityTestRule(SecondActivity.class,
+            true /* initialTouchMode */, false /* launchActivity */);
+
+    private final ActivityLifecycleMonitor mLifecycleMonitor = ActivityLifecycleMonitorRegistry
+            .getInstance();
+    private LifecycleLog mLifecycleLog;
+    private LifecycleTracker mLifecycleTracker;
+
+    @Before
+    @Override
+    public void setUp() throws Exception {
+        super.setUp();
+        // Log transitions for all activities that belong to this app.
+        mLifecycleLog = new LifecycleLog();
+        mLifecycleMonitor.addLifecycleCallback(mLifecycleLog);
+
+        // Track transitions and allow waiting for pending activity states.
+        mLifecycleTracker = new LifecycleTracker(mLifecycleLog);
+        mLifecycleMonitor.addLifecycleCallback(mLifecycleTracker);
+    }
+
+    @After
+    @Override
+    public void tearDown() throws Exception {
+        mLifecycleMonitor.removeLifecycleCallback(mLifecycleLog);
+        mLifecycleMonitor.removeLifecycleCallback(mLifecycleTracker);
+        super.tearDown();
+    }
+
+    /** Launch an activity given a class. */
+    protected Activity launchActivity(Class<? extends Activity> activityClass) {
+        final Intent intent = new Intent(InstrumentationRegistry.getTargetContext(), activityClass);
+        return InstrumentationRegistry.getInstrumentation().startActivitySync(intent);
+    }
+
+    /**
+     * Blocking call that will wait for activities to reach expected states with timeout.
+     */
+    @SafeVarargs
+    final void waitAndAssertActivityStates(Pair<Activity, Stage>... activityStates) {
+        log("Start waitAndAssertActivityStates");
+        mLifecycleTracker.waitAndAssertActivityStates(activityStates);
+    }
+
+    LifecycleLog getLifecycleLog() {
+        return mLifecycleLog;
+    }
+
+    static Pair<Activity, Stage> state(Activity activity, Stage stage) {
+        return new Pair<>(activity, stage);
+    }
+
+    // Test activity
+    public static class FirstActivity extends Activity {
+    }
+
+    // Test activity
+    public static class SecondActivity extends Activity {
+    }
+}
diff --git a/tests/framework/base/activitymanager/src/android/server/am/lifecycle/ActivityLifecycleKeyguardTests.java b/tests/framework/base/activitymanager/src/android/server/am/lifecycle/ActivityLifecycleKeyguardTests.java
new file mode 100644
index 0000000..982f30d
--- /dev/null
+++ b/tests/framework/base/activitymanager/src/android/server/am/lifecycle/ActivityLifecycleKeyguardTests.java
@@ -0,0 +1,46 @@
+package android.server.am.lifecycle;
+
+import static android.support.test.runner.lifecycle.Stage.STOPPED;
+
+import android.app.Activity;
+import android.content.Intent;
+import android.platform.test.annotations.Presubmit;
+import android.support.test.filters.MediumTest;
+import android.support.test.runner.AndroidJUnit4;
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+/**
+ * Build/Install/Run:
+ *     atest CtsActivityManagerDeviceTestCases:ActivityLifecycleKeyguardTests
+ */
+@MediumTest
+@RunWith(AndroidJUnit4.class)
+@Presubmit
+public class ActivityLifecycleKeyguardTests extends ActivityLifecycleClientTestBase {
+
+    @Before
+    @Override
+    public void setUp() throws Exception {
+        super.setUp();
+        setLockCredential();
+        gotoKeyguard();
+    }
+
+    @After
+    @Override
+    public void tearDown() throws Exception {
+        tearDownLockCredentials();
+        super.tearDown();
+    }
+
+    @Test
+    public void testSingleLaunch() throws Exception {
+        final Activity activity = mFirstActivityTestRule.launchActivity(new Intent());
+        waitAndAssertActivityStates(state(activity, STOPPED));
+
+        LifecycleVerifier.assertLaunchAndStopSequence(FirstActivity.class, getLifecycleLog());
+    }
+}
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
new file mode 100644
index 0000000..4ec99fa
--- /dev/null
+++ b/tests/framework/base/activitymanager/src/android/server/am/lifecycle/ActivityLifecycleTests.java
@@ -0,0 +1,71 @@
+package android.server.am.lifecycle;
+
+import static android.support.test.runner.lifecycle.Stage.DESTROYED;
+import static android.support.test.runner.lifecycle.Stage.RESUMED;
+import static android.support.test.runner.lifecycle.Stage.STOPPED;
+
+import android.app.Activity;
+import android.content.Intent;
+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.runner.AndroidJUnit4;
+
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+/**
+ * Build/Install/Run:
+ *     atest CtsActivityManagerDeviceTestCases:ActivityLifecycleTests
+ */
+@MediumTest
+@RunWith(AndroidJUnit4.class)
+@Presubmit
+public class ActivityLifecycleTests extends ActivityLifecycleClientTestBase {
+
+    @Test
+    public void testSingleLaunch() throws Exception {
+        final Activity activity = mFirstActivityTestRule.launchActivity(new Intent());
+        waitAndAssertActivityStates(state(activity, RESUMED));
+
+        LifecycleVerifier.assertLaunchSequence(FirstActivity.class, getLifecycleLog());
+    }
+
+    @Test
+    public void testLaunchOnTop() throws Exception {
+        final Activity firstActivity = mFirstActivityTestRule.launchActivity(new Intent());
+        waitAndAssertActivityStates(state(firstActivity, RESUMED));
+
+        getLifecycleLog().clear();
+        final Activity secondActivity = mSecondActivityTestRule.launchActivity(new Intent());
+        waitAndAssertActivityStates(state(firstActivity, STOPPED),
+                state(secondActivity, RESUMED));
+
+        LifecycleVerifier.assertLaunchSequence(SecondActivity.class, FirstActivity.class,
+                getLifecycleLog());
+    }
+
+    @FlakyTest(bugId = 70649184)
+    @Test
+    public void testLaunchAndDestroy() throws Exception {
+        final Activity activity = mFirstActivityTestRule.launchActivity(new Intent());
+
+        activity.finish();
+        waitAndAssertActivityStates(state(activity, DESTROYED));
+
+        LifecycleVerifier.assertLaunchAndDestroySequence(FirstActivity.class, getLifecycleLog());
+    }
+
+    @Test
+    public void testRelaunch() throws Exception {
+        final Activity activity = mFirstActivityTestRule.launchActivity(new Intent());
+        waitAndAssertActivityStates(state(activity, RESUMED));
+
+        getLifecycleLog().clear();
+        InstrumentationRegistry.getInstrumentation().runOnMainSync(activity::recreate);
+        waitAndAssertActivityStates(state(activity, RESUMED));
+
+        LifecycleVerifier.assertRelaunchSequence(FirstActivity.class, getLifecycleLog());
+    }
+}
diff --git a/tests/framework/base/activitymanager/src/android/server/am/lifecycle/LifecycleLog.java b/tests/framework/base/activitymanager/src/android/server/am/lifecycle/LifecycleLog.java
new file mode 100644
index 0000000..dab6316
--- /dev/null
+++ b/tests/framework/base/activitymanager/src/android/server/am/lifecycle/LifecycleLog.java
@@ -0,0 +1,50 @@
+package android.server.am.lifecycle;
+
+import static android.server.am.StateLogger.log;
+
+import android.app.Activity;
+import android.support.test.runner.lifecycle.ActivityLifecycleCallback;
+import android.support.test.runner.lifecycle.Stage;
+import android.util.Pair;
+
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * Used as a shared log storage of activity lifecycle transitions.
+ */
+class LifecycleLog implements ActivityLifecycleCallback {
+
+    private List<Pair<String, Stage>> mLog = new ArrayList<>();
+
+    /** Clear the entire transition log. */
+    void clear() {
+        mLog.clear();
+    }
+
+    /** Add transition of an activity to the log. */
+    @Override
+    public void onActivityLifecycleChanged(Activity activity, Stage stage) {
+        final String activityName = activity.getClass().getCanonicalName();
+        log("Activity " + activityName + " moved to stage " + stage);
+        mLog.add(new Pair<>(activityName, stage));
+    }
+
+    /** Get logs for all recorded transitions. */
+    List<Pair<String, Stage>> getLog() {
+        return mLog;
+    }
+
+    /** Get transition logs for the specified activity. */
+    List<Stage> getActivityLog(Class<? extends Activity> activityClass) {
+        final String activityName = activityClass.getCanonicalName();
+        log("Looking up log for activity: " + activityName);
+        final List<Stage> activityLog = new ArrayList<>();
+        for (Pair<String, Stage> transition : mLog) {
+            if (transition.first.equals(activityName)) {
+                activityLog.add(transition.second);
+            }
+        }
+        return activityLog;
+    }
+}
diff --git a/tests/framework/base/activitymanager/src/android/server/am/lifecycle/LifecycleTracker.java b/tests/framework/base/activitymanager/src/android/server/am/lifecycle/LifecycleTracker.java
new file mode 100644
index 0000000..df2cff7
--- /dev/null
+++ b/tests/framework/base/activitymanager/src/android/server/am/lifecycle/LifecycleTracker.java
@@ -0,0 +1,76 @@
+package android.server.am.lifecycle;
+
+import static org.junit.Assert.fail;
+
+import android.annotation.SuppressLint;
+import android.app.Activity;
+import android.support.test.runner.lifecycle.ActivityLifecycleCallback;
+import android.support.test.runner.lifecycle.Stage;
+import android.util.Pair;
+
+import java.util.ArrayList;
+import java.util.List;
+import java.util.function.BooleanSupplier;
+
+/**
+ * Gets notified about activity lifecycle updates and provides blocking mechanism to wait until
+ * expected activity states are reached.
+ */
+public class LifecycleTracker implements ActivityLifecycleCallback {
+
+    private LifecycleLog mLifecycleLog;
+
+    LifecycleTracker(LifecycleLog lifecycleLog) {
+        mLifecycleLog = lifecycleLog;
+    }
+
+    void waitAndAssertActivityStates(Pair<Activity, Stage>[] activityStates) {
+        final boolean waitResult = waitForConditionWithTimeout(
+                () -> pendingStates(activityStates).isEmpty(), 5 * 1000);
+
+        if (!waitResult) {
+            fail("Expected lifecycle states not achieved: " + pendingStates(activityStates));
+        }
+    }
+
+    @Override
+    synchronized public void onActivityLifecycleChanged(Activity activity, Stage stage) {
+        notify();
+    }
+
+    /** Get a list of activity states that were not reached yet. */
+    private List<Pair<Activity, Stage>> pendingStates(Pair<Activity, Stage>[] activityStates) {
+        final List<Pair<Activity, Stage>> notReachedActivityStates = new ArrayList<>();
+
+        for (Pair<Activity, Stage> activityState : activityStates) {
+            final Activity activity = activityState.first;
+            final List<Stage> transitionList = mLifecycleLog.getActivityLog(activity.getClass());
+            if (transitionList.isEmpty()
+                    || transitionList.get(transitionList.size() - 1) != activityState.second) {
+                // The activity either hasn't got any state transitions yet or the current state is
+                // not the one we expect.
+                notReachedActivityStates.add(activityState);
+            }
+        }
+        return notReachedActivityStates;
+    }
+
+    /** Blocking call to wait for a condition to become true with max timeout. */
+    synchronized private boolean waitForConditionWithTimeout(BooleanSupplier waitCondition,
+            long timeoutMs) {
+        final long timeout = System.currentTimeMillis() + timeoutMs;
+        while (!waitCondition.getAsBoolean()) {
+            final long waitMs = timeout - System.currentTimeMillis();
+            if (waitMs <= 0) {
+                // Timeout expired.
+                return false;
+            }
+            try {
+                wait(timeoutMs);
+            } catch (InterruptedException e) {
+                // Weird, let's retry.
+            }
+        }
+        return true;
+    }
+}
diff --git a/tests/framework/base/activitymanager/src/android/server/am/lifecycle/LifecycleVerifier.java b/tests/framework/base/activitymanager/src/android/server/am/lifecycle/LifecycleVerifier.java
new file mode 100644
index 0000000..bbb3ad8
--- /dev/null
+++ b/tests/framework/base/activitymanager/src/android/server/am/lifecycle/LifecycleVerifier.java
@@ -0,0 +1,94 @@
+package android.server.am.lifecycle;
+
+import static android.support.test.runner.lifecycle.Stage.CREATED;
+import static android.support.test.runner.lifecycle.Stage.DESTROYED;
+import static android.support.test.runner.lifecycle.Stage.PAUSED;
+import static android.support.test.runner.lifecycle.Stage.PRE_ON_CREATE;
+import static android.support.test.runner.lifecycle.Stage.RESUMED;
+import static android.support.test.runner.lifecycle.Stage.STARTED;
+import static android.support.test.runner.lifecycle.Stage.STOPPED;
+
+import android.app.Activity;
+import android.support.test.runner.lifecycle.Stage;
+import android.util.Pair;
+
+import java.util.Arrays;
+import java.util.List;
+
+import static android.server.am.StateLogger.log;
+import static org.junit.Assert.assertEquals;
+
+/** Util class that verifies correct activity state transition sequences. */
+class LifecycleVerifier {
+
+    static void assertLaunchSequence(Class<? extends Activity> activityClass,
+            LifecycleLog lifecycleLog) {
+        final List<Stage> observedTransitions = lifecycleLog.getActivityLog(activityClass);
+        log("Observed sequence: " + observedTransitions);
+        final String errorMessage = errorDuringTransition(activityClass, "launch");
+
+        final List<Stage> expectedTransitions =
+                Arrays.asList(PRE_ON_CREATE, CREATED, STARTED, RESUMED);
+        assertEquals(errorMessage, expectedTransitions, observedTransitions);
+    }
+
+    static void assertLaunchSequence(Class<? extends Activity> launchingActivity,
+            Class<? extends Activity> existingActivity, LifecycleLog lifecycleLog) {
+        final List<Pair<String, Stage>> observedTransitions = lifecycleLog.getLog();
+        log("Observed sequence: " + observedTransitions);
+        final String errorMessage = errorDuringTransition(launchingActivity, "launch");
+
+        final List<Pair<String, Stage>> expectedTransitions = Arrays.asList(
+                transition(existingActivity, PAUSED),
+                transition(launchingActivity, PRE_ON_CREATE),
+                transition(launchingActivity, CREATED),
+                transition(launchingActivity, STARTED),
+                transition(launchingActivity, RESUMED),
+                transition(existingActivity, STOPPED));
+        assertEquals(errorMessage, expectedTransitions, observedTransitions);
+    }
+
+    static void assertLaunchAndStopSequence(Class<? extends Activity> activityClass,
+            LifecycleLog lifecycleLog) {
+        final List<Stage> observedTransitions = lifecycleLog.getActivityLog(activityClass);
+        log("Observed sequence: " + observedTransitions);
+        final String errorMessage = errorDuringTransition(activityClass, "launch and stop");
+
+        final List<Stage> expectedTransitions =
+                Arrays.asList(PRE_ON_CREATE, CREATED, STARTED, RESUMED, PAUSED, STOPPED);
+        assertEquals(errorMessage, expectedTransitions, observedTransitions);
+    }
+
+    static void assertLaunchAndDestroySequence(Class<? extends Activity> activityClass,
+            LifecycleLog lifecycleLog) {
+        final List<Stage> observedTransitions = lifecycleLog.getActivityLog(activityClass);
+        log("Observed sequence: " + observedTransitions);
+        final String errorMessage = errorDuringTransition(activityClass, "launch and destroy");
+
+        final List<Stage> expectedTransitions =
+                Arrays.asList(PRE_ON_CREATE, CREATED, STARTED, RESUMED, PAUSED, STOPPED, DESTROYED);
+        assertEquals(errorMessage, expectedTransitions, observedTransitions);
+    }
+
+    static void assertRelaunchSequence(Class<? extends Activity> activityClass,
+            LifecycleLog lifecycleLog) {
+        final List<Stage> observedTransitions = lifecycleLog.getActivityLog(activityClass);
+        log("Observed sequence: " + observedTransitions);
+        final String errorMessage = errorDuringTransition(activityClass, "relaunch");
+
+        final List<Stage> expectedTransitions =
+                Arrays.asList(PAUSED, STOPPED, DESTROYED, PRE_ON_CREATE, CREATED, STARTED, RESUMED);
+        assertEquals(errorMessage, expectedTransitions, observedTransitions);
+    }
+
+    private static Pair<String, Stage> transition(
+            Class<? extends Activity> activityClass, Stage state) {
+        return new Pair<>(activityClass.getCanonicalName(), state);
+    }
+
+    private static String errorDuringTransition(Class<? extends Activity> activityClass,
+            String transition) {
+        return "Failed verification during moving activity: " + activityClass.getCanonicalName()
+                + " through transition: " + transition;
+    }
+}
diff --git a/tests/framework/base/activitymanager/testsdk25/Android.mk b/tests/framework/base/activitymanager/testsdk25/Android.mk
new file mode 100644
index 0000000..e55d5f0
--- /dev/null
+++ b/tests/framework/base/activitymanager/testsdk25/Android.mk
@@ -0,0 +1,34 @@
+#
+# 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 optional
+
+LOCAL_PACKAGE_NAME := CtsActivityManagerDeviceSdk25TestCases
+
+LOCAL_SRC_FILES := $(call all-java-files-under, src) \
+    ../src/android/server/am/AspectRatioTestsBase.java
+
+LOCAL_SDK_VERSION := 25
+
+LOCAL_STATIC_JAVA_LIBRARIES := \
+    android-support-test
+
+LOCAL_COMPATIBILITY_SUITE := cts vts general-tests
+
+include $(BUILD_CTS_PACKAGE)
diff --git a/tests/framework/base/activitymanager/testsdk25/AndroidManifest.xml b/tests/framework/base/activitymanager/testsdk25/AndroidManifest.xml
new file mode 100644
index 0000000..f216411
--- /dev/null
+++ b/tests/framework/base/activitymanager/testsdk25/AndroidManifest.xml
@@ -0,0 +1,36 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT 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.server.cts.am.testsdk25">
+
+    <uses-sdk android:targetSdkVersion="25" />
+
+    <application android:label="CtsActivityManagerDeviceSdk25TestCases">
+        <uses-library android:name="android.test.runner" />
+
+        <activity
+            android:name="android.server.am.AspectRatioSdk25Tests$Sdk25MaxAspectRatioActivity"
+            android:label="Sdk25MaxAspectRatioActivity"
+            android:resizeableActivity="false" />
+    </application>
+
+    <instrumentation
+        android:name="android.support.test.runner.AndroidJUnitRunner"
+        android:targetPackage="android.server.cts.am.testsdk25" />
+
+</manifest>
diff --git a/tests/framework/base/activitymanager/testsdk25/AndroidTest.xml b/tests/framework/base/activitymanager/testsdk25/AndroidTest.xml
new file mode 100644
index 0000000..9bbec9e
--- /dev/null
+++ b/tests/framework/base/activitymanager/testsdk25/AndroidTest.xml
@@ -0,0 +1,29 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2017 The Android Open Source Project
+
+     Licensed under the Apache License, Version 2.0 (the "License");
+     you may not use this file except in compliance with the License.
+     You may obtain a copy of the License at
+
+          http://www.apache.org/licenses/LICENSE-2.0
+
+     Unless required by applicable law or agreed to in writing, software
+     distributed under the License is distributed on an "AS IS" BASIS,
+     WITHOUT 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 ActivityManager SDK 25 compatibility test cases">
+    <option name="test-suite-tag" value="cts" />
+    <option name="config-descriptor:metadata" key="component" value="framework" />
+    <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" />
+        <option name="test-file-name" value="CtsActivityManagerDeviceSdk25TestCases.apk" />
+    </target_preparer>
+    <test class="com.android.tradefed.testtype.AndroidJUnitTest">
+        <option name="package" value="android.server.cts.am.testsdk25" />
+        <option name="runtime-hint" value="1m" />
+    </test>
+</configuration>
diff --git a/tests/framework/base/activitymanager/testsdk25/src/android/server/am/AspectRatioSdk25Tests.java b/tests/framework/base/activitymanager/testsdk25/src/android/server/am/AspectRatioSdk25Tests.java
new file mode 100644
index 0000000..6ec1df0
--- /dev/null
+++ b/tests/framework/base/activitymanager/testsdk25/src/android/server/am/AspectRatioSdk25Tests.java
@@ -0,0 +1,57 @@
+/*
+ * 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.server.am;
+
+import static org.junit.Assert.fail;
+
+import android.app.Activity;
+import android.platform.test.annotations.Presubmit;
+import android.support.test.rule.ActivityTestRule;
+import android.support.test.runner.AndroidJUnit4;
+
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+/**
+ * Build: mmma -j32 cts/tests/framework/base/activitymanager
+ * Run: cts/tests/framework/base/activitymanager/util/run-test CtsActivityManagerDeviceSdk25TestCases android.server.am.AspectRatioSdk25Tests
+ */
+@RunWith(AndroidJUnit4.class)
+@Presubmit
+public class AspectRatioSdk25Tests extends AspectRatioTestsBase {
+
+    // Max supported aspect ratio for pre-O apps.
+    private static final float MAX_PRE_O_ASPECT_RATIO = 1.86f;
+
+    // Test target activity that has targetSdk="25" and resizeableActivity="false".
+    public static class Sdk25MaxAspectRatioActivity extends Activity {
+    }
+
+    @Rule
+    public ActivityTestRule<Sdk25MaxAspectRatioActivity> mSdk25MaxAspectRatioActivity =
+            new ActivityTestRule<>(Sdk25MaxAspectRatioActivity.class,
+                    false /* initialTouchMode */, false /* launchActivity */);
+
+    @Test
+    public void testMaxAspectRatioPreOActivity() throws Exception {
+        runAspectRatioTest(mSdk25MaxAspectRatioActivity, actual -> {
+            if (MAX_PRE_O_ASPECT_RATIO >= actual) return;
+            fail("actual=" + actual + " is greater than expected=" + MAX_PRE_O_ASPECT_RATIO);
+        });
+    }
+}
diff --git a/hostsidetests/services/activityandwindowmanager/activitymanager/translucentapp/Android.mk b/tests/framework/base/activitymanager/translucentapp/Android.mk
similarity index 100%
rename from hostsidetests/services/activityandwindowmanager/activitymanager/translucentapp/Android.mk
rename to tests/framework/base/activitymanager/translucentapp/Android.mk
diff --git a/tests/framework/base/activitymanager/translucentapp/AndroidManifest.xml b/tests/framework/base/activitymanager/translucentapp/AndroidManifest.xml
new file mode 100755
index 0000000..edf7129
--- /dev/null
+++ b/tests/framework/base/activitymanager/translucentapp/AndroidManifest.xml
@@ -0,0 +1,33 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+  ~ Copyright (C) 2017 The Android Open Source Project
+  ~
+  ~ Licensed under the Apache License, Version 2.0 (the "License");
+  ~ you may not use this file except in compliance with the License.
+  ~ You may obtain a copy of the License at
+  ~
+  ~      http://www.apache.org/licenses/LICENSE-2.0
+  ~
+  ~ Unless required by applicable law or agreed to in writing, software
+  ~ distributed under the License is distributed on an "AS IS" BASIS,
+  ~ WITHOUT 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.server.am.translucentapp">
+
+    <application android:label="CtsTranslucentApp">
+        <activity
+            android:name=".TranslucentLandscapeActivity"
+            android:exported="true"
+            android:screenOrientation="landscape"
+            android:theme="@android:style/Theme.Translucent.NoTitleBar">
+            <intent-filter>
+                <action android:name="android.intent.action.MAIN" />
+                <category android:name="android.intent.category.LAUNCHER" />
+            </intent-filter>
+        </activity>
+    </application>
+</manifest>
diff --git a/tests/framework/base/activitymanager/translucentapp/src/android/server/am/translucentapp/TranslucentLandscapeActivity.java b/tests/framework/base/activitymanager/translucentapp/src/android/server/am/translucentapp/TranslucentLandscapeActivity.java
new file mode 100644
index 0000000..3d8de4e
--- /dev/null
+++ b/tests/framework/base/activitymanager/translucentapp/src/android/server/am/translucentapp/TranslucentLandscapeActivity.java
@@ -0,0 +1,22 @@
+/*
+ * 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.server.am.translucentapp;
+
+import android.app.Activity;
+
+public class TranslucentLandscapeActivity extends Activity {
+}
diff --git a/hostsidetests/services/activityandwindowmanager/activitymanager/translucentappsdk26/Android.mk b/tests/framework/base/activitymanager/translucentappsdk26/Android.mk
similarity index 100%
rename from hostsidetests/services/activityandwindowmanager/activitymanager/translucentappsdk26/Android.mk
rename to tests/framework/base/activitymanager/translucentappsdk26/Android.mk
diff --git a/tests/framework/base/activitymanager/translucentappsdk26/AndroidManifest.xml b/tests/framework/base/activitymanager/translucentappsdk26/AndroidManifest.xml
new file mode 100755
index 0000000..35c86cc
--- /dev/null
+++ b/tests/framework/base/activitymanager/translucentappsdk26/AndroidManifest.xml
@@ -0,0 +1,33 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+  ~ Copyright (C) 2017 The Android Open Source Project
+  ~
+  ~ Licensed under the Apache License, Version 2.0 (the "License");
+  ~ you may not use this file except in compliance with the License.
+  ~ You may obtain a copy of the License at
+  ~
+  ~      http://www.apache.org/licenses/LICENSE-2.0
+  ~
+  ~ Unless required by applicable law or agreed to in writing, software
+  ~ distributed under the License is distributed on an "AS IS" BASIS,
+  ~ WITHOUT 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.server.am.translucentapp26">
+
+    <application android:label="CtsTranslucentApp26">
+        <activity
+            android:name="android.server.am.translucentapp.TranslucentLandscapeActivity"
+            android:exported="true"
+            android:screenOrientation="landscape"
+            android:theme="@android:style/Theme.Translucent.NoTitleBar">
+            <intent-filter>
+                <action android:name="android.intent.action.MAIN" />
+                <category android:name="android.intent.category.LAUNCHER" />
+            </intent-filter>
+        </activity>
+    </application>
+</manifest>
diff --git a/tests/framework/base/activitymanager/util/Android.mk b/tests/framework/base/activitymanager/util/Android.mk
new file mode 100644
index 0000000..6fa0331
--- /dev/null
+++ b/tests/framework/base/activitymanager/util/Android.mk
@@ -0,0 +1,36 @@
+# Copyright (C) 2012 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#      http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+LOCAL_PATH:= $(call my-dir)
+
+include $(CLEAR_VARS)
+
+LOCAL_MODULE_TAGS := tests optional
+
+LOCAL_SRC_FILES := $(call all-java-files-under, src)
+
+LOCAL_STATIC_JAVA_LIBRARIES := \
+    platformprotosnano \
+    compatibility-device-util \
+    android-support-test
+
+LOCAL_JAVA_LIBRARIES := core-oj core-libart
+
+LOCAL_MODULE := cts-amwm-util
+
+LOCAL_SDK_VERSION := test_current
+
+include $(BUILD_STATIC_JAVA_LIBRARY)
+
+include $(call all-makefiles-under,$(LOCAL_PATH))
diff --git a/hostsidetests/services/activityandwindowmanager/util/run-test b/tests/framework/base/activitymanager/util/run-test
similarity index 100%
rename from hostsidetests/services/activityandwindowmanager/util/run-test
rename to tests/framework/base/activitymanager/util/run-test
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
new file mode 100644
index 0000000..7367875
--- /dev/null
+++ b/tests/framework/base/activitymanager/util/src/android/server/am/ActivityAndWindowManagersState.java
@@ -0,0 +1,1009 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License
+ */
+
+package android.server.am;
+
+import static android.app.ActivityManager.StackId.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;
+import static android.app.WindowConfiguration.WINDOWING_MODE_SPLIT_SCREEN_PRIMARY;
+import static android.app.WindowConfiguration.WINDOWING_MODE_UNDEFINED;
+import static android.content.pm.ActivityInfo.RESIZE_MODE_RESIZEABLE;
+import static android.server.am.ActivityManagerTestBase.componentName;
+import static android.server.am.StateLogger.log;
+import static android.server.am.StateLogger.logE;
+
+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.assertTrue;
+import static org.junit.Assert.fail;
+
+import android.content.ComponentName;
+import android.graphics.Rect;
+import android.server.am.ActivityManagerState.ActivityStack;
+import android.server.am.ActivityManagerState.ActivityTask;
+import android.server.am.WindowManagerState.Display;
+import android.server.am.WindowManagerState.WindowStack;
+import android.server.am.WindowManagerState.WindowState;
+import android.server.am.WindowManagerState.WindowTask;
+
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Objects;
+import java.util.function.BiPredicate;
+import java.util.function.BooleanSupplier;
+import java.util.function.Predicate;
+
+/**
+ * Combined state of the activity manager and window manager.
+ */
+public class ActivityAndWindowManagersState {
+
+    // Clone of android DisplayMetrics.DENSITY_DEFAULT (DENSITY_MEDIUM)
+    // (Needed in host-side tests to convert dp to px.)
+    private static final int DISPLAY_DENSITY_DEFAULT = 160;
+    // TODO: Change to use framework constant.
+    public static final int DEFAULT_DISPLAY_ID = 0;
+
+    // Default minimal size of resizable task, used if none is set explicitly.
+    // Must be kept in sync with 'default_minimal_size_resizable_task' dimen from frameworks/base.
+    private static final int DEFAULT_RESIZABLE_TASK_SIZE_DP = 220;
+
+    // Default minimal size of a resizable PiP task, used if none is set explicitly.
+    // Must be kept in sync with 'default_minimal_size_pip_resizable_task' dimen from
+    // frameworks/base.
+    private static final int DEFAULT_PIP_RESIZABLE_TASK_SIZE_DP = 108;
+
+    private ActivityManagerState mAmState = new ActivityManagerState();
+    private WindowManagerState mWmState = new WindowManagerState();
+
+    @Deprecated
+    public void computeState(String... waitForActivitiesVisible)
+            throws Exception {
+        WaitForValidActivityState[] states = waitForActivitiesVisible != null ?
+                new WaitForValidActivityState[waitForActivitiesVisible.length] : null;
+        if (states != null) {
+            for (int i = 0; i < waitForActivitiesVisible.length; i++) {
+                states[i] =
+                        new WaitForValidActivityState.Builder(waitForActivitiesVisible[i]).build();
+            }
+        }
+        computeState(states);
+    }
+
+    @Deprecated
+    public void computeState() throws Exception {
+        computeState(true);
+    }
+
+    /**
+     * Compute AM and WM state of device, check sanity and bounds.
+     * WM state will include only visible windows, stack and task bounds will be compared.
+     *
+     * @param waitForActivitiesVisible array of activity names to wait for.
+     */
+    public void computeState(WaitForValidActivityState... waitForActivitiesVisible)
+            throws Exception {
+        computeState(true, waitForActivitiesVisible);
+    }
+
+    /**
+     * Compute AM and WM state of device, check sanity and bounds.
+     *
+     * @param compareTaskAndStackBounds pass 'true' if stack and task bounds should be compared,
+     *                                  'false' otherwise.
+     * @param waitForActivitiesVisible  array of activity states to wait for.
+     */
+    void computeState(boolean compareTaskAndStackBounds,
+            WaitForValidActivityState... waitForActivitiesVisible) throws Exception {
+        waitForValidState(compareTaskAndStackBounds, waitForActivitiesVisible);
+        assertSanity();
+        assertValidBounds(compareTaskAndStackBounds);
+    }
+
+    /**
+     * Compute AM and WM state of device, wait for the activity records to be added, and
+     * wait for debugger window to show up.
+     *
+     * This should only be used when starting with -D (debugger) option, where we pop up the
+     * waiting-for-debugger window, but real activity window won't show up since we're waiting
+     * for debugger.
+     */
+    void waitForDebuggerWindowVisible(String[] waitForActivityRecords) {
+        int retriesLeft = 5;
+        do {
+            mAmState.computeState();
+            mWmState.computeState();
+            if (shouldWaitForDebuggerWindow() ||
+                    shouldWaitForActivityRecords(waitForActivityRecords)) {
+                try {
+                    Thread.sleep(1000);
+                } catch (InterruptedException e) {
+                    log(e.toString());
+                    // Well I guess we are not waiting...
+                }
+            } else {
+                break;
+            }
+        } while (retriesLeft-- > 0);
+    }
+
+    /**
+     * Wait for the activity to appear and for valid state in AM and WM.
+     *
+     * @param waitForActivityVisible name of activity to wait for.
+     */
+    @Deprecated
+    void waitForValidState(String waitForActivityVisible)
+            throws Exception {
+        waitForValidState(false /* compareTaskAndStackBounds */,
+                new WaitForValidActivityState(waitForActivityVisible));
+    }
+
+    /** Wait for the activity to appear and for valid state in AM and WM. */
+    void waitForValidState(WaitForValidActivityState... waitForActivityVisible) throws Exception {
+        waitForValidState(false /* compareTaskAndStackBounds */, waitForActivityVisible);
+    }
+
+    /**
+     * Wait for the activity to appear in proper stack and for valid state in AM and WM.
+     *
+     * @param waitForActivityVisible name of activity to wait for.
+     * @param stackId                id of the stack where provided activity should be found.
+     */
+    @Deprecated
+    void waitForValidState(String waitForActivityVisible, int stackId)
+            throws Exception {
+        waitForValidState(false /* compareTaskAndStackBounds */,
+                new WaitForValidActivityState.Builder(waitForActivityVisible)
+                        .setStackId(stackId)
+                        .build());
+    }
+
+    void waitForValidStateWithActivityType(String waitForActivityVisible, int activityType)
+            throws Exception {
+        waitForValidState(false /* compareTaskAndStackBounds */,
+                new WaitForValidActivityState.Builder(waitForActivityVisible)
+                        .setActivityType(activityType)
+                        .build());
+    }
+
+    void waitForValidState(final ComponentName activityName, final int windowingMode,
+            final int activityType) throws Exception {
+        waitForValidState(false /* compareTaskAndStackBounds */,
+                new WaitForValidActivityState.Builder(activityName)
+                        .setActivityType(activityType)
+                        .setWindowingMode(windowingMode)
+                        .build());
+    }
+
+    @Deprecated
+    void waitForValidState(String waitForActivityVisible, int windowingMode, int activityType)
+            throws Exception {
+        waitForValidState(false /* compareTaskAndStackBounds */,
+                new WaitForValidActivityState.Builder(waitForActivityVisible)
+                        .setActivityType(activityType)
+                        .setWindowingMode(windowingMode)
+                        .build());
+    }
+
+    /**
+     * Wait for the activities to appear in proper stacks and for valid state in AM and WM.
+     *
+     * @param compareTaskAndStackBounds flag indicating if we should compare task and stack bounds
+     *                                  for equality.
+     * @param waitForActivitiesVisible  array of activity state to wait for.
+     */
+    private void waitForValidState(boolean compareTaskAndStackBounds,
+            WaitForValidActivityState... waitForActivitiesVisible) throws Exception {
+        waitForValidState(compareTaskAndStackBounds, componentName, waitForActivitiesVisible);
+    }
+
+    /**
+     * Wait for the activities to appear in proper stacks and for valid state in AM and WM.
+     *
+     * @param compareTaskAndStackBounds flag indicating if we should compare task and stack bounds
+     *                                  for equality.
+     * @param packageName               name of the package of activities that we're waiting for.
+     * @param waitForActivitiesVisible  array of activity states to wait for.
+     */
+    void waitForValidState(boolean compareTaskAndStackBounds, String packageName,
+            WaitForValidActivityState... waitForActivitiesVisible) throws Exception {
+        int retriesLeft = 5;
+        do {
+            // TODO: Get state of AM and WM at the same time to avoid mismatches caused by
+            // requesting dump in some intermediate state.
+            mAmState.computeState();
+            mWmState.computeState();
+            if (shouldWaitForValidStacks(compareTaskAndStackBounds)
+                    || shouldWaitForActivities(packageName, waitForActivitiesVisible)
+                    || shouldWaitForWindows()) {
+                log("***Waiting for valid stacks and activities states...");
+                try {
+                    Thread.sleep(1000);
+                } catch (InterruptedException e) {
+                    log(e.toString());
+                    // Well I guess we are not waiting...
+                }
+            } else {
+                break;
+            }
+        } while (retriesLeft-- > 0);
+    }
+
+    /**
+     * Ensures all exiting windows have been removed.
+     */
+    void waitForAllExitingWindows() {
+        int retriesLeft = 5;
+        do {
+            mWmState.computeState();
+            if (mWmState.containsExitingWindow()) {
+                try {
+                    Thread.sleep(1000);
+                } catch (InterruptedException e) {
+                    log(e.toString());
+                    // Well I guess we are not waiting...
+                }
+            } else {
+                break;
+            }
+        } while (retriesLeft-- > 0);
+
+        assertFalse(mWmState.containsExitingWindow());
+    }
+
+    void waitForAllStoppedActivities() throws Exception {
+        int retriesLeft = 5;
+        do {
+            mAmState.computeState();
+            if (mAmState.containsStartedActivities()){
+                log("***Waiting for valid stacks and activities states...");
+                try {
+                    Thread.sleep(1500);
+                } catch (InterruptedException e) {
+                    log(e.toString());
+                    // Well I guess we are not waiting...
+                }
+            } else {
+                break;
+            }
+        } while (retriesLeft-- > 0);
+
+        assertFalse(mAmState.containsStartedActivities());
+    }
+
+    void waitForHomeActivityVisible() throws Exception {
+        ComponentName homeActivity = mAmState.getHomeActivityName();
+        // Sometimes this function is called before we know what Home Activity is
+        if (homeActivity == null) {
+            log("Computing state to determine Home Activity");
+            computeState();
+            homeActivity = mAmState.getHomeActivityName();
+        }
+        assertNotNull("homeActivity should not be null", homeActivity);
+        waitForValidState(new WaitForValidActivityState(homeActivity));
+    }
+
+    /**
+     * @return true if recents activity is visible. Devices without recents will return false
+     */
+    boolean waitForRecentsActivityVisible() throws Exception {
+        waitForWithAmState(ActivityManagerState::isRecentsActivityVisible,
+                "***Waiting for recents activity to be visible...");
+        return mAmState.isRecentsActivityVisible();
+    }
+
+    void waitForKeyguardShowingAndNotOccluded() throws Exception {
+        waitForWithAmState(state -> state.getKeyguardControllerState().keyguardShowing
+                        && !state.getKeyguardControllerState().keyguardOccluded,
+                "***Waiting for Keyguard showing...");
+    }
+
+    void waitForKeyguardShowingAndOccluded() throws Exception {
+        waitForWithAmState(state -> state.getKeyguardControllerState().keyguardShowing
+                        && state.getKeyguardControllerState().keyguardOccluded,
+                "***Waiting for Keyguard showing and occluded...");
+    }
+
+    void waitForKeyguardGone() throws Exception {
+        waitForWithAmState(state -> !state.getKeyguardControllerState().keyguardShowing,
+                "***Waiting for Keyguard gone...");
+    }
+
+    void waitForRotation(int rotation) throws Exception {
+        waitForWithWmState(state -> state.getRotation() == rotation,
+                "***Waiting for Rotation: " + rotation);
+    }
+
+    void waitForDisplayUnfrozen() throws Exception {
+        waitForWithWmState(state -> !state.isDisplayFrozen(),
+                "***Waiting for Display unfrozen");
+    }
+
+    void waitForActivityState(String activityName, String activityState)
+            throws Exception {
+        waitForWithAmState(state -> state.hasActivityState(activityName, activityState),
+                "***Waiting for Activity State: " + activityState);
+    }
+
+    @Deprecated
+    void waitForFocusedStack(int stackId) throws Exception {
+        waitForWithAmState(state -> state.getFocusedStackId() == stackId,
+                "***Waiting for focused stack...");
+    }
+
+    void waitForFocusedStack(int windowingMode, int activityType) throws Exception {
+        waitForWithAmState(state ->
+                        (activityType == ACTIVITY_TYPE_UNDEFINED
+                                || state.getFocusedStackActivityType() == activityType)
+                        && (windowingMode == WINDOWING_MODE_UNDEFINED
+                                || state.getFocusedStackWindowingMode() == windowingMode),
+                "***Waiting for focused stack...");
+    }
+
+    void waitForAppTransitionIdle() throws Exception {
+        waitForWithWmState(
+                state -> WindowManagerState.APP_STATE_IDLE.equals(state.getAppTransitionState()),
+                "***Waiting for app transition idle...");
+    }
+
+    void waitForWithAmState(Predicate<ActivityManagerState> waitCondition,
+            String message) throws Exception {
+        waitFor((amState, wmState) -> waitCondition.test(amState), message);
+    }
+
+    void waitForWithWmState(Predicate<WindowManagerState> waitCondition,
+            String message) throws Exception {
+        waitFor((amState, wmState) -> waitCondition.test(wmState), message);
+    }
+
+    void waitFor(
+            BiPredicate<ActivityManagerState, WindowManagerState> waitCondition, String message)
+            throws Exception {
+        waitFor(message, () -> {
+            try {
+                mAmState.computeState();
+                mWmState.computeState();
+            } catch (Exception e) {
+                logE(e.toString());
+                return false;
+            }
+            return waitCondition.test(mAmState, mWmState);
+        });
+    }
+
+    void waitFor(String message, BooleanSupplier waitCondition) throws Exception {
+        int retriesLeft = 5;
+        do {
+            if (!waitCondition.getAsBoolean()) {
+                log(message);
+                try {
+                    Thread.sleep(1000);
+                } catch (InterruptedException e) {
+                    log(e.toString());
+                    // Well I guess we are not waiting...
+                }
+            } else {
+                break;
+            }
+        } while (retriesLeft-- > 0);
+    }
+
+    /**
+     * @return true if should wait for valid stacks state.
+     */
+    private boolean shouldWaitForValidStacks(boolean compareTaskAndStackBounds) {
+        if (!taskListsInAmAndWmAreEqual()) {
+            // We want to wait for equal task lists in AM and WM in case we caught them in the
+            // middle of some state change operations.
+            log("***taskListsInAmAndWmAreEqual=false");
+            return true;
+        }
+        if (!stackBoundsInAMAndWMAreEqual()) {
+            // We want to wait a little for the stacks in AM and WM to have equal bounds as there
+            // might be a transition animation ongoing when we got the states from WM AM separately.
+            log("***stackBoundsInAMAndWMAreEqual=false");
+            return true;
+        }
+        try {
+            // Temporary fix to avoid catching intermediate state with different task bounds in AM
+            // and WM.
+            assertValidBounds(compareTaskAndStackBounds);
+        } catch (AssertionError e) {
+            log("***taskBoundsInAMAndWMAreEqual=false : " + e.getMessage());
+            return true;
+        }
+        final int stackCount = mAmState.getStackCount();
+        if (stackCount == 0) {
+            log("***stackCount=" + stackCount);
+            return true;
+        }
+        final int resumedActivitiesCount = mAmState.getResumedActivitiesCount();
+        if (!mAmState.getKeyguardControllerState().keyguardShowing && resumedActivitiesCount != 1) {
+            log("***resumedActivitiesCount=" + resumedActivitiesCount);
+            return true;
+        }
+        if (mAmState.getFocusedActivity() == null) {
+            log("***focusedActivity=null");
+            return true;
+        }
+        return false;
+    }
+
+    /**
+     * @return true if should wait for some activities to become visible.
+     */
+    private boolean shouldWaitForActivities(String packageName,
+            WaitForValidActivityState... waitForActivitiesVisible) {
+        if (waitForActivitiesVisible == null || waitForActivitiesVisible.length == 0) {
+            return false;
+        }
+        // If the caller is interested in us waiting for some particular activity windows to be
+        // visible before compute the state. Check for the visibility of those activity windows
+        // and for placing them in correct stacks (if requested).
+        boolean allActivityWindowsVisible = true;
+        boolean tasksInCorrectStacks = true;
+        List<WindowState> matchingWindowStates = new ArrayList<>();
+        for (final WaitForValidActivityState state : waitForActivitiesVisible) {
+            final String activityComponentName;
+            final String windowName;
+            if (state.componentName != null) {
+                activityComponentName = state.componentName;
+                windowName = state.windowName;
+            } else {
+                final String activityName = state.activityName;
+                activityComponentName = (activityName != null)
+                        ? ActivityManagerTestBase.getActivityComponentName(packageName, activityName)
+                        : null;
+                // Check if window is visible - it should be represented as one of the window
+                // states.
+                windowName = (state.windowName != null) ? state.windowName
+                        : ActivityManagerTestBase.getWindowName(packageName, activityName);
+            }
+            final int stackId = state.stackId;
+            final int windowingMode = state.windowingMode;
+            final int activityType = state.activityType;
+
+            mWmState.getMatchingVisibleWindowState(windowName, matchingWindowStates);
+            boolean activityWindowVisible = !matchingWindowStates.isEmpty();
+            if (!activityWindowVisible) {
+                log("Activity window not visible: " + windowName);
+                allActivityWindowsVisible = false;
+            } else if (activityComponentName != null
+                    && !mAmState.isActivityVisible(activityComponentName)) {
+                log("Activity not visible: " + activityComponentName);
+                allActivityWindowsVisible = false;
+            } else {
+                // Check if window is already the correct state requested by test.
+                boolean windowInCorrectState = false;
+                for (WindowState ws : matchingWindowStates) {
+                    if (stackId != INVALID_STACK_ID && ws.getStackId() != stackId) {
+                        continue;
+                    }
+                    if (windowingMode != WINDOWING_MODE_UNDEFINED
+                            && ws.getWindowingMode() != windowingMode) {
+                        continue;
+                    }
+                    if (activityType != ACTIVITY_TYPE_UNDEFINED
+                            && ws.getActivityType() != activityType) {
+                        continue;
+                    }
+                    windowInCorrectState = true;
+                    break;
+                }
+
+                if (!windowInCorrectState) {
+                    log("Window in incorrect stack: " + state);
+                    tasksInCorrectStacks = false;
+                }
+            }
+        }
+        return !allActivityWindowsVisible || !tasksInCorrectStacks;
+    }
+
+    /**
+     * @return true if should wait valid windows state.
+     */
+    private boolean shouldWaitForWindows() {
+        if (mWmState.getFrontWindow() == null) {
+            log("***frontWindow=null");
+            return true;
+        }
+        if (mWmState.getFocusedWindow() == null) {
+            log("***focusedWindow=null");
+            return true;
+        }
+        if (mWmState.getFocusedApp() == null) {
+            log("***focusedApp=null");
+            return true;
+        }
+
+        return false;
+    }
+
+    private boolean shouldWaitForDebuggerWindow() {
+        List<WindowState> matchingWindowStates = new ArrayList<>();
+        mWmState.getMatchingVisibleWindowState("android.server.am", matchingWindowStates);
+        for (WindowState ws : matchingWindowStates) {
+            if (ws.isDebuggerWindow()) {
+                return false;
+            }
+        }
+        log("Debugger window not available yet");
+        return true;
+    }
+
+    private boolean shouldWaitForActivityRecords(String[] waitForActivityRecords) {
+        if (waitForActivityRecords == null || waitForActivityRecords.length == 0) {
+            return false;
+        }
+        // Check if the activity records we're looking for is already added.
+        for (int i = 0; i < waitForActivityRecords.length; i++) {
+            if (!mAmState.isActivityVisible(waitForActivityRecords[i])) {
+                log("ActivityRecord " + waitForActivityRecords[i] + " not visible yet");
+                return true;
+            }
+        }
+        return false;
+    }
+
+    ActivityManagerState getAmState() {
+        return mAmState;
+    }
+
+    public WindowManagerState getWmState() {
+        return mWmState;
+    }
+
+    void assertSanity() throws Exception {
+        assertTrue("Must have stacks", mAmState.getStackCount() > 0);
+        if (!mAmState.getKeyguardControllerState().keyguardShowing) {
+            assertEquals("There should be one and only one resumed activity in the system.",
+                    1, mAmState.getResumedActivitiesCount());
+        }
+        assertNotNull("Must have focus activity.", mAmState.getFocusedActivity());
+
+        for (ActivityStack aStack : mAmState.getStacks()) {
+            final int stackId = aStack.mStackId;
+            for (ActivityTask aTask : aStack.getTasks()) {
+                assertEquals("Stack can only contain its own tasks", stackId, aTask.mStackId);
+            }
+        }
+
+        assertNotNull("Must have front window.", mWmState.getFrontWindow());
+        assertNotNull("Must have focused window.", mWmState.getFocusedWindow());
+        assertNotNull("Must have app.", mWmState.getFocusedApp());
+    }
+
+    void assertContainsStack(String msg, int windowingMode, int activityType) throws Exception {
+        assertTrue(msg, mAmState.containsStack(windowingMode, activityType));
+        assertTrue(msg, mWmState.containsStack(windowingMode, activityType));
+    }
+
+    @Deprecated
+    void assertDoesNotContainStack(String msg, int stackId) throws Exception {
+        assertFalse(msg, mAmState.containsStack(stackId));
+        assertFalse(msg, mWmState.containsStack(stackId));
+    }
+
+    void assertDoesNotContainStack(String msg, int windowingMode, int activityType)
+            throws Exception {
+        assertFalse(msg, mAmState.containsStack(windowingMode, activityType));
+        assertFalse(msg, mWmState.containsStack(windowingMode, activityType));
+    }
+
+    void assertFrontStack(String msg, int stackId) throws Exception {
+        assertEquals(msg, stackId, mAmState.getFrontStackId(DEFAULT_DISPLAY_ID));
+        assertEquals(msg, stackId, mWmState.getFrontStackId(DEFAULT_DISPLAY_ID));
+    }
+
+    void assertFrontStack(String msg, int windowingMode, int activityType)
+            throws Exception {
+        if (windowingMode != WINDOWING_MODE_UNDEFINED) {
+            assertEquals(msg, windowingMode,
+                    mAmState.getFrontStackWindowingMode(DEFAULT_DISPLAY_ID));
+        }
+        if (activityType != ACTIVITY_TYPE_UNDEFINED) {
+            assertEquals(msg, activityType, mAmState.getFrontStackActivityType(DEFAULT_DISPLAY_ID));
+        }
+    }
+
+    void assertFrontStackActivityType(String msg, int activityType) throws Exception {
+        assertEquals(msg, activityType, mAmState.getFrontStackActivityType(DEFAULT_DISPLAY_ID));
+        assertEquals(msg, activityType, mWmState.getFrontStackActivityType(DEFAULT_DISPLAY_ID));
+    }
+
+    @Deprecated
+    void assertFocusedStack(String msg, int stackId) throws Exception {
+        assertEquals(msg, stackId, mAmState.getFocusedStackId());
+    }
+
+    void assertFocusedStack(String msg, int windowingMode, int activityType)
+            throws Exception {
+        if (windowingMode != WINDOWING_MODE_UNDEFINED) {
+            assertEquals(msg, windowingMode, mAmState.getFocusedStackWindowingMode());
+        }
+        if (activityType != ACTIVITY_TYPE_UNDEFINED) {
+            assertEquals(msg, activityType, mAmState.getFocusedStackActivityType());
+        }
+    }
+
+    void assertFocusedActivity(final String msg, final ComponentName activityName) {
+        final String activityComponentName = activityName.flattenToShortString();
+        assertEquals(msg, activityComponentName, mAmState.getFocusedActivity());
+        assertEquals(msg, activityComponentName, mWmState.getFocusedApp());
+    }
+
+    @Deprecated
+    void assertFocusedActivity(final String msg, final String activityName) {
+        final String activityComponentName =
+                ActivityManagerTestBase.getActivityComponentName(activityName);
+        assertEquals(msg, activityComponentName, mAmState.getFocusedActivity());
+        assertEquals(msg, activityComponentName, mWmState.getFocusedApp());
+    }
+
+    void assertNotFocusedActivity(String msg, String activityName) throws Exception {
+        final String componentName = ActivityManagerTestBase.getActivityComponentName(activityName);
+        if (mAmState.getFocusedActivity().equals(componentName)) {
+            assertNotEquals(msg, mAmState.getFocusedActivity(), componentName);
+        }
+        if (mWmState.getFocusedApp().equals(componentName)) {
+            assertNotEquals(msg, mWmState.getFocusedApp(), componentName);
+        }
+    }
+
+    void assertResumedActivity(final String msg, final ComponentName activityName) {
+        assertEquals(msg, activityName.flattenToShortString(), mAmState.getResumedActivity());
+    }
+
+    @Deprecated
+    void assertResumedActivity(String msg, String activityName) throws Exception {
+        final String componentName = ActivityManagerTestBase.getActivityComponentName(activityName);
+        assertEquals(msg, componentName, mAmState.getResumedActivity());
+    }
+
+    void assertNotResumedActivity(String msg, String activityName) throws Exception {
+        final String componentName = ActivityManagerTestBase.getActivityComponentName(activityName);
+        if (mAmState.getResumedActivity().equals(componentName)) {
+            assertNotEquals(msg, mAmState.getResumedActivity(), componentName);
+        }
+    }
+
+    void assertFocusedWindow(String msg, String windowName) {
+        assertEquals(msg, windowName, mWmState.getFocusedWindow());
+    }
+
+    void assertNotFocusedWindow(String msg, String windowName) {
+        if (mWmState.getFocusedWindow().equals(windowName)) {
+            assertNotEquals(msg, mWmState.getFocusedWindow(), windowName);
+        }
+    }
+
+    void assertFrontWindow(String msg, String windowName) {
+        assertEquals(msg, windowName, mWmState.getFrontWindow());
+    }
+
+    @Deprecated
+    public void assertVisibility(String activityName, boolean visible) {
+        final String activityComponentName =
+                ActivityManagerTestBase.getActivityComponentName(activityName);
+        final String windowName =
+                ActivityManagerTestBase.getWindowName(activityName);
+        assertVisibility(activityComponentName, windowName, visible);
+    }
+
+    public void assertVisibility(final ComponentName activityName, final boolean visible) {
+        final String activityComponentName = activityName.flattenToShortString();
+        final String windowName = activityName.flattenToString();
+        assertVisibility(activityComponentName, windowName, visible);
+    }
+
+    private void assertVisibility(String activityComponentName, String windowName,
+            boolean visible) {
+        final boolean activityVisible = mAmState.isActivityVisible(activityComponentName);
+        final boolean windowVisible = mWmState.isWindowVisible(windowName);
+
+        if (visible) {
+            assertTrue("Activity=" + activityComponentName + " must be visible.", activityVisible);
+            assertTrue("Window=" + windowName + " must be visible.", windowVisible);
+        } else {
+            assertFalse("Activity=" + activityComponentName + " must NOT be visible.",
+                    activityVisible);
+            assertFalse("Window=" + windowName + " must NOT be visible.", windowVisible);
+        }
+    }
+
+    void assertHomeActivityVisible(boolean visible) {
+        final ComponentName homeActivity = mAmState.getHomeActivityName();
+        assertNotNull(homeActivity);
+        assertVisibility(homeActivity, visible);
+    }
+
+    /**
+     * Asserts that the device default display minimim width is larger than the minimum task width.
+     */
+    void assertDeviceDefaultDisplaySize(String errorMessage) throws Exception {
+        computeState();
+        final int minTaskSizePx = defaultMinimalTaskSize(DEFAULT_DISPLAY_ID);
+        final Display display = getWmState().getDisplay(DEFAULT_DISPLAY_ID);
+        final Rect displayRect = display.getDisplayRect();
+        if (Math.min(displayRect.width(), displayRect.height()) < minTaskSizePx) {
+            fail(errorMessage);
+        }
+    }
+
+    public void assertKeyguardShowingAndOccluded() {
+        assertTrue(getAmState().getKeyguardControllerState().keyguardShowing);
+        assertTrue(getAmState().getKeyguardControllerState().keyguardOccluded);
+    }
+
+    public void assertKeyguardShowingAndNotOccluded() {
+        assertTrue(getAmState().getKeyguardControllerState().keyguardShowing);
+        assertFalse(getAmState().getKeyguardControllerState().keyguardOccluded);
+    }
+
+    public void assertKeyguardGone() {
+        assertFalse(getAmState().getKeyguardControllerState().keyguardShowing);
+    }
+
+    boolean taskListsInAmAndWmAreEqual() {
+        for (ActivityStack aStack : mAmState.getStacks()) {
+            final int stackId = aStack.mStackId;
+            final WindowStack wStack = mWmState.getStack(stackId);
+            if (wStack == null) {
+                log("Waiting for stack setup in WM, stackId=" + stackId);
+                return false;
+            }
+
+            for (ActivityTask aTask : aStack.getTasks()) {
+                if (wStack.getTask(aTask.mTaskId) == null) {
+                    log("Task is in AM but not in WM, waiting for it to settle, taskId="
+                            + aTask.mTaskId);
+                    return false;
+                }
+            }
+
+            for (WindowTask wTask : wStack.mTasks) {
+                if (aStack.getTask(wTask.mTaskId) == null) {
+                    log("Task is in WM but not in AM, waiting for it to settle, taskId="
+                            + wTask.mTaskId);
+                    return false;
+                }
+            }
+        }
+        return true;
+    }
+
+    /** Get the stack position on its display. */
+    int getStackIndexByActivityType(int activityType) {
+        int wmStackIndex = mWmState.getStackIndexByActivityType(activityType);
+        int amStackIndex = mAmState.getStackIndexByActivityType(activityType);
+        assertEquals("Window and activity manager must have the same stack position index",
+                amStackIndex, wmStackIndex);
+        return wmStackIndex;
+    }
+
+    boolean stackBoundsInAMAndWMAreEqual() {
+        for (ActivityStack aStack : mAmState.getStacks()) {
+            final int stackId = aStack.mStackId;
+            final WindowStack wStack = mWmState.getStack(stackId);
+            if (aStack.isFullscreen() != wStack.isFullscreen()) {
+                log("Waiting for correct fullscreen state, stackId=" + stackId);
+                return false;
+            }
+
+            final Rect aStackBounds = aStack.getBounds();
+            final Rect wStackBounds = wStack.getBounds();
+
+            if (aStack.isFullscreen()) {
+                if (aStackBounds != null) {
+                    log("Waiting for correct stack state in AM, stackId=" + stackId);
+                    return false;
+                }
+            } else if (!Objects.equals(aStackBounds, wStackBounds)) {
+                // If stack is not fullscreen - comparing bounds. Not doing it always because
+                // for fullscreen stack bounds in WM can be either null or equal to display size.
+                log("Waiting for stack bound equality in AM and WM, stackId=" + stackId);
+                return false;
+            }
+        }
+
+        return true;
+    }
+
+    /**
+     * Check task bounds when docked to top/left.
+     */
+    void assertDockedTaskBounds(int taskWidth, int taskHeight, String activityName) {
+        // Task size can be affected by default minimal size.
+        int defaultMinimalTaskSize = defaultMinimalTaskSize(
+                mAmState.getStandardStackByWindowingMode(
+                        WINDOWING_MODE_SPLIT_SCREEN_PRIMARY).mDisplayId);
+        int targetWidth = Math.max(taskWidth, defaultMinimalTaskSize);
+        int targetHeight = Math.max(taskHeight, defaultMinimalTaskSize);
+
+        assertEquals(new Rect(0, 0, targetWidth, targetHeight),
+                mAmState.getTaskByActivityName(activityName).getBounds());
+    }
+
+    void assertValidBounds(boolean compareTaskAndStackBounds) {
+        // Cycle through the stacks and tasks to figure out if the home stack is resizable
+        final ActivityTask homeTask = mAmState.getHomeTask();
+        final boolean homeStackIsResizable = homeTask != null
+                && homeTask.getResizeMode() == RESIZE_MODE_RESIZEABLE;
+
+        for (ActivityStack aStack : mAmState.getStacks()) {
+            final int stackId = aStack.mStackId;
+            final WindowStack wStack = mWmState.getStack(stackId);
+            assertNotNull("stackId=" + stackId + " in AM but not in WM?", wStack);
+
+            assertEquals("Stack fullscreen state in AM and WM must be equal stackId=" + stackId,
+                    aStack.isFullscreen(), wStack.isFullscreen());
+
+            final Rect aStackBounds = aStack.getBounds();
+            final Rect wStackBounds = wStack.getBounds();
+
+            if (aStack.isFullscreen()) {
+                assertNull("Stack bounds in AM must be null stackId=" + stackId, aStackBounds);
+            } else {
+                assertEquals("Stack bounds in AM and WM must be equal stackId=" + stackId,
+                        aStackBounds, wStackBounds);
+            }
+
+            for (ActivityTask aTask : aStack.getTasks()) {
+                final int taskId = aTask.mTaskId;
+                final WindowTask wTask = wStack.getTask(taskId);
+                assertNotNull(
+                        "taskId=" + taskId + " in AM but not in WM? stackId=" + stackId, wTask);
+
+                final boolean aTaskIsFullscreen = aTask.isFullscreen();
+                final boolean wTaskIsFullscreen = wTask.isFullscreen();
+                assertEquals("Task fullscreen state in AM and WM must be equal taskId=" + taskId
+                        + ", stackId=" + stackId, aTaskIsFullscreen, wTaskIsFullscreen);
+
+                final Rect aTaskBounds = aTask.getBounds();
+                final Rect wTaskBounds = wTask.getBounds();
+
+                if (aTaskIsFullscreen) {
+                    assertNull("Task bounds in AM must be null for fullscreen taskId=" + taskId,
+                            aTaskBounds);
+                } else if (!homeStackIsResizable && mWmState.isDockedStackMinimized()
+                        && !isScreenPortrait(aStack.mDisplayId)) {
+                    // When minimized using non-resizable launcher in landscape mode, it will move
+                    // the task offscreen in the negative x direction unlike portrait that crops.
+                    // The x value in the task bounds will not match the stack bounds since the
+                    // only the task was moved.
+                    assertEquals("Task bounds in AM and WM must match width taskId=" + taskId
+                                    + ", stackId" + stackId, aTaskBounds.width(),
+                            wTaskBounds.width());
+                    assertEquals("Task bounds in AM and WM must match height taskId=" + taskId
+                                    + ", stackId" + stackId, aTaskBounds.height(),
+                            wTaskBounds.height());
+                    assertEquals("Task bounds must match stack bounds y taskId=" + taskId
+                                    + ", stackId" + stackId, aTaskBounds.top,
+                            wTaskBounds.top);
+                    assertEquals("Task and stack bounds must match width taskId=" + taskId
+                                    + ", stackId" + stackId, aStackBounds.width(),
+                            wTaskBounds.width());
+                    assertEquals("Task and stack bounds must match height taskId=" + taskId
+                                    + ", stackId" + stackId, aStackBounds.height(),
+                            wTaskBounds.height());
+                    assertEquals("Task and stack bounds must match y taskId=" + taskId
+                                    + ", stackId" + stackId, aStackBounds.top,
+                            wTaskBounds.top);
+                } else {
+                    assertEquals("Task bounds in AM and WM must be equal taskId=" + taskId
+                            + ", stackId=" + stackId, aTaskBounds, wTaskBounds);
+
+                    if (compareTaskAndStackBounds
+                            && aStack.getWindowingMode() != WINDOWING_MODE_FREEFORM) {
+                        int aTaskMinWidth = aTask.getMinWidth();
+                        int aTaskMinHeight = aTask.getMinHeight();
+
+                        if (aTaskMinWidth == -1 || aTaskMinHeight == -1) {
+                            // Minimal dimension(s) not set for task - it should be using defaults.
+                            int defaultMinimalSize =
+                                    aStack.getWindowingMode() == WINDOWING_MODE_PINNED
+                                    ? defaultMinimalPinnedTaskSize(aStack.mDisplayId)
+                                    : defaultMinimalTaskSize(aStack.mDisplayId);
+
+                            if (aTaskMinWidth == -1) {
+                                aTaskMinWidth = defaultMinimalSize;
+                            }
+                            if (aTaskMinHeight == -1) {
+                                aTaskMinHeight = defaultMinimalSize;
+                            }
+                        }
+
+                        if (aStackBounds.width() >= aTaskMinWidth
+                                && aStackBounds.height() >= aTaskMinHeight
+                                || aStack.getWindowingMode() == WINDOWING_MODE_PINNED) {
+                            // Bounds are not smaller then minimal possible, so stack and task
+                            // bounds must be equal.
+                            assertEquals("Task bounds must be equal to stack bounds taskId="
+                                    + taskId + ", stackId=" + stackId, aStackBounds, wTaskBounds);
+                        } else if (aStack.getWindowingMode() == WINDOWING_MODE_SPLIT_SCREEN_PRIMARY
+                                && homeStackIsResizable && mWmState.isDockedStackMinimized()) {
+                            // Portrait if the display height is larger than the width
+                            if (isScreenPortrait(aStack.mDisplayId)) {
+                                assertEquals("Task width must be equal to stack width taskId="
+                                                + taskId + ", stackId=" + stackId,
+                                        aStackBounds.width(), wTaskBounds.width());
+                                assertTrue("Task height must be greater than stack height "
+                                                + "taskId=" + taskId + ", stackId=" + stackId,
+                                        aStackBounds.height() < wTaskBounds.height());
+                                assertEquals("Task and stack x position must be equal taskId="
+                                                + taskId + ", stackId=" + stackId,
+                                        wTaskBounds.left, wStackBounds.left);
+                            } else {
+                                assertTrue("Task width must be greater than stack width taskId="
+                                                + taskId + ", stackId=" + stackId,
+                                        aStackBounds.width() < wTaskBounds.width());
+                                assertEquals("Task height must be equal to stack height taskId="
+                                                + taskId + ", stackId=" + stackId,
+                                        aStackBounds.height(), wTaskBounds.height());
+                                assertEquals("Task and stack y position must be equal taskId="
+                                                + taskId + ", stackId=" + stackId, wTaskBounds.top,
+                                        wStackBounds.top);
+                            }
+                        } else {
+                            // Minimal dimensions affect task size, so bounds of task and stack must
+                            // be different - will compare dimensions instead.
+                            int targetWidth = (int) Math.max(aTaskMinWidth,
+                                    aStackBounds.width());
+                            assertEquals("Task width must be set according to minimal width"
+                                            + " taskId=" + taskId + ", stackId=" + stackId,
+                                    targetWidth, (int) wTaskBounds.width());
+                            int targetHeight = (int) Math.max(aTaskMinHeight,
+                                    aStackBounds.height());
+                            assertEquals("Task height must be set according to minimal height"
+                                            + " taskId=" + taskId + ", stackId=" + stackId,
+                                    targetHeight, (int) wTaskBounds.height());
+                        }
+                    }
+                }
+            }
+        }
+    }
+
+    boolean isScreenPortrait() {
+        final int displayId = mAmState.getStandardStackByWindowingMode(
+            WINDOWING_MODE_SPLIT_SCREEN_PRIMARY).mDisplayId;
+        return isScreenPortrait(displayId);
+    }
+
+    boolean isScreenPortrait(int displayId) {
+        final Rect displayRect = mWmState.getDisplay(displayId).getDisplayRect();
+        return displayRect.height() > displayRect.width();
+    }
+
+    static int dpToPx(float dp, int densityDpi) {
+        return (int) (dp * densityDpi / DISPLAY_DENSITY_DEFAULT + 0.5f);
+    }
+
+    private int defaultMinimalTaskSize(int displayId) {
+        return dpToPx(DEFAULT_RESIZABLE_TASK_SIZE_DP, mWmState.getDisplay(displayId).getDpi());
+    }
+
+    private int defaultMinimalPinnedTaskSize(int displayId) {
+        return dpToPx(DEFAULT_PIP_RESIZABLE_TASK_SIZE_DP, mWmState.getDisplay(displayId).getDpi());
+    }
+}
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
new file mode 100644
index 0000000..7be53c7
--- /dev/null
+++ b/tests/framework/base/activitymanager/util/src/android/server/am/ActivityManagerState.java
@@ -0,0 +1,668 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License
+ */
+
+package android.server.am;
+
+import static android.app.ActivityManager.StackId.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;
+import static android.app.WindowConfiguration.ACTIVITY_TYPE_UNDEFINED;
+import static android.app.WindowConfiguration.WINDOWING_MODE_UNDEFINED;
+import static android.server.am.ProtoExtractors.extract;
+import static android.server.am.StateLogger.log;
+import static android.server.am.StateLogger.logE;
+
+import android.content.ComponentName;
+import android.graphics.Rect;
+import android.os.ParcelFileDescriptor;
+import android.support.test.InstrumentationRegistry;
+
+import com.android.server.am.proto.nano.ActivityDisplayProto;
+import com.android.server.am.proto.nano.ActivityRecordProto;
+import com.android.server.am.proto.nano.ActivityStackProto;
+import com.android.server.am.proto.nano.ActivityStackSupervisorProto;
+import com.android.server.am.proto.nano.KeyguardControllerProto;
+import com.android.server.am.proto.nano.TaskRecordProto;
+import com.android.server.wm.proto.nano.ConfigurationContainerProto;
+
+import com.google.protobuf.nano.InvalidProtocolBufferNanoException;
+
+import java.io.ByteArrayOutputStream;
+import java.io.FileInputStream;
+import java.io.IOException;
+import java.nio.charset.StandardCharsets;
+import java.util.ArrayList;
+import java.util.List;
+
+class ActivityManagerState {
+
+    public static final int DUMP_MODE_ACTIVITIES = 0;
+
+    public static final String STATE_RESUMED = "RESUMED";
+    public static final String STATE_PAUSED = "PAUSED";
+    public static final String STATE_STOPPED = "STOPPED";
+    public static final String STATE_DESTROYED = "DESTROYED";
+
+    private static final String DUMPSYS_ACTIVITY_ACTIVITIES = "dumpsys activity --proto activities";
+
+    // Displays in z-order with the top most at the front of the list, starting with primary.
+    private final List<ActivityDisplay> mDisplays = new ArrayList<>();
+    // 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 String mResumedActivityRecord = null;
+    private final List<String> mResumedActivities = new ArrayList<>();
+
+    void computeState() {
+        computeState(DUMP_MODE_ACTIVITIES);
+    }
+
+    void computeState(int dumpMode) {
+        // It is possible the system is in the middle of transition to the right state when we get
+        // the dump. We try a few times to get the information we need before giving up.
+        int retriesLeft = 3;
+        boolean retry = false;
+        byte[] dump = null;
+
+        log("==============================");
+        log("     ActivityManagerState     ");
+        log("==============================");
+
+        do {
+            if (retry) {
+                log("***Incomplete AM state. Retrying...");
+                // Wait half a second between retries for activity manager to finish transitioning.
+                try {
+                    Thread.sleep(500);
+                } catch (InterruptedException e) {
+                    log(e.toString());
+                    // Well I guess we are not waiting...
+                }
+            }
+
+            String dumpsysCmd = "";
+            switch (dumpMode) {
+                case DUMP_MODE_ACTIVITIES:
+                    dumpsysCmd = DUMPSYS_ACTIVITY_ACTIVITIES;
+                    break;
+            }
+
+            dump = executeShellCommand(dumpsysCmd);
+            try {
+                parseSysDumpProto(dump);
+            } catch (InvalidProtocolBufferNanoException ex) {
+                throw new RuntimeException("Failed to parse dumpsys:\n"
+                        + new String(dump, StandardCharsets.UTF_8), ex);
+            }
+
+            retry = mStacks.isEmpty() || mFocusedStackId == -1 || (mResumedActivityRecord == null
+                    || mResumedActivities.isEmpty()) && !mKeyguardControllerState.keyguardShowing;
+        } while (retry && retriesLeft-- > 0);
+
+        if (mStacks.isEmpty()) {
+            logE("No stacks found...");
+        }
+        if (mFocusedStackId == -1) {
+            logE("No focused stack found...");
+        }
+        if (mResumedActivityRecord == null) {
+            logE("No focused activity found...");
+        }
+        if (mResumedActivities.isEmpty()) {
+            logE("No resumed activities found...");
+        }
+    }
+
+    private byte[] executeShellCommand(String cmd) {
+        try {
+            ParcelFileDescriptor pfd =
+                    InstrumentationRegistry.getInstrumentation().getUiAutomation()
+                            .executeShellCommand(cmd);
+            byte[] buf = new byte[512];
+            int bytesRead;
+            FileInputStream fis = new ParcelFileDescriptor.AutoCloseInputStream(pfd);
+            ByteArrayOutputStream stdout = new ByteArrayOutputStream();
+            while ((bytesRead = fis.read(buf)) != -1) {
+                stdout.write(buf, 0, bytesRead);
+            }
+            fis.close();
+            return stdout.toByteArray();
+        } catch (IOException e) {
+            throw new RuntimeException(e);
+        }
+    }
+
+    private void parseSysDumpProto(byte[] sysDump) throws InvalidProtocolBufferNanoException {
+        reset();
+
+        ActivityStackSupervisorProto state = ActivityStackSupervisorProto.parseFrom(sysDump);
+        for (int i = 0; i < state.displays.length; i++) {
+            ActivityDisplayProto activityDisplay = state.displays[i];
+            mDisplays.add(new ActivityDisplay(activityDisplay, this));
+        }
+        mKeyguardControllerState = new KeyguardControllerState(state.keyguardController);
+        mFocusedStackId = state.focusedStackId;
+        if (state.resumedActivity != null) {
+            mResumedActivityRecord = state.resumedActivity.title;
+        }
+    }
+
+
+    private void reset() {
+        mDisplays.clear();
+        mStacks.clear();
+        mFocusedStackId = -1;
+        mResumedActivityRecord = null;
+        mResumedActivities.clear();
+        mKeyguardControllerState = null;
+    }
+
+    ActivityDisplay getDisplay(int displayId) {
+        for (ActivityDisplay display : mDisplays) {
+            if (display.mId == displayId) {
+                return display;
+            }
+        }
+        return null;
+    }
+
+    int getFrontStackId(int displayId) {
+        return getDisplay(displayId).mStacks.get(0).mStackId;
+    }
+
+    int getFrontStackActivityType(int displayId) {
+        return getDisplay(displayId).mStacks.get(0).getActivityType();
+    }
+
+    int getFrontStackWindowingMode(int displayId) {
+        return getDisplay(displayId).mStacks.get(0).getWindowingMode();
+    }
+
+    int getFocusedStackId() {
+        return mFocusedStackId;
+    }
+
+    int getFocusedStackActivityType() {
+        final ActivityStack stack = getStackById(mFocusedStackId);
+        return stack != null ? stack.getActivityType() : ACTIVITY_TYPE_UNDEFINED;
+    }
+
+    int getFocusedStackWindowingMode() {
+        final ActivityStack stack = getStackById(mFocusedStackId);
+        return stack != null ? stack.getWindowingMode() : WINDOWING_MODE_UNDEFINED;
+    }
+
+    String getFocusedActivity() {
+        return mResumedActivityRecord;
+    }
+
+    String getResumedActivity() {
+        return mResumedActivities.get(0);
+    }
+
+    int getResumedActivitiesCount() {
+        return mResumedActivities.size();
+    }
+
+    public KeyguardControllerState getKeyguardControllerState() {
+        return mKeyguardControllerState;
+    }
+
+    boolean containsStack(int stackId) {
+        return getStackById(stackId) != null;
+    }
+
+    boolean containsStack(int windowingMode, int activityType) {
+        for (ActivityStack stack : mStacks) {
+            if (activityType != ACTIVITY_TYPE_UNDEFINED
+                    && activityType != stack.getActivityType()) {
+                continue;
+            }
+            if (windowingMode != WINDOWING_MODE_UNDEFINED
+                    && windowingMode != stack.getWindowingMode()) {
+                continue;
+            }
+            return true;
+        }
+        return false;
+    }
+
+    ActivityStack getStackById(int stackId) {
+        for (ActivityStack stack : mStacks) {
+            if (stackId == stack.mStackId) {
+                return stack;
+            }
+        }
+        return null;
+    }
+
+    ActivityStack getStackByActivityType(int activityType) {
+        for (ActivityStack stack : mStacks) {
+            if (activityType == stack.getActivityType()) {
+                return stack;
+            }
+        }
+        return null;
+    }
+
+    ActivityStack getStandardStackByWindowingMode(int windowingMode) {
+        for (ActivityStack stack : mStacks) {
+            if (stack.getActivityType() != ACTIVITY_TYPE_STANDARD) {
+                continue;
+            }
+            if (stack.getWindowingMode() == windowingMode) {
+                return stack;
+            }
+        }
+        return null;
+    }
+
+    int getStandardTaskCountByWindowingMode(int windowingMode) {
+        int count = 0;
+        for (ActivityStack stack : mStacks) {
+            if (stack.getActivityType() != ACTIVITY_TYPE_STANDARD) {
+                continue;
+            }
+            if (stack.getWindowingMode() == windowingMode) {
+                count += stack.mTasks.size();
+            }
+        }
+        return count;
+    }
+
+    /** Get the stack position on its display. */
+    int getStackIndexByActivityType(int activityType) {
+        for (ActivityDisplay display : mDisplays) {
+            for (int i = 0; i < display.mStacks.size(); i++) {
+                if (activityType == display.mStacks.get(i).getActivityType()) {
+                    return i;
+                }
+            }
+        }
+        return -1;
+    }
+
+    /** Get the stack position on its display. */
+    int getStackIndexByActivityName(String activityName) {
+        final String fullName = ActivityManagerTestBase.getActivityComponentName(activityName);
+
+        for (ActivityDisplay display : mDisplays) {
+            for (int i = display.mStacks.size() - 1; i >= 0; --i) {
+                final ActivityStack stack = display.mStacks.get(i);
+                for (ActivityTask task : stack.mTasks) {
+                    for (Activity activity : task.mActivities) {
+                        if (activity.name.equals(fullName)) {
+                            return i;
+                        }
+                    }
+                }
+            }
+        }
+        return -1;
+    }
+
+    List<ActivityDisplay> getDisplays() {
+        return new ArrayList<>(mDisplays);
+    }
+
+    List<ActivityStack> getStacks() {
+        return new ArrayList<>(mStacks);
+    }
+
+    int getStackCount() {
+        return mStacks.size();
+    }
+
+    boolean containsActivity(String activityName) {
+        for (ActivityStack stack : mStacks) {
+            for (ActivityTask task : stack.mTasks) {
+                for (Activity activity : task.mActivities) {
+                    if (activity.name.equals(activityName)) {
+                        return true;
+                    }
+                }
+            }
+        }
+        return false;
+    }
+
+    boolean containsActivityInWindowingMode(String activityName, int windowingMode) {
+        final String fullName = ActivityManagerTestBase.getActivityComponentName(activityName);
+        for (ActivityStack stack : mStacks) {
+            for (ActivityTask task : stack.mTasks) {
+                for (Activity activity : task.mActivities) {
+                    if (activity.name.equals(fullName)
+                            && activity.getWindowingMode() == windowingMode) {
+                        return true;
+                    }
+                }
+            }
+        }
+        return false;
+    }
+
+    boolean isActivityVisible(String activityName) {
+        for (ActivityStack stack : mStacks) {
+            for (ActivityTask task : stack.mTasks) {
+                for (Activity activity : task.mActivities) {
+                    if (activity.name.equals(activityName)) {
+                        return activity.visible;
+                    }
+                }
+            }
+        }
+        return false;
+    }
+
+    boolean containsStartedActivities() {
+        for (ActivityStack stack : mStacks) {
+            for (ActivityTask task : stack.mTasks) {
+                for (Activity activity : task.mActivities) {
+                    if (!activity.state.equals(STATE_STOPPED)
+                            && !activity.state.equals(STATE_DESTROYED)) {
+                        return true;
+                    }
+                }
+            }
+        }
+        return false;
+    }
+
+    boolean hasActivityState(String activityName, String activityState) {
+        String fullName = ActivityManagerTestBase.getActivityComponentName(activityName);
+        for (ActivityStack stack : mStacks) {
+            for (ActivityTask task : stack.mTasks) {
+                for (Activity activity : task.mActivities) {
+                    if (activity.name.equals(fullName)) {
+                        return activity.state.equals(activityState);
+                    }
+                }
+            }
+        }
+        return false;
+    }
+
+    int getActivityProcId(String activityName) {
+        for (ActivityStack stack : mStacks) {
+            for (ActivityTask task : stack.mTasks) {
+                for (Activity activity : task.mActivities) {
+                    if (activity.name.equals(activityName)) {
+                        return activity.procId;
+                    }
+                }
+            }
+        }
+        return -1;
+    }
+
+    boolean isRecentsActivityVisible() {
+        final Activity recentsActivity = getRecentsActivity();
+        return recentsActivity != null && recentsActivity.visible;
+    }
+
+    ComponentName getHomeActivityName() {
+        Activity activity = getHomeActivity();
+        if (activity == null) {
+            return null;
+        }
+        return ComponentName.unflattenFromString(activity.name);
+    }
+
+    ActivityTask getHomeTask() {
+        final ActivityStack homeStack = getStackByActivityType(ACTIVITY_TYPE_HOME);
+        if (homeStack != null && !homeStack.mTasks.isEmpty()) {
+            return homeStack.mTasks.get(0);
+        }
+        return null;
+    }
+
+    private ActivityTask getRecentsTask() {
+        final ActivityStack recentsStack = getStackByActivityType(ACTIVITY_TYPE_RECENTS);
+        if (recentsStack != null && !recentsStack.mTasks.isEmpty()) {
+            return recentsStack.mTasks.get(0);
+        }
+        return null;
+    }
+
+    private Activity getHomeActivity() {
+        final ActivityTask homeTask = getHomeTask();
+        return homeTask != null ? homeTask.mActivities.get(homeTask.mActivities.size() - 1) : null;
+    }
+
+    private Activity getRecentsActivity() {
+        final ActivityTask recentsTask = getRecentsTask();
+        return recentsTask != null ? recentsTask.mActivities.get(recentsTask.mActivities.size() - 1)
+                : null;
+    }
+
+    int getStackIdByActivityName(String activityName) {
+        final ActivityTask task = getTaskByActivityName(activityName);
+
+        if (task == null) {
+            return INVALID_STACK_ID;
+        }
+
+        return task.mStackId;
+    }
+
+    ActivityTask getTaskByActivityName(String activityName) {
+        return getTaskByActivityName(activityName, WINDOWING_MODE_UNDEFINED);
+    }
+
+    ActivityTask getTaskByActivityName(String activityName, int windowingMode) {
+        String fullName = ActivityManagerTestBase.getActivityComponentName(activityName);
+        for (ActivityStack stack : mStacks) {
+            if (windowingMode == WINDOWING_MODE_UNDEFINED
+                    || windowingMode == stack.getWindowingMode()) {
+                for (ActivityTask task : stack.mTasks) {
+                    for (Activity activity : task.mActivities) {
+                        if (activity.name.equals(fullName)) {
+                            return task;
+                        }
+                    }
+                }
+            }
+        }
+        return null;
+    }
+
+    static class ActivityDisplay extends ActivityContainer {
+
+        int mId;
+        ArrayList<ActivityStack> mStacks = new ArrayList<>();
+
+        ActivityDisplay(ActivityDisplayProto proto, ActivityManagerState amState) {
+            super(proto.configurationContainer);
+            mId = proto.id;
+            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);
+                }
+            }
+        }
+    }
+
+    static class ActivityStack extends ActivityContainer {
+
+        int mDisplayId;
+        int mStackId;
+        String mResumedActivity;
+        ArrayList<ActivityTask> mTasks = new ArrayList<>();
+
+        ActivityStack(ActivityStackProto proto) {
+            super(proto.configurationContainer);
+            mStackId = proto.id;
+            mDisplayId = proto.displayId;
+            mBounds = extract(proto.bounds);
+            mFullscreen = proto.fullscreen;
+            for (int i = 0; i < proto.tasks.length; i++) {
+                mTasks.add(new ActivityTask(proto.tasks[i]));
+            }
+            if (proto.resumedActivity != null) {
+                mResumedActivity = proto.resumedActivity.title;
+            }
+        }
+
+        /**
+         * @return the bottom task in the stack.
+         */
+        ActivityTask getBottomTask() {
+            if (!mTasks.isEmpty()) {
+                // NOTE: Unlike the ActivityManager internals, we dump the state from top to bottom,
+                //       so the indices are inverted
+                return mTasks.get(mTasks.size() - 1);
+            }
+            return null;
+        }
+
+        /**
+         * @return the top task in the stack.
+         */
+        ActivityTask getTopTask() {
+            if (!mTasks.isEmpty()) {
+                // NOTE: Unlike the ActivityManager internals, we dump the state from top to bottom,
+                //       so the indices are inverted
+                return mTasks.get(0);
+            }
+            return null;
+        }
+
+        List<ActivityTask> getTasks() {
+            return new ArrayList<>(mTasks);
+        }
+
+        ActivityTask getTask(int taskId) {
+            for (ActivityTask task : mTasks) {
+                if (taskId == task.mTaskId) {
+                    return task;
+                }
+            }
+            return null;
+        }
+    }
+
+    static class ActivityTask extends ActivityContainer {
+
+        int mTaskId;
+        int mStackId;
+        Rect mLastNonFullscreenBounds;
+        String mRealActivity;
+        String mOrigActivity;
+        ArrayList<Activity> mActivities = new ArrayList<>();
+        int mTaskType = -1;
+        private int mResizeMode;
+
+        ActivityTask(TaskRecordProto proto) {
+            super(proto.configurationContainer);
+            mTaskId = proto.id;
+            mStackId = proto.stackId;
+            mLastNonFullscreenBounds = extract(proto.lastNonFullscreenBounds);
+            mRealActivity = proto.realActivity;
+            mOrigActivity = proto.origActivity;
+            mTaskType = proto.activityType;
+            mResizeMode = proto.resizeMode;
+            mFullscreen = proto.fullscreen;
+            mBounds = extract(proto.bounds);
+            mMinWidth = proto.minWidth;
+            mMinHeight = proto.minHeight;
+            for (int i = 0;  i < proto.activities.length; i++) {
+                mActivities.add(new Activity(proto.activities[i]));
+            }
+        }
+
+        public int getResizeMode() {
+            return mResizeMode;
+        }
+
+        /**
+         * @return whether this task contains the given activity.
+         */
+        public boolean containsActivity(String activityName) {
+            for (Activity activity : mActivities) {
+                if (activity.name.equals(activityName)) {
+                    return true;
+                }
+            }
+            return false;
+        }
+    }
+
+    static class Activity extends ActivityContainer {
+
+        String name;
+        String state;
+        boolean visible;
+        boolean frontOfTask;
+        int procId = -1;
+
+        Activity(ActivityRecordProto proto) {
+            super(proto.configurationContainer);
+            name = proto.identifier.title;
+            state = proto.state;
+            visible = proto.visible;
+            frontOfTask = proto.frontOfTask;
+            if (proto.procId != 0) {
+                procId = proto.procId;
+            }
+        }
+    }
+
+    static abstract class ActivityContainer extends WindowManagerState.ConfigurationContainer {
+        protected boolean mFullscreen;
+        protected Rect mBounds;
+        protected int mMinWidth = -1;
+        protected int mMinHeight = -1;
+
+        ActivityContainer(ConfigurationContainerProto proto) {
+            super(proto);
+        }
+
+        Rect getBounds() {
+            return mBounds;
+        }
+
+        boolean isFullscreen() {
+            return mFullscreen;
+        }
+
+        int getMinWidth() {
+            return mMinWidth;
+        }
+
+        int getMinHeight() {
+            return mMinHeight;
+        }
+    }
+
+    static class KeyguardControllerState {
+
+        boolean keyguardShowing = false;
+        boolean keyguardOccluded = false;
+
+        KeyguardControllerState(KeyguardControllerProto proto) {
+            if (proto != null) {
+                keyguardShowing = proto.keyguardShowing;
+                keyguardOccluded = proto.keyguardOccluded;
+            }
+        }
+    }
+}
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
new file mode 100644
index 0000000..7fe707a
--- /dev/null
+++ b/tests/framework/base/activitymanager/util/src/android/server/am/ActivityManagerTestBase.java
@@ -0,0 +1,1508 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License
+ */
+
+package android.server.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.WindowConfiguration.ACTIVITY_TYPE_ASSISTANT;
+import static android.app.WindowConfiguration.ACTIVITY_TYPE_RECENTS;
+import static android.app.WindowConfiguration.ACTIVITY_TYPE_STANDARD;
+import static android.app.WindowConfiguration.ACTIVITY_TYPE_UNDEFINED;
+import static android.app.WindowConfiguration.WINDOWING_MODE_SPLIT_SCREEN_PRIMARY;
+import static android.content.pm.PackageManager.FEATURE_EMBEDDED;
+import static android.content.pm.PackageManager.FEATURE_FREEFORM_WINDOW_MANAGEMENT;
+import static android.content.pm.PackageManager.FEATURE_LEANBACK;
+import static android.content.pm.PackageManager.FEATURE_PICTURE_IN_PICTURE;
+import static android.content.pm.PackageManager.FEATURE_SCREEN_LANDSCAPE;
+import static android.content.pm.PackageManager.FEATURE_SCREEN_PORTRAIT;
+import static android.content.pm.PackageManager.FEATURE_VR_MODE;
+import static android.content.pm.PackageManager.FEATURE_VR_MODE_HIGH_PERFORMANCE;
+import static android.content.pm.PackageManager.FEATURE_WATCH;
+import static android.server.am.StateLogger.log;
+import static android.server.am.StateLogger.logE;
+import static android.view.KeyEvent.KEYCODE_APP_SWITCH;
+
+import static org.junit.Assert.assertNull;
+import static org.junit.Assert.assertTrue;
+
+import android.app.ActivityManager;
+import android.content.ComponentName;
+import android.content.Context;
+import android.graphics.Bitmap;
+import android.os.ParcelFileDescriptor;
+import android.provider.Settings;
+import android.server.am.settings.SettingsSession;
+import android.support.annotation.NonNull;
+import android.support.test.InstrumentationRegistry;
+import android.support.test.uiautomator.UiDevice;
+import android.view.Display;
+
+import com.android.compatibility.common.util.SystemUtil;
+
+import org.junit.After;
+import org.junit.Before;
+
+import java.io.FileInputStream;
+import java.io.IOException;
+import java.util.HashSet;
+import java.util.List;
+import java.util.UUID;
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
+
+public abstract class ActivityManagerTestBase {
+    private static final boolean PRETEND_DEVICE_SUPPORTS_PIP = false;
+    private static final boolean PRETEND_DEVICE_SUPPORTS_FREEFORM = false;
+    private static final String LOG_SEPARATOR = "LOG_SEPARATOR";
+
+    protected static final int[] ALL_ACTIVITY_TYPE_BUT_HOME = {
+            ACTIVITY_TYPE_STANDARD, ACTIVITY_TYPE_ASSISTANT, ACTIVITY_TYPE_RECENTS,
+            ACTIVITY_TYPE_UNDEFINED
+    };
+
+    private static final String TASK_ID_PREFIX = "taskId";
+
+    private static final String AM_STACK_LIST = "am stack list";
+
+    private static final String AM_FORCE_STOP_TEST_PACKAGE = "am force-stop android.server.am";
+    private static final String AM_FORCE_STOP_SECOND_TEST_PACKAGE
+            = "am force-stop android.server.am.second";
+    private static final String AM_FORCE_STOP_THIRD_TEST_PACKAGE
+            = "am force-stop android.server.am.third";
+
+    protected static final String AM_START_HOME_ACTIVITY_COMMAND =
+            "am start -a android.intent.action.MAIN -c android.intent.category.HOME";
+
+    private static final String AM_MOVE_TOP_ACTIVITY_TO_PINNED_STACK_COMMAND_FORMAT =
+            "am stack move-top-activity-to-pinned-stack %1d 0 0 500 500";
+
+    static final String LAUNCHING_ACTIVITY = "LaunchingActivity";
+    static final String ALT_LAUNCHING_ACTIVITY = "AltLaunchingActivity";
+    static final String BROADCAST_RECEIVER_ACTIVITY = "BroadcastReceiverActivity";
+
+    /** Broadcast shell command for finishing {@link BroadcastReceiverActivity}. */
+    static final String FINISH_ACTIVITY_BROADCAST
+            = "am broadcast -a trigger_broadcast --ez finish true";
+
+    /** Broadcast shell command for finishing {@link BroadcastReceiverActivity}. */
+    static final String MOVE_TASK_TO_BACK_BROADCAST
+            = "am broadcast -a trigger_broadcast --ez moveToBack true";
+
+    private static final String AM_RESIZE_DOCKED_STACK = "am stack resize-docked-stack ";
+    private static final String AM_RESIZE_STACK = "am stack resize ";
+
+    static final String AM_MOVE_TASK = "am stack move-task ";
+
+    private static final String AM_NO_HOME_SCREEN = "am no-home-screen";
+
+    private static final String LOCK_CREDENTIAL = "1234";
+
+    private static final int INVALID_DISPLAY_ID = Display.INVALID_DISPLAY;
+
+    private static final String DEFAULT_COMPONENT_NAME = "android.server.am";
+
+    private static final int UI_MODE_TYPE_MASK = 0x0f;
+    private static final int UI_MODE_TYPE_VR_HEADSET = 0x07;
+
+    private static Boolean sHasHomeScreen = null;
+
+    // TODO: Remove this when all activity name are specified by {@link ComponentName}.
+    static String componentName = DEFAULT_COMPONENT_NAME;
+
+    protected static final int INVALID_DEVICE_ROTATION = -1;
+
+    protected Context mContext;
+    protected ActivityManager mAm;
+    protected UiDevice mDevice;
+
+    private boolean mLockCredentialsSet;
+
+    private boolean mLockDisabled;
+
+    @Deprecated
+    protected static String getAmStartCmd(final String activityName) {
+        return "am start -n " + getActivityComponentName(activityName);
+    }
+
+    /**
+     * @return the am command to start the given activity with the following extra key/value pairs.
+     *         {@param keyValuePairs} must be a list of arguments defining each key/value extra.
+     */
+    // TODO: Make this more generic, for instance accepting flags or extras of other types.
+    protected static String getAmStartCmd(final ComponentName activityName,
+            final String... keyValuePairs) {
+        return getAmStartCmdInternal(activityName.flattenToShortString(), keyValuePairs);
+    }
+
+    @Deprecated
+    protected static String getAmStartCmd(final String activityName,
+            final String... keyValuePairs) {
+        return getAmStartCmdInternal(getActivityComponentName(activityName), keyValuePairs);
+    }
+
+    private static String getAmStartCmdInternal(final String activityName,
+            final String... keyValuePairs) {
+        final StringBuilder cmd = new StringBuilder("am start -n ").append(activityName);
+        if (keyValuePairs.length % 2 != 0) {
+            throw new RuntimeException("keyValuePairs must be pairs of key/value arguments");
+        }
+        for (int i = 0; i < keyValuePairs.length; i += 2) {
+            final String key = keyValuePairs[i];
+            final String value = keyValuePairs[i + 1];
+            cmd.append(String.format(" --es %s %s", key, value));
+        }
+        return cmd.toString();
+    }
+
+    protected static String getAmStartCmd(final String activityName, final int displayId) {
+        return "am start -n " + getActivityComponentName(activityName) + " -f 0x18000000"
+                + " --display " + displayId;
+    }
+
+    protected static String getAmStartCmdInNewTask(final String activityName) {
+        return "am start -n " + getActivityComponentName(activityName) + " -f 0x18000000";
+    }
+
+    protected static String getAmStartCmdOverHome(final String activityName) {
+        return "am start --activity-task-on-home -n " + getActivityComponentName(activityName);
+    }
+
+    protected static String getMoveToPinnedStackCommand(int stackId) {
+        return String.format(AM_MOVE_TOP_ACTIVITY_TO_PINNED_STACK_COMMAND_FORMAT, stackId);
+    }
+
+    protected static String getOrientationBroadcast(int orientation) {
+        return "am broadcast -a trigger_broadcast --ei orientation " + orientation;
+    }
+
+    // TODO: Remove this when all activity name are specified by {@link ComponentName}.
+    static String getActivityComponentName(final String activityName) {
+        return getActivityComponentName(componentName, activityName);
+    }
+
+    private static boolean isFullyQualifiedActivityName(String name) {
+        return name != null && name.contains(".");
+    }
+
+    static String getActivityComponentName(final String packageName, final String activityName) {
+        return packageName + "/" + (isFullyQualifiedActivityName(activityName) ? "" : ".") +
+                activityName;
+    }
+
+    // TODO: Remove this when all activity name are specified by {@link ComponentName}.
+    // A little ugly, but lets avoid having to strip static everywhere for
+    // now.
+    public static void setComponentName(String name) {
+        componentName = name;
+    }
+
+    protected static void setDefaultComponentName() {
+        setComponentName(DEFAULT_COMPONENT_NAME);
+    }
+
+    protected static String getBaseWindowName() {
+        return getBaseWindowName(componentName);
+    }
+
+    static String getBaseWindowName(final String packageName) {
+        return getBaseWindowName(packageName, true /*prependPackageName*/);
+    }
+
+    static String getBaseWindowName(final String packageName, boolean prependPackageName) {
+        return packageName + "/" + (prependPackageName ? packageName + "." : "");
+    }
+
+    // TODO: Remove this when all activity name are specified by {@link ComponentName}.
+    static String getWindowName(final String activityName) {
+        return getWindowName(componentName, activityName);
+    }
+
+    static String getWindowName(final String packageName, final String activityName) {
+        return getBaseWindowName(packageName, !isFullyQualifiedActivityName(activityName))
+                + activityName;
+    }
+
+    protected ActivityAndWindowManagersState mAmWmState = new ActivityAndWindowManagersState();
+
+    private SurfaceTraceReceiver mSurfaceTraceReceiver;
+    private Thread mSurfaceTraceThread;
+
+    protected void installSurfaceObserver(SurfaceTraceReceiver.SurfaceObserver observer) {
+        mSurfaceTraceReceiver = new SurfaceTraceReceiver(observer);
+        mSurfaceTraceThread = new Thread() {
+            @Override
+            public void run() {
+                try {
+                    registerSurfaceTraceReceiver("wm surface-trace", mSurfaceTraceReceiver);
+                } catch (IOException e) {
+                    logE("Error running wm surface-trace: " + e.toString());
+                }
+            }
+        };
+        mSurfaceTraceThread.start();
+    }
+
+    protected void removeSurfaceObserver() {
+        mSurfaceTraceThread.interrupt();
+    }
+
+    @Before
+    public void setUp() throws Exception {
+        mContext = InstrumentationRegistry.getContext();
+        mAm = mContext.getSystemService(ActivityManager.class);
+        mDevice = UiDevice.getInstance(InstrumentationRegistry.getInstrumentation());
+        setDefaultComponentName();
+        executeShellCommand("pm grant " + mContext.getPackageName()
+                + " android.permission.MANAGE_ACTIVITY_STACKS");
+        executeShellCommand("pm grant " + mContext.getPackageName()
+                + " android.permission.ACTIVITY_EMBEDDING");
+
+        wakeUpAndUnlockDevice();
+        pressHomeButton();
+        removeStacksWithActivityTypes(ALL_ACTIVITY_TYPE_BUT_HOME);
+        mLockCredentialsSet = false;
+        mLockDisabled = isLockDisabled();
+    }
+
+    @After
+    public void tearDown() throws Exception {
+        setLockDisabled(mLockDisabled);
+        executeShellCommand(AM_FORCE_STOP_TEST_PACKAGE);
+        executeShellCommand(AM_FORCE_STOP_SECOND_TEST_PACKAGE);
+        executeShellCommand(AM_FORCE_STOP_THIRD_TEST_PACKAGE);
+        removeStacksWithActivityTypes(ALL_ACTIVITY_TYPE_BUT_HOME);
+        wakeUpAndUnlockDevice();
+        pressHomeButton();
+    }
+
+    protected void removeStacksWithActivityTypes(int... activityTypes) {
+        mAm.removeStacksWithActivityTypes(activityTypes);
+    }
+
+    protected void removeStacksInWindowingModes(int... windowingModes) {
+        mAm.removeStacksInWindowingModes(windowingModes);
+        waitForIdle();
+    }
+
+    public static String executeShellCommand(String command) {
+        log("Shell command: " + command);
+        try {
+            return SystemUtil
+                    .runShellCommand(InstrumentationRegistry.getInstrumentation(), command);
+        } catch (IOException e) {
+            //bubble it up
+            logE("Error running shell command: " + command);
+            throw new RuntimeException(e);
+        }
+    }
+
+    protected static void registerSurfaceTraceReceiver(String command, SurfaceTraceReceiver outputReceiver)
+            throws IOException {
+        log("Shell command: " + command);
+        ParcelFileDescriptor pfd = InstrumentationRegistry.getInstrumentation().getUiAutomation()
+                .executeShellCommand(command);
+        byte[] buf = new byte[512];
+        int bytesRead;
+        FileInputStream fis = new ParcelFileDescriptor.AutoCloseInputStream(pfd);
+        while ((bytesRead = fis.read(buf)) != -1) {
+            outputReceiver.addOutput(buf, 0, bytesRead);
+        }
+        fis.close();
+    }
+
+    protected Bitmap takeScreenshot() throws Exception {
+        return InstrumentationRegistry.getInstrumentation().getUiAutomation().takeScreenshot();
+    }
+
+    @Deprecated
+    protected void launchActivityInComponent(final String componentName,
+            final String targetActivityName, final String... keyValuePairs) throws Exception {
+        final String originalComponentName = ActivityManagerTestBase.componentName;
+        setComponentName(componentName);
+        launchActivity(targetActivityName, keyValuePairs);
+        setComponentName(originalComponentName);
+    }
+
+    protected void launchActivity(final ComponentName activityName, final String... keyValuePairs)
+            throws Exception {
+        executeShellCommand(getAmStartCmd(activityName, keyValuePairs));
+        mAmWmState.waitForValidState(new WaitForValidActivityState(activityName));
+    }
+
+    @Deprecated
+    protected void launchActivity(final String targetActivityName, final String... keyValuePairs)
+            throws Exception {
+        executeShellCommand(getAmStartCmd(targetActivityName, keyValuePairs));
+        mAmWmState.waitForValidState(targetActivityName);
+    }
+
+    protected void launchActivityNoWait(final ComponentName targetActivityName,
+            final String... keyValuePairs) throws Exception {
+        executeShellCommand(getAmStartCmd(targetActivityName, keyValuePairs));
+    }
+
+    @Deprecated
+    protected void launchActivityNoWait(final String targetActivityName,
+            final String... keyValuePairs) throws Exception {
+        executeShellCommand(getAmStartCmd(targetActivityName, keyValuePairs));
+    }
+
+    @Deprecated
+    protected void launchActivityInNewTask(final String targetActivityName) throws Exception {
+        executeShellCommand(getAmStartCmdInNewTask(targetActivityName));
+        mAmWmState.waitForValidState(targetActivityName);
+    }
+
+    /**
+     * Starts an activity in a new stack.
+     * @return the stack id of the newly created stack.
+     */
+    @Deprecated
+    protected int launchActivityInNewDynamicStack(final String activityName) throws Exception {
+        HashSet<Integer> stackIds = getStackIds();
+        executeShellCommand("am stack start " + ActivityAndWindowManagersState.DEFAULT_DISPLAY_ID
+                + " " + getActivityComponentName(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();
+    }
+
+    /** Returns the set of stack ids. */
+    private HashSet<Integer> getStackIds() throws Exception {
+        mAmWmState.computeState();
+        final List<ActivityManagerState.ActivityStack> stacks = mAmWmState.getAmState().getStacks();
+        final HashSet<Integer> stackIds = new HashSet<>();
+        for (ActivityManagerState.ActivityStack s : stacks) {
+            stackIds.add(s.mStackId);
+        }
+        return stackIds;
+    }
+
+    protected void launchHomeActivity()
+            throws Exception {
+        executeShellCommand(AM_START_HOME_ACTIVITY_COMMAND);
+        mAmWmState.waitForHomeActivityVisible();
+    }
+
+    protected void launchActivityOnDisplay(String targetActivityName, int displayId)
+            throws Exception {
+        executeShellCommand(getAmStartCmd(targetActivityName, displayId));
+
+        mAmWmState.waitForValidState(targetActivityName);
+    }
+
+    protected void launchActivity(String activityName, int windowingMode,
+            final String... keyValuePairs) throws Exception {
+        executeShellCommand(getAmStartCmd(activityName, keyValuePairs)
+                + " --windowingMode " + windowingMode);
+        mAmWmState.waitForValidState(new WaitForValidActivityState.Builder(activityName)
+                .setWindowingMode(windowingMode)
+                .build());
+    }
+
+    /**
+     * Launches {@param  activityName} into split-screen primary windowing mode and also makes
+     * the recents activity visible to the side of it.
+     */
+    protected void launchActivityInSplitScreenWithRecents(String activityName) throws Exception {
+        launchActivityInSplitScreenWithRecents(activityName, SPLIT_SCREEN_CREATE_MODE_TOP_OR_LEFT);
+    }
+
+    protected void launchActivityInSplitScreenWithRecents(String activityName, int createMode)
+            throws Exception {
+        launchActivity(activityName);
+        final int taskId = mAmWmState.getAmState().getTaskByActivityName(activityName).mTaskId;
+        mAm.setTaskWindowingModeSplitScreenPrimary(taskId, createMode, true /* onTop */,
+                false /* animate */, null /* initialBounds */, true /* showRecents */);
+
+        mAmWmState.waitForValidState(activityName,
+                WINDOWING_MODE_SPLIT_SCREEN_PRIMARY, ACTIVITY_TYPE_STANDARD);
+        mAmWmState.waitForRecentsActivityVisible();
+    }
+
+    /** @see #launchActivitiesInSplitScreen(LaunchActivityBuilder, LaunchActivityBuilder) */
+    protected void launchActivitiesInSplitScreen(String primaryActivity, String secondaryActivity)
+            throws Exception {
+        launchActivitiesInSplitScreen(
+                getLaunchActivityBuilder().setTargetActivityName(primaryActivity),
+                getLaunchActivityBuilder().setTargetActivityName(secondaryActivity));
+    }
+
+    /**
+     * Launches {@param primaryActivity} into split-screen primary windowing mode
+     * and {@param secondaryActivity} to the side in split-screen secondary windowing mode.
+     */
+    protected void launchActivitiesInSplitScreen(LaunchActivityBuilder primaryActivity,
+            LaunchActivityBuilder secondaryActivity) throws Exception {
+        // Launch split-screen primary.
+        String tmpLaunchingActivityName = primaryActivity.mLaunchingActivityName;
+        primaryActivity
+                // TODO(b/70618153): Work around issues with the activity launch builder where
+                // launching activity doesn't work. We don't really need launching activity in this
+                // case and should probably change activity launcher to work without a launching
+                // activity.
+                .setLaunchingActivityName(primaryActivity.mTargetActivityName)
+                .setWaitForLaunched(true)
+                .execute();
+        primaryActivity.setLaunchingActivityName(tmpLaunchingActivityName);
+
+        final int taskId = mAmWmState.getAmState().getTaskByActivityName(
+                primaryActivity.mTargetActivityName).mTaskId;
+        mAm.setTaskWindowingModeSplitScreenPrimary(taskId, SPLIT_SCREEN_CREATE_MODE_TOP_OR_LEFT,
+                true /* onTop */, false /* animate */, null /* initialBounds */,
+                true /* showRecents */);
+        mAmWmState.waitForRecentsActivityVisible();
+
+        // Launch split-screen secondary
+        tmpLaunchingActivityName = secondaryActivity.mLaunchingActivityName;
+        secondaryActivity
+                // TODO(b/70618153): Work around issues with the activity launch builder where
+                // launching activity doesn't work. We don't really need launching activity in this
+                // case and should probably change activity launcher to work without a launching
+                // activity.
+                .setLaunchingActivityName(secondaryActivity.mTargetActivityName)
+                .setWaitForLaunched(true)
+                .setToSide(true)
+                .execute();
+        secondaryActivity.setLaunchingActivityName(tmpLaunchingActivityName);
+    }
+
+    protected void setActivityTaskWindowingMode(final ComponentName activityName,
+            final int windowingMode) throws Exception {
+        final int taskId = getActivityTaskId(activityName);
+        mAm.setTaskWindowingMode(taskId, windowingMode, true /* toTop */);
+        mAmWmState.waitForValidState(activityName, windowingMode, ACTIVITY_TYPE_STANDARD);
+    }
+
+    @Deprecated
+    protected void setActivityTaskWindowingMode(String activityName, int windowingMode)
+            throws Exception {
+        final int taskId = getActivityTaskId(activityName);
+        mAm.setTaskWindowingMode(taskId, windowingMode, true /* toTop */);
+        mAmWmState.waitForValidState(activityName, windowingMode, ACTIVITY_TYPE_STANDARD);
+    }
+
+    protected void moveActivityToStack(String activityName, int stackId) throws Exception {
+        final int taskId = getActivityTaskId(activityName);
+        final String cmd = AM_MOVE_TASK + taskId + " " + stackId + " true";
+        executeShellCommand(cmd);
+
+        mAmWmState.waitForValidState(activityName, stackId);
+    }
+
+    protected void resizeActivityTask(String activityName, int left, int top, int right, int bottom)
+            throws Exception {
+        final int taskId = getActivityTaskId(activityName);
+        final String cmd = "am task resize "
+                + taskId + " " + left + " " + top + " " + right + " " + bottom;
+        executeShellCommand(cmd);
+    }
+
+    protected void resizeDockedStack(
+            int stackWidth, int stackHeight, int taskWidth, int taskHeight) {
+        executeShellCommand(AM_RESIZE_DOCKED_STACK
+                + "0 0 " + stackWidth + " " + stackHeight
+                + " 0 0 " + taskWidth + " " + taskHeight);
+    }
+
+    protected void resizeStack(int stackId, int stackLeft, int stackTop, int stackWidth,
+            int stackHeight) {
+        executeShellCommand(AM_RESIZE_STACK + String.format("%d %d %d %d %d", stackId, stackLeft,
+                stackTop, stackWidth, stackHeight));
+    }
+
+    protected void pressHomeButton() {
+        mDevice.pressHome();
+    }
+
+    protected void pressBackButton() {
+        mDevice.pressBack();
+    }
+
+    protected void pressAppSwitchButton() throws Exception {
+        mDevice.pressKeyCode(KEYCODE_APP_SWITCH);
+        mAmWmState.waitForRecentsActivityVisible();
+        mAmWmState.waitForAppTransitionIdle();
+    }
+
+    // Utility method for debugging, not used directly here, but useful, so kept around.
+    protected void printStacksAndTasks() {
+        String output = executeShellCommand(AM_STACK_LIST);
+        for (String line : output.split("\\n")) {
+            log(line);
+        }
+    }
+
+    @Deprecated
+    protected int getActivityTaskId(final ComponentName activityName) {
+        return getWindowTaskId(activityName.flattenToString());
+    }
+
+    @Deprecated
+    protected int getActivityTaskId(final String activityName) {
+        return getWindowTaskId(getWindowName(activityName));
+    }
+
+    @Deprecated
+    private int getWindowTaskId(final String windowName) {
+        final String output = executeShellCommand(AM_STACK_LIST);
+        final Pattern activityPattern = Pattern.compile("(.*) " + windowName + " (.*)");
+        for (final String line : output.split("\\n")) {
+            final Matcher matcher = activityPattern.matcher(line);
+            if (matcher.matches()) {
+                for (String word : line.split("\\s+")) {
+                    if (word.startsWith(TASK_ID_PREFIX)) {
+                        final String withColon = word.split("=")[1];
+                        return Integer.parseInt(withColon.substring(0, withColon.length() - 1));
+                    }
+                }
+            }
+        }
+        return -1;
+    }
+
+    protected boolean supportsVrMode() {
+        return hasDeviceFeature(FEATURE_VR_MODE)
+                && hasDeviceFeature(FEATURE_VR_MODE_HIGH_PERFORMANCE);
+    }
+
+    protected boolean supportsPip() {
+        return hasDeviceFeature(FEATURE_PICTURE_IN_PICTURE)
+                || PRETEND_DEVICE_SUPPORTS_PIP;
+    }
+
+    protected boolean supportsFreeform() {
+        return hasDeviceFeature(FEATURE_FREEFORM_WINDOW_MANAGEMENT)
+                || PRETEND_DEVICE_SUPPORTS_FREEFORM;
+    }
+
+    protected boolean isHandheld() {
+        return !hasDeviceFeature(FEATURE_LEANBACK)
+                && !hasDeviceFeature(FEATURE_WATCH)
+                && !hasDeviceFeature(FEATURE_EMBEDDED);
+    }
+
+    protected boolean isTablet() {
+        // Larger than approx 7" tablets
+        return mContext.getResources().getConfiguration().smallestScreenWidthDp >= 600;
+    }
+
+    // TODO: Switch to using a feature flag, when available.
+    protected boolean isUiModeLockedToVrHeadset() {
+        final String output = runCommandAndPrintOutput("dumpsys uimode");
+
+        Integer curUiMode = null;
+        Boolean uiModeLocked = null;
+        for (String line : output.split("\\n")) {
+            line = line.trim();
+            Matcher matcher = sCurrentUiModePattern.matcher(line);
+            if (matcher.find()) {
+                curUiMode = Integer.parseInt(matcher.group(1), 16);
+            }
+            matcher = sUiModeLockedPattern.matcher(line);
+            if (matcher.find()) {
+                uiModeLocked = matcher.group(1).equals("true");
+            }
+        }
+
+        boolean uiModeLockedToVrHeadset = (curUiMode != null) && (uiModeLocked != null)
+                && ((curUiMode & UI_MODE_TYPE_MASK) == UI_MODE_TYPE_VR_HEADSET) && uiModeLocked;
+
+        if (uiModeLockedToVrHeadset) {
+            log("UI mode is locked to VR headset");
+        }
+
+        return uiModeLockedToVrHeadset;
+    }
+
+    protected boolean supportsSplitScreenMultiWindow() {
+        return ActivityManager.supportsSplitScreenMultiWindow(mContext);
+    }
+
+    protected boolean hasHomeScreen() {
+        if (sHasHomeScreen == null) {
+            sHasHomeScreen = !executeShellCommand(AM_NO_HOME_SCREEN).startsWith("true");
+        }
+        return sHasHomeScreen;
+    }
+
+    /**
+     * Rotation support is indicated by explicitly having both landscape and portrait
+     * features or not listing either at all.
+     */
+    protected boolean supportsRotation() {
+        final boolean supportsLandscape = hasDeviceFeature(FEATURE_SCREEN_LANDSCAPE);
+        final boolean supportsPortrait = hasDeviceFeature(FEATURE_SCREEN_PORTRAIT);
+        return (supportsLandscape && supportsPortrait)
+                || (!supportsLandscape && !supportsPortrait);
+    }
+
+    protected boolean hasDeviceFeature(final String requiredFeature) {
+        return InstrumentationRegistry.getContext()
+                .getPackageManager()
+                .hasSystemFeature(requiredFeature);
+    }
+
+    protected boolean isDisplayOn() {
+        String output = executeShellCommand("dumpsys power");
+        for (String line : output.split("\\n")) {
+            line = line.trim();
+
+            final Matcher matcher = sDisplayStatePattern.matcher(line);
+            if (matcher.matches()) {
+                final String state = matcher.group(1);
+                log("power state=" + state);
+                return "ON".equals(state);
+            }
+        }
+        log("power state :(");
+        return false;
+    }
+
+    protected void sleepDevice() {
+        int retriesLeft = 5;
+        runCommandAndPrintOutput("input keyevent SLEEP");
+        do {
+            if (isDisplayOn()) {
+                log("***Waiting for display to turn off...");
+                try {
+                    Thread.sleep(1000);
+                } catch (InterruptedException e) {
+                    log(e.toString());
+                    // Well I guess we are not waiting...
+                }
+            } else {
+                break;
+            }
+        } while (retriesLeft-- > 0);
+    }
+
+    protected void wakeUpAndUnlockDevice() {
+        wakeUpDevice();
+        unlockDevice();
+    }
+
+    protected void wakeUpAndRemoveLock() {
+        wakeUpDevice();
+        setLockDisabled(true);
+    }
+
+    protected void wakeUpDevice() {
+        runCommandAndPrintOutput("input keyevent WAKEUP");
+    }
+
+    protected void unlockDevice() {
+        mDevice.pressMenu();
+    }
+
+    protected void unlockDeviceWithCredential() throws Exception {
+        mDevice.pressMenu();
+        try {
+            Thread.sleep(3000);
+        } catch (InterruptedException e) {
+            //ignored
+        }
+        enterAndConfirmLockCredential();
+    }
+
+    protected void enterAndConfirmLockCredential() throws Exception {
+        mDevice.waitForIdle(3000);
+
+        runCommandAndPrintOutput("input text " + LOCK_CREDENTIAL);
+        mDevice.pressEnter();
+    }
+
+    protected void gotoKeyguard() throws Exception {
+        sleepDevice();
+        wakeUpDevice();
+        mAmWmState.waitForKeyguardShowingAndNotOccluded();
+    }
+
+    protected void setLockCredential() {
+        mLockCredentialsSet = true;
+        runCommandAndPrintOutput("locksettings set-pin " + LOCK_CREDENTIAL);
+    }
+
+    protected void removeLockCredential() {
+        runCommandAndPrintOutput("locksettings clear --old " + LOCK_CREDENTIAL);
+    }
+
+    /**
+     * Returns whether the lock screen is disabled.
+     * @return true if the lock screen is disabled, false otherwise.
+     */
+    private boolean isLockDisabled() {
+        final String isLockDisabled = runCommandAndPrintOutput("locksettings get-disabled").trim();
+        if ("null".equals(isLockDisabled)) {
+            return false;
+        }
+        return Boolean.parseBoolean(isLockDisabled);
+
+    }
+
+    /**
+     * Disable the lock screen.
+     * @param lockDisabled true if should disable, false otherwise.
+     */
+    void setLockDisabled(boolean lockDisabled) {
+        runCommandAndPrintOutput("locksettings set-disabled " + lockDisabled);
+    }
+
+    /** Helper class to save, set & wait, and restore rotation related preferences. */
+    protected class RotationSession extends SettingsSession<Integer> {
+        private final SettingsSession<Integer> mUserRotation;
+
+        RotationSession() throws Exception {
+            // Save accelerometer_rotation preference.
+            super(Settings.System.getUriFor(Settings.System.ACCELEROMETER_ROTATION),
+                    Settings.System::getInt, Settings.System::putInt);
+            mUserRotation = new SettingsSession<>(
+                    Settings.System.getUriFor(Settings.System.USER_ROTATION),
+                    Settings.System::getInt, Settings.System::putInt);
+            // Disable accelerometer_rotation.
+            super.set(0);
+        }
+
+        @Override
+        public void set(@NonNull Integer value) throws Exception {
+            mUserRotation.set(value);
+            // Wait for settling rotation.
+            mAmWmState.waitForRotation(value);
+        }
+
+        @Override
+        public void close() throws Exception {
+            mUserRotation.close();
+            // Restore accelerometer_rotation preference.
+            super.close();
+        }
+    }
+
+    protected int getDeviceRotation(int displayId) {
+        final String displays = runCommandAndPrintOutput("dumpsys display displays").trim();
+        Pattern pattern = Pattern.compile(
+                "(mDisplayId=" + displayId + ")([\\s\\S]*)(mOverrideDisplayInfo)(.*)"
+                        + "(rotation)(\\s+)(\\d+)");
+        Matcher matcher = pattern.matcher(displays);
+        if (matcher.find()) {
+            final String match = matcher.group(7);
+            return Integer.parseInt(match);
+        }
+
+        return INVALID_DEVICE_ROTATION;
+    }
+
+    protected String runCommandAndPrintOutput(String command) {
+        final String output = executeShellCommand(command);
+        log(output);
+        return output;
+    }
+
+    /**
+     * Tries to clear logcat and inserts log separator in case clearing didn't succeed, so we can
+     * always find the starting point from where to evaluate following logs.
+     * @return Unique log separator.
+     */
+    protected String clearLogcat() {
+        executeShellCommand("logcat -c");
+        final String uniqueString = UUID.randomUUID().toString();
+        executeShellCommand("log -t " + LOG_SEPARATOR + " " + uniqueString);
+        return uniqueString;
+    }
+
+    void assertActivityLifecycle(String activityName, boolean relaunched,
+            String logSeparator) {
+        int retriesLeft = 5;
+        String resultString;
+        do {
+            resultString = verifyLifecycleCondition(activityName, logSeparator, relaunched);
+            if (resultString != null) {
+                log("***Waiting for valid lifecycle state: " + resultString);
+                try {
+                    Thread.sleep(1000);
+                } catch (InterruptedException e) {
+                    log(e.toString());
+                }
+            } else {
+                break;
+            }
+        } while (retriesLeft-- > 0);
+
+        assertNull(resultString, resultString);
+    }
+
+    /** @return Error string if lifecycle counts don't match, null if everything is fine. */
+    private String verifyLifecycleCondition(String activityName, String logSeparator,
+            boolean relaunched) {
+        final ActivityLifecycleCounts lifecycleCounts = new ActivityLifecycleCounts(activityName,
+                logSeparator);
+        if (relaunched) {
+            if (lifecycleCounts.mDestroyCount < 1) {
+                return activityName + " must have been destroyed. mDestroyCount="
+                        + lifecycleCounts.mDestroyCount;
+            }
+            if (lifecycleCounts.mCreateCount < 1) {
+                return activityName + " must have been (re)created. mCreateCount="
+                        + lifecycleCounts.mCreateCount;
+            }
+        } else {
+            if (lifecycleCounts.mDestroyCount > 0) {
+                return activityName + " must *NOT* have been destroyed. mDestroyCount="
+                        + lifecycleCounts.mDestroyCount;
+            }
+            if (lifecycleCounts.mCreateCount > 0) {
+                return activityName + " must *NOT* have been (re)created. mCreateCount="
+                        + lifecycleCounts.mCreateCount;
+            }
+            if (lifecycleCounts.mConfigurationChangedCount < 1) {
+                return activityName + " must have received configuration changed. "
+                        + "mConfigurationChangedCount="
+                        + lifecycleCounts.mConfigurationChangedCount;
+            }
+        }
+        return null;
+    }
+
+    protected void assertRelaunchOrConfigChanged(
+            String activityName, int numRelaunch, int numConfigChange, String logSeparator) {
+        int retriesLeft = 5;
+        String resultString;
+        do {
+            resultString = verifyRelaunchOrConfigChanged(activityName, numRelaunch, numConfigChange,
+                    logSeparator);
+            if (resultString != null) {
+                log("***Waiting for relaunch or config changed: " + resultString);
+                try {
+                    Thread.sleep(1000);
+                } catch (InterruptedException e) {
+                    log(e.toString());
+                }
+            } else {
+                break;
+            }
+        } while (retriesLeft-- > 0);
+
+        assertNull(resultString, resultString);
+    }
+
+    /** @return Error string if lifecycle counts don't match, null if everything is fine. */
+    private String verifyRelaunchOrConfigChanged(String activityName, int numRelaunch,
+            int numConfigChange, String logSeparator) {
+        final ActivityLifecycleCounts lifecycleCounts = new ActivityLifecycleCounts(activityName,
+                logSeparator);
+
+        if (lifecycleCounts.mDestroyCount != numRelaunch) {
+            return activityName + " has been destroyed " + lifecycleCounts.mDestroyCount
+                    + " time(s), expecting " + numRelaunch;
+        } else if (lifecycleCounts.mCreateCount != numRelaunch) {
+            return activityName + " has been (re)created " + lifecycleCounts.mCreateCount
+                    + " time(s), expecting " + numRelaunch;
+        } else if (lifecycleCounts.mConfigurationChangedCount != numConfigChange) {
+            return activityName + " has received " + lifecycleCounts.mConfigurationChangedCount
+                    + " onConfigurationChanged() calls, expecting " + numConfigChange;
+        }
+        return null;
+    }
+
+    protected void assertActivityDestroyed(String activityName, String logSeparator) {
+        int retriesLeft = 5;
+        String resultString;
+        do {
+            resultString = verifyActivityDestroyed(activityName, logSeparator);
+            if (resultString != null) {
+                log("***Waiting for activity destroyed: " + resultString);
+                try {
+                    Thread.sleep(1000);
+                } catch (InterruptedException e) {
+                    log(e.toString());
+                }
+            } else {
+                break;
+            }
+        } while (retriesLeft-- > 0);
+
+        assertNull(resultString, resultString);
+    }
+
+    /** @return Error string if lifecycle counts don't match, null if everything is fine. */
+    private String verifyActivityDestroyed(String activityName, String logSeparator) {
+        final ActivityLifecycleCounts lifecycleCounts = new ActivityLifecycleCounts(activityName,
+                logSeparator);
+
+        if (lifecycleCounts.mDestroyCount != 1) {
+            return activityName + " has been destroyed " + lifecycleCounts.mDestroyCount
+                    + " time(s), expecting single destruction.";
+        } else if (lifecycleCounts.mCreateCount != 0) {
+            return activityName + " has been (re)created " + lifecycleCounts.mCreateCount
+                    + " time(s), not expecting any.";
+        } else if (lifecycleCounts.mConfigurationChangedCount != 0) {
+            return activityName + " has received " + lifecycleCounts.mConfigurationChangedCount
+                    + " onConfigurationChanged() calls, not expecting any.";
+        }
+        return null;
+    }
+
+    protected static String[] getDeviceLogsForComponent(String componentName, String logSeparator) {
+        return getDeviceLogsForComponents(new String[]{componentName}, logSeparator);
+    }
+
+    protected static String[] getDeviceLogsForComponents(final String[] componentNames,
+            String logSeparator) {
+        String filters = LOG_SEPARATOR + ":I ";
+        for (String component : componentNames) {
+            filters += component + ":I ";
+        }
+        final String[] result = executeShellCommand("logcat -v brief -d " + filters + " *:S")
+                .split("\\n");
+        if (logSeparator == null) {
+            return result;
+        }
+
+        // Make sure that we only check logs after the separator.
+        int i = 0;
+        boolean lookingForSeparator = true;
+        while (i < result.length && lookingForSeparator) {
+            if (result[i].contains(logSeparator)) {
+                lookingForSeparator = false;
+            }
+            i++;
+        }
+        final String[] filteredResult = new String[result.length - i];
+        for (int curPos = 0; i < result.length; curPos++, i++) {
+            filteredResult[curPos] = result[i];
+        }
+        return filteredResult;
+    }
+
+    void assertSingleLaunch(String activityName, String logSeparator) {
+        int retriesLeft = 5;
+        String resultString;
+        do {
+            resultString = validateLifecycleCounts(activityName, logSeparator, 1 /* createCount */,
+                    1 /* startCount */, 1 /* resumeCount */, 0 /* pauseCount */, 0 /* stopCount */,
+                    0 /* destroyCount */);
+            if (resultString != null) {
+                log("***Waiting for valid lifecycle state: " + resultString);
+                try {
+                    Thread.sleep(1000);
+                } catch (InterruptedException e) {
+                    log(e.toString());
+                }
+            } else {
+                break;
+            }
+        } while (retriesLeft-- > 0);
+
+        assertNull(resultString, resultString);
+    }
+
+    public void assertSingleLaunchAndStop(String activityName, String logSeparator) {
+        int retriesLeft = 5;
+        String resultString;
+        do {
+            resultString = validateLifecycleCounts(activityName, logSeparator, 1 /* createCount */,
+                    1 /* startCount */, 1 /* resumeCount */, 1 /* pauseCount */, 1 /* stopCount */,
+                    0 /* destroyCount */);
+            if (resultString != null) {
+                log("***Waiting for valid lifecycle state: " + resultString);
+                try {
+                    Thread.sleep(1000);
+                } catch (InterruptedException e) {
+                    log(e.toString());
+                }
+            } else {
+                break;
+            }
+        } while (retriesLeft-- > 0);
+
+        assertNull(resultString, resultString);
+    }
+
+    public void assertSingleStartAndStop(String activityName, String logSeparator) {
+        int retriesLeft = 5;
+        String resultString;
+        do {
+            resultString =  validateLifecycleCounts(activityName, logSeparator, 0 /* createCount */,
+                    1 /* startCount */, 1 /* resumeCount */, 1 /* pauseCount */, 1 /* stopCount */,
+                    0 /* destroyCount */);
+            if (resultString != null) {
+                log("***Waiting for valid lifecycle state: " + resultString);
+                try {
+                    Thread.sleep(1000);
+                } catch (InterruptedException e) {
+                    log(e.toString());
+                }
+            } else {
+                break;
+            }
+        } while (retriesLeft-- > 0);
+
+        assertNull(resultString, resultString);
+    }
+
+    void assertSingleStart(String activityName, String logSeparator) {
+        int retriesLeft = 5;
+        String resultString;
+        do {
+            resultString = validateLifecycleCounts(activityName, logSeparator, 0 /* createCount */,
+                    1 /* startCount */, 1 /* resumeCount */, 0 /* pauseCount */, 0 /* stopCount */,
+                    0 /* destroyCount */);
+            if (resultString != null) {
+                log("***Waiting for valid lifecycle state: " + resultString);
+                try {
+                    Thread.sleep(1000);
+                } catch (InterruptedException e) {
+                    log(e.toString());
+                }
+            } else {
+                break;
+            }
+        } while (retriesLeft-- > 0);
+
+        assertNull(resultString, resultString);
+    }
+
+    private String validateLifecycleCounts(String activityName, String logSeparator,
+            int createCount, int startCount, int resumeCount, int pauseCount, int stopCount,
+            int destroyCount) {
+
+        final ActivityLifecycleCounts lifecycleCounts = new ActivityLifecycleCounts(activityName,
+                logSeparator);
+
+        if (lifecycleCounts.mCreateCount != createCount) {
+            return activityName + " created " + lifecycleCounts.mCreateCount + " times.";
+        }
+        if (lifecycleCounts.mStartCount != startCount) {
+            return activityName + " started " + lifecycleCounts.mStartCount + " times.";
+        }
+        if (lifecycleCounts.mResumeCount != resumeCount) {
+            return activityName + " resumed " + lifecycleCounts.mResumeCount + " times.";
+        }
+        if (lifecycleCounts.mPauseCount != pauseCount) {
+            return activityName + " paused " + lifecycleCounts.mPauseCount + " times.";
+        }
+        if (lifecycleCounts.mStopCount != stopCount) {
+            return activityName + " stopped " + lifecycleCounts.mStopCount + " times.";
+        }
+        if (lifecycleCounts.mDestroyCount != destroyCount) {
+            return activityName + " destroyed " + lifecycleCounts.mDestroyCount + " times.";
+        }
+        return null;
+    }
+
+    // TODO: Now that our test are device side, we can convert these to a more direct communication
+    // channel vs. depending on logs.
+    private static final Pattern sCreatePattern = Pattern.compile("(.+): onCreate");
+    private static final Pattern sStartPattern = Pattern.compile("(.+): onStart");
+    private static final Pattern sResumePattern = Pattern.compile("(.+): onResume");
+    private static final Pattern sPausePattern = Pattern.compile("(.+): onPause");
+    private static final Pattern sConfigurationChangedPattern =
+            Pattern.compile("(.+): onConfigurationChanged");
+    private static final Pattern sMovedToDisplayPattern =
+            Pattern.compile("(.+): onMovedToDisplay");
+    private static final Pattern sStopPattern = Pattern.compile("(.+): onStop");
+    private static final Pattern sDestroyPattern = Pattern.compile("(.+): onDestroy");
+    private static final Pattern sMultiWindowModeChangedPattern =
+            Pattern.compile("(.+): onMultiWindowModeChanged");
+    private static final Pattern sPictureInPictureModeChangedPattern =
+            Pattern.compile("(.+): onPictureInPictureModeChanged");
+    private static final Pattern sUserLeaveHintPattern = Pattern.compile("(.+): onUserLeaveHint");
+    private static final Pattern sNewConfigPattern = Pattern.compile(
+            "(.+): config size=\\((\\d+),(\\d+)\\) displaySize=\\((\\d+),(\\d+)\\)"
+            + " metricsSize=\\((\\d+),(\\d+)\\) smallestScreenWidth=(\\d+) densityDpi=(\\d+)"
+            + " orientation=(\\d+)");
+    private static final Pattern sDisplayStatePattern =
+            Pattern.compile("Display Power: state=(.+)");
+    private static final Pattern sCurrentUiModePattern = Pattern.compile("mCurUiMode=0x(\\d+)");
+    private static final Pattern sUiModeLockedPattern =
+            Pattern.compile("mUiModeLocked=(true|false)");
+
+    static class ReportedSizes {
+        int widthDp;
+        int heightDp;
+        int displayWidth;
+        int displayHeight;
+        int metricsWidth;
+        int metricsHeight;
+        int smallestWidthDp;
+        int densityDpi;
+        int orientation;
+
+        @Override
+        public String toString() {
+            return "ReportedSizes: {widthDp=" + widthDp + " heightDp=" + heightDp
+                    + " displayWidth=" + displayWidth + " displayHeight=" + displayHeight
+                    + " metricsWidth=" + metricsWidth + " metricsHeight=" + metricsHeight
+                    + " smallestWidthDp=" + smallestWidthDp + " densityDpi=" + densityDpi
+                    + " orientation=" + orientation + "}";
+        }
+
+        @Override
+        public boolean equals(Object obj) {
+            if ( this == obj ) return true;
+            if ( !(obj instanceof ReportedSizes) ) return false;
+            ReportedSizes that = (ReportedSizes) obj;
+            return widthDp == that.widthDp
+                    && heightDp == that.heightDp
+                    && displayWidth == that.displayWidth
+                    && displayHeight == that.displayHeight
+                    && metricsWidth == that.metricsWidth
+                    && metricsHeight == that.metricsHeight
+                    && smallestWidthDp == that.smallestWidthDp
+                    && densityDpi == that.densityDpi
+                    && orientation == that.orientation;
+        }
+    }
+
+    ReportedSizes getLastReportedSizesForActivity(String activityName, String logSeparator) {
+        int retriesLeft = 5;
+        ReportedSizes result;
+        do {
+            result = readLastReportedSizes(activityName, logSeparator);
+            if (result == null) {
+                log("***Waiting for sizes to be reported...");
+                try {
+                    Thread.sleep(1000);
+                } catch (InterruptedException e) {
+                    log(e.toString());
+                    // Well I guess we are not waiting...
+                }
+            } else {
+                break;
+            }
+        } while (retriesLeft-- > 0);
+        return result;
+    }
+
+    private ReportedSizes readLastReportedSizes(String activityName, String logSeparator) {
+        final String[] lines = getDeviceLogsForComponent(activityName, logSeparator);
+        for (int i = lines.length - 1; i >= 0; i--) {
+            final String line = lines[i].trim();
+            final Matcher matcher = sNewConfigPattern.matcher(line);
+            if (matcher.matches()) {
+                ReportedSizes details = new ReportedSizes();
+                details.widthDp = Integer.parseInt(matcher.group(2));
+                details.heightDp = Integer.parseInt(matcher.group(3));
+                details.displayWidth = Integer.parseInt(matcher.group(4));
+                details.displayHeight = Integer.parseInt(matcher.group(5));
+                details.metricsWidth = Integer.parseInt(matcher.group(6));
+                details.metricsHeight = Integer.parseInt(matcher.group(7));
+                details.smallestWidthDp = Integer.parseInt(matcher.group(8));
+                details.densityDpi = Integer.parseInt(matcher.group(9));
+                details.orientation = Integer.parseInt(matcher.group(10));
+                return details;
+            }
+        }
+        return null;
+    }
+
+    /** Waits for at least one onMultiWindowModeChanged event. */
+    ActivityLifecycleCounts waitForOnMultiWindowModeChanged(
+            String activityName, String logSeparator) {
+        int retriesLeft = 5;
+        ActivityLifecycleCounts result;
+        do {
+            result = new ActivityLifecycleCounts(activityName, logSeparator);
+            if (result.mMultiWindowModeChangedCount < 1) {
+                log("***waitForOnMultiWindowModeChanged...");
+                try {
+                    Thread.sleep(1000);
+                } catch (InterruptedException e) {
+                    log(e.toString());
+                    // Well I guess we are not waiting...
+                }
+            } else {
+                break;
+            }
+        } while (retriesLeft-- > 0);
+        return result;
+
+    }
+
+    // TODO: Now that our test are device side, we can convert these to a more direct communication
+    // channel vs. depending on logs.
+    static class ActivityLifecycleCounts {
+        int mCreateCount;
+        int mStartCount;
+        int mResumeCount;
+        int mConfigurationChangedCount;
+        int mLastConfigurationChangedLineIndex;
+        int mMovedToDisplayCount;
+        int mMultiWindowModeChangedCount;
+        int mLastMultiWindowModeChangedLineIndex;
+        int mPictureInPictureModeChangedCount;
+        int mLastPictureInPictureModeChangedLineIndex;
+        int mUserLeaveHintCount;
+        int mPauseCount;
+        int mStopCount;
+        int mLastStopLineIndex;
+        int mDestroyCount;
+
+        ActivityLifecycleCounts(String activityName, String logSeparator) {
+            int lineIndex = 0;
+            waitForIdle();
+            for (String line : getDeviceLogsForComponent(activityName, logSeparator)) {
+                line = line.trim();
+                lineIndex++;
+
+                Matcher matcher = sCreatePattern.matcher(line);
+                if (matcher.matches()) {
+                    mCreateCount++;
+                    continue;
+                }
+
+                matcher = sStartPattern.matcher(line);
+                if (matcher.matches()) {
+                    mStartCount++;
+                    continue;
+                }
+
+                matcher = sResumePattern.matcher(line);
+                if (matcher.matches()) {
+                    mResumeCount++;
+                    continue;
+                }
+
+                matcher = sConfigurationChangedPattern.matcher(line);
+                if (matcher.matches()) {
+                    mConfigurationChangedCount++;
+                    mLastConfigurationChangedLineIndex = lineIndex;
+                    continue;
+                }
+
+                matcher = sMovedToDisplayPattern.matcher(line);
+                if (matcher.matches()) {
+                    mMovedToDisplayCount++;
+                    continue;
+                }
+
+                matcher = sMultiWindowModeChangedPattern.matcher(line);
+                if (matcher.matches()) {
+                    mMultiWindowModeChangedCount++;
+                    mLastMultiWindowModeChangedLineIndex = lineIndex;
+                    continue;
+                }
+
+                matcher = sPictureInPictureModeChangedPattern.matcher(line);
+                if (matcher.matches()) {
+                    mPictureInPictureModeChangedCount++;
+                    mLastPictureInPictureModeChangedLineIndex = lineIndex;
+                    continue;
+                }
+
+                matcher = sUserLeaveHintPattern.matcher(line);
+                if (matcher.matches()) {
+                    mUserLeaveHintCount++;
+                    continue;
+                }
+
+                matcher = sPausePattern.matcher(line);
+                if (matcher.matches()) {
+                    mPauseCount++;
+                    continue;
+                }
+
+                matcher = sStopPattern.matcher(line);
+                if (matcher.matches()) {
+                    mStopCount++;
+                    mLastStopLineIndex = lineIndex;
+                    continue;
+                }
+
+                matcher = sDestroyPattern.matcher(line);
+                if (matcher.matches()) {
+                    mDestroyCount++;
+                    continue;
+                }
+            }
+        }
+    }
+
+    protected void stopTestPackage(final ComponentName activityName) throws Exception {
+        executeShellCommand("am force-stop " + activityName.getPackageName());
+    }
+
+    protected LaunchActivityBuilder getLaunchActivityBuilder() {
+        return new LaunchActivityBuilder(mAmWmState);
+    }
+
+    protected static class LaunchActivityBuilder {
+        private final ActivityAndWindowManagersState mAmWmState;
+
+        // The activity to be launched
+        private String mTargetActivityName = "TestActivity";
+        private String mTargetPackage = componentName;
+        private boolean mToSide;
+        private boolean mRandomData;
+        private boolean mNewTask;
+        private boolean mMultipleTask;
+        private int mDisplayId = INVALID_DISPLAY_ID;
+        // A proxy activity that launches other activities including mTargetActivityName
+        private String mLaunchingActivityName = LAUNCHING_ACTIVITY;
+        private boolean mReorderToFront;
+        private boolean mWaitForLaunched;
+        // Use of the following variables indicates that a broadcast receiver should be used instead
+        // of a launching activity;
+        private String mBroadcastReceiverComponent;
+        private String mBroadcastReceiverAction;
+
+        public LaunchActivityBuilder(ActivityAndWindowManagersState amWmState) {
+            mAmWmState = amWmState;
+            mWaitForLaunched = true;
+        }
+
+        public LaunchActivityBuilder setToSide(boolean toSide) {
+            mToSide = toSide;
+            return this;
+        }
+
+        public LaunchActivityBuilder setRandomData(boolean randomData) {
+            mRandomData = randomData;
+            return this;
+        }
+
+        public LaunchActivityBuilder setNewTask(boolean newTask) {
+            mNewTask = newTask;
+            return this;
+        }
+
+        public LaunchActivityBuilder setMultipleTask(boolean multipleTask) {
+            mMultipleTask = multipleTask;
+            return this;
+        }
+
+        public LaunchActivityBuilder setReorderToFront(boolean reorderToFront) {
+            mReorderToFront = reorderToFront;
+            return this;
+        }
+
+        public LaunchActivityBuilder setTargetActivity(ComponentName activity) {
+            mTargetActivityName = activity.getShortClassName();
+            mTargetPackage = activity.getPackageName();
+            return this;
+        }
+
+        public LaunchActivityBuilder setTargetActivityName(String name) {
+            mTargetActivityName = name;
+            return this;
+        }
+
+        public LaunchActivityBuilder setTargetPackage(String pkg) {
+            mTargetPackage = pkg;
+            return this;
+        }
+
+        public LaunchActivityBuilder setDisplayId(int id) {
+            mDisplayId = id;
+            return this;
+        }
+
+        public LaunchActivityBuilder setLaunchingActivityName(String name) {
+            mLaunchingActivityName = name;
+            return this;
+        }
+
+        public LaunchActivityBuilder setWaitForLaunched(boolean shouldWait) {
+            mWaitForLaunched = shouldWait;
+            return this;
+        }
+
+        /** Use broadcast receiver instead of launching activity. */
+        public LaunchActivityBuilder setUseBroadcastReceiver(final ComponentName broadcastReceiver,
+                final String broadcastAction) {
+            mBroadcastReceiverComponent = broadcastReceiver.flattenToShortString();
+            mBroadcastReceiverAction = broadcastAction;
+            return this;
+        }
+
+        /** Use {@link #setUseBroadcastReceiver(ComponentName, String)} instead. */
+        @Deprecated
+        public LaunchActivityBuilder setUseBroadcastReceiver(String componentName,
+                String broadcastAction) {
+            mBroadcastReceiverComponent = componentName;
+            mBroadcastReceiverAction = broadcastAction;
+            return this;
+        }
+
+        public void execute() throws Exception {
+            StringBuilder commandBuilder = new StringBuilder();
+            if (mBroadcastReceiverComponent != null && mBroadcastReceiverAction != null) {
+                // Use broadcast receiver to launch the target.
+                commandBuilder.append("am broadcast -a ").append(mBroadcastReceiverAction);
+                commandBuilder.append(" -p ").append(mBroadcastReceiverComponent);
+            } else {
+                // Use launching activity to launch the target.
+                commandBuilder.append(getAmStartCmd(mLaunchingActivityName));
+                commandBuilder.append(" -f 0x20000000");
+            }
+
+            // Add a flag to ensure we actually mean to launch an activity.
+            commandBuilder.append(" --ez launch_activity true");
+
+            if (mToSide) {
+                commandBuilder.append(" --ez launch_to_the_side true");
+            }
+            if (mRandomData) {
+                commandBuilder.append(" --ez random_data true");
+            }
+            if (mNewTask) {
+                commandBuilder.append(" --ez new_task true");
+            }
+            if (mMultipleTask) {
+                commandBuilder.append(" --ez multiple_task true");
+            }
+            if (mReorderToFront) {
+                commandBuilder.append(" --ez reorder_to_front true");
+            }
+            if (mTargetActivityName != null) {
+                commandBuilder.append(" --es target_activity ").append(mTargetActivityName);
+                commandBuilder.append(" --es package_name ").append(mTargetPackage);
+            }
+            if (mDisplayId != INVALID_DISPLAY_ID) {
+                commandBuilder.append(" --ei display_id ").append(mDisplayId);
+            }
+            executeShellCommand(commandBuilder.toString());
+
+            if (mWaitForLaunched) {
+                mAmWmState.waitForValidState(false /* compareTaskAndStackBounds */, mTargetPackage,
+                        new WaitForValidActivityState.Builder(mTargetActivityName).build());
+            }
+        }
+    }
+
+    protected void tearDownLockCredentials() throws Exception {
+        if (!mLockCredentialsSet) {
+            return;
+        }
+
+        removeLockCredential();
+        // Dismiss active keyguard after credential is cleared, so
+        // keyguard doesn't ask for the stale credential.
+        pressBackButton();
+        sleepDevice();
+        wakeUpAndUnlockDevice();
+    }
+}
diff --git a/tests/framework/base/activitymanager/util/src/android/server/am/ProtoExtractors.java b/tests/framework/base/activitymanager/util/src/android/server/am/ProtoExtractors.java
new file mode 100644
index 0000000..060fc60
--- /dev/null
+++ b/tests/framework/base/activitymanager/util/src/android/server/am/ProtoExtractors.java
@@ -0,0 +1,67 @@
+/*
+ * 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.server.am;
+
+import android.app.WindowConfiguration;
+import android.app.nano.WindowConfigurationProto;
+import android.content.nano.ConfigurationProto;
+import android.content.res.Configuration;
+import android.graphics.Rect;
+import android.graphics.nano.RectProto;
+
+/**
+ * Utility class for extracting some common framework object from nano proto objects.
+ * Normally the extractors will be in the framework object class, but we don't want the framework to
+ * depend on nano proto due to size cost.
+ * TODO: This class should probably be in frameworks/base/lib project so it can be used
+ * outside of CTS.
+ */
+public class ProtoExtractors {
+    public static Configuration extract(ConfigurationProto proto) {
+        final Configuration config = new Configuration();
+        if (proto == null) {
+            return config;
+        }
+        config.windowConfiguration.setTo(extract(proto.windowConfiguration));
+        config.densityDpi = proto.densityDpi;
+        config.orientation = proto.orientation;
+        config.screenHeightDp = proto.screenHeightDp;
+        config.screenWidthDp = proto.screenWidthDp;
+        config.smallestScreenWidthDp = proto.smallestScreenWidthDp;
+        config.screenLayout = proto.screenLayout;
+        config.uiMode = proto.uiMode;
+        return config;
+    }
+
+    public static WindowConfiguration extract(WindowConfigurationProto proto) {
+        final WindowConfiguration config = new WindowConfiguration();
+        if (proto == null) {
+            return config;
+        }
+        config.setAppBounds(extract(proto.appBounds));
+        config.setWindowingMode(proto.windowingMode);
+        config.setActivityType(proto.activityType);
+        return config;
+    }
+
+    public static Rect extract(RectProto proto) {
+        if (proto == null) {
+            return null;
+        }
+        return new Rect(proto.left, proto.top, proto.right, proto.bottom);
+    }
+}
diff --git a/tests/framework/base/activitymanager/util/src/android/server/am/StateLogger.java b/tests/framework/base/activitymanager/util/src/android/server/am/StateLogger.java
new file mode 100644
index 0000000..e3ad3be
--- /dev/null
+++ b/tests/framework/base/activitymanager/util/src/android/server/am/StateLogger.java
@@ -0,0 +1,45 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License
+ */
+
+package android.server.am;
+
+import android.util.Log;
+
+/**
+ * Util class to perform simple state logging.
+ */
+public class StateLogger {
+
+    private static final boolean DEBUG = false;
+    private static String TAG = "AMWM";
+
+    /**
+     * Simple info-level logging gated by {@link #DEBUG} flag
+     */
+    public static void log(String logText) {
+        if (DEBUG) {
+            Log.i(TAG, logText);
+        }
+    }
+
+    public static void logAlways(String logText) {
+        Log.i(TAG, logText);
+    }
+
+    public static void logE(String logText) {
+        Log.e(TAG, logText);
+    }
+}
diff --git a/tests/framework/base/activitymanager/util/src/android/server/am/SurfaceTraceReceiver.java b/tests/framework/base/activitymanager/util/src/android/server/am/SurfaceTraceReceiver.java
new file mode 100644
index 0000000..4b3953c
--- /dev/null
+++ b/tests/framework/base/activitymanager/util/src/android/server/am/SurfaceTraceReceiver.java
@@ -0,0 +1,363 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License
+ */
+
+package android.server.am;
+
+import static android.server.am.StateLogger.logE;
+
+import static org.junit.Assert.fail;
+
+import android.graphics.RectF;
+
+import java.io.ByteArrayInputStream;
+import java.io.DataInputStream;
+import java.io.IOException;
+
+// Parses a trace of surface commands from the WM (in real time)
+// and dispenses them via the SurfaceObserver interface.
+//
+// Data enters through addOutput
+public class SurfaceTraceReceiver {
+
+    final SurfaceObserver mObserver;
+
+    private State mState = State.CMD;
+    private String mCurrentWindowName = null;
+    private int mArgPosition = 0;
+    private float[] mTmpFloats = new float[10];
+    private int[] mTmpInts = new int[10];
+    private RectF mTmpRect = new RectF();
+    private byte[] mUnprocessedBytes = new byte[16384];
+    private byte[] mFullData = new byte[32768];
+    private int mUnprocessedBytesLength;
+
+    public interface SurfaceObserver {
+        default void setAlpha(String windowName, float alpha) {}
+        default void setLayer(String windowName, int layer) {}
+        default void setPosition(String windowName, float x, float y) {}
+        default void setSize(String widnowName, int width, int height) {}
+        default void setLayerStack(String windowName, int layerStack) {}
+        default void setMatrix(String windowName, float dsdx, float dtdx, float dsdy, float dtdy) {}
+        default void setCrop(String windowName, RectF crop) {}
+        default void setFinalCrop(String windowName, RectF finalCrop) {}
+        default void hide(String windowName) {}
+        default void show(String windowName) {}
+        default void setGeometryAppliesWithResize(String windowName) {}
+        default void openTransaction() {}
+        default void closeTransaction() {}
+    };
+
+    enum State {
+        CMD,
+        SET_ALPHA,
+        SET_LAYER,
+        SET_POSITION,
+        SET_SIZE,
+        SET_CROP,
+        SET_FINAL_CROP,
+        SET_LAYER_STACK,
+        SET_MATRIX,
+        HIDE,
+        SHOW,
+        GEOMETRY_APPLIES_WITH_RESIZE
+    };
+
+    SurfaceTraceReceiver(SurfaceObserver observer) {
+        mObserver = observer;
+    }
+
+    // Reset state and prepare to accept a new command.
+    void nextCmd(DataInputStream d) {
+        mState = State.CMD;
+        mCurrentWindowName = null;
+        mArgPosition = 0;
+
+        try {
+            // Consume the sigil
+            d.readByte();
+            d.readByte();
+            d.readByte();
+            d.readByte();
+        } catch (Exception e) {
+            logE("Exception consuming sigil: " + e);
+        }
+    }
+
+    // When the command parsing functions below are called, the window name
+    // will already be parsed. The responsibility of these functions
+    // is to parse other arguments 1 by 1 and accumlate them until the appropriate number
+    // is reached. At that point the parser should emit an event to the observer and
+    // call nextCmd
+    void parseAlpha(DataInputStream d) throws IOException {
+        float alpha = d.readFloat();
+        mObserver.setAlpha(mCurrentWindowName, alpha);
+        nextCmd(d);
+    }
+
+    void parseLayer(DataInputStream d) throws IOException {
+        int layer = d.readInt();
+        mObserver.setLayer(mCurrentWindowName, layer);
+        nextCmd(d);
+    }
+
+    void parsePosition(DataInputStream d) throws IOException {
+        mTmpFloats[mArgPosition] = d.readFloat();
+        mArgPosition++;
+        if (mArgPosition == 2) {
+            mObserver.setPosition(mCurrentWindowName, mTmpFloats[0], mTmpFloats[1]);
+            nextCmd(d);
+        }
+    }
+
+    void parseSize(DataInputStream d) throws IOException {
+        mTmpInts[mArgPosition] = d.readInt();
+        mArgPosition++;
+        if (mArgPosition == 2) {
+            mObserver.setSize(mCurrentWindowName, mTmpInts[0], mTmpInts[1]);
+            nextCmd(d);
+        }
+    }
+
+    // Careful Android rectangle rep is top-left-right-bottom awt is top-left-width-height
+    void parseCrop(DataInputStream d) throws IOException {
+        mTmpFloats[mArgPosition] = d.readFloat();
+        mArgPosition++;
+        if (mArgPosition == 4) {
+            mTmpRect.set(mTmpFloats[0], mTmpFloats[1], mTmpFloats[2],
+                    mTmpFloats[3]);
+            mObserver.setCrop(mCurrentWindowName, mTmpRect);
+            nextCmd(d);
+        }
+    }
+
+    void parseFinalCrop(DataInputStream d) throws IOException {
+        mTmpFloats[mArgPosition] = d.readInt();
+        mArgPosition++;
+        if (mArgPosition == 4) {
+            mTmpRect.set(mTmpFloats[0], mTmpFloats[1], mTmpFloats[2],
+                    mTmpFloats[3]);
+            mObserver.setFinalCrop(mCurrentWindowName, mTmpRect);
+            nextCmd(d);
+        }
+    }
+
+    void parseLayerStack(DataInputStream d) throws IOException {
+        int layerStack = d.readInt();
+        mObserver.setLayerStack(mCurrentWindowName, layerStack);
+        nextCmd(d);
+    }
+
+    void parseSetMatrix(DataInputStream d) throws IOException {
+        mTmpFloats[mArgPosition] = d.readFloat();
+        mArgPosition++;
+        if (mArgPosition == 4) {
+            mObserver.setMatrix(mCurrentWindowName, mTmpFloats[0],
+                    mTmpFloats[1], mTmpFloats[2], mTmpFloats[3]);
+            nextCmd(d);
+        }
+    }
+
+    void parseHide(DataInputStream d) throws IOException {
+        mObserver.hide(mCurrentWindowName);
+        nextCmd(d);
+    }
+
+    void parseShow(DataInputStream d) throws IOException {
+        mObserver.show(mCurrentWindowName);
+        nextCmd(d);
+    }
+
+    void parseGeometryAppliesWithResize(DataInputStream d) throws IOException {
+        mObserver.setGeometryAppliesWithResize(mCurrentWindowName);
+        nextCmd(d);
+    }
+
+    public int indexAfterLastSigil(byte[] data, int offset, int length) {
+        int idx = offset + length - 1;
+        int sigilsNeeded = 4;
+        byte sigil = (byte) 0xfc;
+        while (idx > offset) {
+            if (data[idx] == sigil) {
+                sigilsNeeded--;
+                if (sigilsNeeded == 0) {
+                    return idx + 4;
+                }
+            } else {
+                sigilsNeeded = 4;
+            }
+            idx--;
+        }
+        return idx; // idx == offset at this point
+    }
+
+    // The tricky bit here is ADB may break up our words, and not send us complete messages,
+    // or even complete integers! To ensure we process the data in appropriate chunks,
+    // We look for a sigil (0xfcfcfcfc) and only process data when it ends in as sigil.
+    // Otherwise we save it and wait to receive a sigil, then process the merged data.
+    public void addOutput(byte[] data, int offset, int length) {
+        byte[] combinedData = data;
+
+        // First we have to merge any unprocessed bytes from the last call in to
+        // a combined array.
+        if (mUnprocessedBytesLength > 0) {
+            System.arraycopy(mUnprocessedBytes, 0, mFullData, 0, mUnprocessedBytesLength);
+            System.arraycopy(data, offset, mFullData, mUnprocessedBytesLength, length);
+            combinedData = mFullData;
+            length = mUnprocessedBytesLength + length;
+            offset = 0;
+            mUnprocessedBytesLength = 0;
+        }
+
+        // Now we find the last sigil in our combined array. Everything before this index is
+        // a properly terminated message ready to be parsed.
+        int completedIndex = indexAfterLastSigil(combinedData, offset, length);
+        // If there are any bytes left after the last sigil, save them for next time.
+        if (completedIndex != length + offset) {
+            mUnprocessedBytesLength = (length + offset) - (completedIndex);
+            System.arraycopy(combinedData, completedIndex,
+                    mUnprocessedBytes, 0, mUnprocessedBytesLength);
+        }
+        //  If there was no sigil, we have nothing to process yet.
+        if (completedIndex <= offset) {
+            return;
+        }
+        ByteArrayInputStream b = new ByteArrayInputStream(combinedData, offset,
+                completedIndex - offset);
+        DataInputStream d = new DataInputStream(b);
+
+        // We may not receive an entire message at once (for example we may receive
+        // a command without its arguments), so we track our current state, over multiple
+        // addOutput calls. When we are in State.CMD it means we next expect a new command.
+        // If we are not expecting a command, then all commands with arguments, begin with
+        // a window name. Once we have the window name, individual parseAlpha,
+        // parseLayer, etc...statements will parse command arguments one at a time. Once
+        // the appropriate number of arguments is collected the observer will be invoked
+        // and the state reset. For commands which have no arguments (e.g. open/close transaction),
+        // parseCmd can emit the observer event and call nextCmd() right away.
+        try {
+            while (b.available() > 0) {
+                if (mState != State.CMD && mCurrentWindowName == null) {
+                    mCurrentWindowName = d.readUTF();
+                    if (b.available() == 0) {
+                        return;
+                    }
+                }
+                switch (mState) {
+                    case CMD: {
+                        String cmd = d.readUTF();
+                        parseCmd(d, cmd);
+                        break;
+                    }
+                    case SET_ALPHA: {
+                        parseAlpha(d);
+                        break;
+                    }
+                    case SET_LAYER: {
+                        parseLayer(d);
+                        break;
+                    }
+                    case SET_POSITION: {
+                        parsePosition(d);
+                        break;
+                    }
+                    case SET_SIZE: {
+                        parseSize(d);
+                        break;
+                    }
+                    case SET_CROP: {
+                        parseCrop(d);
+                        break;
+                    }
+                    case SET_FINAL_CROP: {
+                        parseFinalCrop(d);
+                        break;
+                    }
+                    case SET_LAYER_STACK: {
+                        parseLayerStack(d);
+                        break;
+                    }
+                    case SET_MATRIX: {
+                        parseSetMatrix(d);
+                        break;
+                    }
+                    case HIDE: {
+                        parseHide(d);
+                        break;
+                    }
+                    case SHOW: {
+                        parseShow(d);
+                        break;
+                    }
+                    case GEOMETRY_APPLIES_WITH_RESIZE: {
+                        parseGeometryAppliesWithResize(d);
+                        break;
+                    }
+                }
+            }
+        } catch (Exception e) {
+            logE("Error in surface trace receiver: " + e.toString());
+        }
+    }
+
+    void parseCmd(DataInputStream d, String cmd) {
+        switch (cmd) {
+            case "Alpha":
+                mState = State.SET_ALPHA;
+                break;
+            case "Layer":
+                mState = State.SET_LAYER;
+                break;
+            case "Position":
+                mState = State.SET_POSITION;
+                break;
+            case "Size":
+                mState = State.SET_SIZE;
+                break;
+            case "Crop":
+                mState = State.SET_CROP;
+                break;
+            case "FinalCrop":
+                mState = State.SET_FINAL_CROP;
+                break;
+            case "LayerStack":
+                mState = State.SET_LAYER_STACK;
+                break;
+            case "Matrix":
+                mState = State.SET_MATRIX;
+                break;
+            case "Hide":
+                mState = State.HIDE;
+                break;
+            case "Show":
+                mState = State.SHOW;
+                break;
+            case "GeometryAppliesWithResize":
+                mState = State.GEOMETRY_APPLIES_WITH_RESIZE;
+                break;
+            case "OpenTransaction":
+                mObserver.openTransaction();
+                nextCmd(d);
+                break;
+            case "CloseTransaction":
+                mObserver.closeTransaction();
+                nextCmd(d);
+                break;
+            default:
+                fail("Unexpected surface command: " + cmd);
+                break;
+        }
+    }
+}
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
new file mode 100644
index 0000000..5d5366a0
--- /dev/null
+++ b/tests/framework/base/activitymanager/util/src/android/server/am/WaitForValidActivityState.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 android.server.am;
+
+import static android.app.ActivityManager.StackId.INVALID_STACK_ID;
+import static android.app.WindowConfiguration.ACTIVITY_TYPE_UNDEFINED;
+import static android.app.WindowConfiguration.WINDOWING_MODE_UNDEFINED;
+
+import android.content.ComponentName;
+import android.support.annotation.Nullable;
+
+public class WaitForValidActivityState {
+    @Nullable
+    public final String componentName;
+    @Nullable
+    public final String windowName;
+    /** Use {@link #componentName} and  {@link #windowName}. */
+    @Deprecated
+    @Nullable
+    public final String activityName;
+    public final int stackId;
+    public final int windowingMode;
+    public final int activityType;
+
+    public static WaitForValidActivityState forWindow(final String windowName) {
+        return new Builder().setWindowName(windowName).build();
+    }
+
+    public WaitForValidActivityState(final ComponentName activityName) {
+        this.componentName = activityName.flattenToShortString();
+        this.windowName = activityName.flattenToString();
+        this.activityName = getSimpleClassName(activityName);
+        this.stackId = INVALID_STACK_ID;
+        this.windowingMode = WINDOWING_MODE_UNDEFINED;
+        this.activityType = ACTIVITY_TYPE_UNDEFINED;
+    }
+
+    /** Use {@link #WaitForValidActivityState(ComponentName)}. */
+    @Deprecated
+    public WaitForValidActivityState(String activityName) {
+        this.componentName = null;
+        this.windowName = null;
+        this.activityName = activityName;
+        this.stackId = INVALID_STACK_ID;
+        this.windowingMode = WINDOWING_MODE_UNDEFINED;
+        this.activityType = ACTIVITY_TYPE_UNDEFINED;
+    }
+
+    private WaitForValidActivityState(final Builder builder) {
+        this.componentName = builder.mComponentName;
+        this.windowName = builder.mWindowName;
+        this.activityName = builder.mActivityName;
+        this.stackId = builder.mStackId;
+        this.windowingMode = builder.mWindowingMode;
+        this.activityType = builder.mActivityType;
+    }
+
+    /**
+     * @return the class name of <code>componentName</code>, either fully qualified class name or in
+     *         a shortened form (WITHOUT a leading '.') if it is a suffix of the package.
+     * @see ComponentName#getShortClassName()
+     */
+    private static String getSimpleClassName(final ComponentName componentName) {
+        final String packageName = componentName.getPackageName();
+        final String className = componentName.getClassName();
+        if (className.startsWith(packageName)) {
+            final int packageNameLen = packageName.length();
+            if (className.length() > packageNameLen && className.charAt(packageNameLen) == '.') {
+                return className.substring(packageNameLen + 1);
+            }
+        }
+        return className;
+    }
+
+    public static class Builder {
+        @Nullable
+        private String mComponentName = null;
+        @Nullable
+        private String mWindowName = null;
+        @Nullable
+        private String mActivityName = null;
+        private int mStackId = INVALID_STACK_ID;
+        private int mWindowingMode = WINDOWING_MODE_UNDEFINED;
+        private int mActivityType = ACTIVITY_TYPE_UNDEFINED;
+
+        private Builder() {}
+
+        public Builder(final ComponentName activityName) {
+            mComponentName = activityName.flattenToShortString();
+            mWindowName = activityName.flattenToString();
+            mActivityName = getSimpleClassName(activityName);
+        }
+
+        /** Use {@link #Builder(ComponentName)}. */
+        @Deprecated
+        public Builder(String activityName) {
+            mActivityName = activityName;
+        }
+
+        private Builder setWindowName(String windowName) {
+            mWindowName = windowName;
+            return this;
+        }
+
+        public Builder setStackId(int stackId) {
+            mStackId = stackId;
+            return this;
+        }
+
+        public Builder setWindowingMode(int windowingMode) {
+            mWindowingMode = windowingMode;
+            return this;
+        }
+
+        public Builder setActivityType(int activityType) {
+            mActivityType = activityType;
+            return this;
+        }
+
+        public WaitForValidActivityState build() {
+            return new WaitForValidActivityState(this);
+        }
+    }
+}
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
new file mode 100644
index 0000000..1eb855e
--- /dev/null
+++ b/tests/framework/base/activitymanager/util/src/android/server/am/WindowManagerState.java
@@ -0,0 +1,923 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License
+ */
+
+package android.server.am;
+
+import static android.app.WindowConfiguration.ACTIVITY_TYPE_STANDARD;
+import static android.app.WindowConfiguration.ACTIVITY_TYPE_UNDEFINED;
+import static android.app.WindowConfiguration.WINDOWING_MODE_UNDEFINED;
+import static android.server.am.ProtoExtractors.extract;
+import static android.server.am.StateLogger.log;
+import static android.server.am.StateLogger.logE;
+import static android.view.Display.DEFAULT_DISPLAY;
+
+import static org.junit.Assert.fail;
+
+import android.content.res.Configuration;
+import android.graphics.Rect;
+import android.os.ParcelFileDescriptor;
+import android.support.test.InstrumentationRegistry;
+import android.view.nano.DisplayInfoProto;
+
+import com.android.server.wm.proto.nano.AppTransitionProto;
+import com.android.server.wm.proto.nano.AppWindowTokenProto;
+import com.android.server.wm.proto.nano.ConfigurationContainerProto;
+import com.android.server.wm.proto.nano.DisplayFramesProto;
+import com.android.server.wm.proto.nano.DisplayProto;
+import com.android.server.wm.proto.nano.IdentifierProto;
+import com.android.server.wm.proto.nano.PinnedStackControllerProto;
+import com.android.server.wm.proto.nano.StackProto;
+import com.android.server.wm.proto.nano.TaskProto;
+import com.android.server.wm.proto.nano.WindowContainerProto;
+import com.android.server.wm.proto.nano.WindowManagerServiceProto;
+import com.android.server.wm.proto.nano.WindowStateAnimatorProto;
+import com.android.server.wm.proto.nano.WindowStateProto;
+import com.android.server.wm.proto.nano.WindowSurfaceControllerProto;
+import com.android.server.wm.proto.nano.WindowTokenProto;
+
+import com.google.protobuf.nano.InvalidProtocolBufferNanoException;
+
+import java.io.ByteArrayOutputStream;
+import java.io.FileInputStream;
+import java.io.IOException;
+import java.nio.charset.StandardCharsets;
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.LinkedList;
+import java.util.List;
+import java.util.Map;
+
+public class WindowManagerState {
+    public static final String TRANSIT_ACTIVITY_OPEN = "TRANSIT_ACTIVITY_OPEN";
+    public static final String TRANSIT_ACTIVITY_CLOSE = "TRANSIT_ACTIVITY_CLOSE";
+    public static final String TRANSIT_TASK_OPEN = "TRANSIT_TASK_OPEN";
+    public static final String TRANSIT_TASK_CLOSE = "TRANSIT_TASK_CLOSE";
+
+    public static final String TRANSIT_WALLPAPER_OPEN = "TRANSIT_WALLPAPER_OPEN";
+    public static final String TRANSIT_WALLPAPER_CLOSE = "TRANSIT_WALLPAPER_CLOSE";
+    public static final String TRANSIT_WALLPAPER_INTRA_OPEN = "TRANSIT_WALLPAPER_INTRA_OPEN";
+    public static final String TRANSIT_WALLPAPER_INTRA_CLOSE = "TRANSIT_WALLPAPER_INTRA_CLOSE";
+
+    public static final String TRANSIT_KEYGUARD_GOING_AWAY = "TRANSIT_KEYGUARD_GOING_AWAY";
+    public static final String TRANSIT_KEYGUARD_GOING_AWAY_ON_WALLPAPER =
+            "TRANSIT_KEYGUARD_GOING_AWAY_ON_WALLPAPER";
+    public static final String TRANSIT_KEYGUARD_OCCLUDE = "TRANSIT_KEYGUARD_OCCLUDE";
+    public static final String TRANSIT_KEYGUARD_UNOCCLUDE = "TRANSIT_KEYGUARD_UNOCCLUDE";
+
+    public static final String APP_STATE_IDLE = "APP_STATE_IDLE";
+
+    private static final String DUMPSYS_WINDOW = "dumpsys window -a --proto";
+
+    private static final String STARTING_WINDOW_PREFIX = "Starting ";
+    private static final String DEBUGGER_WINDOW_PREFIX = "Waiting For Debugger: ";
+
+    // Windows in z-order with the top most at the front of the list.
+    private List<WindowState> mWindowStates = new ArrayList();
+    // Stacks in z-order with the top most at the front of the list, starting with primary display.
+    private final List<WindowStack> mStacks = new ArrayList();
+    // Stacks on all attached displays, in z-order with the top most at the front of the list.
+    private final Map<Integer, List<WindowStack>> mDisplayStacks
+            = new HashMap<>();
+    private List<Display> mDisplays = new ArrayList();
+    private String mFocusedWindow = null;
+    private String mFocusedApp = null;
+    private String mLastTransition = null;
+    private String mAppTransitionState = null;
+    private String mInputMethodWindowAppToken = null;
+    private Rect mDefaultPinnedStackBounds = new Rect();
+    private Rect mPinnedStackMovementBounds = new Rect();
+    private final LinkedList<String> mSysDump = new LinkedList();
+    private int mRotation;
+    private int mLastOrientation;
+    private boolean mDisplayFrozen;
+    private boolean mIsDockedStackMinimized;
+
+    public void computeState() {
+        // It is possible the system is in the middle of transition to the right state when we get
+        // the dump. We try a few times to get the information we need before giving up.
+        int retriesLeft = 3;
+        boolean retry = false;
+        byte[] dump = null;
+
+        log("==============================");
+        log("      WindowManagerState      ");
+        log("==============================");
+        do {
+            if (retry) {
+                log("***Incomplete WM state. Retrying...");
+                // Wait half a second between retries for window manager to finish transitioning...
+                try {
+                    Thread.sleep(500);
+                } catch (InterruptedException e) {
+                    log(e.toString());
+                    // Well I guess we are not waiting...
+                }
+            }
+
+            dump = executeShellCommand(DUMPSYS_WINDOW);
+            try {
+                parseSysDumpProto(dump);
+            } catch (InvalidProtocolBufferNanoException ex) {
+                throw new RuntimeException("Failed to parse dumpsys:\n"
+                        + new String(dump, StandardCharsets.UTF_8), ex);
+            }
+
+            retry = mWindowStates.isEmpty() || mFocusedApp == null;
+        } while (retry && retriesLeft-- > 0);
+
+        if (mWindowStates.isEmpty()) {
+            logE("No Windows found...");
+        }
+        if (mFocusedWindow == null) {
+            logE("No Focused Window...");
+        }
+        if (mFocusedApp == null) {
+            logE("No Focused App...");
+        }
+    }
+
+    private byte[] executeShellCommand(String cmd) {
+        try {
+            ParcelFileDescriptor pfd =
+                    InstrumentationRegistry.getInstrumentation().getUiAutomation()
+                            .executeShellCommand(cmd);
+            byte[] buf = new byte[512];
+            int bytesRead;
+            FileInputStream fis = new ParcelFileDescriptor.AutoCloseInputStream(pfd);
+            ByteArrayOutputStream stdout = new ByteArrayOutputStream();
+            while ((bytesRead = fis.read(buf)) != -1) {
+                stdout.write(buf, 0, bytesRead);
+            }
+            fis.close();
+            return stdout.toByteArray();
+        } catch (IOException e) {
+            throw new RuntimeException(e);
+        }
+    }
+
+
+    private void parseSysDumpProto(byte[] sysDump) throws InvalidProtocolBufferNanoException {
+        reset();
+        WindowManagerServiceProto state = WindowManagerServiceProto.parseFrom(sysDump);
+        List<WindowState> allWindows = new ArrayList<>();
+        Map<String, WindowState> windowMap = new HashMap<>();
+        if (state.focusedWindow != null) {
+            mFocusedWindow = state.focusedWindow.title;
+        }
+        mFocusedApp = state.focusedApp;
+        for (int i = 0; i < state.rootWindowContainer.displays.length; i++) {
+            DisplayProto displayProto = state.rootWindowContainer.displays[i];
+            final Display display = new Display(displayProto);
+            mDisplays.add(display);
+            allWindows.addAll(display.getWindows());
+            List<WindowStack> stacks = new ArrayList<>();
+            for (int j = 0; j < displayProto.stacks.length; j++) {
+                StackProto stackProto = displayProto.stacks[j];
+                final WindowStack stack = new WindowStack(stackProto);
+                mStacks.add(stack);
+                stacks.add(stack);
+                allWindows.addAll(stack.getWindows());
+            }
+            mDisplayStacks.put(display.mDisplayId, stacks);
+
+            // use properties from the default display only
+            if (display.getDisplayId() == DEFAULT_DISPLAY) {
+                if (displayProto.dockedStackDividerController != null) {
+                    mIsDockedStackMinimized =
+                            displayProto.dockedStackDividerController.minimizedDock;
+                }
+                PinnedStackControllerProto pinnedStackProto = displayProto.pinnedStackController;
+                if (pinnedStackProto != null) {
+                    mDefaultPinnedStackBounds = extract(pinnedStackProto.defaultBounds);
+                    mPinnedStackMovementBounds = extract(pinnedStackProto.movementBounds);
+                }
+            }
+        }
+        for (WindowState w : allWindows) {
+            windowMap.put(w.getToken(), w);
+        }
+        for (int i = 0; i < state.rootWindowContainer.windows.length; i++) {
+            IdentifierProto identifierProto = state.rootWindowContainer.windows[i];
+            String hash_code = Integer.toHexString(identifierProto.hashCode);
+            mWindowStates.add(windowMap.get(hash_code));
+        }
+        if (state.inputMethodWindow != null) {
+            mInputMethodWindowAppToken = Integer.toHexString(state.inputMethodWindow.hashCode);
+        }
+        mDisplayFrozen = state.displayFrozen;
+        mRotation = state.rotation;
+        mLastOrientation = state.lastOrientation;
+        AppTransitionProto appTransitionProto = state.appTransition;
+        int appState = 0;
+        int lastTransition = 0;
+        if (appTransitionProto != null) {
+            appState = appTransitionProto.appTransitionState;
+            lastTransition = appTransitionProto.lastUsedAppTransition;
+        }
+        mAppTransitionState = appStateToString(appState);
+        mLastTransition = appTransitionToString(lastTransition);
+    }
+
+    static String appStateToString(int appState) {
+        switch (appState) {
+            case AppTransitionProto.APP_STATE_IDLE:
+                return "APP_STATE_IDLE";
+            case AppTransitionProto.APP_STATE_READY:
+                return "APP_STATE_READY";
+            case AppTransitionProto.APP_STATE_RUNNING:
+                return "APP_STATE_RUNNING";
+            case AppTransitionProto.APP_STATE_TIMEOUT:
+                return "APP_STATE_TIMEOUT";
+            default:
+                fail("Invalid AppTransitionState");
+                return null;
+        }
+    }
+
+    static String appTransitionToString(int transition) {
+        switch (transition) {
+            case AppTransitionProto.TRANSIT_UNSET: {
+                return "TRANSIT_UNSET";
+            }
+            case AppTransitionProto.TRANSIT_NONE: {
+                return "TRANSIT_NONE";
+            }
+            case AppTransitionProto.TRANSIT_ACTIVITY_OPEN: {
+                return TRANSIT_ACTIVITY_OPEN;
+            }
+            case AppTransitionProto.TRANSIT_ACTIVITY_CLOSE: {
+                return TRANSIT_ACTIVITY_CLOSE;
+            }
+            case AppTransitionProto.TRANSIT_TASK_OPEN: {
+                return TRANSIT_TASK_OPEN;
+            }
+            case AppTransitionProto.TRANSIT_TASK_CLOSE: {
+                return TRANSIT_TASK_CLOSE;
+            }
+            case AppTransitionProto.TRANSIT_TASK_TO_FRONT: {
+                return "TRANSIT_TASK_TO_FRONT";
+            }
+            case AppTransitionProto.TRANSIT_TASK_TO_BACK: {
+                return "TRANSIT_TASK_TO_BACK";
+            }
+            case AppTransitionProto.TRANSIT_WALLPAPER_CLOSE: {
+                return TRANSIT_WALLPAPER_CLOSE;
+            }
+            case AppTransitionProto.TRANSIT_WALLPAPER_OPEN: {
+                return TRANSIT_WALLPAPER_OPEN;
+            }
+            case AppTransitionProto.TRANSIT_WALLPAPER_INTRA_OPEN: {
+                return TRANSIT_WALLPAPER_INTRA_OPEN;
+            }
+            case AppTransitionProto.TRANSIT_WALLPAPER_INTRA_CLOSE: {
+                return TRANSIT_WALLPAPER_INTRA_CLOSE;
+            }
+            case AppTransitionProto.TRANSIT_TASK_OPEN_BEHIND: {
+                return "TRANSIT_TASK_OPEN_BEHIND";
+            }
+            case AppTransitionProto.TRANSIT_ACTIVITY_RELAUNCH: {
+                return "TRANSIT_ACTIVITY_RELAUNCH";
+            }
+            case AppTransitionProto.TRANSIT_DOCK_TASK_FROM_RECENTS: {
+                return "TRANSIT_DOCK_TASK_FROM_RECENTS";
+            }
+            case AppTransitionProto.TRANSIT_KEYGUARD_GOING_AWAY: {
+                return TRANSIT_KEYGUARD_GOING_AWAY;
+            }
+            case AppTransitionProto.TRANSIT_KEYGUARD_GOING_AWAY_ON_WALLPAPER: {
+                return TRANSIT_KEYGUARD_GOING_AWAY_ON_WALLPAPER;
+            }
+            case AppTransitionProto.TRANSIT_KEYGUARD_OCCLUDE: {
+                return TRANSIT_KEYGUARD_OCCLUDE;
+            }
+            case AppTransitionProto.TRANSIT_KEYGUARD_UNOCCLUDE: {
+                return TRANSIT_KEYGUARD_UNOCCLUDE;
+            }
+            default: {
+                fail("Invalid lastUsedAppTransition");
+                return null;
+            }
+        }
+    }
+
+    void getMatchingWindowTokens(final String windowName, List<String> tokenList) {
+        tokenList.clear();
+
+        for (WindowState ws : mWindowStates) {
+            if (windowName.equals(ws.getName())) {
+                tokenList.add(ws.getToken());
+            }
+        }
+    }
+
+    public void getMatchingVisibleWindowState(final String windowName, List<WindowState> windowList) {
+        windowList.clear();
+        for (WindowState ws : mWindowStates) {
+            if (ws.isShown() && windowName.equals(ws.getName())) {
+                windowList.add(ws);
+            }
+        }
+    }
+
+    public boolean containsExitingWindow() {
+        for (WindowState ws : mWindowStates) {
+            if (ws.isExitingWindow()) {
+                return true;
+            }
+        }
+
+        return false;
+    }
+
+    public WindowState getWindowByPackageName(String packageName, int windowType) {
+        for (WindowState ws : mWindowStates) {
+            final String name = ws.getName();
+            if (name == null || !name.contains(packageName)) {
+                continue;
+            }
+            if (windowType != ws.getType()) {
+                continue;
+            }
+            return ws;
+        }
+
+        return null;
+    }
+
+    public void getWindowsByPackageName(String packageName, List<Integer> restrictToTypeList,
+            List<WindowState> outWindowList) {
+        outWindowList.clear();
+        for (WindowState ws : mWindowStates) {
+            final String name = ws.getName();
+            if (name == null || !name.contains(packageName)) {
+                continue;
+            }
+            if (restrictToTypeList != null && !restrictToTypeList.contains(ws.getType())) {
+                continue;
+            }
+            outWindowList.add(ws);
+        }
+    }
+
+    WindowState getWindowStateForAppToken(String appToken) {
+        for (WindowState ws : mWindowStates) {
+            if (ws.getToken().equals(appToken)) {
+                return ws;
+            }
+        }
+        return null;
+    }
+
+    Display getDisplay(int displayId) {
+        for (Display display : mDisplays) {
+            if (displayId == display.getDisplayId()) {
+                return display;
+            }
+        }
+        return null;
+    }
+
+    String getFrontWindow() {
+        if (mWindowStates == null || mWindowStates.isEmpty()) {
+            return null;
+        }
+        return mWindowStates.get(0).getName();
+    }
+
+    public String getFocusedWindow() {
+        return mFocusedWindow;
+    }
+
+    public String getFocusedApp() {
+        return mFocusedApp;
+    }
+
+    String getLastTransition() {
+        return mLastTransition;
+    }
+
+    String getAppTransitionState() {
+        return mAppTransitionState;
+    }
+
+    int getFrontStackId(int displayId) {
+        return mDisplayStacks.get(displayId).get(0).mStackId;
+    }
+
+    int getFrontStackActivityType(int displayId) {
+        return mDisplayStacks.get(displayId).get(0).getActivityType();
+    }
+
+    public int getRotation() {
+        return mRotation;
+    }
+
+    int getLastOrientation() {
+        return mLastOrientation;
+    }
+
+    boolean containsStack(int stackId) {
+        for (WindowStack stack : mStacks) {
+            if (stackId == stack.mStackId) {
+                return true;
+            }
+        }
+        return false;
+    }
+
+    boolean containsStack(int windowingMode, int activityType) {
+        for (WindowStack stack : mStacks) {
+            if (activityType != ACTIVITY_TYPE_UNDEFINED
+                    && activityType != stack.getActivityType()) {
+                continue;
+            }
+            if (windowingMode != WINDOWING_MODE_UNDEFINED
+                    && windowingMode != stack.getWindowingMode()) {
+                continue;
+            }
+            return true;
+        }
+        return false;
+    }
+
+    /**
+     * Check if there exists a window record with matching windowName.
+     */
+    boolean containsWindow(String windowName) {
+        for (WindowState window : mWindowStates) {
+            if (window.getName().equals(windowName)) {
+                return true;
+            }
+        }
+        return false;
+    }
+
+    /**
+     * Check if at least one window which matches provided window name is visible.
+     */
+    boolean isWindowVisible(String windowName) {
+        for (WindowState window : mWindowStates) {
+            if (window.getName().equals(windowName)) {
+                if (window.isShown()) {
+                    return true;
+                }
+            }
+        }
+        return false;
+    }
+
+    boolean allWindowsVisible(String windowName) {
+        boolean allVisible = false;
+        for (WindowState window : mWindowStates) {
+            if (window.getName().equals(windowName)) {
+                if (!window.isShown()) {
+                    log("[VISIBLE] not visible" + windowName);
+                    return false;
+                }
+                log("[VISIBLE] visible" + windowName);
+                allVisible = true;
+            }
+        }
+        return allVisible;
+    }
+
+    WindowStack getStack(int stackId) {
+        for (WindowStack stack : mStacks) {
+            if (stackId == stack.mStackId) {
+                return stack;
+            }
+        }
+        return null;
+    }
+
+    WindowStack getStandardStackByWindowingMode(int windowingMode) {
+        for (WindowStack stack : mStacks) {
+            if (stack.getActivityType() != ACTIVITY_TYPE_STANDARD) {
+                continue;
+            }
+            if (stack.getWindowingMode() == windowingMode) {
+                return stack;
+            }
+        }
+        return null;
+    }
+
+    /** Get the stack position on its display. */
+    int getStackIndexByActivityType(int activityType) {
+        for (Integer displayId : mDisplayStacks.keySet()) {
+            List<WindowStack> stacks = mDisplayStacks.get(displayId);
+            for (int i = 0; i < stacks.size(); i++) {
+                if (activityType == stacks.get(i).getActivityType()) {
+                    return i;
+                }
+            }
+        }
+        return -1;
+    }
+
+    WindowState getInputMethodWindowState() {
+        return getWindowStateForAppToken(mInputMethodWindowAppToken);
+    }
+
+    Rect getStableBounds() {
+        return getDisplay(DEFAULT_DISPLAY).mStableBounds;
+    }
+
+    Rect getDefaultPinnedStackBounds() {
+        return new Rect(mDefaultPinnedStackBounds);
+    }
+
+    Rect getPinnedStackMomentBounds() {
+        return new Rect(mPinnedStackMovementBounds);
+    }
+
+    WindowState findFirstWindowWithType(int type) {
+        for (WindowState window : mWindowStates) {
+            if (window.getType() == type) {
+                return window;
+            }
+        }
+        return null;
+    }
+
+    public boolean isDisplayFrozen() {
+        return mDisplayFrozen;
+    }
+
+    public boolean isDockedStackMinimized() {
+        return mIsDockedStackMinimized;
+    }
+
+    public int getZOrder(WindowState w) {
+        return mWindowStates.size() - mWindowStates.indexOf(w);
+    }
+
+    private void reset() {
+        mSysDump.clear();
+        mStacks.clear();
+        mDisplays.clear();
+        mWindowStates.clear();
+        mDisplayStacks.clear();
+        mFocusedWindow = null;
+        mFocusedApp = null;
+        mLastTransition = null;
+        mInputMethodWindowAppToken = null;
+        mIsDockedStackMinimized = false;
+        mDefaultPinnedStackBounds.setEmpty();
+        mPinnedStackMovementBounds.setEmpty();
+        mRotation = 0;
+        mLastOrientation = 0;
+        mDisplayFrozen = false;
+    }
+
+    static class WindowStack extends WindowContainer {
+
+        int mStackId;
+        ArrayList<WindowTask> mTasks = new ArrayList<>();
+        boolean mWindowAnimationBackgroundSurfaceShowing;
+
+        WindowStack(StackProto proto) {
+            super(proto.windowContainer);
+            mStackId = proto.id;
+            mFullscreen = proto.fillsParent;
+            mBounds = extract(proto.bounds);
+            for (int i = 0; i < proto.tasks.length; i++) {
+                TaskProto taskProto = proto.tasks[i];
+                WindowTask task = new WindowTask(taskProto);
+                mTasks.add(task);
+                mSubWindows.addAll(task.getWindows());
+            }
+            mWindowAnimationBackgroundSurfaceShowing = proto.animationBackgroundSurfaceIsDimming;
+        }
+
+        WindowTask getTask(int taskId) {
+            for (WindowTask task : mTasks) {
+                if (taskId == task.mTaskId) {
+                    return task;
+                }
+            }
+            return null;
+        }
+
+        boolean isWindowAnimationBackgroundSurfaceShowing() {
+            return mWindowAnimationBackgroundSurfaceShowing;
+        }
+    }
+
+    static class WindowTask extends WindowContainer {
+
+        int mTaskId;
+        Rect mTempInsetBounds;
+        List<String> mAppTokens = new ArrayList<>();
+
+        WindowTask(TaskProto proto) {
+            super(proto.windowContainer);
+            mTaskId = proto.id;
+            mFullscreen = proto.fillsParent;
+            mBounds = extract(proto.bounds);
+            for (int i = 0; i < proto.appWindowTokens.length; i++) {
+                AppWindowTokenProto appWindowTokenProto = proto.appWindowTokens[i];
+                mAppTokens.add(appWindowTokenProto.name);
+                WindowTokenProto windowTokenProto = appWindowTokenProto.windowToken;
+                for (int j = 0; j < windowTokenProto.windows.length; j++) {
+                    WindowStateProto windowProto = windowTokenProto.windows[j];
+                    WindowState window = new WindowState(windowProto);
+                    mSubWindows.add(window);
+                    mSubWindows.addAll(window.getWindows());
+                }
+            }
+            mTempInsetBounds = extract(proto.tempInsetBounds);
+        }
+    }
+
+    static class ConfigurationContainer {
+        final Configuration mOverrideConfiguration = new Configuration();
+        final Configuration mFullConfiguration = new Configuration();
+        final Configuration mMergedOverrideConfiguration = new Configuration();
+
+        ConfigurationContainer(ConfigurationContainerProto proto) {
+            if (proto == null) {
+                return;
+            }
+            mOverrideConfiguration.setTo(extract(proto.overrideConfiguration));
+            mFullConfiguration.setTo(extract(proto.fullConfiguration));
+            mMergedOverrideConfiguration.setTo(extract(proto.mergedOverrideConfiguration));
+        }
+
+        int getWindowingMode() {
+            if (mFullConfiguration == null) {
+                return WINDOWING_MODE_UNDEFINED;
+            }
+            return mFullConfiguration.windowConfiguration.getWindowingMode();
+        }
+
+        int getActivityType() {
+            if (mFullConfiguration == null) {
+                return ACTIVITY_TYPE_UNDEFINED;
+            }
+            return mFullConfiguration.windowConfiguration.getActivityType();
+        }
+    }
+
+    static abstract class WindowContainer extends ConfigurationContainer {
+
+        protected boolean mFullscreen;
+        protected Rect mBounds;
+        protected int mOrientation;
+        protected List<WindowState> mSubWindows = new ArrayList<>();
+
+        WindowContainer(WindowContainerProto proto) {
+            super(proto.configurationContainer);
+            mOrientation = proto.orientation;
+        }
+
+        Rect getBounds() {
+            return mBounds;
+        }
+
+        boolean isFullscreen() {
+            return mFullscreen;
+        }
+
+        List<WindowState> getWindows() {
+            return mSubWindows;
+        }
+    }
+
+    static class Display extends WindowContainer {
+
+        private final int mDisplayId;
+        private Rect mDisplayRect = new Rect();
+        private Rect mAppRect = new Rect();
+        private int mDpi;
+        private Rect mStableBounds;
+
+        public Display(DisplayProto proto) {
+            super(proto.windowContainer);
+            mDisplayId = proto.id;
+            for (int i = 0; i < proto.aboveAppWindows.length; i++) {
+                addWindowsFromTokenProto(proto.aboveAppWindows[i]);
+            }
+            for (int i = 0; i < proto.belowAppWindows.length; i++) {
+                addWindowsFromTokenProto(proto.belowAppWindows[i]);
+            }
+            for (int i = 0; i < proto.imeWindows.length; i++) {
+                addWindowsFromTokenProto(proto.imeWindows[i]);
+            }
+            mDpi = proto.dpi;
+            DisplayInfoProto infoProto = proto.displayInfo;
+            if (infoProto != null) {
+                mDisplayRect.set(0, 0, infoProto.logicalWidth, infoProto.logicalHeight);
+                mAppRect.set(0, 0, infoProto.appWidth, infoProto.appHeight);
+            }
+            final DisplayFramesProto displayFramesProto = proto.displayFrames;
+            if (displayFramesProto != null) {
+                mStableBounds = extract(displayFramesProto.stableBounds);
+            }
+        }
+
+        private void addWindowsFromTokenProto(WindowTokenProto proto) {
+            for (int j = 0; j < proto.windows.length; j++) {
+                WindowStateProto windowProto = proto.windows[j];
+                WindowState childWindow = new WindowState(windowProto);
+                mSubWindows.add(childWindow);
+                mSubWindows.addAll(childWindow.getWindows());
+            }
+        }
+
+        int getDisplayId() {
+            return mDisplayId;
+        }
+
+        int getDpi() {
+            return mDpi;
+        }
+
+        Rect getDisplayRect() {
+            return mDisplayRect;
+        }
+
+        Rect getAppRect() {
+            return mAppRect;
+        }
+
+        @Override
+        public String toString() {
+            return "Display #" + mDisplayId + ": mDisplayRect=" + mDisplayRect
+                    + " mAppRect=" + mAppRect;
+        }
+    }
+
+    public static class WindowState extends WindowContainer {
+
+        private static final int WINDOW_TYPE_NORMAL = 0;
+        private static final int WINDOW_TYPE_STARTING = 1;
+        private static final int WINDOW_TYPE_EXITING = 2;
+        private static final int WINDOW_TYPE_DEBUGGER = 3;
+
+        private String mName;
+        private final String mAppToken;
+        private final int mWindowType;
+        private int mType = 0;
+        private int mDisplayId;
+        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 mSurfaceInsets = new Rect();
+        private Rect mContentInsets = new Rect();
+        private Rect mGivenContentInsets = new Rect();
+        private Rect mCrop = new Rect();
+
+        WindowState(WindowStateProto proto) {
+            super(proto.windowContainer);
+            IdentifierProto identifierProto = proto.identifier;
+            mName = identifierProto.title;
+            mAppToken = Integer.toHexString(identifierProto.hashCode);
+            mDisplayId = proto.displayId;
+            mStackId = proto.stackId;
+            if (proto.attributes != null) {
+                mType = proto.attributes.type;
+            }
+            WindowStateAnimatorProto animatorProto = proto.animator;
+            if (animatorProto != null) {
+                if (animatorProto.surface != null) {
+                    WindowSurfaceControllerProto surfaceProto = animatorProto.surface;
+                    mShown = surfaceProto.shown;
+                    mLayer = surfaceProto.layer;
+                }
+                mCrop = extract(animatorProto.lastClipRect);
+            }
+            mGivenContentInsets = extract(proto.givenContentInsets);
+            mFrame = extract(proto.frame);
+            mContainingFrame = extract(proto.containingFrame);
+            mParentFrame = extract(proto.parentFrame);
+            mContentFrame = extract(proto.contentFrame);
+            mContentInsets = extract(proto.contentInsets);
+            mSurfaceInsets = extract(proto.surfaceInsets);
+            if (mName.startsWith(STARTING_WINDOW_PREFIX)) {
+                mWindowType = WINDOW_TYPE_STARTING;
+                // Existing code depends on the prefix being removed
+                mName = mName.substring(STARTING_WINDOW_PREFIX.length());
+            } else if (proto.animatingExit) {
+                mWindowType = WINDOW_TYPE_EXITING;
+            } else if (mName.startsWith(DEBUGGER_WINDOW_PREFIX)) {
+                mWindowType = WINDOW_TYPE_STARTING;
+                mName = mName.substring(DEBUGGER_WINDOW_PREFIX.length());
+            } else {
+                mWindowType = 0;
+            }
+            for (int i = 0; i < proto.childWindows.length; i++) {
+                WindowStateProto childProto = proto.childWindows[i];
+                WindowState childWindow = new WindowState(childProto);
+                mSubWindows.add(childWindow);
+                mSubWindows.addAll(childWindow.getWindows());
+            }
+        }
+
+        public String getName() {
+            return mName;
+        }
+
+        String getToken() {
+            return mAppToken;
+        }
+
+        boolean isStartingWindow() {
+            return mWindowType == WINDOW_TYPE_STARTING;
+        }
+
+        boolean isExitingWindow() {
+            return mWindowType == WINDOW_TYPE_EXITING;
+        }
+
+        boolean isDebuggerWindow() {
+            return mWindowType == WINDOW_TYPE_DEBUGGER;
+        }
+
+        int getDisplayId() {
+            return mDisplayId;
+        }
+
+        int getStackId() {
+            return mStackId;
+        }
+
+        Rect getContainingFrame() {
+            return mContainingFrame;
+        }
+
+        public Rect getFrame() {
+            return mFrame;
+        }
+
+        Rect getSurfaceInsets() {
+            return mSurfaceInsets;
+        }
+
+        Rect getContentInsets() {
+            return mContentInsets;
+        }
+
+        Rect getGivenContentInsets() {
+            return mGivenContentInsets;
+        }
+
+        public Rect getContentFrame() {
+            return mContentFrame;
+        }
+
+        Rect getParentFrame() {
+            return mParentFrame;
+        }
+
+        Rect getCrop() {
+            return mCrop;
+        }
+
+        boolean isShown() {
+            return mShown;
+        }
+
+        public int getType() {
+            return mType;
+        }
+
+        private String getWindowTypeSuffix(int windowType) {
+            switch (windowType) {
+                case WINDOW_TYPE_STARTING:
+                    return " STARTING";
+                case WINDOW_TYPE_EXITING:
+                    return " EXITING";
+                case WINDOW_TYPE_DEBUGGER:
+                    return " DEBUGGER";
+                default:
+                    break;
+            }
+            return "";
+        }
+
+        @Override
+        public String toString() {
+            return "WindowState: {" + mAppToken + " " + mName
+                    + getWindowTypeSuffix(mWindowType) + "}" + " type=" + mType
+                    + " cf=" + mContainingFrame + " pf=" + mParentFrame;
+        }
+    }
+}
diff --git a/tests/framework/base/activitymanager/util/src/android/server/am/settings/SettingsSession.java b/tests/framework/base/activitymanager/util/src/android/server/am/settings/SettingsSession.java
new file mode 100644
index 0000000..9877209
--- /dev/null
+++ b/tests/framework/base/activitymanager/util/src/android/server/am/settings/SettingsSession.java
@@ -0,0 +1,164 @@
+package android.server.am.settings;
+
+import android.content.ContentResolver;
+import android.net.Uri;
+import android.provider.Settings.SettingNotFoundException;
+import android.support.annotation.NonNull;
+import android.support.test.InstrumentationRegistry;
+import android.util.Log;
+
+import java.util.HashMap;
+import java.util.Map;
+
+/**
+ * Helper class to save, set, and restore global system-level preferences.
+ * <p>
+ * To use this class, testing APK must be self-instrumented and have
+ * {@link android.Manifest.permission#WRITE_SECURE_SETTINGS}.
+ * <p>
+ * A test that changes system-level preferences can be written easily and reliably.
+ * <pre>
+ * static class PrefSession extends SettingsSession<String> {
+ *     PrefSession() {
+ *         super(android.provider.Settings.Secure.getUriFor(
+ *                       android.provider.Settings.Secure.PREFERENCE_KEY),
+ *               android.provider.Settings.Secure::getString,
+ *               android.provider.Settings.Secure::putString);
+ *     }
+ * }
+ *
+ * @Test
+ * public void doTest() throws Exception {
+ *     try (final PrefSession prefSession = new PrefSession()) {
+ *         prefSession.set("value 1");
+ *         doTest1();
+ *         prefSession.set("value 2");
+ *         doTest2();
+ *     }
+ * }
+ * </pre>
+ */
+public class SettingsSession<T> implements AutoCloseable {
+    private static final String TAG = SettingsSession.class.getSimpleName();
+    private static final boolean DEBUG = false;
+
+    @FunctionalInterface
+    public interface SettingsGetter<T> {
+        T get(ContentResolver cr, String key) throws SettingNotFoundException;
+    }
+
+    @FunctionalInterface
+    public interface SettingsSetter<T> {
+        void set(ContentResolver cr, String key, T value);
+    }
+
+    /**
+     * To debug to detect nested sessions for the same key. Enabled when {@link #DEBUG} is true.
+     * Note that nested sessions can be merged into one session.
+     */
+    private static final SessionCounters sSessionCounters = new SessionCounters();
+
+    private final Uri mUri;
+    private final SettingsGetter<T> mGetter;
+    private final SettingsSetter<T> mSetter;
+    private final boolean mHasInitialValue;
+    private final T mInitialValue;
+
+    public SettingsSession(final Uri uri, final SettingsGetter<T> getter,
+            final SettingsSetter<T> setter) {
+        mUri = uri;
+        mGetter = getter;
+        mSetter = setter;
+        T initialValue;
+        boolean hasInitialValue;
+        try {
+            initialValue = get(uri, getter);
+            hasInitialValue = true;
+        } catch (SettingNotFoundException e) {
+            initialValue = null;
+            hasInitialValue = false;
+        }
+        mInitialValue = initialValue;
+        mHasInitialValue = hasInitialValue;
+        if (DEBUG) {
+            Log.i(TAG, "start: uri=" + uri
+                    + (mHasInitialValue ? " value=" + mInitialValue : " undefined"));
+            sSessionCounters.open(uri);
+        }
+    }
+
+    public void set(final @NonNull T value) throws Exception {
+        put(mUri, mSetter, value);
+        if (DEBUG) {
+            Log.i(TAG, "  set: uri=" + mUri + " value=" + value);
+        }
+    }
+
+    public T get() throws SettingNotFoundException {
+        return get(mUri, mGetter);
+    }
+
+    @Override
+    public void close() throws Exception {
+        if (mHasInitialValue) {
+            put(mUri, mSetter, mInitialValue);
+            if (DEBUG) {
+                Log.i(TAG, "close: uri=" + mUri + " value=" + mInitialValue);
+            }
+        } else {
+            try {
+                delete(mUri);
+                if (DEBUG) {
+                    Log.i(TAG, "close: uri=" + mUri + " deleted");
+                }
+            } catch (IllegalArgumentException e) {
+                Log.w(TAG, "Can't delete settings " + mUri, e);
+            }
+        }
+        if (DEBUG) {
+            sSessionCounters.close(mUri);
+        }
+    }
+
+    private static <T> void put(final Uri uri, final SettingsSetter<T> setter, T value)
+            throws SettingNotFoundException {
+        setter.set(getContentResolver(), uri.getLastPathSegment(), value);
+    }
+
+    private static <T> T get(final Uri uri, final SettingsGetter<T> getter)
+            throws SettingNotFoundException {
+        return getter.get(getContentResolver(), uri.getLastPathSegment());
+    }
+
+    private static void delete(final Uri uri) throws IllegalArgumentException {
+        getContentResolver().delete(uri, null, null);
+    }
+
+    private static ContentResolver getContentResolver() {
+        return InstrumentationRegistry.getTargetContext().getContentResolver();
+    }
+
+    private static class SessionCounters {
+        private final Map<Uri, Integer> mOpenSessions = new HashMap<>();
+
+        void open(final Uri uri) {
+            final Integer count = mOpenSessions.get(uri);
+            if (count == null) {
+                mOpenSessions.put(uri, 1);
+                return;
+            }
+            mOpenSessions.put(uri, count + 1);
+            Log.w(TAG, "Open nested session for " + uri, new Throwable());
+        }
+
+        void close(final Uri uri) {
+            final int count = mOpenSessions.get(uri);
+            if (count == 1) {
+                mOpenSessions.remove(uri);
+                return;
+            }
+            mOpenSessions.put(uri, count - 1);
+            Log.w(TAG, "Close nested session for " + uri, new Throwable());
+        }
+    }
+}
diff --git a/tests/framework/base/windowmanager/Android.mk b/tests/framework/base/windowmanager/Android.mk
new file mode 100644
index 0000000..2111f19
--- /dev/null
+++ b/tests/framework/base/windowmanager/Android.mk
@@ -0,0 +1,42 @@
+#
+# 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.
+#
+LOCAL_PATH := $(call my-dir)
+
+include $(CLEAR_VARS)
+
+LOCAL_MODULE_TAGS := tests optional
+
+LOCAL_SRC_FILES := \
+    $(call all-java-files-under, src) \
+    $(call all-java-files-under, alertwindowservice/src)
+
+LOCAL_PACKAGE_NAME := CtsWindowManagerDeviceTestCases
+
+LOCAL_JAVA_LIBRARIES := android.test.runner
+
+LOCAL_STATIC_JAVA_LIBRARIES := \
+    compatibility-device-util \
+    android-support-test \
+    platform-test-annotations \
+    cts-amwm-util
+
+LOCAL_COMPATIBILITY_SUITE := cts vts general-tests
+
+LOCAL_SDK_VERSION := test_current
+
+include $(BUILD_CTS_PACKAGE)
+
+include $(call all-makefiles-under,$(LOCAL_PATH))
diff --git a/tests/framework/base/windowmanager/AndroidManifest.xml b/tests/framework/base/windowmanager/AndroidManifest.xml
new file mode 100644
index 0000000..4bcd576
--- /dev/null
+++ b/tests/framework/base/windowmanager/AndroidManifest.xml
@@ -0,0 +1,40 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT 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.server.cts.wm">
+
+    <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" />
+
+    <application android:label="CtsWindowManagerDeviceTestCases">
+        <uses-library android:name="android.test.runner"/>
+
+        <service
+            android:name="android.server.wm.TestLogService"
+            android:enabled="true"
+            android:exported="true">
+        </service>
+    </application>
+
+    <instrumentation android:name="android.support.test.runner.AndroidJUnitRunner"
+                     android:targetPackage="android.server.cts.wm"
+                     android:label="CTS tests of WindowManager">
+    </instrumentation>
+
+</manifest>
diff --git a/tests/framework/base/windowmanager/AndroidTest.xml b/tests/framework/base/windowmanager/AndroidTest.xml
new file mode 100644
index 0000000..ff1bc97
--- /dev/null
+++ b/tests/framework/base/windowmanager/AndroidTest.xml
@@ -0,0 +1,36 @@
+<?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 WindowManager test cases">
+    <option name="test-suite-tag" value="cts" />
+    <option name="config-descriptor:metadata" key="component" value="framework"/>
+    <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"/>
+        <option name="test-file-name" value="CtsWindowManagerDeviceTestCases.apk"/>
+        <option name="test-file-name" value="CtsDragAndDropSourceApp.apk"/>
+        <option name="test-file-name" value="CtsDragAndDropTargetApp.apk"/>
+        <option name="test-file-name" value="CtsDragAndDropTargetAppSdk23.apk"/>
+        <option name="test-file-name" value="CtsDeviceWindowFramesTestApp.apk"/>
+        <option name="test-file-name" value="CtsDeviceAlertWindowTestApp.apk"/>
+        <option name="test-file-name" value="CtsDeviceAlertWindowTestAppSdk25.apk"/>
+        <option name="test-file-name" value="CtsAlertWindowService.apk"/>
+    </target_preparer>
+    <test class="com.android.tradefed.testtype.AndroidJUnitTest">
+        <option name="package" value="android.server.cts.wm"/>
+        <option name="runtime-hint" value="8m"/>
+    </test>
+
+</configuration>
diff --git a/hostsidetests/services/activityandwindowmanager/windowmanager/alertwindowapp/Android.mk b/tests/framework/base/windowmanager/alertwindowapp/Android.mk
similarity index 100%
rename from hostsidetests/services/activityandwindowmanager/windowmanager/alertwindowapp/Android.mk
rename to tests/framework/base/windowmanager/alertwindowapp/Android.mk
diff --git a/tests/framework/base/windowmanager/alertwindowapp/AndroidManifest.xml b/tests/framework/base/windowmanager/alertwindowapp/AndroidManifest.xml
new file mode 100755
index 0000000..446e2fa
--- /dev/null
+++ b/tests/framework/base/windowmanager/alertwindowapp/AndroidManifest.xml
@@ -0,0 +1,33 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT 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"
+          xmlns:androidprv="http://schemas.android.com/apk/prv/res/android"
+          package="android.server.wm.alertwindowapp">
+    <uses-permission android:name="android.permission.SYSTEM_ALERT_WINDOW" />
+
+    <application android:label="CtsAlertWindow">
+        <activity android:name=".AlertWindowTestActivity"
+                  android:exported="true" android:windowSoftInputMode="stateAlwaysVisible">
+            <intent-filter>
+                <action android:name="android.intent.action.MAIN"/>
+                <category android:name="android.intent.category.LAUNCHER"/>
+            </intent-filter>
+        </activity>
+    </application>
+</manifest>
+
diff --git a/tests/framework/base/windowmanager/alertwindowapp/src/android/server/wm/alertwindowapp/AlertWindowTestActivity.java b/tests/framework/base/windowmanager/alertwindowapp/src/android/server/wm/alertwindowapp/AlertWindowTestActivity.java
new file mode 100644
index 0000000..2185dd6
--- /dev/null
+++ b/tests/framework/base/windowmanager/alertwindowapp/src/android/server/wm/alertwindowapp/AlertWindowTestActivity.java
@@ -0,0 +1,49 @@
+/*
+ * 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.server.wm.alertwindowapp;
+
+import android.os.Bundle;
+import android.server.wm.alertwindowappsdk25.AlertWindowTestBaseActivity;
+
+import static android.view.WindowManager.LayoutParams.TYPE_APPLICATION_OVERLAY;
+import static android.view.WindowManager.LayoutParams.TYPE_PHONE;
+import static android.view.WindowManager.LayoutParams.TYPE_PRIORITY_PHONE;
+import static android.view.WindowManager.LayoutParams.TYPE_SYSTEM_ALERT;
+import static android.view.WindowManager.LayoutParams.TYPE_SYSTEM_ERROR;
+import static android.view.WindowManager.LayoutParams.TYPE_SYSTEM_OVERLAY;
+
+public class AlertWindowTestActivity extends AlertWindowTestBaseActivity {
+    private static final int[] ALERT_WINDOW_TYPES = {
+            TYPE_PHONE,
+            TYPE_PRIORITY_PHONE,
+            TYPE_SYSTEM_ALERT,
+            TYPE_SYSTEM_ERROR,
+            TYPE_SYSTEM_OVERLAY,
+            TYPE_APPLICATION_OVERLAY
+    };
+
+    @Override
+    protected void onCreate(Bundle icicle) {
+        super.onCreate(icicle);
+        createAllAlertWindows(getPackageName());
+    }
+
+    @Override
+    protected int[] getAlertWindowTypes() {
+        return ALERT_WINDOW_TYPES;
+    }
+}
diff --git a/hostsidetests/services/activityandwindowmanager/windowmanager/alertwindowappsdk25/Android.mk b/tests/framework/base/windowmanager/alertwindowappsdk25/Android.mk
similarity index 100%
rename from hostsidetests/services/activityandwindowmanager/windowmanager/alertwindowappsdk25/Android.mk
rename to tests/framework/base/windowmanager/alertwindowappsdk25/Android.mk
diff --git a/tests/framework/base/windowmanager/alertwindowappsdk25/AndroidManifest.xml b/tests/framework/base/windowmanager/alertwindowappsdk25/AndroidManifest.xml
new file mode 100755
index 0000000..0ac1c91
--- /dev/null
+++ b/tests/framework/base/windowmanager/alertwindowappsdk25/AndroidManifest.xml
@@ -0,0 +1,32 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+  ~ Copyright (C) 2017 The Android Open Source Project
+  ~
+  ~ Licensed under the Apache License, Version 2.0 (the "License");
+  ~ you may not use this file except in compliance with the License.
+  ~ You may obtain a copy of the License at
+  ~
+  ~      http://www.apache.org/licenses/LICENSE-2.0
+  ~
+  ~ Unless required by applicable law or agreed to in writing, software
+  ~ distributed under the License is distributed on an "AS IS" BASIS,
+  ~ WITHOUT 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.server.wm.alertwindowappsdk25">
+    <uses-permission android:name="android.permission.SYSTEM_ALERT_WINDOW" />
+
+    <application android:label="CtsAlertWindowSdk25">
+        <activity android:name=".AlertWindowTestActivitySdk25"
+                  android:exported="true" android:windowSoftInputMode="stateAlwaysVisible">
+            <intent-filter>
+                <action android:name="android.intent.action.MAIN"/>
+                <category android:name="android.intent.category.LAUNCHER"/>
+            </intent-filter>
+        </activity>
+    </application>
+</manifest>
+
diff --git a/tests/framework/base/windowmanager/alertwindowappsdk25/src/android/server/wm/alertwindowappsdk25/AlertWindowTestActivitySdk25.java b/tests/framework/base/windowmanager/alertwindowappsdk25/src/android/server/wm/alertwindowappsdk25/AlertWindowTestActivitySdk25.java
new file mode 100644
index 0000000..47057f1
--- /dev/null
+++ b/tests/framework/base/windowmanager/alertwindowappsdk25/src/android/server/wm/alertwindowappsdk25/AlertWindowTestActivitySdk25.java
@@ -0,0 +1,46 @@
+/*
+ * 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.server.wm.alertwindowappsdk25;
+
+import android.os.Bundle;
+
+import static android.view.WindowManager.LayoutParams.TYPE_PHONE;
+import static android.view.WindowManager.LayoutParams.TYPE_PRIORITY_PHONE;
+import static android.view.WindowManager.LayoutParams.TYPE_SYSTEM_ALERT;
+import static android.view.WindowManager.LayoutParams.TYPE_SYSTEM_ERROR;
+import static android.view.WindowManager.LayoutParams.TYPE_SYSTEM_OVERLAY;
+
+public class AlertWindowTestActivitySdk25 extends AlertWindowTestBaseActivity {
+    private static final int[] ALERT_WINDOW_TYPES = {
+            TYPE_PHONE,
+            TYPE_PRIORITY_PHONE,
+            TYPE_SYSTEM_ALERT,
+            TYPE_SYSTEM_ERROR,
+            TYPE_SYSTEM_OVERLAY
+    };
+
+    @Override
+    protected void onCreate(Bundle icicle) {
+        super.onCreate(icicle);
+        createAllAlertWindows(getPackageName());
+    }
+
+    @Override
+    protected int[] getAlertWindowTypes() {
+        return ALERT_WINDOW_TYPES;
+    }
+}
diff --git a/tests/framework/base/windowmanager/alertwindowappsdk25/src/android/server/wm/alertwindowappsdk25/AlertWindowTestBaseActivity.java b/tests/framework/base/windowmanager/alertwindowappsdk25/src/android/server/wm/alertwindowappsdk25/AlertWindowTestBaseActivity.java
new file mode 100644
index 0000000..021b886
--- /dev/null
+++ b/tests/framework/base/windowmanager/alertwindowappsdk25/src/android/server/wm/alertwindowappsdk25/AlertWindowTestBaseActivity.java
@@ -0,0 +1,82 @@
+/*
+ * 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.server.wm.alertwindowappsdk25;
+
+import android.app.Activity;
+import android.graphics.Color;
+import android.graphics.Point;
+import android.util.Log;
+import android.view.WindowManager;
+import android.widget.TextView;
+
+import static android.view.Gravity.LEFT;
+import static android.view.Gravity.TOP;
+import static android.view.WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE;
+import static android.view.WindowManager.LayoutParams.FLAG_NOT_TOUCHABLE;
+import static android.view.WindowManager.LayoutParams.FLAG_WATCH_OUTSIDE_TOUCH;
+
+public abstract class AlertWindowTestBaseActivity extends Activity {
+
+    protected void createAllAlertWindows(String windowName) {
+        final int[] alertWindowTypes = getAlertWindowTypes();
+        for (int type : alertWindowTypes) {
+            try {
+                createAlertWindow(type, windowName);
+            } catch (Exception e) {
+                Log.e("AlertWindowTestBaseActivity", "Can't create type=" + type, e);
+            }
+        }
+    }
+
+    protected void createAlertWindow(int type) {
+        createAlertWindow(type, getPackageName());
+    }
+
+    protected void createAlertWindow(int type, String windowName) {
+        if (!isSystemAlertWindowType(type)) {
+            throw new IllegalArgumentException("Well...you are not an alert window type=" + type);
+        }
+
+        final Point size = new Point();
+        final WindowManager wm = getSystemService(WindowManager.class);
+        wm.getDefaultDisplay().getSize(size);
+
+        WindowManager.LayoutParams params = new WindowManager.LayoutParams(
+                type, FLAG_NOT_FOCUSABLE | FLAG_WATCH_OUTSIDE_TOUCH | FLAG_NOT_TOUCHABLE);
+        params.width = size.x / 3;
+        params.height = size.y / 3;
+        params.gravity = TOP | LEFT;
+        params.setTitle(windowName);
+
+        final TextView view = new TextView(this);
+        view.setText(windowName + "   type=" + type);
+        view.setBackgroundColor(Color.RED);
+        wm.addView(view, params);
+    }
+
+    private boolean isSystemAlertWindowType(int type) {
+        final int[] alertWindowTypes = getAlertWindowTypes();
+        for (int current : alertWindowTypes) {
+            if (current == type) {
+                return true;
+            }
+        }
+        return false;
+    }
+
+    protected abstract int[] getAlertWindowTypes();
+}
diff --git a/tests/framework/base/windowmanager/alertwindowservice/Android.mk b/tests/framework/base/windowmanager/alertwindowservice/Android.mk
new file mode 100644
index 0000000..c012a65
--- /dev/null
+++ b/tests/framework/base/windowmanager/alertwindowservice/Android.mk
@@ -0,0 +1,36 @@
+# 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 := \
+    compatibility-device-util \
+
+LOCAL_SRC_FILES := \
+    $(call all-java-files-under, src) \
+
+LOCAL_SDK_VERSION := current
+
+LOCAL_PACKAGE_NAME := CtsAlertWindowService
+
+# Tag this module as a cts test artifact
+LOCAL_COMPATIBILITY_SUITE := cts vts general-tests
+
+LOCAL_DEX_PREOPT := false
+
+include $(BUILD_CTS_SUPPORT_PACKAGE)
diff --git a/tests/framework/base/windowmanager/alertwindowservice/AndroidManifest.xml b/tests/framework/base/windowmanager/alertwindowservice/AndroidManifest.xml
new file mode 100644
index 0000000..76c30bd
--- /dev/null
+++ b/tests/framework/base/windowmanager/alertwindowservice/AndroidManifest.xml
@@ -0,0 +1,27 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+  ~ Copyright (C) 2017 The Android Open Source Project
+  ~
+  ~ Licensed under the Apache License, Version 2.0 (the "License");
+  ~ you may not use this file except in compliance with the License.
+  ~ You may obtain a copy of the License at
+  ~
+  ~      http://www.apache.org/licenses/LICENSE-2.0
+  ~
+  ~ Unless required by applicable law or agreed to in writing, software
+  ~ distributed under the License is distributed on an "AS IS" BASIS,
+  ~ WITHOUT 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.server.wm.alertwindowservice">
+    <uses-permission android:name="android.permission.SYSTEM_ALERT_WINDOW" />
+
+    <application>
+        <uses-library android:name="android.test.runner" />
+
+        <service android:name=".AlertWindowService"
+                 android:exported="true"/>
+    </application>
+</manifest>
diff --git a/tests/framework/base/windowmanager/alertwindowservice/src/android/server/wm/alertwindowservice/AlertWindowService.java b/tests/framework/base/windowmanager/alertwindowservice/src/android/server/wm/alertwindowservice/AlertWindowService.java
new file mode 100644
index 0000000..d531bbd
--- /dev/null
+++ b/tests/framework/base/windowmanager/alertwindowservice/src/android/server/wm/alertwindowservice/AlertWindowService.java
@@ -0,0 +1,146 @@
+/*
+ * 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.server.wm.alertwindowservice;
+
+import static android.graphics.Color.BLUE;
+import static android.view.Gravity.LEFT;
+import static android.view.Gravity.TOP;
+import static android.view.WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE;
+import static android.view.WindowManager.LayoutParams.FLAG_NOT_TOUCHABLE;
+import static android.view.WindowManager.LayoutParams.FLAG_WATCH_OUTSIDE_TOUCH;
+import static android.view.WindowManager.LayoutParams.TYPE_APPLICATION_OVERLAY;
+
+import android.app.Service;
+import android.content.Intent;
+import android.graphics.Point;
+import android.os.Handler;
+import android.os.IBinder;
+import android.os.Message;
+import android.os.Messenger;
+import android.os.RemoteException;
+import android.util.Log;
+import android.view.View;
+import android.view.WindowManager;
+import android.widget.TextView;
+
+import java.util.LinkedList;
+
+/** Service for creating and managing alert windows. */
+public final class AlertWindowService extends Service {
+
+    private static final String TAG = "AlertWindowService";
+    private static final boolean DEBUG = false;
+
+    public static final String PACKAGE_NAME = "android.server.wm.alertwindowservice";
+    public static final String EXTRA_MESSENGER = "messenger";
+
+    public static final int MSG_ADD_ALERT_WINDOW = 1;
+    public static final int MSG_REMOVE_ALERT_WINDOW = 2;
+    public static final int MSG_REMOVE_ALL_ALERT_WINDOWS = 3;
+
+    public static final int MSG_ON_ALERT_WINDOW_ADDED = 4;
+    public static final int MSG_ON_ALERT_WINDOW_REMOVED = 5;
+
+    private LinkedList<View> mAlertWindows = new LinkedList<>();
+
+    private Messenger mOutgoingMessenger = null;
+    private final Messenger mIncomingMessenger = new Messenger(new IncomingHandler());
+
+    private class IncomingHandler extends Handler {
+        @Override
+        public void handleMessage(Message msg) {
+            switch (msg.what) {
+                case MSG_ADD_ALERT_WINDOW:
+                    addAlertWindow();
+                    break;
+                case MSG_REMOVE_ALERT_WINDOW:
+                    removeAlertWindow();
+                    break;
+                case MSG_REMOVE_ALL_ALERT_WINDOWS:
+                    removeAllAlertWindows();
+                    break;
+                default:
+                    super.handleMessage(msg);
+            }
+        }
+    }
+
+    private void addAlertWindow() {
+        final Point size = new Point();
+        final WindowManager wm = getSystemService(WindowManager.class);
+        wm.getDefaultDisplay().getSize(size);
+
+        final WindowManager.LayoutParams params = new WindowManager.LayoutParams(
+                TYPE_APPLICATION_OVERLAY,
+                FLAG_NOT_FOCUSABLE | FLAG_WATCH_OUTSIDE_TOUCH | FLAG_NOT_TOUCHABLE);
+        params.width = size.x / 3;
+        params.height = size.y / 3;
+        params.gravity = TOP | LEFT;
+
+        final TextView view = new TextView(this);
+        view.setText("AlertWindowService" + mAlertWindows.size());
+        view.setBackgroundColor(BLUE);
+        wm.addView(view, params);
+        mAlertWindows.add(view);
+
+        if (DEBUG) Log.e(TAG, "addAlertWindow " + mAlertWindows.size());
+        if (mOutgoingMessenger != null) {
+            try {
+                mOutgoingMessenger.send(Message.obtain(null, MSG_ON_ALERT_WINDOW_ADDED));
+            } catch (RemoteException e) {
+
+            }
+        }
+    }
+
+    private void removeAlertWindow() {
+        if (mAlertWindows.size() == 0) {
+            return;
+        }
+        final WindowManager wm = getSystemService(WindowManager.class);
+        wm.removeView(mAlertWindows.pop());
+
+        if (DEBUG) Log.e(TAG, "removeAlertWindow " + mAlertWindows.size());
+        if (mOutgoingMessenger != null) {
+            try {
+                mOutgoingMessenger.send(Message.obtain(null, MSG_ON_ALERT_WINDOW_REMOVED));
+            } catch (RemoteException e) {
+
+            }
+        }
+    }
+
+    private void removeAllAlertWindows() {
+        while (mAlertWindows.size() > 0) {
+            removeAlertWindow();
+        }
+    }
+
+    @Override
+    public IBinder onBind(Intent intent) {
+        if (DEBUG) Log.e(TAG, "onBind");
+        mOutgoingMessenger = intent.getParcelableExtra(EXTRA_MESSENGER);
+        return mIncomingMessenger.getBinder();
+    }
+
+    @Override
+    public boolean onUnbind(Intent intent) {
+        if (DEBUG) Log.e(TAG, "onUnbind");
+        removeAllAlertWindows();
+        return super.onUnbind(intent);
+    }
+}
diff --git a/tests/framework/base/windowmanager/dndsourceapp/Android.mk b/tests/framework/base/windowmanager/dndsourceapp/Android.mk
new file mode 100644
index 0000000..7cfd03f
--- /dev/null
+++ b/tests/framework/base/windowmanager/dndsourceapp/Android.mk
@@ -0,0 +1,32 @@
+# Copyright (C) 2016 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#      http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+LOCAL_PATH:= $(call my-dir)
+
+include $(CLEAR_VARS)
+
+# Don't include this package in any target.
+LOCAL_MODULE_TAGS := tests
+
+LOCAL_SRC_FILES := $(call all-java-files-under, src) \
+    ../src/android/server/wm/TestLogClient.java
+
+LOCAL_SDK_VERSION := current
+
+# Tag this module as a cts test artifact
+LOCAL_COMPATIBILITY_SUITE := cts vts general-tests
+
+LOCAL_PACKAGE_NAME := CtsDragAndDropSourceApp
+
+include $(BUILD_CTS_SUPPORT_PACKAGE)
diff --git a/tests/framework/base/windowmanager/dndsourceapp/AndroidManifest.xml b/tests/framework/base/windowmanager/dndsourceapp/AndroidManifest.xml
new file mode 100644
index 0000000..4c8f0bb
--- /dev/null
+++ b/tests/framework/base/windowmanager/dndsourceapp/AndroidManifest.xml
@@ -0,0 +1,31 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- 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.
+-->
+
+<manifest xmlns:android="http://schemas.android.com/apk/res/android"
+        package="android.server.wm.dndsourceapp">
+    <application android:label="CtsDnDSource">
+        <activity android:name="android.server.wm.dndsourceapp.DragSource">
+            <intent-filter>
+                <action android:name="android.intent.action.MAIN"/>
+                <category android:name="android.intent.category.LAUNCHER"/>
+            </intent-filter>
+        </activity>
+
+        <provider android:name="android.server.wm.dndsourceapp.DragSourceContentProvider"
+                  android:authorities="android.server.wm.dndsource.contentprovider"
+                  android:grantUriPermissions="true"/>
+    </application>
+</manifest>
diff --git a/hostsidetests/services/activityandwindowmanager/windowmanager/dndsourceapp/res/layout/source_activity.xml b/tests/framework/base/windowmanager/dndsourceapp/res/layout/source_activity.xml
similarity index 100%
rename from hostsidetests/services/activityandwindowmanager/windowmanager/dndsourceapp/res/layout/source_activity.xml
rename to tests/framework/base/windowmanager/dndsourceapp/res/layout/source_activity.xml
diff --git a/tests/framework/base/windowmanager/dndsourceapp/src/android/server/wm/dndsourceapp/DragSource.java b/tests/framework/base/windowmanager/dndsourceapp/src/android/server/wm/dndsourceapp/DragSource.java
new file mode 100644
index 0000000..9c7ed7b
--- /dev/null
+++ b/tests/framework/base/windowmanager/dndsourceapp/src/android/server/wm/dndsourceapp/DragSource.java
@@ -0,0 +1,132 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.server.wm.dndsourceapp;
+
+import android.app.Activity;
+import android.content.ClipData;
+import android.content.ClipDescription;
+import android.net.Uri;
+import android.os.Bundle;
+import android.os.Handler;
+import android.os.FileUriExposedException;
+import android.os.PersistableBundle;
+import android.server.wm.TestLogClient;
+import android.view.MotionEvent;
+import android.view.View;
+import android.widget.TextView;
+
+import java.io.File;
+
+public class DragSource extends Activity{
+    private static final String RESULT_KEY_START_DRAG = "START_DRAG";
+    private static final String RESULT_KEY_DETAILS = "DETAILS";
+    private static final String RESULT_OK = "OK";
+    private static final String RESULT_EXCEPTION = "Exception";
+
+    private static final String URI_PREFIX =
+            "content://" + DragSourceContentProvider.AUTHORITY + "/data";
+
+    private static final String MAGIC_VALUE = "42";
+    private static final long TIMEOUT_CANCEL = 150;
+
+    private TextView mTextView;
+    private TestLogClient mLogClient;
+
+    @Override
+    public void onCreate(Bundle savedInstanceState) {
+        super.onCreate(savedInstanceState);
+
+        mLogClient = new TestLogClient(this, getIntent().getStringExtra("logtag"));
+
+        View view = getLayoutInflater().inflate(R.layout.source_activity, null);
+        setContentView(view);
+
+        final Uri plainUri = Uri.parse(URI_PREFIX + "/" + MAGIC_VALUE);
+
+        setUpDragSource("disallow_global", plainUri, 0);
+        setUpDragSource("cancel_soon", plainUri, View.DRAG_FLAG_GLOBAL);
+
+        setUpDragSource("grant_none", plainUri, View.DRAG_FLAG_GLOBAL);
+        setUpDragSource("grant_read", plainUri,
+                View.DRAG_FLAG_GLOBAL | View.DRAG_FLAG_GLOBAL_URI_READ);
+        setUpDragSource("grant_write", plainUri,
+                View.DRAG_FLAG_GLOBAL | View.DRAG_FLAG_GLOBAL_URI_WRITE);
+        setUpDragSource("grant_read_persistable", plainUri,
+                View.DRAG_FLAG_GLOBAL | View.DRAG_FLAG_GLOBAL_URI_READ |
+                        View.DRAG_FLAG_GLOBAL_PERSISTABLE_URI_PERMISSION);
+
+        final Uri prefixUri = Uri.parse(URI_PREFIX);
+
+        setUpDragSource("grant_read_prefix", prefixUri,
+                View.DRAG_FLAG_GLOBAL | View.DRAG_FLAG_GLOBAL_URI_READ |
+                        View.DRAG_FLAG_GLOBAL_PREFIX_URI_PERMISSION);
+        setUpDragSource("grant_read_noprefix", prefixUri,
+                View.DRAG_FLAG_GLOBAL | View.DRAG_FLAG_GLOBAL_URI_READ);
+
+        final Uri fileUri = Uri.fromFile(new File("/sdcard/sample.jpg"));
+
+        setUpDragSource("file_local", fileUri, 0);
+        setUpDragSource("file_global", fileUri, View.DRAG_FLAG_GLOBAL);
+    }
+
+    private void setUpDragSource(String mode, final Uri uri, final int flags) {
+        if (!mode.equals(getIntent().getStringExtra("mode"))) {
+            return;
+        }
+        mTextView = (TextView) findViewById(R.id.drag_source);
+        mTextView.setText(mode);
+        mTextView.setOnTouchListener(new View.OnTouchListener() {
+            @Override
+            public boolean onTouch(View v, MotionEvent event) {
+                if (event.getAction() != MotionEvent.ACTION_DOWN) {
+                    return false;
+                }
+                try {
+                    final ClipDescription clipDescription = new ClipDescription("", new String[] {
+                            ClipDescription.MIMETYPE_TEXT_URILIST });
+                    PersistableBundle extras = new PersistableBundle(1);
+                    extras.putString("extraKey", "extraValue");
+                    clipDescription.setExtras(extras);
+                    final ClipData clipData = new ClipData(clipDescription, new ClipData.Item(uri));
+                    v.startDragAndDrop(
+                            clipData,
+                            new View.DragShadowBuilder(v),
+                            null,
+                            flags);
+                    logResult(RESULT_KEY_START_DRAG, RESULT_OK);
+                } catch (FileUriExposedException e) {
+                    logResult(RESULT_KEY_DETAILS, e.getMessage());
+                    logResult(RESULT_KEY_START_DRAG, RESULT_EXCEPTION);
+                }
+                if (mode.equals("cancel_soon")) {
+                    new Handler().postDelayed(new Runnable() {
+                        @Override
+                        public void run() {
+                            v.cancelDragAndDrop();
+                        }
+                    }, TIMEOUT_CANCEL);
+                }
+                return true;
+            }
+        });
+    }
+
+    private void logResult(String key, String value) {
+        mLogClient.record(key, value);
+        mTextView.setText(mTextView.getText() + "\n" + key + "=" + value);
+    }
+}
diff --git a/tests/framework/base/windowmanager/dndsourceapp/src/android/server/wm/dndsourceapp/DragSourceContentProvider.java b/tests/framework/base/windowmanager/dndsourceapp/src/android/server/wm/dndsourceapp/DragSourceContentProvider.java
new file mode 100644
index 0000000..1ec1e58
--- /dev/null
+++ b/tests/framework/base/windowmanager/dndsourceapp/src/android/server/wm/dndsourceapp/DragSourceContentProvider.java
@@ -0,0 +1,71 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.server.wm.dndsourceapp;
+
+import android.content.ContentProvider;
+import android.content.ContentValues;
+import android.content.UriMatcher;
+import android.database.Cursor;
+import android.net.Uri;
+
+public class DragSourceContentProvider extends ContentProvider {
+
+    public static final String AUTHORITY = "android.server.wm.dndsource.contentprovider";
+
+    private static final UriMatcher sMatcher = new UriMatcher(UriMatcher.NO_MATCH);
+
+    private static final int URI_DATA = 1;
+
+    static {
+        sMatcher.addURI(AUTHORITY, "data/#", URI_DATA);
+    }
+
+    @Override
+    public boolean onCreate() {
+        return false;
+    }
+
+    @Override
+    public Cursor query(Uri uri, String[] projection, String selection, String[] selectionArgs,
+                        String sortOrder) {
+        switch (sMatcher.match(uri)) {
+            case URI_DATA:
+                return new DragSourceCursor(uri.getLastPathSegment());
+        }
+        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/tests/framework/base/windowmanager/dndsourceapp/src/android/server/wm/dndsourceapp/DragSourceCursor.java b/tests/framework/base/windowmanager/dndsourceapp/src/android/server/wm/dndsourceapp/DragSourceCursor.java
new file mode 100644
index 0000000..468842f
--- /dev/null
+++ b/tests/framework/base/windowmanager/dndsourceapp/src/android/server/wm/dndsourceapp/DragSourceCursor.java
@@ -0,0 +1,80 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.server.wm.dndsourceapp;
+
+import android.database.AbstractCursor;
+
+public class DragSourceCursor extends AbstractCursor {
+    private static final String COLUMN_KEY = "key";
+
+    private final String mValue;
+
+    public DragSourceCursor(String value) {
+        mValue = value;
+    }
+
+    @Override
+    public int getCount() {
+        return 1;
+    }
+
+    @Override
+    public String[] getColumnNames() {
+        return new String[] {COLUMN_KEY};
+    }
+
+    @Override
+    public String getString(int column) {
+        if (getPosition() != 0) {
+            throw new IllegalArgumentException("Incorrect position: " + getPosition());
+        }
+        if (column != 0) {
+            throw new IllegalArgumentException("Incorrect column: " + column);
+        }
+        return mValue;
+    }
+
+    @Override
+    public short getShort(int column) {
+        return 0;
+    }
+
+    @Override
+    public int getInt(int column) {
+        return 0;
+    }
+
+    @Override
+    public long getLong(int column) {
+        return 0;
+    }
+
+    @Override
+    public float getFloat(int column) {
+        return 0;
+    }
+
+    @Override
+    public double getDouble(int column) {
+        return 0;
+    }
+
+    @Override
+    public boolean isNull(int column) {
+        return false;
+    }
+}
diff --git a/tests/framework/base/windowmanager/dndtargetapp/Android.mk b/tests/framework/base/windowmanager/dndtargetapp/Android.mk
new file mode 100644
index 0000000..d291df4
--- /dev/null
+++ b/tests/framework/base/windowmanager/dndtargetapp/Android.mk
@@ -0,0 +1,32 @@
+# Copyright (C) 2016 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#      http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+LOCAL_PATH:= $(call my-dir)
+
+include $(CLEAR_VARS)
+
+# Don't include this package in any target.
+LOCAL_MODULE_TAGS := tests
+
+LOCAL_SRC_FILES := $(call all-java-files-under, src) \
+    ../src/android/server/wm/TestLogClient.java
+
+LOCAL_SDK_VERSION := current
+
+# Tag this module as a cts test artifact
+LOCAL_COMPATIBILITY_SUITE := cts vts general-tests
+
+LOCAL_PACKAGE_NAME := CtsDragAndDropTargetApp
+
+include $(BUILD_CTS_SUPPORT_PACKAGE)
diff --git a/tests/framework/base/windowmanager/dndtargetapp/AndroidManifest.xml b/tests/framework/base/windowmanager/dndtargetapp/AndroidManifest.xml
new file mode 100644
index 0000000..33c7a0f
--- /dev/null
+++ b/tests/framework/base/windowmanager/dndtargetapp/AndroidManifest.xml
@@ -0,0 +1,27 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- 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.
+-->
+
+<manifest xmlns:android="http://schemas.android.com/apk/res/android"
+        package="android.server.wm.dndtargetapp">
+    <application android:label="CtsDnDTarget">
+        <activity android:name="android.server.wm.dndtargetapp.DropTarget">
+            <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/services/activityandwindowmanager/windowmanager/dndtargetapp/res/layout/target_activity.xml b/tests/framework/base/windowmanager/dndtargetapp/res/layout/target_activity.xml
similarity index 100%
rename from hostsidetests/services/activityandwindowmanager/windowmanager/dndtargetapp/res/layout/target_activity.xml
rename to tests/framework/base/windowmanager/dndtargetapp/res/layout/target_activity.xml
diff --git a/tests/framework/base/windowmanager/dndtargetapp/src/android/server/wm/dndtargetapp/DropTarget.java b/tests/framework/base/windowmanager/dndtargetapp/src/android/server/wm/dndtargetapp/DropTarget.java
new file mode 100644
index 0000000..0500ef4
--- /dev/null
+++ b/tests/framework/base/windowmanager/dndtargetapp/src/android/server/wm/dndtargetapp/DropTarget.java
@@ -0,0 +1,309 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.server.wm.dndtargetapp;
+
+import android.app.Activity;
+import android.content.ClipData;
+import android.content.ClipDescription;
+import android.content.ContentValues;
+import android.database.Cursor;
+import android.net.Uri;
+import android.os.Bundle;
+import android.os.PersistableBundle;
+import android.server.wm.TestLogClient;
+import android.view.DragAndDropPermissions;
+import android.view.DragEvent;
+import android.view.View;
+import android.widget.TextView;
+
+public class DropTarget extends Activity {
+    private static final String RESULT_KEY_DRAG_STARTED = "DRAG_STARTED";
+    private static final String RESULT_KEY_DRAG_ENDED = "DRAG_ENDED";
+    private static final String RESULT_KEY_EXTRAS = "EXTRAS";
+    private static final String RESULT_KEY_DROP_RESULT = "DROP";
+    private static final String RESULT_KEY_DETAILS = "DETAILS";
+    private static final String RESULT_KEY_ACCESS_AFTER = "AFTER";
+    private static final String RESULT_KEY_ACCESS_BEFORE = "BEFORE";
+    private static final String RESULT_KEY_CLIP_DATA_ERROR = "CLIP_DATA_ERROR";
+    private static final String RESULT_KEY_CLIP_DESCR_ERROR = "CLIP_DESCR_ERROR";
+    private static final String RESULT_KEY_LOCAL_STATE_ERROR = "LOCAL_STATE_ERROR";
+
+    public static final String RESULT_OK = "OK";
+    public static final String RESULT_EXCEPTION = "Exception";
+    public static final String RESULT_MISSING = "MISSING";
+    public static final String RESULT_LEAKING = "LEAKING";
+
+    protected static final String MAGIC_VALUE = "42";
+
+    private TextView mTextView;
+    private TestLogClient mLogClient;
+
+    @Override
+    public void onCreate(Bundle savedInstanceState) {
+        super.onCreate(savedInstanceState);
+
+        mLogClient = new TestLogClient(this, getIntent().getStringExtra("logtag"));
+
+        View view = getLayoutInflater().inflate(R.layout.target_activity, null);
+        setContentView(view);
+
+        setUpDropTarget("request_none", new OnDragUriReadListener(false));
+        setUpDropTarget("request_read", new OnDragUriReadListener());
+        setUpDropTarget("request_write", new OnDragUriWriteListener());
+        setUpDropTarget("request_read_nested", new OnDragUriReadPrefixListener());
+        setUpDropTarget("request_take_persistable", new OnDragUriTakePersistableListener());
+    }
+
+    private void setUpDropTarget(String mode, OnDragUriListener listener) {
+        if (!mode.equals(getIntent().getStringExtra("mode"))) {
+            return;
+        }
+        mTextView = (TextView)findViewById(R.id.drag_target);
+        mTextView.setText(mode);
+        mTextView.setOnDragListener(listener);
+    }
+
+    private String checkExtraValue(DragEvent event) {
+        PersistableBundle extras = event.getClipDescription().getExtras();
+        if (extras == null) {
+            return "Null";
+        }
+
+        final String value = extras.getString("extraKey");
+        if ("extraValue".equals(value)) {
+            return RESULT_OK;
+        }
+        return value;
+    }
+
+    private void logResult(String key, String value) {
+        mLogClient.record(key, value);
+        mTextView.setText(mTextView.getText() + "\n" + key + "=" + value);
+    }
+
+    private abstract class OnDragUriListener implements View.OnDragListener {
+        private final boolean requestPermissions;
+
+        public OnDragUriListener(boolean requestPermissions) {
+            this.requestPermissions = requestPermissions;
+        }
+
+        @Override
+        public boolean onDrag(View v, DragEvent event) {
+            checkDragEvent(event);
+
+            switch (event.getAction()) {
+                case DragEvent.ACTION_DRAG_STARTED:
+                    logResult(RESULT_KEY_DRAG_STARTED, RESULT_OK);
+                    logResult(RESULT_KEY_EXTRAS, checkExtraValue(event));
+                    return true;
+
+                case DragEvent.ACTION_DRAG_ENTERED:
+                    return true;
+
+                case DragEvent.ACTION_DRAG_LOCATION:
+                    return true;
+
+                case DragEvent.ACTION_DRAG_EXITED:
+                    return true;
+
+                case DragEvent.ACTION_DROP:
+                    // Try accessing the Uri without the permissions grant.
+                    accessContent(event, RESULT_KEY_ACCESS_BEFORE, false);
+
+                    // Try accessing the Uri with the permission grant (if required);
+                    accessContent(event, RESULT_KEY_DROP_RESULT, requestPermissions);
+
+                    // Try accessing the Uri after the permissions have been released.
+                    accessContent(event, RESULT_KEY_ACCESS_AFTER, false);
+                    return true;
+
+                case DragEvent.ACTION_DRAG_ENDED:
+                    logResult(RESULT_KEY_DRAG_ENDED, RESULT_OK);
+                    return true;
+
+                default:
+                    return false;
+            }
+        }
+
+        private void accessContent(DragEvent event, String resultKey, boolean requestPermissions) {
+            String result;
+            try {
+                result = processDrop(event, requestPermissions);
+            } catch (SecurityException e) {
+                result = RESULT_EXCEPTION;
+                if (resultKey.equals(RESULT_KEY_DROP_RESULT)) {
+                    logResult(RESULT_KEY_DETAILS, e.getMessage());
+                }
+            }
+            logResult(resultKey, result);
+        }
+
+        private String processDrop(DragEvent event, boolean requestPermissions) {
+            final ClipData clipData = event.getClipData();
+            if (clipData == null) {
+                return "Null ClipData";
+            }
+            if (clipData.getItemCount() == 0) {
+                return "Empty ClipData";
+            }
+            ClipData.Item item = clipData.getItemAt(0);
+            if (item == null) {
+                return "Null ClipData.Item";
+            }
+            Uri uri = item.getUri();
+            if (uri == null) {
+                return "Null Uri";
+            }
+
+            DragAndDropPermissions permissions = null;
+            if (requestPermissions) {
+                permissions = requestDragAndDropPermissions(event);
+                if (permissions == null) {
+                    return "Null DragAndDropPermissions";
+                }
+            }
+
+            try {
+                return processUri(uri);
+            } finally {
+                if (permissions != null) {
+                    permissions.release();
+                }
+            }
+        }
+
+        abstract protected String processUri(Uri uri);
+    }
+
+    private void checkDragEvent(DragEvent event) {
+        final int action = event.getAction();
+
+        // ClipData should be available for ACTION_DROP only.
+        final ClipData clipData = event.getClipData();
+        if (action == DragEvent.ACTION_DROP) {
+            if (clipData == null) {
+                logResult(RESULT_KEY_CLIP_DATA_ERROR, RESULT_MISSING);
+            }
+        } else {
+            if (clipData != null) {
+                logResult(RESULT_KEY_CLIP_DATA_ERROR, RESULT_LEAKING + action);
+            }
+        }
+
+        // ClipDescription should be always available except for ACTION_DRAG_ENDED.
+        final ClipDescription clipDescription = event.getClipDescription();
+        if (action != DragEvent.ACTION_DRAG_ENDED) {
+            if (clipDescription == null) {
+                logResult(RESULT_KEY_CLIP_DESCR_ERROR, RESULT_MISSING + action);
+            }
+        } else {
+            if (clipDescription != null) {
+                logResult(RESULT_KEY_CLIP_DESCR_ERROR, RESULT_LEAKING);
+            }
+        }
+
+        // Local state should be always null for cross-app drags.
+        final Object localState = event.getLocalState();
+        if (localState != null) {
+            logResult(RESULT_KEY_LOCAL_STATE_ERROR, RESULT_LEAKING + action);
+        }
+    }
+
+    private class OnDragUriReadListener extends OnDragUriListener {
+        OnDragUriReadListener(boolean requestPermissions) {
+            super(requestPermissions);
+        }
+
+        OnDragUriReadListener() {
+            super(true);
+        }
+
+        protected String processUri(Uri uri) {
+            return checkQueryResult(uri, MAGIC_VALUE);
+        }
+
+        protected String checkQueryResult(Uri uri, String expectedValue) {
+            Cursor cursor = null;
+            try {
+                cursor = getContentResolver().query(uri, null, null, null, null);
+                if (cursor == null) {
+                    return "Null Cursor";
+                }
+                cursor.moveToPosition(0);
+                String value = cursor.getString(0);
+                if (!expectedValue.equals(value)) {
+                    return "Wrong value: " + value;
+                }
+                return RESULT_OK;
+            } finally {
+                if (cursor != null) {
+                    cursor.close();
+                }
+            }
+        }
+    }
+
+    private class OnDragUriWriteListener extends OnDragUriListener {
+        OnDragUriWriteListener() {
+            super(true);
+        }
+
+        protected String processUri(Uri uri) {
+            ContentValues values = new ContentValues();
+            values.put("key", 100);
+            getContentResolver().update(uri, values, null, null);
+            return RESULT_OK;
+        }
+    }
+
+    private class OnDragUriReadPrefixListener extends OnDragUriReadListener {
+        @Override
+        protected String processUri(Uri uri) {
+            final String result1 = queryPrefixed(uri, "1");
+            if (!result1.equals(RESULT_OK)) {
+                return result1;
+            }
+            final String result2 = queryPrefixed(uri, "2");
+            if (!result2.equals(RESULT_OK)) {
+                return result2;
+            }
+            return queryPrefixed(uri, "3");
+        }
+
+        private String queryPrefixed(Uri uri, String selector) {
+            final Uri prefixedUri = Uri.parse(uri.toString() + "/" + selector);
+            return checkQueryResult(prefixedUri, selector);
+        }
+    }
+
+    private class OnDragUriTakePersistableListener extends OnDragUriListener {
+        OnDragUriTakePersistableListener() {
+            super(true);
+        }
+
+        @Override
+        protected String processUri(Uri uri) {
+            getContentResolver().takePersistableUriPermission(
+                    uri, View.DRAG_FLAG_GLOBAL_URI_READ);
+            getContentResolver().releasePersistableUriPermission(
+                    uri, View.DRAG_FLAG_GLOBAL_URI_READ);
+            return RESULT_OK;
+        }
+    }
+}
diff --git a/tests/framework/base/windowmanager/dndtargetappsdk23/Android.mk b/tests/framework/base/windowmanager/dndtargetappsdk23/Android.mk
new file mode 100644
index 0000000..59f4ab1
--- /dev/null
+++ b/tests/framework/base/windowmanager/dndtargetappsdk23/Android.mk
@@ -0,0 +1,32 @@
+# Copyright (C) 2016 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#      http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+LOCAL_PATH:= $(call my-dir)
+
+include $(CLEAR_VARS)
+
+# Don't include this package in any target.
+LOCAL_MODULE_TAGS := tests
+
+LOCAL_SRC_FILES := $(call all-java-files-under, src) \
+    ../src/android/server/wm/TestLogClient.java
+
+LOCAL_SDK_VERSION := 23
+
+# Tag this module as a cts test artifact
+LOCAL_COMPATIBILITY_SUITE := cts vts general-tests
+
+LOCAL_PACKAGE_NAME := CtsDragAndDropTargetAppSdk23
+
+include $(BUILD_CTS_SUPPORT_PACKAGE)
diff --git a/tests/framework/base/windowmanager/dndtargetappsdk23/AndroidManifest.xml b/tests/framework/base/windowmanager/dndtargetappsdk23/AndroidManifest.xml
new file mode 100644
index 0000000..d10a548
--- /dev/null
+++ b/tests/framework/base/windowmanager/dndtargetappsdk23/AndroidManifest.xml
@@ -0,0 +1,27 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- 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.
+-->
+
+<manifest xmlns:android="http://schemas.android.com/apk/res/android"
+        package="android.server.wm.dndtargetappsdk23">
+    <application android:label="CtsDnDTarget">
+        <activity android:name="android.server.wm.dndtargetappsdk23.DropTarget">
+            <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/services/activityandwindowmanager/windowmanager/dndtargetappsdk23/res/layout/target_activity.xml b/tests/framework/base/windowmanager/dndtargetappsdk23/res/layout/target_activity.xml
similarity index 100%
rename from hostsidetests/services/activityandwindowmanager/windowmanager/dndtargetappsdk23/res/layout/target_activity.xml
rename to tests/framework/base/windowmanager/dndtargetappsdk23/res/layout/target_activity.xml
diff --git a/tests/framework/base/windowmanager/dndtargetappsdk23/src/android/server/wm/dndtargetappsdk23/DropTarget.java b/tests/framework/base/windowmanager/dndtargetappsdk23/src/android/server/wm/dndtargetappsdk23/DropTarget.java
new file mode 100644
index 0000000..93a8659
--- /dev/null
+++ b/tests/framework/base/windowmanager/dndtargetappsdk23/src/android/server/wm/dndtargetappsdk23/DropTarget.java
@@ -0,0 +1,86 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.server.wm.dndtargetappsdk23;
+
+import android.app.Activity;
+import android.os.Bundle;
+import android.server.wm.TestLogClient;
+import android.view.DragEvent;
+import android.view.View;
+import android.widget.TextView;
+
+/**
+ * This application is compiled against SDK 23 and used to verify that apps targeting SDK 23 and
+ * below do not receive global drags.
+ */
+public class DropTarget extends Activity {
+    private static final String RESULT_KEY_DRAG_STARTED = "DRAG_STARTED";
+    private static final String RESULT_KEY_DROP_RESULT = "DROP";
+
+    public static final String RESULT_OK = "OK";
+
+    private TextView mTextView;
+    private TestLogClient mLogClient;
+
+    @Override
+    public void onCreate(Bundle savedInstanceState) {
+        super.onCreate(savedInstanceState);
+
+        View view = getLayoutInflater().inflate(R.layout.target_activity, null);
+        setContentView(view);
+
+        mTextView = (TextView) findViewById(R.id.drag_target);
+        mTextView.setOnDragListener(new OnDragListener());
+
+        mLogClient = new TestLogClient(this, getIntent().getStringExtra("logtag"));
+    }
+
+    private void logResult(String key, String value) {
+        mLogClient.record(key, value);
+        mTextView.setText(key + "=" + value);
+    }
+
+    private class OnDragListener implements View.OnDragListener {
+        @Override
+        public boolean onDrag(View v, DragEvent event) {
+            switch (event.getAction()) {
+                case DragEvent.ACTION_DRAG_STARTED:
+                    logResult(RESULT_KEY_DRAG_STARTED, RESULT_OK);
+                    return true;
+
+                case DragEvent.ACTION_DRAG_ENTERED:
+                    return true;
+
+                case DragEvent.ACTION_DRAG_LOCATION:
+                    return true;
+
+                case DragEvent.ACTION_DRAG_EXITED:
+                    return true;
+
+                case DragEvent.ACTION_DROP:
+                    logResult(RESULT_KEY_DROP_RESULT, RESULT_OK);
+                    return true;
+
+                case DragEvent.ACTION_DRAG_ENDED:
+                    return true;
+
+                default:
+                    return false;
+            }
+        }
+    }
+}
diff --git a/hostsidetests/services/activityandwindowmanager/windowmanager/frametestapp/Android.mk b/tests/framework/base/windowmanager/frametestapp/Android.mk
similarity index 100%
rename from hostsidetests/services/activityandwindowmanager/windowmanager/frametestapp/Android.mk
rename to tests/framework/base/windowmanager/frametestapp/Android.mk
diff --git a/tests/framework/base/windowmanager/frametestapp/AndroidManifest.xml b/tests/framework/base/windowmanager/frametestapp/AndroidManifest.xml
new file mode 100755
index 0000000..90dd8f0
--- /dev/null
+++ b/tests/framework/base/windowmanager/frametestapp/AndroidManifest.xml
@@ -0,0 +1,29 @@
+<?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.
+ -->
+
+<manifest xmlns:android="http://schemas.android.com/apk/res/android"
+    package="android.server.wm.frametestapp">
+
+    <application android:theme="@android:style/Theme.Material">
+        <activity
+            android:name=".DialogTestActivity"
+            android:exported="true" />
+        <activity
+            android:name=".MovingChildTestActivity"
+            android:exported="true" />
+    </application>
+</manifest>
diff --git a/tests/framework/base/windowmanager/frametestapp/src/android/server/wm/frametestapp/DialogTestActivity.java b/tests/framework/base/windowmanager/frametestapp/src/android/server/wm/frametestapp/DialogTestActivity.java
new file mode 100644
index 0000000..c0d8ffd
--- /dev/null
+++ b/tests/framework/base/windowmanager/frametestapp/src/android/server/wm/frametestapp/DialogTestActivity.java
@@ -0,0 +1,205 @@
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.server.wm.frametestapp;
+
+import android.app.Activity;
+import android.app.AlertDialog;
+import android.content.Intent;
+import android.os.Bundle;
+import android.view.WindowManager;
+import android.view.Window;
+import android.view.Gravity;
+
+public class DialogTestActivity extends Activity {
+
+    private static final String DIALOG_WINDOW_NAME = "TestDialog";
+
+    /**
+     * Extra key for test case name.
+     * @see android.server.wm.ParentChildTestBase#EXTRA_TEST_CASE
+     */
+    private static final String EXTRA_TEST_CASE = "test-case";
+
+    private AlertDialog mDialog;
+
+    protected void onCreate(Bundle icicle) {
+        super.onCreate(icicle);
+    }
+
+    protected void onStop() {
+        super.onStop();
+        mDialog.dismiss();
+    }
+
+    protected void onResume() {
+        super.onResume();
+        setupTest(getIntent());
+    }
+
+    private void setupTest(Intent intent) {
+        final String testCase = intent.getStringExtra(EXTRA_TEST_CASE);
+        switch (testCase) {
+            case "MatchParent":
+                testMatchParent();
+                break;
+            case "MatchParentLayoutInOverscan":
+                testMatchParentLayoutInOverscan();
+                break;
+            case "ExplicitSize":
+                testExplicitSize();
+                break;
+            case "ExplicitSizeTopLeftGravity":
+                testExplicitSizeTopLeftGravity();
+                break;
+            case "ExplicitSizeBottomRightGravity":
+                testExplicitSizeBottomRightGravity();
+                break;
+            case "OversizedDimensions":
+                testOversizedDimensions();
+                break;
+            case "OversizedDimensionsNoLimits":
+                testOversizedDimensionsNoLimits();
+                break;
+            case "ExplicitPositionMatchParent":
+                testExplicitPositionMatchParent();
+                break;
+            case "ExplicitPositionMatchParentNoLimits":
+                testExplicitPositionMatchParentNoLimits();
+                break;
+            case "NoFocus":
+                testNoFocus();
+                break;
+            case "WithMargins":
+                testWithMargins();
+                break;
+            default:
+                break;
+        }
+    }
+
+    interface DialogLayoutParamsTest {
+        void doSetup(WindowManager.LayoutParams p);
+    }
+
+    private void doLayoutParamTest(DialogLayoutParamsTest t) {
+        mDialog = new AlertDialog.Builder(this).create();
+
+        mDialog.setMessage("Testing is fun!");
+        mDialog.setTitle(DIALOG_WINDOW_NAME);
+        mDialog.create();
+
+        Window w = mDialog.getWindow();
+        final WindowManager.LayoutParams params = w.getAttributes();
+        t.doSetup(params);
+        w.setAttributes(params);
+
+        mDialog.show();
+    }
+
+    private void testMatchParent() {
+        doLayoutParamTest((WindowManager.LayoutParams params) -> {
+            params.width = WindowManager.LayoutParams.MATCH_PARENT;
+            params.height = WindowManager.LayoutParams.MATCH_PARENT;
+        });
+    }
+
+    private void testMatchParentLayoutInOverscan() {
+        doLayoutParamTest((WindowManager.LayoutParams params) -> {
+            params.width = WindowManager.LayoutParams.MATCH_PARENT;
+            params.height = WindowManager.LayoutParams.MATCH_PARENT;
+            params.flags |= WindowManager.LayoutParams.FLAG_LAYOUT_IN_SCREEN;
+            params.flags |= WindowManager.LayoutParams.FLAG_LAYOUT_IN_OVERSCAN;
+        });
+    }
+
+    private void testExplicitSize() {
+        doLayoutParamTest((WindowManager.LayoutParams params) -> {
+            params.width = 200;
+            params.height = 200;
+        });
+    }
+
+    private void testExplicitSizeTopLeftGravity() {
+        doLayoutParamTest((WindowManager.LayoutParams params) -> {
+            params.width = 200;
+            params.height = 200;
+            params.gravity = Gravity.TOP | Gravity.LEFT;
+        });
+    }
+
+    private void testExplicitSizeBottomRightGravity() {
+        doLayoutParamTest((WindowManager.LayoutParams params) -> {
+            params.width = 200;
+            params.height = 200;
+            params.gravity = Gravity.BOTTOM | Gravity.RIGHT;
+        });
+    }
+
+    private void testOversizedDimensions() {
+        doLayoutParamTest((WindowManager.LayoutParams params) -> {
+            params.width = 100000;
+            params.height = 100000;
+        });
+    }
+
+    private void testOversizedDimensionsNoLimits() {
+        doLayoutParamTest((WindowManager.LayoutParams params) -> {
+            params.width = 5000;
+            params.height = 5000;
+            params.flags |= WindowManager.LayoutParams.FLAG_LAYOUT_NO_LIMITS;
+            params.gravity = Gravity.LEFT | Gravity.TOP;
+        });
+    }
+
+    private void testExplicitPositionMatchParent() {
+        doLayoutParamTest((WindowManager.LayoutParams params) -> {
+            params.width = WindowManager.LayoutParams.MATCH_PARENT;
+            params.height = WindowManager.LayoutParams.MATCH_PARENT;
+            params.x = 100;
+            params.y = 100;
+        });
+    }
+
+    private void testExplicitPositionMatchParentNoLimits() {
+        doLayoutParamTest((WindowManager.LayoutParams params) -> {
+            params.width = WindowManager.LayoutParams.MATCH_PARENT;
+            params.height = WindowManager.LayoutParams.MATCH_PARENT;
+            params.gravity = Gravity.LEFT | Gravity.TOP;
+            params.flags |= WindowManager.LayoutParams.FLAG_LAYOUT_NO_LIMITS;
+            params.x = 100;
+            params.y = 100;
+        });
+    }
+
+    private void testNoFocus() {
+        doLayoutParamTest((WindowManager.LayoutParams params) -> {
+            params.flags |= WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE;
+        });
+    }
+
+    private void testWithMargins() {
+        doLayoutParamTest((WindowManager.LayoutParams params) -> {
+            params.gravity = Gravity.LEFT | Gravity.TOP;
+            params.horizontalMargin = .25f;
+            params.verticalMargin = .35f;
+            params.width = 200;
+            params.height = 200;
+            params.x = 0;
+            params.y = 0;
+        });
+    }
+}
diff --git a/tests/framework/base/windowmanager/frametestapp/src/android/server/wm/frametestapp/MovingChildTestActivity.java b/tests/framework/base/windowmanager/frametestapp/src/android/server/wm/frametestapp/MovingChildTestActivity.java
new file mode 100644
index 0000000..78f8fa0
--- /dev/null
+++ b/tests/framework/base/windowmanager/frametestapp/src/android/server/wm/frametestapp/MovingChildTestActivity.java
@@ -0,0 +1,83 @@
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.server.wm.frametestapp;
+
+import android.app.Activity;
+import android.content.Context;
+import android.os.Bundle;
+import android.view.ViewGroup.LayoutParams;
+import android.view.Window;
+import android.view.WindowManager;
+import android.widget.Button;
+import android.widget.Space;
+
+// This activity will parent a Child to the main window, and then move
+// the main window around. We can use this to verify the Child
+// is properly updated.
+public class MovingChildTestActivity extends Activity {
+
+    private static final String POPUP_WINDOW_NAME = "ChildWindow";
+
+    private Space mView;
+    private int mX = 0;
+    private int mY = 0;
+
+    final Runnable moveWindow = new Runnable() {
+            @Override
+            public void run() {
+                final Window w = getWindow();
+                final WindowManager.LayoutParams attribs = w.getAttributes();
+                attribs.privateFlags = WindowManager.LayoutParams.PRIVATE_FLAG_NO_MOVE_ANIMATION;
+                attribs.x = mX % 1000;
+                attribs.y = mY % 1000;
+                w.setAttributes(attribs);
+                mX += 5;
+                mY += 5;
+                mView.postDelayed(this, 50);
+            }
+    };
+
+    final Runnable makeChild = new Runnable() {
+            @Override
+            public void run() {
+                Button b = new Button(MovingChildTestActivity.this);
+                WindowManager.LayoutParams p = new WindowManager.LayoutParams(
+                        WindowManager.LayoutParams.TYPE_APPLICATION_PANEL);
+                p.privateFlags = WindowManager.LayoutParams.PRIVATE_FLAG_NO_MOVE_ANIMATION;
+                p.x = 0;
+                p.y = 0;
+                p.token = mView.getWindowToken();
+                p.setTitle(POPUP_WINDOW_NAME);
+
+                ((WindowManager)getSystemService(Context.WINDOW_SERVICE)).addView(b, p);
+
+                mView.postDelayed(moveWindow, 50);
+            }
+    };
+
+    protected void onCreate(Bundle icicle) {
+        super.onCreate(icicle);
+
+        final LayoutParams p = new LayoutParams(100, 100);
+        final Window w = getWindow();
+        w.setLayout(100, 100);
+        mView = new Space(this);
+
+        setContentView(mView, p);
+        mView.post(makeChild);
+    }
+}
diff --git a/tests/framework/base/windowmanager/src/android/server/wm/AlertWindowsImportanceTests.java b/tests/framework/base/windowmanager/src/android/server/wm/AlertWindowsImportanceTests.java
new file mode 100644
index 0000000..7e4bdbc
--- /dev/null
+++ b/tests/framework/base/windowmanager/src/android/server/wm/AlertWindowsImportanceTests.java
@@ -0,0 +1,257 @@
+/*
+ * 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.server.wm;
+
+import static android.app.ActivityManager.RunningAppProcessInfo.IMPORTANCE_PERCEPTIBLE;
+import static android.app.ActivityManager.RunningAppProcessInfo.IMPORTANCE_PERCEPTIBLE_PRE_26;
+import static android.app.ActivityManager.RunningAppProcessInfo.IMPORTANCE_VISIBLE;
+import static android.content.Context.BIND_ALLOW_OOM_MANAGEMENT;
+import static android.content.Context.BIND_AUTO_CREATE;
+import static android.content.Context.BIND_NOT_FOREGROUND;
+
+import static org.junit.Assert.assertEquals;
+
+import android.app.ActivityManager;
+import android.content.ComponentName;
+import android.content.Context;
+import android.content.Intent;
+import android.content.res.Configuration;
+import android.content.ServiceConnection;
+import android.os.Handler;
+import android.os.IBinder;
+import android.os.Looper;
+import android.os.Message;
+import android.os.Messenger;
+import android.os.SystemClock;
+import android.platform.test.annotations.Presubmit;
+import android.server.wm.alertwindowservice.AlertWindowService;
+import android.support.test.InstrumentationRegistry;
+import android.support.test.runner.AndroidJUnit4;
+import android.util.Log;
+
+import com.android.compatibility.common.util.SystemUtil;
+
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+import java.util.concurrent.TimeUnit;
+import java.util.function.ToIntFunction;
+
+/**
+ * Build: mmma -j32 cts/tests/framework/base
+ * Run: cts/tests/framework/base/activitymanager/util/run-test CtsWindowManagerDeviceTestCases android.server.wm.AlertWindowsImportanceTests
+ */
+@Presubmit
+@RunWith(AndroidJUnit4.class)
+public final class AlertWindowsImportanceTests {
+
+    private static final String TAG = "AlertWindowsTests";
+
+    private static final boolean DEBUG = false;
+    private static final long WAIT_TIME_MS = 2 * 1000;
+
+    private static final String SDK25_PACKAGE_NAME = "android.server.wm.alertwindowappsdk25";
+
+    private Messenger mService;
+    private String mServicePackageName;
+
+    private ActivityManager mAm;
+    private ActivityManager mAm25; // ActivityManager created for an SDK 25 app context.
+
+    private final Messenger mMessenger = new Messenger(new IncomingHandler(Looper.getMainLooper()));
+    private final Object mAddedLock = new Object();
+    private final Object mRemoveLock = new Object();
+
+    @Before
+    public void setUp() throws Exception {
+        if (DEBUG) Log.e(TAG, "setUp");
+        final Context context = InstrumentationRegistry.getTargetContext();
+
+        mAm = context.getSystemService(ActivityManager.class);
+        mAm25 = context.createPackageContext(SDK25_PACKAGE_NAME, 0)
+                .getSystemService(ActivityManager.class);
+
+        final Intent intent = new Intent()
+                .setClassName(AlertWindowService.PACKAGE_NAME, AlertWindowService.class.getName())
+                .putExtra(AlertWindowService.EXTRA_MESSENGER, mMessenger);
+        // Needs to be both BIND_NOT_FOREGROUND and BIND_ALLOW_OOM_MANAGEMENT to avoid the binding
+        // to this instrumentation test from increasing its importance.
+        context.bindService(intent, mConnection,
+                BIND_AUTO_CREATE | BIND_NOT_FOREGROUND | BIND_ALLOW_OOM_MANAGEMENT);
+        synchronized (mConnection) {
+            // Wait for alert window service to be connection before processing.
+            mConnection.wait(WAIT_TIME_MS);
+        }
+    }
+
+    @After
+    public void tearDown() throws Exception {
+        if (DEBUG) Log.e(TAG, "tearDown");
+        if (mService != null) {
+            mService.send(Message.obtain(null, AlertWindowService.MSG_REMOVE_ALL_ALERT_WINDOWS));
+        }
+        final Context context = InstrumentationRegistry.getTargetContext();
+        context.unbindService(mConnection);
+        mAm = null;
+        mAm25 = null;
+    }
+
+    @Test
+    public void testAlertWindowOomAdj() throws Exception {
+        // Alert windows are always hidden when running in VR.
+        if (isRunningInVR()) {
+            return;
+        }
+        setAlertWindowPermission(true /* allow */);
+
+        assertPackageImportance(IMPORTANCE_PERCEPTIBLE, IMPORTANCE_PERCEPTIBLE_PRE_26);
+
+        // TODO AM.getUidImportance() sometimes return a different value from what
+        // getPackageImportance() returns... b/37950472
+        // assertUidImportance(IMPORTANCE_PERCEPTIBLE, IMPORTANCE_PERCEPTIBLE_PRE_26);
+
+        addAlertWindow();
+        // Process importance should be increased to visible when the service has an alert window.
+        assertPackageImportance(IMPORTANCE_VISIBLE, IMPORTANCE_VISIBLE);
+
+        addAlertWindow();
+        assertPackageImportance(IMPORTANCE_VISIBLE, IMPORTANCE_VISIBLE);
+
+        setAlertWindowPermission(false /* allow */);
+        // Process importance should no longer be visible since its alert windows are not allowed to
+        // be visible.
+        assertPackageImportance(IMPORTANCE_PERCEPTIBLE, IMPORTANCE_PERCEPTIBLE_PRE_26);
+
+        setAlertWindowPermission(true /* allow */);
+        // They can show again so importance should be visible again.
+        assertPackageImportance(IMPORTANCE_VISIBLE, IMPORTANCE_VISIBLE);
+
+        removeAlertWindow();
+        assertPackageImportance(IMPORTANCE_VISIBLE, IMPORTANCE_VISIBLE);
+
+        removeAlertWindow();
+        // Process importance should no longer be visible when the service no longer as alert
+        // windows.
+        assertPackageImportance(IMPORTANCE_PERCEPTIBLE, IMPORTANCE_PERCEPTIBLE_PRE_26);
+    }
+
+    private void addAlertWindow() throws Exception {
+        mService.send(Message.obtain(null, AlertWindowService.MSG_ADD_ALERT_WINDOW));
+        synchronized (mAddedLock) {
+            // Wait for window addition confirmation before proceeding.
+            mAddedLock.wait(WAIT_TIME_MS);
+        }
+    }
+
+    private void removeAlertWindow() throws Exception {
+        mService.send(Message.obtain(null, AlertWindowService.MSG_REMOVE_ALERT_WINDOW));
+        synchronized (mRemoveLock) {
+            // Wait for window removal confirmation before proceeding.
+            mRemoveLock.wait(WAIT_TIME_MS);
+        }
+    }
+
+    private void setAlertWindowPermission(boolean allow) throws Exception {
+        final String cmd = "appops set " + mServicePackageName
+                + " android:system_alert_window " + (allow ? "allow" : "deny");
+        SystemUtil.runShellCommand(InstrumentationRegistry.getInstrumentation(), cmd);
+    }
+
+    private void assertImportance(ToIntFunction<ActivityManager> apiCaller,
+            int expectedForO, int expectedForPreO) throws Exception {
+        final long TIMEOUT = SystemClock.uptimeMillis() + TimeUnit.SECONDS.toMillis(30);
+        int actual;
+
+        do {
+            // TODO: We should try to use ActivityManagerTest.UidImportanceListener here to listen
+            // for changes in the uid importance. However, the way it is currently structured
+            // doesn't really work for this use case right now...
+            Thread.sleep(500);
+            actual = apiCaller.applyAsInt(mAm);
+        } while (actual != expectedForO && (SystemClock.uptimeMillis() < TIMEOUT));
+
+        assertEquals(expectedForO, actual);
+
+        // Check the result for pre-O apps.
+        assertEquals(expectedForPreO, apiCaller.applyAsInt(mAm25));
+    }
+
+    /**
+     * Make sure {@link ActivityManager#getPackageImportance} returns the expected value.
+     */
+    private void assertPackageImportance(int expectedForO, int expectedForPreO) throws Exception {
+        assertImportance(am -> am.getPackageImportance(mServicePackageName),
+                expectedForO, expectedForPreO);
+    }
+
+    private final ServiceConnection mConnection = new ServiceConnection() {
+        @Override
+        public void onServiceConnected(ComponentName name, IBinder service) {
+            if (DEBUG) Log.e(TAG, "onServiceConnected");
+            mService = new Messenger(service);
+            mServicePackageName = name.getPackageName();
+            synchronized (mConnection) {
+                notifyAll();
+            }
+        }
+
+        @Override
+        public void onServiceDisconnected(ComponentName name) {
+            if (DEBUG) Log.e(TAG, "onServiceDisconnected");
+            mService = null;
+            mServicePackageName = null;
+        }
+    };
+
+    private class IncomingHandler extends Handler {
+
+        IncomingHandler(Looper looper) {
+            super(looper);
+        }
+
+        @Override
+        public void handleMessage(Message msg) {
+            switch (msg.what) {
+                case AlertWindowService.MSG_ON_ALERT_WINDOW_ADDED:
+                    synchronized (mAddedLock) {
+                        if (DEBUG) Log.e(TAG, "MSG_ON_ALERT_WINDOW_ADDED");
+                        mAddedLock.notifyAll();
+                    }
+                    break;
+                case AlertWindowService.MSG_ON_ALERT_WINDOW_REMOVED:
+                    synchronized (mRemoveLock) {
+                        if (DEBUG) Log.e(TAG, "MSG_ON_ALERT_WINDOW_REMOVED");
+                        mRemoveLock.notifyAll();
+                    }
+                    break;
+                default:
+                    super.handleMessage(msg);
+            }
+        }
+    }
+
+    private boolean isRunningInVR() {
+        final Context context = InstrumentationRegistry.getTargetContext();
+        if ((context.getResources().getConfiguration().uiMode & Configuration.UI_MODE_TYPE_MASK)
+             == Configuration.UI_MODE_TYPE_VR_HEADSET) {
+            return true;
+        }
+        return false;
+    }
+}
diff --git a/tests/framework/base/windowmanager/src/android/server/wm/AlertWindowsTests.java b/tests/framework/base/windowmanager/src/android/server/wm/AlertWindowsTests.java
new file mode 100644
index 0000000..5b3ddb8
--- /dev/null
+++ b/tests/framework/base/windowmanager/src/android/server/wm/AlertWindowsTests.java
@@ -0,0 +1,180 @@
+/*
+ * 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.server.wm;
+
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertTrue;
+
+import android.content.ComponentName;
+import android.platform.test.annotations.Presubmit;
+import android.server.am.ActivityManagerTestBase;
+import android.server.am.WaitForValidActivityState;
+import android.server.am.WindowManagerState;
+
+import org.junit.After;
+import org.junit.Test;
+
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.List;
+
+/**
+ * Build: mmma -j32 cts/tests/framework/base
+ * Run: cts/tests/framework/base/activitymanager/util/run-test CtsWindowManagerDeviceTestCases android.server.wm.AlertWindowsTests
+ */
+@Presubmit
+public class AlertWindowsTests extends ActivityManagerTestBase {
+
+    private static final ComponentName TEST_ACTIVITY = ComponentName.createRelative(
+            "android.server.wm.alertwindowapp", ".AlertWindowTestActivity");
+    private static final ComponentName SDK25_TEST_ACTIVITY = ComponentName.createRelative(
+            "android.server.wm.alertwindowappsdk25", ".AlertWindowTestActivitySdk25");
+
+    // From WindowManager.java
+    private static final int TYPE_BASE_APPLICATION      = 1;
+    private static final int FIRST_SYSTEM_WINDOW        = 2000;
+
+    private static final int TYPE_PHONE                 = FIRST_SYSTEM_WINDOW + 2;
+    private static final int TYPE_SYSTEM_ALERT          = FIRST_SYSTEM_WINDOW + 3;
+    private static final int TYPE_SYSTEM_OVERLAY        = FIRST_SYSTEM_WINDOW + 6;
+    private static final int TYPE_PRIORITY_PHONE        = FIRST_SYSTEM_WINDOW + 7;
+    private static final int TYPE_SYSTEM_ERROR          = FIRST_SYSTEM_WINDOW + 10;
+    private static final int TYPE_APPLICATION_OVERLAY   = FIRST_SYSTEM_WINDOW + 38;
+
+    private static final int TYPE_STATUS_BAR            = FIRST_SYSTEM_WINDOW;
+    private static final int TYPE_INPUT_METHOD          = FIRST_SYSTEM_WINDOW + 11;
+    private static final int TYPE_NAVIGATION_BAR        = FIRST_SYSTEM_WINDOW + 19;
+
+    private final List<Integer> mAlertWindowTypes = Arrays.asList(
+            TYPE_PHONE,
+            TYPE_PRIORITY_PHONE,
+            TYPE_SYSTEM_ALERT,
+            TYPE_SYSTEM_ERROR,
+            TYPE_SYSTEM_OVERLAY,
+            TYPE_APPLICATION_OVERLAY);
+    private final List<Integer> mSystemWindowTypes = Arrays.asList(
+            TYPE_STATUS_BAR,
+            TYPE_INPUT_METHOD,
+            TYPE_NAVIGATION_BAR);
+
+    @After
+    @Override
+    public void tearDown() throws Exception {
+        super.tearDown();
+        setAlertWindowPermission(TEST_ACTIVITY, false);
+        setAlertWindowPermission(SDK25_TEST_ACTIVITY, false);
+        stopTestPackage(TEST_ACTIVITY);
+        stopTestPackage(SDK25_TEST_ACTIVITY);
+    }
+
+    @Test
+    public void testAlertWindowAllowed() throws Exception {
+        runAlertWindowTest(TEST_ACTIVITY, true /* hasAlertWindowPermission */,
+                true /* atLeastO */);
+    }
+
+    @Test
+    public void testAlertWindowDisallowed() throws Exception {
+        runAlertWindowTest(TEST_ACTIVITY, false /* hasAlertWindowPermission */,
+                true /* atLeastO */);
+    }
+
+    @Test
+    public void testAlertWindowAllowedSdk25() throws Exception {
+        runAlertWindowTest(SDK25_TEST_ACTIVITY, true /* hasAlertWindowPermission */,
+                false /* atLeastO */);
+    }
+
+    @Test
+    public void testAlertWindowDisallowedSdk25() throws Exception {
+        runAlertWindowTest(SDK25_TEST_ACTIVITY, false /* hasAlertWindowPermission */,
+                false /* atLeastO */);
+    }
+
+    private void runAlertWindowTest(final ComponentName activityName,
+            final boolean hasAlertWindowPermission, final boolean atLeastO) throws Exception {
+        setAlertWindowPermission(activityName, hasAlertWindowPermission);
+
+        executeShellCommand(getAmStartCmd(activityName));
+        mAmWmState.computeState(new WaitForValidActivityState(activityName));
+        mAmWmState.assertVisibility(activityName, true);
+
+        assertAlertWindows(activityName, hasAlertWindowPermission, atLeastO);
+    }
+
+    private void assertAlertWindows(final ComponentName activityName,
+            final boolean hasAlertWindowPermission, final boolean atLeastO) {
+        final String packageName = activityName.getPackageName();
+        final WindowManagerState wMState = mAmWmState.getWmState();
+
+        final ArrayList<WindowManagerState.WindowState> alertWindows = new ArrayList<>();
+        wMState.getWindowsByPackageName(packageName, mAlertWindowTypes, alertWindows);
+
+        if (!hasAlertWindowPermission) {
+            assertTrue("Should be empty alertWindows=" + alertWindows, alertWindows.isEmpty());
+            return;
+        }
+
+        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);
+            }
+        }
+
+        final WindowManagerState.WindowState mainAppWindow =
+                wMState.getWindowByPackageName(packageName, TYPE_BASE_APPLICATION);
+
+        assertNotNull(mainAppWindow);
+
+        final WindowManagerState.WindowState lowestAlertWindow = alertWindows.get(0);
+        final WindowManagerState.WindowState highestAlertWindow =
+                alertWindows.get(alertWindows.size() - 1);
+
+        // Assert that the alert windows have higher z-order than the main app window
+        final WindowManagerState wmState = mAmWmState.getWmState();
+        assertTrue("lowestAlertWindow=" + lowestAlertWindow + " less than mainAppWindow="
+                + mainAppWindow,
+                wmState.getZOrder(lowestAlertWindow) > 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));
+        }
+
+        // Assert that alert windows are below key system windows.
+        final ArrayList<WindowManagerState.WindowState> systemWindows = new ArrayList<>();
+        wMState.getWindowsByPackageName(packageName, mSystemWindowTypes, systemWindows);
+        if (!systemWindows.isEmpty()) {
+            final WindowManagerState.WindowState lowestSystemWindow = alertWindows.get(0);
+            assertTrue("highestAlertWindow=" + highestAlertWindow
+                            + " greater than lowestSystemWindow=" + lowestSystemWindow,
+                    wmState.getZOrder(highestAlertWindow) < wmState.getZOrder(lowestSystemWindow));
+        }
+    }
+
+    private void setAlertWindowPermission(final ComponentName activityName, final boolean allow) {
+        final String packageName = activityName.getPackageName();
+        executeShellCommand("appops set " + packageName + " android:system_alert_window "
+                + (allow ? "allow" : "deny"));
+    }
+}
diff --git a/tests/framework/base/windowmanager/src/android/server/wm/ChildMovementTests.java b/tests/framework/base/windowmanager/src/android/server/wm/ChildMovementTests.java
new file mode 100644
index 0000000..41f620d
--- /dev/null
+++ b/tests/framework/base/windowmanager/src/android/server/wm/ChildMovementTests.java
@@ -0,0 +1,145 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.server.wm;
+
+import static android.server.am.StateLogger.logE;
+
+import static org.junit.Assert.assertTrue;
+
+import android.content.ComponentName;
+import android.server.am.SurfaceTraceReceiver;
+import android.server.am.WaitForValidActivityState;
+import android.server.am.WindowManagerState.WindowState;
+
+import org.junit.Test;
+
+import java.util.ArrayList;
+import java.util.List;
+
+public class ChildMovementTests extends ParentChildTestBase {
+
+    private static final ComponentName MOVING_CHILD_TEST_ACTIVITY = ComponentName
+            .unflattenFromString("android.server.wm.frametestapp/.MovingChildTestActivity");
+
+    /** @see android.server.wm.frametestapp.MovingChildTestActivity#POPUP_WINDOW_NAME */
+    private static final String POPUP_WINDOW_NAME = "ChildWindow";
+
+    private List<WindowState> mWindowList = new ArrayList<>();
+
+    @Override
+    ComponentName activityName() {
+        return MOVING_CHILD_TEST_ACTIVITY;
+    }
+
+    private WindowState getSingleWindow(final String windowName) {
+        try {
+            mAmWmState.getWmState().getMatchingVisibleWindowState(windowName, mWindowList);
+            return mWindowList.get(0);
+        } catch (Exception e) {
+            logE("Couldn't find window: " + windowName);
+            return null;
+        }
+    }
+
+    @Override
+    void doSingleTest(ParentChildTest t) throws Exception {
+        final WaitForValidActivityState waitForVisible =
+                WaitForValidActivityState.forWindow(POPUP_WINDOW_NAME);
+
+        mAmWmState.computeState(waitForVisible);
+        WindowState popup = getSingleWindow(POPUP_WINDOW_NAME);
+        WindowState parent = getSingleWindow(activityName().flattenToString());
+
+        t.doTest(parent, popup);
+    }
+
+    private final Object monitor = new Object();
+    private boolean testPassed = false;
+    private String popupName = null;
+    private String mainName = null;
+
+    private final SurfaceTraceReceiver.SurfaceObserver observer =
+            new SurfaceTraceReceiver.SurfaceObserver() {
+        int transactionCount = 0;
+        boolean sawChildMove = false;
+        boolean sawMainMove = false;
+        int timesSeen = 0;
+
+        @Override
+        public void openTransaction() {
+            transactionCount++;
+            if (transactionCount == 1) {
+                sawChildMove = false;
+                sawMainMove = false;
+            }
+        }
+
+        @Override
+        public void closeTransaction() {
+            transactionCount--;
+            if (transactionCount != 0) {
+                return;
+            }
+            synchronized (monitor) {
+                if (sawChildMove ^ sawMainMove) {
+                    monitor.notifyAll();
+                    return;
+                }
+                if (timesSeen > 10) {
+                    testPassed = true;
+                    monitor.notifyAll();
+                }
+            }
+        }
+
+        @Override
+        public void setPosition(String windowName, float x, float y) {
+            if (windowName.equals(popupName)) {
+                sawChildMove = true;
+                timesSeen++;
+            } else if (windowName.equals(mainName)) {
+                sawMainMove = true;
+            }
+        }
+    };
+
+    /**
+     * Here we test that a Child moves in the same transaction
+     * as its parent. We launch an activity with a Child which will
+     * move around its own main window. Then we listen to WindowManager transactions.
+     * Since the Child is static within the window, if we ever see one of
+     * them move xor the other one we have a problem!
+     */
+    @Test
+    public void testSurfaceMovesWithParent() throws Exception {
+        doFullscreenTest("MovesWithParent",
+                (WindowState parent, WindowState popup) -> {
+                    popupName = popup.getName();
+                    mainName = parent.getName();
+                    installSurfaceObserver(observer);
+                    try {
+                        synchronized (monitor) {
+                            monitor.wait(5000);
+                        }
+                    } catch (InterruptedException e) {
+                    } finally {
+                        assertTrue(testPassed);
+                        removeSurfaceObserver();
+                    }
+                });
+    }
+}
diff --git a/tests/framework/base/windowmanager/src/android/server/wm/CrossAppDragAndDropTests.java b/tests/framework/base/windowmanager/src/android/server/wm/CrossAppDragAndDropTests.java
new file mode 100644
index 0000000..c9b8b17
--- /dev/null
+++ b/tests/framework/base/windowmanager/src/android/server/wm/CrossAppDragAndDropTests.java
@@ -0,0 +1,528 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.server.wm;
+
+import static android.app.WindowConfiguration.WINDOWING_MODE_FREEFORM;
+import static android.app.WindowConfiguration.WINDOWING_MODE_FULLSCREEN_OR_SPLIT_SCREEN_SECONDARY;
+import static android.app.WindowConfiguration.WINDOWING_MODE_PINNED;
+import static android.app.WindowConfiguration.WINDOWING_MODE_SPLIT_SCREEN_PRIMARY;
+import static android.content.pm.PackageManager.FEATURE_FREEFORM_WINDOW_MANAGEMENT;
+import static android.server.am.ActivityManagerTestBase.executeShellCommand;
+import static android.server.am.StateLogger.log;
+
+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.junit.Assume.assumeTrue;
+
+import android.app.ActivityManager;
+import android.content.Context;
+import android.graphics.Point;
+import android.os.RemoteException;
+import android.os.SystemClock;
+import android.support.test.InstrumentationRegistry;
+import android.support.test.uiautomator.UiDevice;
+import android.util.Log;
+
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Test;
+
+import java.util.Map;
+import java.util.regex.Pattern;
+
+/**
+ * Build: mmma -j32 cts/tests/framework/base
+ * Run: cts/tests/framework/base/activitymanager/util/run-test CtsWindowManagerDeviceTestCases android.server.wm.CrossAppDragAndDropTests
+ */
+//@Presubmit b/68038788
+public class CrossAppDragAndDropTests {
+    private static final String TAG = "CrossAppDragAndDrop";
+
+    private static final String AM_FORCE_STOP = "am force-stop ";
+    private static final String AM_MOVE_TASK = "am stack move-task ";
+    private static final String AM_RESIZE_TASK = "am task resize ";
+    private static final String AM_REMOVE_STACK = "am stack remove ";
+    private static final String AM_START_N = "am start -n ";
+    private static final String AM_STACK_LIST = "am stack list";
+    private static final String TASK_ID_PREFIX = "taskId";
+
+    // Regex pattern to match adb shell am stack list output of the form:
+    // taskId=<TASK_ID>: <componentName> bounds=[LEFT,TOP][RIGHT,BOTTOM]
+    private static final String TASK_REGEX_PATTERN_STRING =
+            "taskId=[0-9]+: %s bounds=\\[[0-9]+,[0-9]+\\]\\[[0-9]+,[0-9]+\\]";
+
+    private static final int SWIPE_STEPS = 100;
+
+    private static final String SOURCE_PACKAGE_NAME = "android.server.wm.dndsourceapp";
+    private static final String TARGET_PACKAGE_NAME = "android.server.wm.dndtargetapp";
+    private static final String TARGET_23_PACKAGE_NAME = "android.server.wm.dndtargetappsdk23";
+
+
+    private static final String SOURCE_ACTIVITY_NAME = "DragSource";
+    private static final String TARGET_ACTIVITY_NAME = "DropTarget";
+
+    private static final String FILE_GLOBAL = "file_global";
+    private static final String FILE_LOCAL = "file_local";
+    private static final String DISALLOW_GLOBAL = "disallow_global";
+    private static final String CANCEL_SOON = "cancel_soon";
+    private static final String GRANT_NONE = "grant_none";
+    private static final String GRANT_READ = "grant_read";
+    private static final String GRANT_WRITE = "grant_write";
+    private static final String GRANT_READ_PREFIX = "grant_read_prefix";
+    private static final String GRANT_READ_NOPREFIX = "grant_read_noprefix";
+    private static final String GRANT_READ_PERSISTABLE = "grant_read_persistable";
+
+    private static final String REQUEST_NONE = "request_none";
+    private static final String REQUEST_READ = "request_read";
+    private static final String REQUEST_READ_NESTED = "request_read_nested";
+    private static final String REQUEST_TAKE_PERSISTABLE = "request_take_persistable";
+    private static final String REQUEST_WRITE = "request_write";
+
+    private static final String SOURCE_LOG_TAG = "DragSource";
+    private static final String TARGET_LOG_TAG = "DropTarget";
+
+    private static final String RESULT_KEY_START_DRAG = "START_DRAG";
+    private static final String RESULT_KEY_DRAG_STARTED = "DRAG_STARTED";
+    private static final String RESULT_KEY_DRAG_ENDED = "DRAG_ENDED";
+    private static final String RESULT_KEY_EXTRAS = "EXTRAS";
+    private static final String RESULT_KEY_DROP_RESULT = "DROP";
+    private static final String RESULT_KEY_ACCESS_BEFORE = "BEFORE";
+    private static final String RESULT_KEY_ACCESS_AFTER = "AFTER";
+    private static final String RESULT_KEY_CLIP_DATA_ERROR = "CLIP_DATA_ERROR";
+    private static final String RESULT_KEY_CLIP_DESCR_ERROR = "CLIP_DESCR_ERROR";
+    private static final String RESULT_KEY_LOCAL_STATE_ERROR = "LOCAL_STATE_ERROR";
+
+    private static final String RESULT_MISSING = "Missing";
+    private static final String RESULT_OK = "OK";
+    private static final String RESULT_EXCEPTION = "Exception";
+    private static final String RESULT_NULL_DROP_PERMISSIONS = "Null DragAndDropPermissions";
+
+    protected Context mContext;
+    protected ActivityManager mAm;
+    private UiDevice mDevice;
+
+    private Map<String, String> mSourceResults;
+    private Map<String, String> mTargetResults;
+
+    private String mSourcePackageName;
+    private String mTargetPackageName;
+
+    private String mSessionId;
+    private String mSourceLogTag;
+    private String mTargetLogTag;
+
+    @Before
+    public void setUp() throws Exception {
+        assumeTrue(supportsDragAndDrop());
+
+        // Use uptime in seconds as unique test invocation id.
+        mSessionId = Long.toString(SystemClock.uptimeMillis() / 1000);
+        mSourceLogTag = SOURCE_LOG_TAG + mSessionId;
+        mTargetLogTag = TARGET_LOG_TAG + mSessionId;
+
+        mContext = InstrumentationRegistry.getContext();
+        mAm = mContext.getSystemService(ActivityManager.class);
+        mDevice = UiDevice.getInstance(InstrumentationRegistry.getInstrumentation());
+
+        mSourcePackageName = SOURCE_PACKAGE_NAME;
+        mTargetPackageName = TARGET_PACKAGE_NAME;
+        cleanupState();
+    }
+
+    @After
+    public void tearDown() throws Exception {
+        assumeTrue(supportsDragAndDrop());
+
+        executeShellCommand(AM_FORCE_STOP + mSourcePackageName);
+        executeShellCommand(AM_FORCE_STOP + mTargetPackageName);
+    }
+
+    private void clearLogs() {
+        executeShellCommand("logcat -c");
+    }
+
+    private String getStartCommand(String componentName, String modeExtra, String logtag) {
+        return AM_START_N + componentName + " -e mode " + modeExtra + " -e logtag " + logtag;
+    }
+
+    private String getMoveTaskCommand(int taskId, int stackId) {
+        return AM_MOVE_TASK + taskId + " " + stackId + " true";
+    }
+
+    private String getResizeTaskCommand(int taskId, Point topLeft, Point bottomRight)
+            throws Exception {
+        return AM_RESIZE_TASK + taskId + " " + topLeft.x + " " + topLeft.y + " " + bottomRight.x
+                + " " + bottomRight.y;
+    }
+
+    private String getComponentName(String packageName, String activityName) {
+        return packageName + "/" + packageName + "." + activityName;
+    }
+
+    /**
+     * Make sure that the special activity stacks are removed and the ActivityManager/WindowManager
+     * is in a good state.
+     */
+    private void cleanupState() throws Exception {
+        executeShellCommand(AM_FORCE_STOP + SOURCE_PACKAGE_NAME);
+        executeShellCommand(AM_FORCE_STOP + TARGET_PACKAGE_NAME);
+        executeShellCommand(AM_FORCE_STOP + TARGET_23_PACKAGE_NAME);
+        unlockDevice();
+        clearLogs();
+
+        // Remove special stacks.
+        mAm.removeStacksInWindowingModes(new int[] {
+                WINDOWING_MODE_PINNED,
+                WINDOWING_MODE_SPLIT_SCREEN_PRIMARY,
+                WINDOWING_MODE_FREEFORM
+        });
+    }
+
+    private void launchDockedActivity(String packageName, String activityName, String mode,
+            String logtag) throws Exception {
+        clearLogs();
+        final String componentName = getComponentName(packageName, activityName);
+        executeShellCommand(getStartCommand(componentName, mode, logtag) + " --windowingMode "
+                + WINDOWING_MODE_SPLIT_SCREEN_PRIMARY);
+        waitForResume(packageName, activityName);
+    }
+
+    private void launchFullscreenActivity(String packageName, String activityName, String mode,
+            String logtag) throws Exception {
+        clearLogs();
+        final String componentName = getComponentName(packageName, activityName);
+        executeShellCommand(getStartCommand(componentName, mode, logtag) + " --windowingMode "
+                + WINDOWING_MODE_FULLSCREEN_OR_SPLIT_SCREEN_SECONDARY);
+        waitForResume(packageName, activityName);
+    }
+
+    /**
+     * @param displaySize size of the display
+     * @param leftSide {@code true} to launch the app taking up the left half of the display,
+     *         {@code false} to launch the app taking up the right half of the display.
+     */
+    private void launchFreeformActivity(String packageName, String activityName, String mode,
+            String logtag, Point displaySize, boolean leftSide) throws Exception {
+        clearLogs();
+        final String componentName = getComponentName(packageName, activityName);
+        executeShellCommand(getStartCommand(componentName, mode, logtag) + " --windowingMode "
+                + WINDOWING_MODE_FREEFORM);
+        waitForResume(packageName, activityName);
+        Point topLeft = new Point(leftSide ? 0 : displaySize.x / 2, 0);
+        Point bottomRight = new Point(leftSide ? displaySize.x / 2 : displaySize.x, displaySize.y);
+        executeShellCommand(getResizeTaskCommand(getActivityTaskId(componentName), topLeft,
+                bottomRight));
+    }
+
+    private void waitForResume(String packageName, String activityName) throws Exception {
+        final String fullActivityName = packageName + "." + activityName;
+        int retryCount = 3;
+        do {
+            Thread.sleep(500);
+            String logs = executeShellCommand("logcat -d -b events");
+            for (String line : logs.split("\\n")) {
+                if (line.contains("am_on_resume_called") && line.contains(fullActivityName)) {
+                    return;
+                }
+            }
+        } while (retryCount-- > 0);
+
+        throw new Exception(fullActivityName + " has failed to start");
+    }
+
+    private void injectInput(Point from, Point to, int steps) throws Exception {
+        mDevice.drag(from.x, from.y, to.x, to.y, steps);
+    }
+
+    private String findTaskInfo(String name) {
+        final String output = executeShellCommand(AM_STACK_LIST);
+        final StringBuilder builder = new StringBuilder();
+        builder.append("Finding task info for task: ");
+        builder.append(name);
+        builder.append("\nParsing adb shell am output: ");
+        builder.append(output);
+        log(builder.toString());
+        final Pattern pattern = Pattern.compile(String.format(TASK_REGEX_PATTERN_STRING, name));
+        for (String line : output.split("\\n")) {
+            final String truncatedLine;
+            // Only look for the activity name before the "topActivity" string.
+            final int pos = line.indexOf("topActivity");
+            if (pos > 0) {
+                truncatedLine = line.substring(0, pos);
+            } else {
+                truncatedLine = line;
+            }
+            if (pattern.matcher(truncatedLine).find()) {
+                return truncatedLine;
+            }
+        }
+        return "";
+    }
+
+    private boolean getWindowBounds(String name, Point from, Point to) throws Exception {
+        final String taskInfo = findTaskInfo(name);
+        final String[] sections = taskInfo.split("\\[");
+        if (sections.length > 2) {
+            try {
+                parsePoint(sections[1], from);
+                parsePoint(sections[2], to);
+                return true;
+            } catch (Exception e) {
+                return false;
+            }
+        }
+        return false;
+    }
+
+    private int getActivityTaskId(String name) {
+        final String taskInfo = findTaskInfo(name);
+        for (String word : taskInfo.split("\\s+")) {
+            if (word.startsWith(TASK_ID_PREFIX)) {
+                final String withColon = word.split("=")[1];
+                return Integer.parseInt(withColon.substring(0, withColon.length() - 1));
+            }
+        }
+        return -1;
+    }
+
+    private Point getDisplaySize() throws Exception {
+        final String output = executeShellCommand("wm size");
+        final String[] sizes = output.split(" ")[2].split("x");
+        return new Point(Integer.valueOf(sizes[0].trim()), Integer.valueOf(sizes[1].trim()));
+    }
+
+    private Point getWindowCenter(String name) throws Exception {
+        Point p1 = new Point();
+        Point p2 = new Point();
+        if (getWindowBounds(name, p1, p2)) {
+            return new Point((p1.x + p2.x) / 2, (p1.y + p2.y) / 2);
+        }
+        return null;
+    }
+
+    private void parsePoint(String string, Point point) {
+        final String[] parts = string.split("[,|\\]]");
+        point.x = Integer.parseInt(parts[0]);
+        point.y = Integer.parseInt(parts[1]);
+    }
+
+    private void unlockDevice() {
+        // Wake up the device, if necessary.
+        try {
+            mDevice.wakeUp();
+        } catch (RemoteException e) {
+            throw new RuntimeException(e);
+        }
+        // Unlock the screen.
+        mDevice.pressMenu();
+    }
+
+    private void assertDropResult(String sourceMode, String targetMode, String expectedDropResult)
+            throws Exception {
+        assertDragAndDropResults(sourceMode, targetMode, RESULT_OK, expectedDropResult, RESULT_OK);
+    }
+
+    private void assertNoGlobalDragEvents(String sourceMode, String expectedStartDragResult)
+            throws Exception {
+        assertDragAndDropResults(
+                sourceMode, REQUEST_NONE, expectedStartDragResult, RESULT_MISSING, RESULT_MISSING);
+    }
+
+    private void assertDragAndDropResults(String sourceMode, String targetMode,
+            String expectedStartDragResult, String expectedDropResult,
+            String expectedListenerResults) throws Exception {
+        Log.e(TAG, "session: " + mSessionId + ", source: " + sourceMode
+                + ", target: " + targetMode);
+
+        if (supportsSplitScreenMultiWindow()) {
+            launchDockedActivity(
+                    mSourcePackageName, SOURCE_ACTIVITY_NAME, sourceMode, mSourceLogTag);
+            launchFullscreenActivity(
+                    mTargetPackageName, TARGET_ACTIVITY_NAME, targetMode, mTargetLogTag);
+        } else if (supportsFreeformMultiWindow()) {
+            // Fallback to try to launch two freeform windows side by side.
+            Point displaySize = getDisplaySize();
+            launchFreeformActivity(
+                    mSourcePackageName, SOURCE_ACTIVITY_NAME, sourceMode, mSourceLogTag,
+                    displaySize, true /* leftSide */);
+            launchFreeformActivity(
+                    mTargetPackageName, TARGET_ACTIVITY_NAME, targetMode, mTargetLogTag,
+                    displaySize, false /* leftSide */);
+        } else {
+            return;
+        }
+
+        Point p1 = getWindowCenter(getComponentName(mSourcePackageName, SOURCE_ACTIVITY_NAME));
+        assertNotNull(p1);
+        Point p2 = getWindowCenter(getComponentName(mTargetPackageName, TARGET_ACTIVITY_NAME));
+        assertNotNull(p2);
+
+        TestLogService.registerClient(mSourceLogTag, RESULT_KEY_START_DRAG);
+        TestLogService.registerClient(mTargetLogTag, RESULT_KEY_DRAG_ENDED);
+
+        injectInput(p1, p2, SWIPE_STEPS);
+
+        mSourceResults = TestLogService.getResultsForClient(mSourceLogTag, 1000);
+        assertSourceResult(RESULT_KEY_START_DRAG, expectedStartDragResult);
+
+        mTargetResults = TestLogService.getResultsForClient(mTargetLogTag, 1000);
+        assertTargetResult(RESULT_KEY_DROP_RESULT, expectedDropResult);
+        if (!RESULT_MISSING.equals(expectedDropResult)) {
+            assertTargetResult(RESULT_KEY_ACCESS_BEFORE, RESULT_EXCEPTION);
+            assertTargetResult(RESULT_KEY_ACCESS_AFTER, RESULT_EXCEPTION);
+        }
+        assertListenerResults(expectedListenerResults);
+    }
+
+    private void assertListenerResults(String expectedResult) throws Exception {
+        assertTargetResult(RESULT_KEY_DRAG_STARTED, expectedResult);
+        assertTargetResult(RESULT_KEY_DRAG_ENDED, expectedResult);
+        assertTargetResult(RESULT_KEY_EXTRAS, expectedResult);
+
+        assertTargetResult(RESULT_KEY_CLIP_DATA_ERROR, RESULT_MISSING);
+        assertTargetResult(RESULT_KEY_CLIP_DESCR_ERROR, RESULT_MISSING);
+        assertTargetResult(RESULT_KEY_LOCAL_STATE_ERROR, RESULT_MISSING);
+    }
+
+    private void assertSourceResult(String resultKey, String expectedResult) throws Exception {
+        assertResult(mSourceResults, resultKey, expectedResult);
+    }
+
+    private void assertTargetResult(String resultKey, String expectedResult) throws Exception {
+        assertResult(mTargetResults, resultKey, expectedResult);
+    }
+
+    private void assertResult(Map<String, String> results, String resultKey, String expectedResult)
+            throws Exception {
+        if (RESULT_MISSING.equals(expectedResult)) {
+            if (results.containsKey(resultKey)) {
+                fail("Unexpected " + resultKey + "=" + results.get(resultKey));
+            }
+        } else {
+            assertTrue("Missing " + resultKey, results.containsKey(resultKey));
+            assertEquals(resultKey + " result mismatch,", expectedResult,
+                    results.get(resultKey));
+        }
+    }
+
+    private static boolean supportsDragAndDrop() {
+        return ActivityManager.supportsMultiWindow(InstrumentationRegistry.getContext());
+    }
+
+    private static boolean supportsSplitScreenMultiWindow() {
+        return ActivityManager.supportsSplitScreenMultiWindow(InstrumentationRegistry.getContext());
+    }
+
+    private static boolean supportsFreeformMultiWindow() {
+        return InstrumentationRegistry.getContext()
+                .getPackageManager()
+                .hasSystemFeature(FEATURE_FREEFORM_WINDOW_MANAGEMENT);
+    }
+
+    @Test
+    public void testCancelSoon() throws Exception {
+        assertDropResult(CANCEL_SOON, REQUEST_NONE, RESULT_MISSING);
+    }
+
+    @Test
+    public void testDisallowGlobal() throws Exception {
+        assertNoGlobalDragEvents(DISALLOW_GLOBAL, RESULT_OK);
+    }
+
+    @Test
+    public void testDisallowGlobalBelowSdk24() throws Exception {
+        mTargetPackageName = TARGET_23_PACKAGE_NAME;
+        assertNoGlobalDragEvents(GRANT_NONE, RESULT_OK);
+    }
+
+    @Test
+    public void testFileUriLocal() throws Exception {
+        assertNoGlobalDragEvents(FILE_LOCAL, RESULT_OK);
+    }
+
+    @Test
+    public void testFileUriGlobal() throws Exception {
+        assertNoGlobalDragEvents(FILE_GLOBAL, RESULT_EXCEPTION);
+    }
+
+    @Test
+    public void testGrantNoneRequestNone() throws Exception {
+        assertDropResult(GRANT_NONE, REQUEST_NONE, RESULT_EXCEPTION);
+    }
+
+    @Test
+    public void testGrantNoneRequestRead() throws Exception {
+        assertDropResult(GRANT_NONE, REQUEST_READ, RESULT_NULL_DROP_PERMISSIONS);
+    }
+
+    @Test
+    public void testGrantNoneRequestWrite() throws Exception {
+        assertDropResult(GRANT_NONE, REQUEST_WRITE, RESULT_NULL_DROP_PERMISSIONS);
+    }
+
+    @Test
+    public void testGrantReadRequestNone() throws Exception {
+        assertDropResult(GRANT_READ, REQUEST_NONE, RESULT_EXCEPTION);
+    }
+
+    @Test
+    public void testGrantReadRequestRead() throws Exception {
+        assertDropResult(GRANT_READ, REQUEST_READ, RESULT_OK);
+    }
+
+    @Test
+    public void testGrantReadRequestWrite() throws Exception {
+        assertDropResult(GRANT_READ, REQUEST_WRITE, RESULT_EXCEPTION);
+    }
+
+    @Test
+    public void testGrantReadNoPrefixRequestReadNested() throws Exception {
+        assertDropResult(GRANT_READ_NOPREFIX, REQUEST_READ_NESTED, RESULT_EXCEPTION);
+    }
+
+    @Test
+    public void testGrantReadPrefixRequestReadNested() throws Exception {
+        assertDropResult(GRANT_READ_PREFIX, REQUEST_READ_NESTED, RESULT_OK);
+    }
+
+    @Test
+    public void testGrantPersistableRequestTakePersistable() throws Exception {
+        assertDropResult(GRANT_READ_PERSISTABLE, REQUEST_TAKE_PERSISTABLE, RESULT_OK);
+    }
+
+    @Test
+    public void testGrantReadRequestTakePersistable() throws Exception {
+        assertDropResult(GRANT_READ, REQUEST_TAKE_PERSISTABLE, RESULT_EXCEPTION);
+    }
+
+    @Test
+    public void testGrantWriteRequestNone() throws Exception {
+        assertDropResult(GRANT_WRITE, REQUEST_NONE, RESULT_EXCEPTION);
+    }
+
+    @Test
+    public void testGrantWriteRequestRead() throws Exception {
+        assertDropResult(GRANT_WRITE, REQUEST_READ, RESULT_EXCEPTION);
+    }
+
+    @Test
+    public void testGrantWriteRequestWrite() throws Exception {
+        assertDropResult(GRANT_WRITE, REQUEST_WRITE, RESULT_OK);
+    }
+}
diff --git a/tests/framework/base/windowmanager/src/android/server/wm/DialogFrameTests.java b/tests/framework/base/windowmanager/src/android/server/wm/DialogFrameTests.java
new file mode 100644
index 0000000..a5e5b4b
--- /dev/null
+++ b/tests/framework/base/windowmanager/src/android/server/wm/DialogFrameTests.java
@@ -0,0 +1,244 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.server.wm;
+
+import static android.server.am.StateLogger.logE;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertTrue;
+
+import android.content.ComponentName;
+import android.graphics.Rect;
+import android.server.am.WaitForValidActivityState;
+import android.server.am.WindowManagerState;
+import android.server.am.WindowManagerState.WindowState;
+
+import org.junit.Test;
+
+import java.util.ArrayList;
+import java.util.List;
+
+public class DialogFrameTests extends ParentChildTestBase {
+
+    private static final ComponentName DIALOG_TEST_ACTIVITY = ComponentName
+            .unflattenFromString("android.server.wm.frametestapp/.DialogTestActivity");
+
+    /** @see android.server.wm.frametestapp.DialogTestActivity#DIALOG_WINDOW_NAME */
+    private static final String DIALOG_WINDOW_NAME = "TestDialog";
+
+    private List<WindowState> mWindowList = new ArrayList<>();
+
+    @Override
+    ComponentName activityName() {
+        return DIALOG_TEST_ACTIVITY;
+    }
+
+    private WindowState getSingleWindow(final String windowName) {
+        try {
+            mAmWmState.getWmState().getMatchingVisibleWindowState(windowName, mWindowList);
+            return mWindowList.get(0);
+        } catch (Exception e) {
+            logE("Couldn't find window: " + windowName);
+            return null;
+        }
+    }
+
+    @Override
+    void doSingleTest(ParentChildTest t) throws Exception {
+        final WaitForValidActivityState waitForVisible =
+                WaitForValidActivityState.forWindow(DIALOG_WINDOW_NAME);
+
+        mAmWmState.computeState(waitForVisible);
+        WindowState dialog = getSingleWindow(DIALOG_WINDOW_NAME);
+        WindowState parent = getSingleWindow(activityName().flattenToString());
+
+        t.doTest(parent, dialog);
+    }
+
+    // With Width and Height as MATCH_PARENT we should fill
+    // the same content frame as the main activity window
+    @Test
+    public void testMatchParentDialog() throws Exception {
+        doParentChildTest("MatchParent",
+                (WindowState parent, WindowState dialog) -> {
+                    assertEquals(parent.getContentFrame(), dialog.getFrame());
+                });
+    }
+
+    // If we have LAYOUT_IN_SCREEN and LAYOUT_IN_OVERSCAN with MATCH_PARENT,
+    // we will not be constrained to the insets and so we will be the same size
+    // as the main window main frame.
+    @Test
+    public void testMatchParentDialogLayoutInOverscan() throws Exception {
+        doParentChildTest("MatchParentLayoutInOverscan",
+                (WindowState parent, WindowState dialog) -> {
+                    assertEquals(parent.getFrame(), dialog.getFrame());
+                });
+    }
+
+    private static final int explicitDimension = 200;
+
+    // The default gravity for dialogs should center them.
+    @Test
+    public void testExplicitSizeDefaultGravity() throws Exception {
+        doParentChildTest("ExplicitSize",
+                (WindowState parent, WindowState dialog) -> {
+                    Rect contentFrame = parent.getContentFrame();
+                    Rect expectedFrame = new Rect(
+                            contentFrame.left + (contentFrame.width() - explicitDimension) / 2,
+                            contentFrame.top + (contentFrame.height() - explicitDimension) / 2,
+                            contentFrame.left + (contentFrame.width() + explicitDimension) / 2,
+                            contentFrame.top + (contentFrame.height() + explicitDimension) / 2);
+                    assertEquals(expectedFrame, dialog.getFrame());
+                });
+    }
+
+    @Test
+    public void testExplicitSizeTopLeftGravity() throws Exception {
+        doParentChildTest("ExplicitSizeTopLeftGravity",
+                (WindowState parent, WindowState dialog) -> {
+                    Rect contentFrame = parent.getContentFrame();
+                    Rect expectedFrame = new Rect(
+                            contentFrame.left,
+                            contentFrame.top,
+                            contentFrame.left + explicitDimension,
+                            contentFrame.top + explicitDimension);
+                    assertEquals(expectedFrame, dialog.getFrame());
+                });
+    }
+
+    @Test
+    public void testExplicitSizeBottomRightGravity() throws Exception {
+        doParentChildTest("ExplicitSizeBottomRightGravity",
+                (WindowState parent, WindowState dialog) -> {
+                    Rect contentFrame = parent.getContentFrame();
+                    Rect expectedFrame = new Rect(
+                            contentFrame.left + contentFrame.width() - explicitDimension,
+                            contentFrame.top + contentFrame.height() - explicitDimension,
+                            contentFrame.left + contentFrame.width(),
+                            contentFrame.top + contentFrame.height());
+                    assertEquals(expectedFrame, dialog.getFrame());
+                });
+    }
+
+    // TODO: Commented out for now because it doesn't work. We end up
+    // insetting the decor on the bottom. I think this is a bug
+    // probably in the default dialog flags:
+    // b/30127373
+    //    public void testOversizedDimensions() throws Exception {
+    //        doParentChildTest("OversizedDimensions",
+    //            (WindowState parent, WindowState dialog) -> {
+    // With the default flags oversize should result in clipping to
+    // parent frame.
+    //                assertEquals(parent.getContentFrame(), dialog.getFrame());
+    //         });
+    //    }
+
+
+
+    // TODO(b/63993863) : Disabled pending public API to fetch maximum surface size.
+    //static final int oversizedDimension = 5000;
+    // With FLAG_LAYOUT_NO_LIMITS  we should get the size we request, even if its much
+    // larger than the screen.
+    // @Test
+    // public void testOversizedDimensionsNoLimits() throws Exception {
+        // TODO(b/36890978): We only run this in fullscreen because of the
+        // unclear status of NO_LIMITS for non-child surfaces in MW modes
+    // doFullscreenTest("OversizedDimensionsNoLimits",
+    // (WindowState parent, WindowState dialog) -> {
+    // Rect contentFrame = parent.getContentFrame();
+    // Rect expectedFrame = new Rect(contentFrame.left, contentFrame.top,
+    // contentFrame.left + oversizedDimension,
+    // contentFrame.top + oversizedDimension);
+    // assertEquals(expectedFrame, dialog.getFrame());
+    // });
+    //}
+
+    // If we request the MATCH_PARENT and a non-zero position, we wouldn't be
+    // able to fit all of our content, so we should be adjusted to just fit the
+    // content frame.
+    @Test
+    public void testExplicitPositionMatchParent() throws Exception {
+        doParentChildTest("ExplicitPositionMatchParent",
+                (WindowState parent, WindowState dialog) -> {
+                    assertEquals(parent.getContentFrame(),
+                            dialog.getFrame());
+                });
+    }
+
+    // Unless we pass NO_LIMITS in which case our requested position should
+    // be honored.
+    @Test
+    public void testExplicitPositionMatchParentNoLimits() throws Exception {
+        final int explicitPosition = 100;
+        doParentChildTest("ExplicitPositionMatchParentNoLimits",
+                (WindowState parent, WindowState dialog) -> {
+                    Rect contentFrame = parent.getContentFrame();
+                    Rect expectedFrame = new Rect(contentFrame);
+                    expectedFrame.offset(explicitPosition, explicitPosition);
+                    assertEquals(expectedFrame, dialog.getFrame());
+                });
+    }
+
+    // We run the two focus tests fullscreen only because switching to the
+    // docked stack will strip away focus from the task anyway.
+    @Test
+    public void testDialogReceivesFocus() throws Exception {
+        doFullscreenTest("MatchParent",
+                (WindowState parent, WindowState dialog) -> {
+                    assertEquals(dialog.getName(), mAmWmState.getWmState().getFocusedWindow());
+                });
+    }
+
+    @Test
+    public void testNoFocusDialog() throws Exception {
+        doFullscreenTest("NoFocus",
+                (WindowState parent, WindowState dialog) -> {
+                    assertEquals(parent.getName(), mAmWmState.getWmState().getFocusedWindow());
+                });
+    }
+
+    @Test
+    public void testMarginsArePercentagesOfContentFrame() throws Exception {
+        float horizontalMargin = .25f;
+        float verticalMargin = .35f;
+        doParentChildTest("WithMargins",
+                (WindowState parent, WindowState dialog) -> {
+                    Rect frame = parent.getContentFrame();
+                    Rect expectedFrame = new Rect(
+                            (int) (horizontalMargin * frame.width() + frame.left),
+                            (int) (verticalMargin * frame.height() + frame.top),
+                            (int) (horizontalMargin * frame.width() + frame.left)
+                                    + explicitDimension,
+                            (int) (verticalMargin * frame.height() + frame.top)
+                                    + explicitDimension);
+                    assertEquals(expectedFrame, dialog.getFrame());
+                });
+    }
+
+    @Test
+    public void testDialogPlacedAboveParent() throws Exception {
+        final WindowManagerState wmState = mAmWmState.getWmState();
+        doParentChildTest("MatchParent",
+                (WindowState parent, WindowState dialog) -> {
+                    // Not only should the dialog be higher, but it should be
+                    // leave multiple layers of space inbetween for DimLayers,
+                    // etc...
+                    assertTrue(wmState.getZOrder(dialog) > wmState.getZOrder(parent));
+                });
+    }
+}
diff --git a/tests/framework/base/windowmanager/src/android/server/wm/ParentChildTestBase.java b/tests/framework/base/windowmanager/src/android/server/wm/ParentChildTestBase.java
new file mode 100644
index 0000000..08f25c2
--- /dev/null
+++ b/tests/framework/base/windowmanager/src/android/server/wm/ParentChildTestBase.java
@@ -0,0 +1,70 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.server.wm;
+
+import static android.app.WindowConfiguration.WINDOWING_MODE_SPLIT_SCREEN_PRIMARY;
+import static android.server.am.StateLogger.log;
+
+import android.content.ComponentName;
+import android.server.am.ActivityManagerTestBase;
+import android.server.am.WindowManagerState.WindowState;
+
+abstract class ParentChildTestBase extends ActivityManagerTestBase {
+
+    /** Extra key for test case name. */
+    private static final String EXTRA_TEST_CASE = "test-case";
+
+    interface ParentChildTest {
+        void doTest(WindowState parent, WindowState child);
+    }
+
+    private void startTestCase(String testCase) throws Exception {
+        executeShellCommand(getAmStartCmd(activityName(), EXTRA_TEST_CASE, testCase));
+    }
+
+    private void startTestCaseDocked(String testCase) throws Exception {
+        startTestCase(testCase);
+        setActivityTaskWindowingMode(activityName(), WINDOWING_MODE_SPLIT_SCREEN_PRIMARY);
+    }
+
+    abstract ComponentName activityName();
+
+    abstract void doSingleTest(ParentChildTest t) throws Exception;
+
+    void doFullscreenTest(String testCase, ParentChildTest t) throws Exception {
+        log("Running test fullscreen");
+        startTestCase(testCase);
+        doSingleTest(t);
+        stopTestPackage(activityName());
+    }
+
+    private void doDockedTest(String testCase, ParentChildTest t) throws Exception {
+        log("Running test docked");
+        if (!supportsSplitScreenMultiWindow()) {
+            log("Skipping test: no split multi-window support");
+            return;
+        }
+        startTestCaseDocked(testCase);
+        doSingleTest(t);
+        stopTestPackage(activityName());
+    }
+
+    void doParentChildTest(String testCase, ParentChildTest t) throws Exception {
+        doFullscreenTest(testCase, t);
+        doDockedTest(testCase, t);
+    }
+}
diff --git a/tests/framework/base/windowmanager/src/android/server/wm/TestLogClient.java b/tests/framework/base/windowmanager/src/android/server/wm/TestLogClient.java
new file mode 100644
index 0000000..725ef09
--- /dev/null
+++ b/tests/framework/base/windowmanager/src/android/server/wm/TestLogClient.java
@@ -0,0 +1,52 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.server.wm;
+
+import android.content.ComponentName;
+import android.content.Context;
+import android.content.Intent;
+
+/**
+ * A class that sends log data to {@link TestLogService}.
+ */
+public class TestLogClient {
+
+    public static final String EXTRA_LOG_TAG = "logtag";
+    public static final String EXTRA_KEY = "key";
+    public static final String EXTRA_VALUE = "value";
+
+    private static final String TEST_LOGGER_PACKAGE_NAME = "android.server.cts.wm";
+    private static final String TEST_LOGGER_SERVICE_NAME = "android.server.wm.TestLogService";
+
+    private final Context mContext;
+    private final String mLogTag;
+
+    public TestLogClient(Context context, String logtag) {
+        mContext = context;
+        mLogTag = logtag;
+    }
+
+    public void record(String key, String value) {
+        Intent intent = new Intent();
+        intent.setComponent(
+                new ComponentName(TEST_LOGGER_PACKAGE_NAME, TEST_LOGGER_SERVICE_NAME));
+        intent.putExtra(EXTRA_LOG_TAG, mLogTag);
+        intent.putExtra(EXTRA_KEY, key);
+        intent.putExtra(EXTRA_VALUE, value);
+        mContext.startService(intent);
+    }
+}
diff --git a/tests/framework/base/windowmanager/src/android/server/wm/TestLogService.java b/tests/framework/base/windowmanager/src/android/server/wm/TestLogService.java
new file mode 100644
index 0000000..8bd5099
--- /dev/null
+++ b/tests/framework/base/windowmanager/src/android/server/wm/TestLogService.java
@@ -0,0 +1,119 @@
+/*
+ * 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.server.wm;
+
+import android.app.Service;
+import android.content.Intent;
+import android.os.IBinder;
+import android.util.Log;
+import java.util.HashMap;
+import java.util.Map;
+import java.util.concurrent.CountDownLatch;
+import java.util.concurrent.TimeUnit;
+
+/**
+ *  A service collecting data from other apps used by a test.
+ *
+ *  Use {@link TestLogClient} to send data to this service.
+ */
+public class TestLogService extends Service {
+    private static final String TAG = "TestLogService";
+
+    private static final Object mLock = new Object();
+
+    static class ClientChannel {
+        final String mStopKey;
+        final CountDownLatch mLatch = new CountDownLatch(1);
+        final Map<String, String> mResults = new HashMap<>();
+
+        ClientChannel(String stopKey) {
+            mStopKey = stopKey;
+        }
+    }
+
+    private static Map<String, ClientChannel> mChannels = new HashMap<>();
+
+    @Override
+    public IBinder onBind(Intent intent) {
+        return null;
+    }
+
+    @Override
+    public int onStartCommand(Intent intent, int flags, int startId) {
+        record(intent.getStringExtra(TestLogClient.EXTRA_LOG_TAG),
+                intent.getStringExtra(TestLogClient.EXTRA_KEY),
+                intent.getStringExtra(TestLogClient.EXTRA_VALUE));
+        return START_NOT_STICKY;
+    }
+
+    /**
+     * Prepare to receive results from a client with a specified tag.
+     *
+     * @param logtag Unique tag for the client.
+     * @param stopKey The key that signals that the client has completed all required actions.
+     */
+    public static void registerClient(String logtag, String stopKey) {
+        synchronized (mLock) {
+            if (mChannels.containsKey(logtag)) {
+                throw new IllegalArgumentException(logtag);
+            }
+            mChannels.put(logtag, new ClientChannel(stopKey));
+        }
+    }
+
+    /**
+     * Wait for the client to complete all required actions and return the results.
+     *
+     * @param logtag Unique tag for the client.
+     * @param timeoutMs Latch timeout in ms.
+     * @return The map of results from the client
+     */
+    public static Map<String, String> getResultsForClient(String logtag, int timeoutMs) {
+        Map<String, String> result = new HashMap<>();
+        CountDownLatch latch;
+        synchronized (mLock) {
+            if (!mChannels.containsKey(logtag)) {
+                return result;
+            }
+            latch = mChannels.get(logtag).mLatch;
+        }
+        try {
+            latch.await(timeoutMs, TimeUnit.MILLISECONDS);
+        } catch (InterruptedException ignore) {
+        }
+        synchronized (mLock) {
+            for (Map.Entry<String, String> e : mChannels.get(logtag).mResults.entrySet()) {
+                result.put(e.getKey(), e.getValue());
+            }
+        }
+        return result;
+    }
+
+    private static void record(String logtag, String key, String value) {
+        synchronized (mLock) {
+            if (!mChannels.containsKey(logtag)) {
+                Log.e(TAG, "Unexpected logtag: " + logtag);
+                return;
+            }
+            ClientChannel channel = mChannels.get(logtag);
+            channel.mResults.put(key, value);
+            if (key.equals(channel.mStopKey)) {
+                channel.mLatch.countDown();
+            }
+        }
+    }
+}
diff --git a/tests/inputmethod/Android.mk b/tests/inputmethod/Android.mk
index 0c1ed5f..5300762 100644
--- a/tests/inputmethod/Android.mk
+++ b/tests/inputmethod/Android.mk
@@ -31,12 +31,19 @@
 LOCAL_STATIC_JAVA_LIBRARIES := \
     android-support-test \
     compatibility-device-util \
-    ctstestrunner
+    ctstestrunner \
+    CtsMockInputMethod
 
-LOCAL_SRC_FILES := $(call all-java-files-under, src)
+LOCAL_SRC_FILES := \
+    $(call all-java-files-under, src) \
+    $(call all-Iaidl-files-under, src)
+
+LOCAL_AIDL_INCLUDES += $(LOCAL_PATH)/src
 
 LOCAL_PACKAGE_NAME := CtsInputMethodTestCases
 
+LOCAL_SDK_VERSION := test_current
+
 include $(BUILD_CTS_PACKAGE)
 
 include $(call all-makefiles-under,$(LOCAL_PATH))
diff --git a/tests/inputmethod/AndroidManifest.xml b/tests/inputmethod/AndroidManifest.xml
index 11f008d..a5bc532 100644
--- a/tests/inputmethod/AndroidManifest.xml
+++ b/tests/inputmethod/AndroidManifest.xml
@@ -34,6 +34,50 @@
             </intent-filter>
         </activity>
 
+        <activity
+            android:name="android.view.inputmethod.cts.util.TestActivity"
+            android:label="TestActivity">
+            <intent-filter>
+                <action android:name="android.intent.action.MAIN" />
+                <category android:name="android.intent.category.FRAMEWORK_INSTRUMENTATION_TEST" />
+            </intent-filter>
+        </activity>
+
+        <activity
+            android:name="android.view.inputmethod.cts.util.StateInitializeActivity"
+            android:label="StateInitializeActivity">
+            <intent-filter>
+                <action android:name="android.intent.action.MAIN" />
+                <category android:name="android.intent.category.FRAMEWORK_INSTRUMENTATION_TEST" />
+            </intent-filter>
+        </activity>
+
+        <!-- In order to test typical use cases, let this MockIME run in a separate process -->
+        <!--
+          TODO: Move this sevice definition into MockIME package and let the build system to merge
+          manifests once soong supports to build aar package.
+        -->
+        <service
+            android:name="com.android.cts.mockime.MockIme"
+            android:label="Mock IME"
+            android:permission="android.permission.BIND_INPUT_METHOD"
+            android:process=":mockime">
+            <intent-filter>
+                <action android:name="android.view.InputMethod" />
+            </intent-filter>
+            <meta-data
+                android:name="android.view.im"
+                android:resource="@xml/method" />
+        </service>
+
+        <!--
+          In order to test window-focus-stealing from other process, let this service run in a
+          separate process. -->
+        <service android:name="android.view.inputmethod.cts.util.WindowFocusStealerService"
+            android:process=":focusstealer"
+            android:exported="false">
+        </service>
+
     </application>
 
     <instrumentation
diff --git a/tests/inputmethod/AndroidTest.xml b/tests/inputmethod/AndroidTest.xml
index b696a52..2cb0099 100644
--- a/tests/inputmethod/AndroidTest.xml
+++ b/tests/inputmethod/AndroidTest.xml
@@ -16,9 +16,16 @@
 -->
 
 <configuration description="Config for CTS InputMethod test cases">
+    <option name="test-suite-tag" value="cts" />
     <option name="config-descriptor:metadata" key="component" value="framework" />
     <target_preparer class="com.android.tradefed.targetprep.suite.SuiteApkInstaller">
         <option name="cleanup-apks" value="true" />
+        <!--
+            TODO(yukawa): come up with a proper way to take care of devices that do not support
+            installable IMEs.  Ideally target_preparer should have an option to annotate required
+            features, e.g. android.software.input_methods so that we can conditionally install APKs
+            based on the feature supported in the target device.
+        -->
         <option name="test-file-name" value="CtsInputMethodTestCases.apk" />
     </target_preparer>
     <test class="com.android.tradefed.testtype.AndroidJUnitTest" >
diff --git a/tests/inputmethod/mockime/Android.mk b/tests/inputmethod/mockime/Android.mk
new file mode 100644
index 0000000..10f80a6
--- /dev/null
+++ b/tests/inputmethod/mockime/Android.mk
@@ -0,0 +1,33 @@
+# 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 := CtsMockInputMethod
+LOCAL_MODULE_TAGS := tests
+
+LOCAL_SDK_VERSION := current
+
+# tag this module as a cts test artifact
+LOCAL_COMPATIBILITY_SUITE := cts vts general-tests
+
+LOCAL_SRC_FILES := $(call all-java-files-under, src)
+LOCAL_JAVA_LIBRARIES := junit
+LOCAL_STATIC_JAVA_LIBRARIES := \
+   android-support-annotations \
+   compatibility-device-util
+
+include $(BUILD_STATIC_JAVA_LIBRARY)
diff --git a/tests/inputmethod/mockime/src/com/android/cts/mockime/ImeCommand.java b/tests/inputmethod/mockime/src/com/android/cts/mockime/ImeCommand.java
new file mode 100644
index 0000000..48d4e05
--- /dev/null
+++ b/tests/inputmethod/mockime/src/com/android/cts/mockime/ImeCommand.java
@@ -0,0 +1,81 @@
+/*
+ * 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.mockime;
+
+import android.os.Bundle;
+import android.support.annotation.NonNull;
+
+public final class ImeCommand {
+
+    private static final String NAME_KEY = "name";
+    private static final String ID_KEY = "id";
+    private static final String DISPATCH_TO_MAIN_THREAD_KEY = "dispatchToMainThread";
+    private static final String EXTRA_KEY = "extra";
+
+    @NonNull
+    private final String mName;
+    private final long mId;
+    private final boolean mDispatchToMainThread;
+    @NonNull
+    private final Bundle mExtras;
+
+    ImeCommand(@NonNull String name, long id, boolean dispatchToMainThread,
+            @NonNull Bundle extras) {
+        mName = name;
+        mId = id;
+        mDispatchToMainThread = dispatchToMainThread;
+        mExtras = extras;
+    }
+
+    private ImeCommand(@NonNull Bundle bundle) {
+        mName = bundle.getString(NAME_KEY);
+        mId = bundle.getLong(ID_KEY);
+        mDispatchToMainThread = bundle.getBoolean(DISPATCH_TO_MAIN_THREAD_KEY);
+        mExtras = bundle.getParcelable(EXTRA_KEY);
+    }
+
+    static ImeCommand fromBundle(@NonNull Bundle bundle) {
+        return new ImeCommand(bundle);
+    }
+
+    Bundle toBundle() {
+        final Bundle bundle = new Bundle();
+        bundle.putString(NAME_KEY, mName);
+        bundle.putLong(ID_KEY, mId);
+        bundle.putBoolean(DISPATCH_TO_MAIN_THREAD_KEY, mDispatchToMainThread);
+        bundle.putParcelable(EXTRA_KEY, mExtras);
+        return bundle;
+    }
+
+    @NonNull
+    public String getName() {
+        return mName;
+    }
+
+    public long getId() {
+        return mId;
+    }
+
+    public boolean shouldDispatchToMainThread() {
+        return mDispatchToMainThread;
+    }
+
+    @NonNull
+    public Bundle getExtras() {
+        return mExtras;
+    }
+}
\ No newline at end of file
diff --git a/tests/inputmethod/mockime/src/com/android/cts/mockime/ImeEvent.java b/tests/inputmethod/mockime/src/com/android/cts/mockime/ImeEvent.java
new file mode 100644
index 0000000..a0dc613
--- /dev/null
+++ b/tests/inputmethod/mockime/src/com/android/cts/mockime/ImeEvent.java
@@ -0,0 +1,201 @@
+/*
+ * 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.mockime;
+
+import android.os.Bundle;
+import android.support.annotation.NonNull;
+
+/**
+ * An immutable object that stores event happened in the {@link MockIme}.
+ */
+public final class ImeEvent {
+
+    ImeEvent(@NonNull String eventName, int nestLevel, @NonNull String threadName, int threadId,
+            boolean isMainThread, long enterTimestamp, long exitTimestamp, long enterWallTime,
+            long exitWallTime, @NonNull ImeState enterState, @NonNull ImeState exitState,
+            @NonNull Bundle arguments) {
+        mEventName = eventName;
+        mNestLevel = nestLevel;
+        mThreadName = threadName;
+        mThreadId = threadId;
+        mIsMainThread = isMainThread;
+        mEnterTimestamp = enterTimestamp;
+        mExitTimestamp = exitTimestamp;
+        mEnterWallTime = enterWallTime;
+        mExitWallTime = exitWallTime;
+        mEnterState = enterState;
+        mExitState = exitState;
+        mArguments = arguments;
+    }
+
+    @NonNull
+    final Bundle toBundle() {
+        final Bundle bundle = new Bundle();
+        bundle.putString("mEventName", mEventName);
+        bundle.putInt("mNestLevel", mNestLevel);
+        bundle.putString("mThreadName", mThreadName);
+        bundle.putInt("mThreadId", mThreadId);
+        bundle.putBoolean("mIsMainThread", mIsMainThread);
+        bundle.putLong("mEnterTimestamp", mEnterTimestamp);
+        bundle.putLong("mExitTimestamp", mExitTimestamp);
+        bundle.putLong("mEnterWallTime", mEnterWallTime);
+        bundle.putLong("mExitWallTime", mExitWallTime);
+        bundle.putBundle("mEnterState", mEnterState.toBundle());
+        bundle.putBundle("mExitState", mExitState.toBundle());
+        bundle.putBundle("mArguments", mArguments);
+        return bundle;
+    }
+
+    @NonNull
+    static ImeEvent fromBundle(@NonNull Bundle bundle) {
+        final String eventName = bundle.getString("mEventName");
+        final int nestLevel = bundle.getInt("mNestLevel");
+        final String threadName = bundle.getString("mThreadName");
+        final int threadId = bundle.getInt("mThreadId");
+        final boolean isMainThread = bundle.getBoolean("mIsMainThread");
+        final long enterTimestamp = bundle.getLong("mEnterTimestamp");
+        final long exitTimestamp = bundle.getLong("mExitTimestamp");
+        final long enterWallTime = bundle.getLong("mEnterWallTime");
+        final long exitWallTime = bundle.getLong("mExitWallTime");
+        final ImeState enterState = ImeState.fromBundle(bundle.getBundle("mEnterState"));
+        final ImeState exitState = ImeState.fromBundle(bundle.getBundle("mExitState"));
+        final Bundle arguments = bundle.getBundle("mArguments");
+        return new ImeEvent(eventName, nestLevel, threadName,
+                threadId, isMainThread, enterTimestamp, exitTimestamp, enterWallTime, exitWallTime,
+                enterState, exitState, arguments);
+    }
+
+    /**
+     * Returns a string that represents the type of this event.
+     *
+     * <p>Examples: &quot;onCreate&quot;, &quot;onStartInput&quot;, ...</p>
+     *
+     * <p>TODO: Use enum type or something like that instead of raw String type.</p>
+     * @return A string that represents the type of this event.
+     */
+    @NonNull
+    public String getEventName() {
+        return mEventName;
+    }
+
+    /**
+     * Returns the nest level of this event.
+     *
+     * <p>For instance, when &quot;showSoftInput&quot; internally calls
+     * &quot;onStartInputView&quot;, the event for &quot;onStartInputView&quot; has 1 level higher
+     * nest level than &quot;showSoftInput&quot;.</p>
+     */
+    public int getNestLevel() {
+        return mNestLevel;
+    }
+
+    /**
+     * @return Name of the thread, where the event was consumed.
+     */
+    @NonNull
+    public String getThreadName() {
+        return mThreadName;
+    }
+
+    /**
+     * @return Thread ID (TID) of the thread, where the event was consumed.
+     */
+    public int getThreadId() {
+        return mThreadId;
+    }
+
+    /**
+     * @return {@code true} if the event was being consumed in the main thread.
+     */
+    public boolean isMainThread() {
+        return mIsMainThread;
+    }
+
+    /**
+     * @return Monotonic time measured by {@link android.os.SystemClock#elapsedRealtimeNanos()} when
+     *         the corresponding event handler was called back.
+     */
+    public long getEnterTimestamp() {
+        return mEnterTimestamp;
+    }
+
+    /**
+     * @return Monotonic time measured by {@link android.os.SystemClock#elapsedRealtimeNanos()} when
+     *         the corresponding event handler finished.
+     */
+    public long getExitTimestamp() {
+        return mExitTimestamp;
+    }
+
+    /**
+     * @return Wall-clock time measured by {@link System#currentTimeMillis()} when the corresponding
+     *         event handler was called back.
+     */
+    public long getEnterWallTime() {
+        return mEnterWallTime;
+    }
+
+    /**
+     * @return Wall-clock time measured by {@link System#currentTimeMillis()} when the corresponding
+     *         event handler finished.
+     */
+    public long getExitWallTime() {
+        return mExitWallTime;
+    }
+
+    /**
+     * @return IME state snapshot taken when the corresponding event handler was called back.
+     */
+    @NonNull
+    public ImeState getEnterState() {
+        return mEnterState;
+    }
+
+    /**
+     * @return IME state snapshot taken when the corresponding event handler finished.
+     */
+    @NonNull
+    public ImeState getExitState() {
+        return mExitState;
+    }
+
+    /**
+     * @return {@link Bundle} that stores parameters passed to the corresponding event handler.
+     */
+    @NonNull
+    public Bundle getArguments() {
+        return mArguments;
+    }
+
+    @NonNull
+    private final String mEventName;
+    private final int mNestLevel;
+    @NonNull
+    private final String mThreadName;
+    private final int mThreadId;
+    private final boolean mIsMainThread;
+    private final long mEnterTimestamp;
+    private final long mExitTimestamp;
+    private final long mEnterWallTime;
+    private final long mExitWallTime;
+    @NonNull
+    private final ImeState mEnterState;
+    @NonNull
+    private final ImeState mExitState;
+    @NonNull
+    private final Bundle mArguments;
+}
diff --git a/tests/inputmethod/mockime/src/com/android/cts/mockime/ImeEventStream.java b/tests/inputmethod/mockime/src/com/android/cts/mockime/ImeEventStream.java
new file mode 100644
index 0000000..7d021df
--- /dev/null
+++ b/tests/inputmethod/mockime/src/com/android/cts/mockime/ImeEventStream.java
@@ -0,0 +1,209 @@
+/*
+ * 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.mockime;
+
+import android.os.Bundle;
+import android.support.annotation.IntRange;
+import android.support.annotation.NonNull;
+import android.view.inputmethod.EditorInfo;
+
+import java.text.SimpleDateFormat;
+import java.util.Date;
+import java.util.Locale;
+import java.util.Optional;
+import java.util.function.Predicate;
+import java.util.function.Supplier;
+
+/**
+ * A utility class that provides basic query operations and wait primitives for a series of
+ * {@link ImeEvent} sent from the {@link MockIme}.
+ *
+ * <p>All public methods are not thread-safe.</p>
+ */
+public final class ImeEventStream {
+
+    @NonNull
+    private final Supplier<ImeEventArray> mEventSupplier;
+    private int mCurrentPosition;
+
+    ImeEventStream(@NonNull Supplier<ImeEventArray> supplier) {
+        this(supplier, 0 /* position */);
+    }
+
+    private ImeEventStream(@NonNull Supplier<ImeEventArray> supplier, int position) {
+        mEventSupplier = supplier;
+        mCurrentPosition = position;
+    }
+
+    /**
+     * Create a copy that starts from the same event position of this stream. Once a copy is created
+     * further event position change on this stream will not affect the copy.
+     *
+     * @return A new copy of this stream
+     */
+    public ImeEventStream copy() {
+        return new ImeEventStream(mEventSupplier, mCurrentPosition);
+    }
+
+    /**
+     * Advances the current event position by skipping events.
+     *
+     * @param length number of events to be skipped
+     * @throws IllegalArgumentException {@code length} is negative
+     */
+    public void skip(@IntRange(from = 0) int length) {
+        if (length < 0) {
+            throw new IllegalArgumentException("length cannot be negative: " + length);
+        }
+        mCurrentPosition += length;
+    }
+
+    /**
+     * Advances the current event position to the next to the last position.
+     */
+    public void skipAll() {
+        mCurrentPosition = mEventSupplier.get().mLength;
+    }
+
+    /**
+     * Find the first event that matches the given condition from the current position.
+     *
+     * <p>If there is such an event, this method returns such an event without moving the current
+     * event position.</p>
+     *
+     * <p>If there is such an event, this method returns {@link Optional#empty()} without moving the
+     * current event position.</p>
+     *
+     * @param condition the event condition to be matched
+     * @return {@link Optional#empty()} if there is no such an event. Otherwise the matched event is
+     *         returned
+     */
+    @NonNull
+    public Optional<ImeEvent> findFirst(Predicate<ImeEvent> condition) {
+        final ImeEventArray latest = mEventSupplier.get();
+        int index = mCurrentPosition;
+        while (true) {
+            if (index >= latest.mLength) {
+                return Optional.empty();
+            }
+            if (condition.test(latest.mArray[index])) {
+                return Optional.of(latest.mArray[index]);
+            }
+            ++index;
+        }
+    }
+
+    /**
+     * Find the first event that matches the given condition from the current position.
+     *
+     * <p>If there is such an event, this method returns such an event and set the current event
+     * position to that event.</p>
+     *
+     * <p>If there is such an event, this method returns {@link Optional#empty()} without moving the
+     * current event position.</p>
+     *
+     * @param condition the event condition to be matched
+     * @return {@link Optional#empty()} if there is no such an event. Otherwise the matched event is
+     *         returned
+     */
+    @NonNull
+    public Optional<ImeEvent> seekToFirst(Predicate<ImeEvent> condition) {
+        final ImeEventArray latest = mEventSupplier.get();
+        while (true) {
+            if (mCurrentPosition >= latest.mLength) {
+                return Optional.empty();
+            }
+            if (condition.test(latest.mArray[mCurrentPosition])) {
+                return Optional.of(latest.mArray[mCurrentPosition]);
+            }
+            ++mCurrentPosition;
+        }
+    }
+
+    /**
+     * @return Debug info as a {@link String}.
+     */
+    public String dump() {
+        final ImeEventArray latest = mEventSupplier.get();
+        final StringBuilder sb = new StringBuilder();
+        final SimpleDateFormat dataFormat =
+                new SimpleDateFormat("yyyy-MM-dd HH:mm:ss.SSS", Locale.US);
+        sb.append("ImeEventStream:\n");
+        sb.append("  latest: array[").append(latest.mArray.length).append("] + {\n");
+        for (int i = 0; i < latest.mLength; ++i) {
+            final ImeEvent event = latest.mArray[i];
+            if (i == mCurrentPosition) {
+                sb.append("  ======== CurrentPosition ========  \n");
+            }
+            sb.append("   ").append(i).append(" :");
+            if (event != null) {
+                for (int j = 0; j < event.getNestLevel(); ++j) {
+                    sb.append(' ');
+                }
+                sb.append('{');
+                sb.append(dataFormat.format(new Date(event.getEnterWallTime())));
+                sb.append(" event=").append(event.getEventName());
+                sb.append(": args=");
+                dumpBundle(sb, event.getArguments());
+                sb.append("},\n");
+            } else {
+                sb.append("{null},\n");
+            }
+        }
+        if (mCurrentPosition >= latest.mLength) {
+            sb.append("  ======== CurrentPosition ========  \n");
+        }
+        sb.append("}\n");
+        return sb.toString();
+    }
+
+    private static final void dumpBundle(@NonNull StringBuilder sb, @NonNull Bundle bundle) {
+        sb.append('{');
+        boolean first = true;
+        for (String key : bundle.keySet()) {
+            if (first) {
+                first = false;
+            } else {
+                sb.append(' ');
+            }
+            final Object object = bundle.get(key);
+            sb.append(key);
+            sb.append('=');
+            if (object instanceof EditorInfo) {
+                final EditorInfo info = (EditorInfo) object;
+                sb.append("EditorInfo{packageName=").append(info.packageName);
+                sb.append(" fieldId=").append(info.fieldId);
+                sb.append(" hintText=").append(info.hintText);
+                sb.append(" privateImeOptions=").append(info.privateImeOptions);
+                sb.append("}");
+            } else {
+                sb.append(object);
+            }
+        }
+        sb.append('}');
+    }
+
+    final static class ImeEventArray {
+        @NonNull
+        public final ImeEvent[] mArray;
+        public final int mLength;
+        public ImeEventArray(ImeEvent[] array, int length) {
+            mArray = array;
+            mLength = length;
+        }
+    }
+}
diff --git a/tests/inputmethod/mockime/src/com/android/cts/mockime/ImeEventStreamTestUtils.java b/tests/inputmethod/mockime/src/com/android/cts/mockime/ImeEventStreamTestUtils.java
new file mode 100644
index 0000000..5c1ef24
--- /dev/null
+++ b/tests/inputmethod/mockime/src/com/android/cts/mockime/ImeEventStreamTestUtils.java
@@ -0,0 +1,193 @@
+/*
+ * 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.mockime;
+
+import android.os.SystemClock;
+import android.support.annotation.NonNull;
+import android.text.TextUtils;
+import android.view.inputmethod.InputBinding;
+
+import java.util.Optional;
+import java.util.concurrent.TimeoutException;
+import java.util.function.Predicate;
+
+/**
+ * A set of utility methods to avoid boilerplate code when writing end-to-end tests.
+ */
+public final class ImeEventStreamTestUtils {
+    private static final long TIME_SLICE = 50;  // msec
+
+    /**
+     * Cannot be instantiated
+     */
+    private ImeEventStreamTestUtils() {}
+
+    /**
+     * Wait until an event that matches the given {@code condition} is found in the stream.
+     *
+     * <p>When this method succeeds to find an event that matches the given {@code condition}, the
+     * stream position will be set to the next to the found object then the event found is returned.
+     * </p>
+     *
+     * @param stream {@link ImeEventStream} to be checked.
+     * @param condition the event condition to be matched
+     * @param timeout timeout in millisecond
+     * @return {@link ImeEvent} found
+     * @throws TimeoutException when the no event is matched to the given condition within
+     *                          {@code timeout}
+     */
+    @NonNull
+    public static ImeEvent expectEvent(@NonNull ImeEventStream stream,
+            @NonNull Predicate<ImeEvent> condition, long timeout) throws TimeoutException {
+        try {
+            Optional<ImeEvent> result;
+            while (true) {
+                if (timeout < 0) {
+                    throw new TimeoutException(
+                            "event not found within the timeout: " + stream.dump());
+                }
+                result = stream.seekToFirst(condition);
+                if (result.isPresent()) {
+                    break;
+                }
+                Thread.sleep(TIME_SLICE);
+                timeout -= TIME_SLICE;
+            }
+            final ImeEvent event = result.get();
+            if (event == null) {
+                throw new NullPointerException("found event is null: " + stream.dump());
+            }
+            stream.skip(1);
+            return event;
+        } catch (InterruptedException e) {
+            throw new RuntimeException("expectEvent failed: " + stream.dump(), e);
+        }
+    }
+
+    /**
+     * Wait until an event that matches the given command is consumed by the {@link MockIme}.
+     *
+     * @param stream {@link ImeEventStream} to be checked.
+     * @param command {@link ImeCommand} to be waited for.
+     * @param timeout timeout in millisecond
+     * @return {@link ImeEvent} found
+     * @throws TimeoutException when the no event is matched to the given condition within
+     *                          {@code timeout}
+     */
+    @NonNull
+    public static ImeEvent expectCommand(@NonNull ImeEventStream stream,
+            @NonNull ImeCommand command, long timeout) throws TimeoutException {
+        final Predicate<ImeEvent> predicate = event -> {
+            if (!TextUtils.equals("onHandleCommand", event.getEventName())) {
+                return false;
+            }
+            final ImeCommand eventCommand =
+                    ImeCommand.fromBundle(event.getArguments().getBundle("command"));
+            return eventCommand.getId() == command.getId();
+        };
+        return expectEvent(stream, predicate, timeout);
+    }
+
+    /**
+     * Assert that an event that matches the given {@code condition} will no be found in the stream
+     * within the given {@code timeout}.
+     *
+     * <p>Fails with {@link junit.framework.Assert#fail} if such an event is  found within the given
+     * {@code timeout}.</p>
+     *
+     * <p>When this method succeeds, the stream position will not change.</p>
+     *
+     * @param stream {@link ImeEventStream} to be checked.
+     * @param condition the event condition to be matched
+     * @param timeout timeout in millisecond
+     */
+    public static void notExpectEvent(@NonNull ImeEventStream stream,
+            @NonNull Predicate<ImeEvent> condition, long timeout) {
+        try {
+            while (true) {
+                if (timeout < 0) {
+                    return;
+                }
+                if (stream.findFirst(condition).isPresent()) {
+                    throw new AssertionError("notExpectEvent failed: " + stream.dump());
+                }
+                Thread.sleep(TIME_SLICE);
+                timeout -= TIME_SLICE;
+            }
+        } catch (InterruptedException e) {
+            throw new RuntimeException("notExpectEvent failed: " + stream.dump(), e);
+        }
+    }
+
+    /**
+     * A specialized version of {@link #expectEvent(ImeEventStream, Predicate, long)} to wait for
+     * {@link android.view.inputmethod.InputMethod#bindInput(InputBinding)}.
+     *
+     * @param stream {@link ImeEventStream} to be checked.
+     * @param targetProcessPid PID to be matched to {@link InputBinding#getPid()}
+     * @param timeout timeout in millisecond
+     * @throws TimeoutException when "bindInput" is not called within {@code timeout} msec
+     */
+    public static void expectBindInput(@NonNull ImeEventStream stream, int targetProcessPid,
+            long timeout) throws TimeoutException {
+        expectEvent(stream, event -> {
+            if (!TextUtils.equals("bindInput", event.getEventName())) {
+                return false;
+            }
+            final InputBinding binding = event.getArguments().getParcelable("binding");
+            return binding.getPid() == targetProcessPid;
+        }, timeout);
+    }
+
+    /**
+     * Waits until {@code MockIme} does not send {@code "onInputViewLayoutChanged"} event
+     * for a certain period of time ({@code stableThresholdTime} msec).
+     *
+     * <p>When this returns non-null {@link ImeLayoutInfo}, the stream position will be set to
+     * the next event of the returned layout event.  Otherwise this method does not change stream
+     * position.</p>
+     * @param stream {@link ImeEventStream} to be checked.
+     * @param stableThresholdTime threshold time to consider that {@link MockIme}'s layout is
+     *                            stable, in millisecond
+     * @return last {@link ImeLayoutInfo} if {@link MockIme} sent one or more
+     *         {@code "onInputViewLayoutChanged"} event.  Otherwise {@code null}
+     */
+    public static ImeLayoutInfo waitForInputViewLayoutStable(@NonNull ImeEventStream stream,
+            long stableThresholdTime) {
+        ImeLayoutInfo lastLayout = null;
+        final Predicate<ImeEvent> layoutFilter =
+                event -> "onInputViewLayoutChanged".equals(event.getEventName());
+        try {
+            long deadline = SystemClock.elapsedRealtime() + stableThresholdTime;
+            while (true) {
+                if (deadline < SystemClock.elapsedRealtime()) {
+                    return lastLayout;
+                }
+                final Optional<ImeEvent> event = stream.seekToFirst(layoutFilter);
+                if (event.isPresent()) {
+                    // Remember the last event and extend the deadline again.
+                    lastLayout = ImeLayoutInfo.readFromBundle(event.get().getArguments());
+                    deadline = SystemClock.elapsedRealtime() + stableThresholdTime;
+                    stream.skip(1);
+                }
+                Thread.sleep(TIME_SLICE);
+            }
+        } catch (InterruptedException e) {
+            throw new RuntimeException("notExpectEvent failed: " + stream.dump(), e);
+        }
+    }
+}
diff --git a/tests/inputmethod/mockime/src/com/android/cts/mockime/ImeLayoutInfo.java b/tests/inputmethod/mockime/src/com/android/cts/mockime/ImeLayoutInfo.java
new file mode 100644
index 0000000..9c729b3
--- /dev/null
+++ b/tests/inputmethod/mockime/src/com/android/cts/mockime/ImeLayoutInfo.java
@@ -0,0 +1,197 @@
+/*
+ * 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.mockime;
+
+import android.graphics.Point;
+import android.graphics.Rect;
+import android.os.Bundle;
+import android.support.annotation.NonNull;
+import android.support.annotation.Nullable;
+import android.view.Display;
+import android.view.View;
+import android.view.WindowInsets;
+
+/**
+ * A collection of layout-related information when
+ * {@link View.OnLayoutChangeListener#onLayoutChange(View, int, int, int, int, int, int, int, int)}
+ * is called back for the input view (the view returned from {@link MockIme#onCreateInputView()}).
+ */
+public final class ImeLayoutInfo {
+
+    private final static String NEW_LAYOUT_KEY = "newLayout";
+    private final static String OLD_LAYOUT_KEY = "oldLayout";
+    private final static String VIEW_ORIGIN_ON_SCREEN_KEY = "viewOriginOnScreen";
+    private final static String DISPLAY_SIZE_KEY = "displaySize";
+    private final static String SYSTEM_WINDOW_INSET_KEY = "systemWindowInset";
+    private final static String STABLE_INSET_KEY = "stableInset";
+
+    @NonNull
+    private final Rect mNewLayout;
+    @NonNull
+    private final Rect mOldLayout;
+    @Nullable
+    private Point mViewOriginOnScreen;
+    @Nullable
+    private Point mDisplaySize;
+    @Nullable
+    private Rect mSystemWindowInset;
+    @Nullable
+    private Rect mStableInset;
+
+    /**
+     * Returns the bounding box of the {@link View} passed to
+     * {@link android.inputmethodservice.InputMethodService#onCreateInputView()} in screen
+     * coordinates.
+     *
+     * <p>Currently this method assumes that no {@link View} in the hierarchy uses
+     * transformations such as {@link View#setRotation(float)}.</p>
+     *
+     * @return Region in screen coordinates.
+     */
+    @Nullable
+    public Rect getInputViewBoundsInScreen() {
+        return new Rect(
+                mViewOriginOnScreen.x, mViewOriginOnScreen.y,
+                mViewOriginOnScreen.x + mNewLayout.width(),
+                mViewOriginOnScreen.y + mNewLayout.height());
+    }
+
+    /**
+     * Returns the screen area in screen coordinates that does not overlap with the system
+     * window inset, which represents the area of a full-screen window that is partially or
+     * fully obscured by the status bar, navigation bar, IME or other system windows.
+     *
+     * <p>May return {@code null} when this information is not yet ready.</p>
+     *
+     * @return Region in screen coordinates. {@code null} when it is not available
+     *
+     * @see WindowInsets#hasSystemWindowInsets()
+     * @see WindowInsets#getSystemWindowInsetBottom()
+     * @see WindowInsets#getSystemWindowInsetLeft()
+     * @see WindowInsets#getSystemWindowInsetRight()
+     * @see WindowInsets#getSystemWindowInsetTop()
+     */
+    @Nullable
+    public Rect getScreenRectWithoutSystemWindowInset() {
+        if (mDisplaySize == null) {
+            return null;
+        }
+        if (mSystemWindowInset == null) {
+            return new Rect(0, 0, mDisplaySize.x, mDisplaySize.y);
+        }
+        return new Rect(mSystemWindowInset.left, mSystemWindowInset.top,
+                mDisplaySize.x - mSystemWindowInset.right,
+                mDisplaySize.y - mSystemWindowInset.bottom);
+    }
+
+    /**
+     * Returns the screen area in screen coordinates that does not overlap with the stable
+     * inset, which represents the area of a full-screen window that <b>may</b> be partially or
+     * fully obscured by the system UI elements.
+     *
+     * <p>May return {@code null} when this information is not yet ready.</p>
+     *
+     * @return Region in screen coordinates. {@code null} when it is not available
+     *
+     * @see WindowInsets#hasStableInsets()
+     * @see WindowInsets#getStableInsetBottom()
+     * @see WindowInsets#getStableInsetLeft()
+     * @see WindowInsets#getStableInsetRight()
+     * @see WindowInsets#getStableInsetTop()
+     */
+    @Nullable
+    public Rect getScreenRectWithoutStableInset() {
+        if (mDisplaySize == null) {
+            return null;
+        }
+        if (mStableInset == null) {
+            return new Rect(0, 0, mDisplaySize.x, mDisplaySize.y);
+        }
+        return new Rect(mStableInset.left, mStableInset.top,
+                mDisplaySize.x - mStableInset.right,
+                mDisplaySize.y - mStableInset.bottom);
+    }
+
+    ImeLayoutInfo(@NonNull Rect newLayout, @NonNull Rect oldLayout,
+            @NonNull Point viewOriginOnScreen, @Nullable Point displaySize,
+            @Nullable Rect systemWindowInset, @Nullable Rect stableInset) {
+        mNewLayout = new Rect(newLayout);
+        mOldLayout = new Rect(oldLayout);
+        mViewOriginOnScreen = new Point(viewOriginOnScreen);
+        mDisplaySize = new Point(displaySize);
+        mSystemWindowInset = systemWindowInset;
+        mStableInset = stableInset;
+    }
+
+    void writeToBundle(@NonNull Bundle bundle) {
+        bundle.putParcelable(NEW_LAYOUT_KEY, mNewLayout);
+        bundle.putParcelable(OLD_LAYOUT_KEY, mOldLayout);
+        bundle.putParcelable(VIEW_ORIGIN_ON_SCREEN_KEY, mViewOriginOnScreen);
+        bundle.putParcelable(DISPLAY_SIZE_KEY, mDisplaySize);
+        bundle.putParcelable(SYSTEM_WINDOW_INSET_KEY, mSystemWindowInset);
+        bundle.putParcelable(STABLE_INSET_KEY, mStableInset);
+    }
+
+    static ImeLayoutInfo readFromBundle(@NonNull Bundle bundle) {
+        final Rect newLayout = bundle.getParcelable(NEW_LAYOUT_KEY);
+        final Rect oldLayout = bundle.getParcelable(OLD_LAYOUT_KEY);
+        final Point viewOrigin = bundle.getParcelable(VIEW_ORIGIN_ON_SCREEN_KEY);
+        final Point displaySize = bundle.getParcelable(DISPLAY_SIZE_KEY);
+        final Rect systemWindowInset = bundle.getParcelable(SYSTEM_WINDOW_INSET_KEY);
+        final Rect stableInset = bundle.getParcelable(STABLE_INSET_KEY);
+
+        return new ImeLayoutInfo(newLayout, oldLayout, viewOrigin, displaySize, systemWindowInset,
+                stableInset);
+    }
+
+    static ImeLayoutInfo fromLayoutListenerCallback(View v, int left, int top, int right,
+            int bottom, int oldLeft, int oldTop, int oldRight, int oldBottom) {
+        final Rect newLayout = new Rect(left, top, right, bottom);
+        final Rect oldLayout = new Rect(oldLeft, oldTop, oldRight, oldBottom);
+        final int[] viewOriginArray = new int[2];
+        v.getLocationOnScreen(viewOriginArray);
+        final Point viewOrigin = new Point(viewOriginArray[0], viewOriginArray[1]);
+        final Display display = v.getDisplay();
+        final Point displaySize;
+        if (display != null) {
+            displaySize = new Point();
+            display.getRealSize(displaySize);
+        } else {
+            displaySize = null;
+        }
+        final WindowInsets windowInsets = v.getRootWindowInsets();
+        final Rect systemWindowInset;
+        if (windowInsets != null && windowInsets.hasSystemWindowInsets()) {
+            systemWindowInset = new Rect(
+                    windowInsets.getSystemWindowInsetLeft(), windowInsets.getSystemWindowInsetTop(),
+                    windowInsets.getSystemWindowInsetRight(),
+                    windowInsets.getSystemWindowInsetBottom());
+        } else {
+            systemWindowInset = null;
+        }
+        final Rect stableInset;
+        if (windowInsets != null && windowInsets.hasStableInsets()) {
+            stableInset = new Rect(
+                    windowInsets.getStableInsetLeft(), windowInsets.getStableInsetTop(),
+                    windowInsets.getStableInsetRight(), windowInsets.getStableInsetBottom());
+        } else {
+            stableInset = null;
+        }
+        return new ImeLayoutInfo(newLayout, oldLayout, viewOrigin, displaySize, systemWindowInset,
+                stableInset);
+    }
+}
diff --git a/tests/inputmethod/mockime/src/com/android/cts/mockime/ImeSettings.java b/tests/inputmethod/mockime/src/com/android/cts/mockime/ImeSettings.java
new file mode 100644
index 0000000..7b79c26
--- /dev/null
+++ b/tests/inputmethod/mockime/src/com/android/cts/mockime/ImeSettings.java
@@ -0,0 +1,207 @@
+/*
+ * 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.mockime;
+
+import android.os.Parcel;
+import android.os.PersistableBundle;
+import android.support.annotation.ColorInt;
+import android.support.annotation.NonNull;
+import android.support.annotation.Nullable;
+
+/**
+ * An immutable data store to control the behavior of {@link MockIme}.
+ */
+public class ImeSettings {
+
+    @NonNull
+    private final String mEventCallbackActionName;
+
+    private static final String BACKGROUND_COLOR_KEY = "BackgroundColor";
+    private static final String NAVIGATION_BAR_COLOR_KEY = "NavigationBarColor";
+    private static final String INPUT_VIEW_HEIGHT_WITHOUT_SYSTEM_WINDOW_INSET =
+            "InputViewHeightWithoutSystemWindowInset";
+    private static final String WINDOW_FLAGS = "WindowFlags";
+    private static final String WINDOW_FLAGS_MASK = "WindowFlagsMask";
+    private static final String FULLSCREEN_MODE_ALLOWED = "FullscreenModeAllowed";
+    private static final String INPUT_VIEW_SYSTEM_UI_VISIBILITY = "InputViewSystemUiVisibility";
+    private static final String HARD_KEYBOARD_CONFIGURATION_BEHAVIOR_ALLOWED =
+            "HardKeyboardConfigurationBehaviorAllowed";
+
+    @NonNull
+    private final PersistableBundle mBundle;
+
+    ImeSettings(@NonNull Parcel parcel) {
+        mEventCallbackActionName = parcel.readString();
+        mBundle = parcel.readPersistableBundle();
+    }
+
+    @Nullable
+    String getEventCallbackActionName() {
+        return mEventCallbackActionName;
+    }
+
+    public boolean fullscreenModeAllowed(boolean defaultValue) {
+        return mBundle.getBoolean(FULLSCREEN_MODE_ALLOWED, defaultValue);
+    }
+
+    @ColorInt
+    public int getBackgroundColor(@ColorInt int defaultColor) {
+        return mBundle.getInt(BACKGROUND_COLOR_KEY, defaultColor);
+    }
+
+    public boolean hasNavigationBarColor() {
+        return mBundle.keySet().contains(NAVIGATION_BAR_COLOR_KEY);
+    }
+
+    @ColorInt
+    public int getNavigationBarColor() {
+        return mBundle.getInt(NAVIGATION_BAR_COLOR_KEY);
+    }
+
+    public int getInputViewHeightWithoutSystemWindowInset(int defaultHeight) {
+        return mBundle.getInt(INPUT_VIEW_HEIGHT_WITHOUT_SYSTEM_WINDOW_INSET, defaultHeight);
+    }
+
+    public int getWindowFlags(int defaultFlags) {
+        return mBundle.getInt(WINDOW_FLAGS, defaultFlags);
+    }
+
+    public int getWindowFlagsMask(int defaultFlags) {
+        return mBundle.getInt(WINDOW_FLAGS_MASK, defaultFlags);
+    }
+
+    public int getInputViewSystemUiVisibility(int defaultFlags) {
+        return mBundle.getInt(INPUT_VIEW_SYSTEM_UI_VISIBILITY, defaultFlags);
+    }
+
+    public boolean getHardKeyboardConfigurationBehaviorAllowed(boolean defaultValue) {
+        return mBundle.getBoolean(HARD_KEYBOARD_CONFIGURATION_BEHAVIOR_ALLOWED, defaultValue);
+    }
+
+    static void writeToParcel(@NonNull Parcel parcel, @NonNull String eventCallbackActionName,
+            @Nullable Builder builder) {
+        parcel.writeString(eventCallbackActionName);
+        if (builder != null) {
+            parcel.writePersistableBundle(builder.mBundle);
+        } else {
+            parcel.writePersistableBundle(PersistableBundle.EMPTY);
+        }
+    }
+
+    /**
+     * The builder class for {@link ImeSettings}.
+     */
+    public static final class Builder {
+        private final PersistableBundle mBundle = new PersistableBundle();
+
+        /**
+         * Controls whether fullscreen mode is allowed or not.
+         *
+         * <p>By default, fullscreen mode is not allowed in {@link MockIme}.</p>
+         *
+         * @param allowed {@code true} if fullscreen mode is allowed
+         * @see MockIme#onEvaluateFullscreenMode()
+         */
+        public Builder setFullscreenModeAllowed(boolean allowed) {
+            mBundle.putBoolean(FULLSCREEN_MODE_ALLOWED, allowed);
+            return this;
+        }
+
+        /**
+         * Sets the background color of the {@link MockIme}.
+         * @param color background color to be used
+         */
+        public Builder setBackgroundColor(@ColorInt int color) {
+            mBundle.putInt(BACKGROUND_COLOR_KEY, color);
+            return this;
+        }
+
+        /**
+         * Sets the color to be passed to {@link android.view.Window#setNavigationBarColor(int)}.
+         *
+         * @param color color to be passed to {@link android.view.Window#setNavigationBarColor(int)}
+         * @see android.view.View
+         */
+        public Builder setNavigationBarColor(@ColorInt int color) {
+            mBundle.putInt(NAVIGATION_BAR_COLOR_KEY, color);
+            return this;
+        }
+
+        /**
+         * Sets the input view height measured from the bottom system window inset.
+         * @param height height of the soft input view. This does not include the system window
+         *               inset such as navigation bar
+         */
+        public Builder setInputViewHeightWithoutSystemWindowInset(int height) {
+            mBundle.putInt(INPUT_VIEW_HEIGHT_WITHOUT_SYSTEM_WINDOW_INSET, height);
+            return this;
+        }
+
+        /**
+         * Sets window flags to be specified to {@link android.view.Window#setFlags(int, int)} of
+         * the main {@link MockIme} window.
+         *
+         * <p>When {@link android.view.WindowManager.LayoutParams#FLAG_LAYOUT_IN_OVERSCAN} is set,
+         * {@link MockIme} tries to render the navigation bar by itself.</p>
+         *
+         * @param flags flags to be specified
+         * @param flagsMask mask bits that specify what bits need to be cleared before setting
+         *                  {@code flags}
+         * @see android.view.WindowManager
+         */
+        public Builder setWindowFlags(int flags, int flagsMask) {
+            mBundle.putInt(WINDOW_FLAGS, flags);
+            mBundle.putInt(WINDOW_FLAGS_MASK, flagsMask);
+            return this;
+        }
+
+        /**
+         * Sets flags to be specified to {@link android.view.View#setSystemUiVisibility(int)} of
+         * the main soft input view (the returned view from {@link MockIme#onCreateInputView()}).
+         *
+         * @param visibilityFlags flags to be specified
+         * @see android.view.View
+         */
+        public Builder setInputViewSystemUiVisibility(int visibilityFlags) {
+            mBundle.putInt(INPUT_VIEW_SYSTEM_UI_VISIBILITY, visibilityFlags);
+            return this;
+        }
+
+        /**
+         * Controls whether {@link MockIme} is allowed to change the behavior based on
+         * {@link android.content.res.Configuration#keyboard} and
+         * {@link android.content.res.Configuration#hardKeyboardHidden}.
+         *
+         * <p>Methods in {@link android.inputmethodservice.InputMethodService} such as
+         * {@link android.inputmethodservice.InputMethodService#onEvaluateInputViewShown()} and
+         * {@link android.inputmethodservice.InputMethodService#onShowInputRequested(int, boolean)}
+         * change their behaviors when a hardware keyboard is attached.  This is confusing when
+         * writing tests so by default {@link MockIme} tries to cancel those behaviors.  This
+         * settings re-enables such a behavior.</p>
+         *
+         * @param allowed {@code true} when {@link MockIme} is allowed to change the behavior when
+         *                a hardware keyboard is attached
+         *
+         * @see android.inputmethodservice.InputMethodService#onEvaluateInputViewShown()
+         * @see android.inputmethodservice.InputMethodService#onShowInputRequested(int, boolean)
+         */
+        public Builder setHardKeyboardConfigurationBehaviorAllowed(boolean allowed) {
+            mBundle.putBoolean(HARD_KEYBOARD_CONFIGURATION_BEHAVIOR_ALLOWED, allowed);
+            return this;
+        }
+    }
+}
diff --git a/tests/inputmethod/mockime/src/com/android/cts/mockime/ImeState.java b/tests/inputmethod/mockime/src/com/android/cts/mockime/ImeState.java
new file mode 100644
index 0000000..0b7fd04
--- /dev/null
+++ b/tests/inputmethod/mockime/src/com/android/cts/mockime/ImeState.java
@@ -0,0 +1,60 @@
+/*
+ * 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.mockime;
+
+import android.os.Bundle;
+import android.support.annotation.NonNull;
+
+/**
+ * An immutable object that stores several runtime state of {@link MockIme}.
+ */
+public final class ImeState {
+    private final boolean mHasInputBinding;
+    private final boolean mHasDummyInputConnection;
+
+    /**
+     * @return {@code true} if {@link MockIme#getCurrentInputBinding()} returned non-null
+     *         {@link android.view.inputmethod.InputBinding} when this snapshot was taken.
+     */
+    public boolean hasInputBinding() { return mHasInputBinding; }
+
+    /**
+     * @return {@code true} if {@link MockIme#getCurrentInputConnection()} returned non-dummy
+     *         {@link android.view.inputmethod.InputConnection} when this snapshot was taken.
+     */
+    public boolean hasDummyInputConnection() { return mHasDummyInputConnection; }
+
+    ImeState(boolean hasInputBinding, boolean hasDummyInputConnection) {
+        mHasInputBinding = hasInputBinding;
+        mHasDummyInputConnection = hasDummyInputConnection;
+    }
+
+    @NonNull
+    final Bundle toBundle() {
+        final Bundle bundle = new Bundle();
+        bundle.putBoolean("mHasInputBinding", mHasInputBinding);
+        bundle.putBoolean("mHasDummyInputConnection", mHasDummyInputConnection);
+        return bundle;
+    }
+
+    @NonNull
+    static ImeState fromBundle(@NonNull Bundle bundle) {
+        final boolean hasInputBinding = bundle.getBoolean("mHasInputBinding");
+        final boolean hasDummyInputConnection = bundle.getBoolean("mHasDummyInputConnection");
+        return new ImeState(hasInputBinding, hasDummyInputConnection);
+    }
+}
diff --git a/tests/inputmethod/mockime/src/com/android/cts/mockime/MockIme.java b/tests/inputmethod/mockime/src/com/android/cts/mockime/MockIme.java
new file mode 100644
index 0000000..79c35f1
--- /dev/null
+++ b/tests/inputmethod/mockime/src/com/android/cts/mockime/MockIme.java
@@ -0,0 +1,652 @@
+/*
+ * 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.mockime;
+
+import static android.view.WindowManager.LayoutParams.FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS;
+
+import static com.android.cts.mockime.MockImeSession.MOCK_IME_SETTINGS_FILE;
+
+import android.content.BroadcastReceiver;
+import android.content.ComponentName;
+import android.content.Context;
+import android.content.Intent;
+import android.content.IntentFilter;
+import android.content.res.Configuration;
+import android.inputmethodservice.InputMethodService;
+import android.os.Bundle;
+import android.os.Handler;
+import android.os.HandlerThread;
+import android.os.IBinder;
+import android.os.Looper;
+import android.os.Parcel;
+import android.os.Process;
+import android.os.ResultReceiver;
+import android.os.SystemClock;
+import android.support.annotation.AnyThread;
+import android.support.annotation.CallSuper;
+import android.support.annotation.NonNull;
+import android.support.annotation.Nullable;
+import android.support.annotation.WorkerThread;
+import android.text.TextUtils;
+import android.util.Log;
+import android.util.TypedValue;
+import android.view.Gravity;
+import android.view.View;
+import android.view.Window;
+import android.view.WindowInsets;
+import android.view.inputmethod.EditorInfo;
+import android.view.inputmethod.InputBinding;
+import android.view.inputmethod.InputMethod;
+import android.widget.LinearLayout;
+import android.widget.RelativeLayout;
+import android.widget.TextView;
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.util.concurrent.atomic.AtomicReference;
+import java.util.function.BooleanSupplier;
+import java.util.function.Consumer;
+import java.util.function.Supplier;
+
+/**
+ * Mock IME for end-to-end tests.
+ */
+public final class MockIme extends InputMethodService {
+
+    private static final String TAG = "MockIme";
+
+    static ComponentName getComponentName(@NonNull String packageName) {
+        return new ComponentName(packageName, MockIme.class.getName());
+    }
+
+    static String getImeId(@NonNull String packageName) {
+        return new ComponentName(packageName, MockIme.class.getName()).flattenToShortString();
+    }
+
+    static String getCommandActionName(@NonNull String eventActionName) {
+        return eventActionName + ".command";
+    }
+
+    private final HandlerThread mHandlerThread = new HandlerThread("CommandReceiver");
+
+    private final Handler mMainHandler = new Handler();
+
+    private static final class CommandReceiver extends BroadcastReceiver {
+        @NonNull
+        private final String mActionName;
+        @NonNull
+        private final Consumer<ImeCommand> mOnReceiveCommand;
+
+        public CommandReceiver(@NonNull String actionName,
+                @NonNull Consumer<ImeCommand> onReceiveCommand) {
+            mActionName = actionName;
+            mOnReceiveCommand = onReceiveCommand;
+        }
+
+        @Override
+        public void onReceive(Context context, Intent intent) {
+            if (TextUtils.equals(mActionName, intent.getAction())) {
+                mOnReceiveCommand.accept(ImeCommand.fromBundle(intent.getExtras()));
+            }
+        }
+    }
+
+    @WorkerThread
+    private void onReceiveCommand(@NonNull ImeCommand command) {
+        getTracer().onReceiveCommand(command, () -> {
+            if (command.shouldDispatchToMainThread()) {
+                mMainHandler.post(() -> onHandleCommand(command));
+            } else {
+                onHandleCommand(command);
+            }
+        });
+    }
+
+    @AnyThread
+    private void onHandleCommand(@NonNull ImeCommand command) {
+        getTracer().onHandleCommand(command, () -> {
+            if (command.shouldDispatchToMainThread()) {
+                if (Looper.myLooper() != Looper.getMainLooper()) {
+                    throw new IllegalStateException("command " + command
+                            + " should be handled on the main thread");
+                }
+                switch (command.getName()) {
+                    case "commitText": {
+                        final CharSequence text = command.getExtras().getString("text");
+                        final int newCursorPosition =
+                                command.getExtras().getInt("newCursorPosition");
+                        getCurrentInputConnection().commitText(text, newCursorPosition);
+                        break;
+                    }
+                }
+            }
+        });
+    }
+
+    @Nullable
+    private CommandReceiver mCommandReceiver;
+
+    @Nullable
+    private ImeSettings mSettings;
+
+    private final AtomicReference<String> mImeEventActionName = new AtomicReference<>();
+
+    @Nullable
+    String getImeEventActionName() {
+        return mImeEventActionName.get();
+    }
+
+    private class MockInputMethodImpl extends InputMethodImpl {
+        @Override
+        public void showSoftInput(int flags, ResultReceiver resultReceiver) {
+            getTracer().showSoftInput(flags, resultReceiver,
+                    () -> super.showSoftInput(flags, resultReceiver));
+        }
+
+        @Override
+        public void hideSoftInput(int flags, ResultReceiver resultReceiver) {
+            getTracer().hideSoftInput(flags, resultReceiver,
+                    () -> super.hideSoftInput(flags, resultReceiver));
+        }
+
+        @Override
+        public void attachToken(IBinder token) {
+            getTracer().attachToken(token, () -> super.attachToken(token));
+        }
+
+        @Override
+        public void bindInput(InputBinding binding) {
+            getTracer().bindInput(binding, () -> super.bindInput(binding));
+        }
+
+        @Override
+        public void unbindInput() {
+            getTracer().unbindInput(() -> super.unbindInput());
+        }
+    }
+
+    @Nullable
+    private ImeSettings readSettings() {
+        try (InputStream is = openFileInput(MOCK_IME_SETTINGS_FILE)) {
+            Parcel parcel = null;
+            try {
+                parcel = Parcel.obtain();
+                final byte[] buffer = new byte[4096];
+                while (true) {
+                    final int numRead = is.read(buffer);
+                    if (numRead <= 0) {
+                        break;
+                    }
+                    parcel.unmarshall(buffer, 0, numRead);
+                }
+                parcel.setDataPosition(0);
+                return new ImeSettings(parcel);
+            } finally {
+                if (parcel != null) {
+                    parcel.recycle();
+                }
+            }
+        } catch (IOException e) {
+        }
+        return null;
+    }
+
+    @Override
+    public void onCreate() {
+        getTracer().onCreate(() -> {
+            super.onCreate();
+            mSettings = readSettings();
+            if (mSettings == null) {
+                throw new IllegalStateException("Settings file is not found. "
+                        + "Make sure MockImeSession.create() is used to launch Mock IME.");
+            }
+
+            mHandlerThread.start();
+            final String actionName = getCommandActionName(mSettings.getEventCallbackActionName());
+            mCommandReceiver = new CommandReceiver(actionName, this::onReceiveCommand);
+            registerReceiver(mCommandReceiver,
+                    new IntentFilter(actionName), null /* broadcastPermission */,
+                    new Handler(mHandlerThread.getLooper()));
+
+            mImeEventActionName.set(mSettings.getEventCallbackActionName());
+            final int windowFlags = mSettings.getWindowFlags(0);
+            final int windowFlagsMask = mSettings.getWindowFlagsMask(0);
+            if (windowFlags != 0 || windowFlagsMask != 0) {
+                final int prevFlags = getWindow().getWindow().getAttributes().flags;
+                getWindow().getWindow().setFlags(windowFlags, windowFlagsMask);
+                // For some reasons, seems that we need to post another requestLayout() when
+                // FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS bit is changed.
+                // TODO: Investigate the reason.
+                if ((windowFlagsMask & FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS) != 0) {
+                    final boolean hadFlag = (prevFlags & FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS) != 0;
+                    final boolean hasFlag = (windowFlags & FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS) != 0;
+                    if (hadFlag != hasFlag) {
+                        final View decorView = getWindow().getWindow().getDecorView();
+                        decorView.post(() -> decorView.requestLayout());
+                    }
+                }
+            }
+
+            if (mSettings.hasNavigationBarColor()) {
+                getWindow().getWindow().setNavigationBarColor(mSettings.getNavigationBarColor());
+            }
+        });
+    }
+
+    @Override
+    public void onConfigureWindow(Window win, boolean isFullscreen, boolean isCandidatesOnly) {
+        getTracer().onConfigureWindow(win, isFullscreen, isCandidatesOnly,
+                () -> super.onConfigureWindow(win, isFullscreen, isCandidatesOnly));
+    }
+
+    @Override
+    public boolean onEvaluateFullscreenMode() {
+        return getTracer().onEvaluateFullscreenMode(() ->
+                mSettings.fullscreenModeAllowed(false) && super.onEvaluateFullscreenMode());
+    }
+
+    private static final class KeyboardLayoutView extends LinearLayout {
+        @NonNull
+        private final ImeSettings mSettings;
+        @NonNull
+        private final View.OnLayoutChangeListener mLayoutListener;
+
+        public KeyboardLayoutView(Context context, @NonNull ImeSettings imeSettings,
+                @Nullable Consumer<ImeLayoutInfo> onInputViewLayoutChangedCallback) {
+            super(context);
+
+            mSettings = imeSettings;
+
+            setOrientation(VERTICAL);
+
+            final int defaultBackgroundColor =
+                    getResources().getColor(android.R.color.holo_orange_dark, null);
+            setBackgroundColor(mSettings.getBackgroundColor(defaultBackgroundColor));
+
+            final int mainSpacerHeight = mSettings.getInputViewHeightWithoutSystemWindowInset(
+                    LayoutParams.WRAP_CONTENT);
+            {
+                final RelativeLayout layout = new RelativeLayout(getContext());
+                final TextView textView = new TextView(getContext());
+                final RelativeLayout.LayoutParams params = new RelativeLayout.LayoutParams(
+                        RelativeLayout.LayoutParams.MATCH_PARENT,
+                        RelativeLayout.LayoutParams.WRAP_CONTENT);
+                params.addRule(RelativeLayout.CENTER_IN_PARENT, RelativeLayout.TRUE);
+                textView.setLayoutParams(params);
+                textView.setTextSize(TypedValue.COMPLEX_UNIT_SP, 20);
+                textView.setGravity(Gravity.CENTER);
+                textView.setText(getImeId(getContext().getPackageName()));
+                layout.addView(textView);
+                addView(layout, LayoutParams.MATCH_PARENT, mainSpacerHeight);
+            }
+
+            final int systemUiVisibility = mSettings.getInputViewSystemUiVisibility(0);
+            if (systemUiVisibility != 0) {
+                setSystemUiVisibility(systemUiVisibility);
+            }
+
+            mLayoutListener = (View v, int left, int top, int right, int bottom, int oldLeft,
+                    int oldTop, int oldRight, int oldBottom) ->
+                    onInputViewLayoutChangedCallback.accept(ImeLayoutInfo.fromLayoutListenerCallback(
+                            v, left, top, right, bottom, oldLeft, oldTop, oldRight, oldBottom));
+            this.addOnLayoutChangeListener(mLayoutListener);
+        }
+
+        private void updateBottomPaddingIfNecessary(int newPaddingBottom) {
+            if (getPaddingBottom() != newPaddingBottom) {
+                setPadding(getPaddingLeft(), getPaddingTop(), getPaddingRight(), newPaddingBottom);
+            }
+        }
+
+        @Override
+        public WindowInsets onApplyWindowInsets(WindowInsets insets) {
+            if (insets.isConsumed()
+                    || (getSystemUiVisibility() & SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION) == 0) {
+                // In this case we are not interested in consuming NavBar region.
+                // Make sure that the bottom padding is empty.
+                updateBottomPaddingIfNecessary(0);
+                return insets;
+            }
+
+            // In some cases the bottom system window inset is not a navigation bar. Wear devices
+            // that have bottom chin are examples.  For now, assume that it's a navigation bar if it
+            // has the same height as the root window's stable bottom inset.
+            final WindowInsets rootWindowInsets = getRootWindowInsets();
+            if (rootWindowInsets != null && (rootWindowInsets.getStableInsetBottom() !=
+                    insets.getSystemWindowInsetBottom())) {
+                // This is probably not a NavBar.
+                updateBottomPaddingIfNecessary(0);
+                return insets;
+            }
+
+            final int possibleNavBarHeight = insets.getSystemWindowInsetBottom();
+            updateBottomPaddingIfNecessary(possibleNavBarHeight);
+            return possibleNavBarHeight <= 0
+                    ? insets
+                    : insets.replaceSystemWindowInsets(
+                            insets.getSystemWindowInsetLeft(),
+                            insets.getSystemWindowInsetTop(),
+                            insets.getSystemWindowInsetRight(),
+                            0 /* bottom */);
+        }
+
+        @Override
+        protected void onDetachedFromWindow() {
+            super.onDetachedFromWindow();
+            removeOnLayoutChangeListener(mLayoutListener);
+        }
+    }
+
+    private void onInputViewLayoutChanged(@NonNull ImeLayoutInfo layoutInfo) {
+        getTracer().onInputViewLayoutChanged(layoutInfo, () -> {});
+    }
+
+    @Override
+    public View onCreateInputView() {
+        return getTracer().onCreateInputView(() ->
+                new KeyboardLayoutView(this, mSettings, this::onInputViewLayoutChanged));
+    }
+
+    @Override
+    public void onStartInput(EditorInfo editorInfo, boolean restarting) {
+        getTracer().onStartInput(editorInfo, restarting,
+                () -> super.onStartInput(editorInfo, restarting));
+    }
+
+    @Override
+    public void onStartInputView(EditorInfo editorInfo, boolean restarting) {
+        getTracer().onStartInputView(editorInfo, restarting,
+                () -> super.onStartInputView(editorInfo, restarting));
+    }
+
+    @Override
+    public void onFinishInputView(boolean finishingInput) {
+        getTracer().onFinishInputView(finishingInput,
+                () -> super.onFinishInputView(finishingInput));
+    }
+
+    @Override
+    public void onFinishInput() {
+        getTracer().onFinishInput(() -> super.onFinishInput());
+    }
+
+    @CallSuper
+    public boolean onEvaluateInputViewShown() {
+        return getTracer().onEvaluateInputViewShown(() -> {
+            // onShowInputRequested() is indeed @CallSuper so we always call this, even when the
+            // result is ignored.
+            final boolean originalResult = super.onEvaluateInputViewShown();
+            if (!mSettings.getHardKeyboardConfigurationBehaviorAllowed(false)) {
+                final Configuration config = getResources().getConfiguration();
+                if (config.keyboard != Configuration.KEYBOARD_NOKEYS
+                        && config.hardKeyboardHidden != Configuration.HARDKEYBOARDHIDDEN_YES) {
+                    // Override the behavior of InputMethodService#onEvaluateInputViewShown()
+                    return true;
+                }
+            }
+            return originalResult;
+        });
+    }
+
+    @Override
+    public boolean onShowInputRequested(int flags, boolean configChange) {
+        return getTracer().onShowInputRequested(flags, configChange, () -> {
+            // onShowInputRequested() is not marked with @CallSuper, but just in case.
+            final boolean originalResult = super.onShowInputRequested(flags, configChange);
+            if (!mSettings.getHardKeyboardConfigurationBehaviorAllowed(false)) {
+                if ((flags & InputMethod.SHOW_EXPLICIT) == 0
+                        && getResources().getConfiguration().keyboard
+                        != Configuration.KEYBOARD_NOKEYS) {
+                    // Override the behavior of InputMethodService#onShowInputRequested()
+                    return true;
+                }
+            }
+            return originalResult;
+        });
+    }
+
+    @Override
+    public void onDestroy() {
+        getTracer().onDestroy(() -> {
+            super.onDestroy();
+            unregisterReceiver(mCommandReceiver);
+            mHandlerThread.quitSafely();
+        });
+    }
+
+    @Override
+    public AbstractInputMethodImpl onCreateInputMethodInterface() {
+        return getTracer().onCreateInputMethodInterface(() -> new MockInputMethodImpl());
+    }
+
+    private final ThreadLocal<Tracer> mThreadLocalTracer = new ThreadLocal<>();
+
+    private Tracer getTracer() {
+        Tracer tracer = mThreadLocalTracer.get();
+        if (tracer == null) {
+            tracer = new Tracer(this);
+            mThreadLocalTracer.set(tracer);
+        }
+        return tracer;
+    }
+
+    @NonNull
+    private ImeState getState() {
+        final boolean hasInputBinding = getCurrentInputBinding() != null;
+        final boolean hasDummyInputConnectionConnection =
+                !hasInputBinding
+                        || getCurrentInputConnection() == getCurrentInputBinding().getConnection();
+        return new ImeState(hasInputBinding, hasDummyInputConnectionConnection);
+    }
+
+    /**
+     * Event tracing helper class for {@link MockIme}.
+     */
+    private static final class Tracer {
+
+        @NonNull
+        private final MockIme mIme;
+
+        private final int mThreadId = Process.myTid();
+
+        @NonNull
+        private final String mThreadName =
+                Thread.currentThread().getName() != null ? Thread.currentThread().getName() : "";
+
+        private final boolean mIsMainThread =
+                Looper.getMainLooper().getThread() == Thread.currentThread();
+
+        private int mNestLevel = 0;
+
+        private String mImeEventActionName;
+
+        public Tracer(@NonNull MockIme mockIme) {
+            mIme = mockIme;
+        }
+
+        private void sendEventInternal(@NonNull ImeEvent event) {
+            final Intent intent = new Intent();
+            intent.setPackage(mIme.getPackageName());
+            if (mImeEventActionName == null) {
+                mImeEventActionName = mIme.getImeEventActionName();
+            }
+            if (mImeEventActionName == null) {
+                Log.e(TAG, "Tracer cannot be used before onCreate()");
+                return;
+            }
+            intent.setAction(mImeEventActionName);
+            intent.putExtras(event.toBundle());
+            mIme.sendBroadcast(intent);
+        }
+
+        private void recordEventInternal(@NonNull String eventName, @NonNull Runnable runnable) {
+            recordEventInternal(eventName, runnable, new Bundle());
+        }
+
+        private void recordEventInternal(@NonNull String eventName, @NonNull Runnable runnable,
+                @NonNull Bundle arguments) {
+            recordEventInternal(eventName, () -> { runnable.run(); return null; }, arguments);
+        }
+
+        private <T> T recordEventInternal(@NonNull String eventName,
+                @NonNull Supplier<T> supplier) {
+            return recordEventInternal(eventName, supplier, new Bundle());
+        }
+
+        private <T> T recordEventInternal(@NonNull String eventName,
+                @NonNull Supplier<T> supplier, @NonNull Bundle arguments) {
+            final ImeState enterState = mIme.getState();
+            final long enterTimestamp = SystemClock.elapsedRealtimeNanos();
+            final long enterWallTime = System.currentTimeMillis();
+            final int nestLevel = mNestLevel;
+            ++mNestLevel;
+            T result;
+            try {
+                result = supplier.get();
+            } finally {
+                --mNestLevel;
+            }
+            final long exitTimestamp = SystemClock.elapsedRealtimeNanos();
+            final long exitWallTime = System.currentTimeMillis();
+            final ImeState exitState = mIme.getState();
+            sendEventInternal(new ImeEvent(eventName, nestLevel, mThreadName,
+                    mThreadId, mIsMainThread, enterTimestamp, exitTimestamp, enterWallTime,
+                    exitWallTime, enterState, exitState, arguments));
+            return result;
+        }
+
+        public void onCreate(@NonNull Runnable runnable) {
+            recordEventInternal("onCreate", runnable);
+        }
+
+        public void onConfigureWindow(Window win, boolean isFullscreen,
+                boolean isCandidatesOnly, @NonNull Runnable runnable) {
+            final Bundle arguments = new Bundle();
+            arguments.putBoolean("isFullscreen", isFullscreen);
+            arguments.putBoolean("isCandidatesOnly", isCandidatesOnly);
+            recordEventInternal("onConfigureWindow", runnable, arguments);
+        }
+
+        public boolean onEvaluateFullscreenMode(@NonNull BooleanSupplier supplier) {
+            return recordEventInternal("onEvaluateFullscreenMode", supplier::getAsBoolean);
+        }
+
+        public boolean onEvaluateInputViewShown(@NonNull BooleanSupplier supplier) {
+            return recordEventInternal("onEvaluateInputViewShown", supplier::getAsBoolean);
+        }
+
+        public View onCreateInputView(@NonNull Supplier<View> supplier) {
+            return recordEventInternal("onCreateInputView", supplier);
+        }
+
+        public void onStartInput(EditorInfo editorInfo, boolean restarting,
+                @NonNull Runnable runnable) {
+            final Bundle arguments = new Bundle();
+            arguments.putParcelable("editorInfo", editorInfo);
+            arguments.putBoolean("restarting", restarting);
+            recordEventInternal("onStartInput", runnable, arguments);
+        }
+
+        public void onStartInputView(EditorInfo editorInfo, boolean restarting,
+                @NonNull Runnable runnable) {
+            final Bundle arguments = new Bundle();
+            arguments.putParcelable("editorInfo", editorInfo);
+            arguments.putBoolean("restarting", restarting);
+            recordEventInternal("onStartInputView", runnable, arguments);
+        }
+
+        public void onFinishInputView(boolean finishingInput, @NonNull Runnable runnable) {
+            final Bundle arguments = new Bundle();
+            arguments.putBoolean("finishingInput", finishingInput);
+            recordEventInternal("onFinishInputView", runnable, arguments);
+        }
+
+        public void onFinishInput(@NonNull Runnable runnable) {
+            recordEventInternal("onFinishInput", runnable);
+        }
+
+        public boolean onShowInputRequested(int flags, boolean configChange,
+                @NonNull BooleanSupplier supplier) {
+            final Bundle arguments = new Bundle();
+            arguments.putInt("flags", flags);
+            arguments.putBoolean("configChange", configChange);
+            return recordEventInternal("onShowInputRequested", supplier::getAsBoolean, arguments);
+        }
+
+        public void onDestroy(@NonNull Runnable runnable) {
+            recordEventInternal("onDestroy", runnable);
+        }
+
+        public void attachToken(IBinder token, @NonNull Runnable runnable) {
+            final Bundle arguments = new Bundle();
+            arguments.putBinder("token", token);
+            recordEventInternal("attachToken", runnable, arguments);
+        }
+
+        public void bindInput(InputBinding binding, @NonNull Runnable runnable) {
+            final Bundle arguments = new Bundle();
+            arguments.putParcelable("binding", binding);
+            recordEventInternal("bindInput", runnable, arguments);
+        }
+
+        public void unbindInput(@NonNull Runnable runnable) {
+            recordEventInternal("unbindInput", runnable);
+        }
+
+        public void showSoftInput(int flags, ResultReceiver resultReceiver,
+                @NonNull Runnable runnable) {
+            final Bundle arguments = new Bundle();
+            arguments.putInt("flags", flags);
+            arguments.putParcelable("resultReceiver", resultReceiver);
+            recordEventInternal("showSoftInput", runnable, arguments);
+        }
+
+        public void hideSoftInput(int flags, ResultReceiver resultReceiver,
+                @NonNull Runnable runnable) {
+            final Bundle arguments = new Bundle();
+            arguments.putInt("flags", flags);
+            arguments.putParcelable("resultReceiver", resultReceiver);
+            recordEventInternal("hideSoftInput", runnable, arguments);
+        }
+
+        public AbstractInputMethodImpl onCreateInputMethodInterface(
+                @NonNull Supplier<AbstractInputMethodImpl> supplier) {
+            return recordEventInternal("onCreateInputMethodInterface", supplier);
+        }
+
+        public void onReceiveCommand(
+                @NonNull ImeCommand command, @NonNull Runnable runnable) {
+            final Bundle arguments = new Bundle();
+            arguments.putBundle("command", command.toBundle());
+            recordEventInternal("onReceiveCommand", runnable, arguments);
+        }
+
+        public void onHandleCommand(
+                @NonNull ImeCommand command, @NonNull Runnable runnable) {
+            final Bundle arguments = new Bundle();
+            arguments.putBundle("command", command.toBundle());
+            recordEventInternal("onHandleCommand", runnable, arguments);
+        }
+
+        public void onInputViewLayoutChanged(@NonNull ImeLayoutInfo imeLayoutInfo,
+                @NonNull Runnable runnable) {
+            final Bundle arguments = new Bundle();
+            imeLayoutInfo.writeToBundle(arguments);
+            recordEventInternal("onInputViewLayoutChanged", runnable, arguments);
+        }
+    }
+}
diff --git a/tests/inputmethod/mockime/src/com/android/cts/mockime/MockImeSession.java b/tests/inputmethod/mockime/src/com/android/cts/mockime/MockImeSession.java
new file mode 100644
index 0000000..4fe9f5a
--- /dev/null
+++ b/tests/inputmethod/mockime/src/com/android/cts/mockime/MockImeSession.java
@@ -0,0 +1,300 @@
+/*
+ * 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.mockime;
+
+import static android.content.Context.MODE_PRIVATE;
+
+import android.app.UiAutomation;
+import android.content.BroadcastReceiver;
+import android.content.ComponentName;
+import android.content.Context;
+import android.content.Intent;
+import android.content.IntentFilter;
+import android.os.Bundle;
+import android.os.Handler;
+import android.os.HandlerThread;
+import android.os.Parcel;
+import android.os.ParcelFileDescriptor;
+import android.os.SystemClock;
+import android.provider.Settings;
+import android.support.annotation.GuardedBy;
+import android.support.annotation.NonNull;
+import android.support.annotation.Nullable;
+import android.text.TextUtils;
+import android.view.inputmethod.InputMethodManager;
+
+import com.android.compatibility.common.util.PollingCheck;
+
+import java.io.IOException;
+import java.io.OutputStream;
+import java.util.concurrent.TimeUnit;
+
+/**
+ * Represents an active Mock IME session, which provides basic primitives to write end-to-end tests
+ * for IME APIs.
+ *
+ * <p>To use {@link MockIme} via {@link MockImeSession}, you need to </p>
+ * <p>Public methods are not thread-safe.</p>
+ */
+public class MockImeSession implements AutoCloseable {
+    private final String mImeEventActionName =
+            "com.android.cts.mockime.action.IME_EVENT." + SystemClock.elapsedRealtimeNanos();
+
+    /** Setting file name to store initialization settings for {@link MockIme}. */
+    static final String MOCK_IME_SETTINGS_FILE = "mockimesettings.data";
+
+    private static final long TIMEOUT = TimeUnit.SECONDS.toMillis(10);
+
+    @NonNull
+    private final Context mContext;
+    @NonNull
+    private final UiAutomation mUiAutomation;
+
+    private final HandlerThread mHandlerThread = new HandlerThread("EventReceiver");
+
+    private final static class EventStore {
+        private final static int INITIAL_ARRAY_SIZE = 32;
+
+        @NonNull
+        public final ImeEvent[] mArray;
+        public int mLength;
+
+        public EventStore() {
+            mArray = new ImeEvent[INITIAL_ARRAY_SIZE];
+            mLength = 0;
+        }
+
+        public EventStore(EventStore src, int newLength) {
+            mArray = new ImeEvent[newLength];
+            mLength = src.mLength;
+            System.arraycopy(src.mArray, 0, mArray, 0, src.mLength);
+        }
+
+        public EventStore add(ImeEvent event) {
+            if (mLength + 1 <= mArray.length) {
+                mArray[mLength] = event;
+                ++mLength;
+                return this;
+            } else {
+                return new EventStore(this, mLength * 2).add(event);
+            }
+        }
+
+        public ImeEventStream.ImeEventArray takeSnapshot() {
+            return new ImeEventStream.ImeEventArray(mArray, mLength);
+        }
+    }
+
+    private static final class MockImeEventReceiver extends BroadcastReceiver {
+        private final Object mLock = new Object();
+
+        @GuardedBy("mLock")
+        @NonNull
+        private EventStore mCurrentEventStore = new EventStore();
+
+        @NonNull
+        private final String mActionName;
+
+        public MockImeEventReceiver(@NonNull String actionName) {
+            mActionName = actionName;
+        }
+
+        @Override
+        public void onReceive(Context context, Intent intent) {
+            if (TextUtils.equals(mActionName, intent.getAction())) {
+                synchronized (mLock) {
+                    mCurrentEventStore =
+                            mCurrentEventStore.add(ImeEvent.fromBundle(intent.getExtras()));
+                }
+            }
+        }
+
+        public ImeEventStream.ImeEventArray takeEventSnapshot() {
+            synchronized (mLock) {
+                return mCurrentEventStore.takeSnapshot();
+            }
+        }
+    }
+    private final MockImeEventReceiver mEventReceiver =
+            new MockImeEventReceiver(mImeEventActionName);
+
+    private final ImeEventStream mEventStream =
+            new ImeEventStream(mEventReceiver::takeEventSnapshot);
+
+    private static String executeShellCommand(
+            @NonNull UiAutomation uiAutomation, @NonNull String command) throws IOException {
+        try (ParcelFileDescriptor.AutoCloseInputStream in =
+                     new ParcelFileDescriptor.AutoCloseInputStream(
+                             uiAutomation.executeShellCommand(command))) {
+            final StringBuilder sb = new StringBuilder();
+            final byte[] buffer = new byte[4096];
+            while (true) {
+                final int numRead = in.read(buffer);
+                if (numRead <= 0) {
+                    break;
+                }
+                sb.append(new String(buffer, 0, numRead));
+            }
+            return sb.toString();
+        }
+    }
+
+    @Nullable
+    private String getCurrentInputMethodId() {
+        // TODO: Replace this with IMM#getCurrentInputMethodIdForTesting()
+        return Settings.Secure.getString(mContext.getContentResolver(),
+                Settings.Secure.DEFAULT_INPUT_METHOD);
+    }
+
+    @Nullable
+    private static void writeMockImeSettings(@NonNull Context context,
+            @NonNull String imeEventActionName,
+            @Nullable ImeSettings.Builder imeSettings) throws Exception {
+        context.deleteFile(MOCK_IME_SETTINGS_FILE);
+        try (OutputStream os = context.openFileOutput(MOCK_IME_SETTINGS_FILE, MODE_PRIVATE)) {
+            Parcel parcel = null;
+            try {
+                parcel = Parcel.obtain();
+                ImeSettings.writeToParcel(parcel, imeEventActionName, imeSettings);
+                os.write(parcel.marshall());
+            } finally {
+                if (parcel != null) {
+                    parcel.recycle();
+                }
+            }
+            os.flush();
+        }
+    }
+
+    private ComponentName getMockImeComponentName() {
+        return MockIme.getComponentName(mContext.getPackageName());
+    }
+
+    private String getMockImeId() {
+        return MockIme.getImeId(mContext.getPackageName());
+    }
+
+    private MockImeSession(@NonNull Context context, @NonNull UiAutomation uiAutomation) {
+        mContext = context;
+        mUiAutomation = uiAutomation;
+    }
+
+    private void initialize(@Nullable ImeSettings.Builder imeSettings) throws Exception {
+        // Make sure that MockIME is not selected.
+        if (mContext.getSystemService(InputMethodManager.class)
+                .getInputMethodList()
+                .stream()
+                .anyMatch(info -> getMockImeComponentName().equals(info.getComponent()))) {
+            executeShellCommand(mUiAutomation, "ime reset");
+        }
+        if (mContext.getSystemService(InputMethodManager.class)
+                .getEnabledInputMethodList()
+                .stream()
+                .anyMatch(info -> getMockImeComponentName().equals(info.getComponent()))) {
+            throw new IllegalStateException();
+        }
+
+        writeMockImeSettings(mContext, mImeEventActionName, imeSettings);
+
+        mHandlerThread.start();
+        mContext.registerReceiver(mEventReceiver,
+                new IntentFilter(mImeEventActionName), null /* broadcastPermission */,
+                new Handler(mHandlerThread.getLooper()));
+
+        executeShellCommand(mUiAutomation, "ime enable " + getMockImeId());
+        executeShellCommand(mUiAutomation, "ime set " + getMockImeId());
+
+        PollingCheck.check("Make sure that MockIME becomes available", TIMEOUT,
+                () -> getMockImeId().equals(getCurrentInputMethodId()));
+    }
+
+    /**
+     * Creates a new Mock IME session. During this session, you can receive various events from
+     * {@link MockIme}.
+     *
+     * @param context {@link Context} to be used to receive inter-process events from the
+     *                {@link MockIme} (e.g. via {@link BroadcastReceiver}
+     * @param uiAutomation {@link UiAutomation} object to change the device state that are typically
+     *                     guarded by permissions.
+     * @param imeSettings Key-value pairs to be passed to the {@link MockIme}.
+     * @return A session object, with which you can retrieve event logs from the {@link MockIme} and
+     *         can clean up the session.
+     */
+    @NonNull
+    public static MockImeSession create(
+            @NonNull Context context,
+            @NonNull UiAutomation uiAutomation,
+            @Nullable ImeSettings.Builder imeSettings) throws Exception {
+        final MockImeSession client = new MockImeSession(context, uiAutomation);
+        client.initialize(imeSettings);
+        return client;
+    }
+
+    /**
+     * @return {@link ImeEventStream} object that stores events sent from {@link MockIme} since the
+     *         session is created.
+     */
+    public ImeEventStream openEventStream() {
+        return mEventStream.copy();
+    }
+
+    /**
+     * Closes the active session and de-selects {@link MockIme}. Currently which IME will be
+     * selected next is up to the system.
+     */
+    public void close() throws Exception {
+        executeShellCommand(mUiAutomation, "ime reset");
+
+        PollingCheck.check("Make sure that MockIME becomes unavailable", TIMEOUT, () ->
+                mContext.getSystemService(InputMethodManager.class)
+                        .getEnabledInputMethodList()
+                        .stream()
+                        .noneMatch(info -> getMockImeComponentName().equals(info.getComponent())));
+
+        mContext.unregisterReceiver(mEventReceiver);
+        mHandlerThread.quitSafely();
+        mContext.deleteFile(MOCK_IME_SETTINGS_FILE);
+    }
+
+    /**
+     * Lets {@link MockIme} to call
+     * {@link android.view.inputmethod.InputConnection#commitText(CharSequence, int)} with the given
+     * parameters.
+     *
+     * <p>This triggers {@code getCurrentInputConnection().commitText(text, newCursorPosition)}.</p>
+     *
+     * @param text to be passed as the {@code text} parameter
+     * @param newCursorPosition to be passed as the {@code newCursorPosition} parameter
+     * @return {@link ImeCommand} object that can be passed to
+     *         {@link ImeEventStreamTestUtils#expectCommand(ImeEventStream, ImeCommand, long)} to
+     *         wait until this event is handled by {@link MockIme}
+     */
+    @NonNull
+    public ImeCommand callCommitText(@NonNull CharSequence text, int newCursorPosition) {
+        final Bundle params = new Bundle();
+        params.putCharSequence("text", text);
+        params.putInt("newCursorPosition", newCursorPosition);
+        final ImeCommand command = new ImeCommand(
+                "commitText", SystemClock.elapsedRealtimeNanos(), true, params);
+        final Intent intent = new Intent();
+        intent.setPackage(mContext.getPackageName());
+        intent.setAction(MockIme.getCommandActionName(mImeEventActionName));
+        intent.putExtras(command.toBundle());
+        mContext.sendBroadcast(intent);
+        return command;
+    }
+}
diff --git a/tests/inputmethod/res/xml/method.xml b/tests/inputmethod/res/xml/method.xml
new file mode 100644
index 0000000..2266fba
--- /dev/null
+++ b/tests/inputmethod/res/xml/method.xml
@@ -0,0 +1,19 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+  Copyright (C) 2017 The Android Open Source Project
+
+  Licensed under the Apache License, Version 2.0 (the "License");
+  you may not use this file except in compliance with the License.
+  You may obtain a copy of the License at
+
+       http://www.apache.org/licenses/LICENSE-2.0
+
+  Unless required by applicable law or agreed to in writing, software
+  distributed under the License is distributed on an "AS IS" BASIS,
+  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+  See the License for the specific language governing permissions and
+  limitations under the License.
+-->
+
+<input-method xmlns:android="http://schemas.android.com/apk/res/android">
+</input-method>
diff --git a/tests/inputmethod/src/android/view/inputmethod/cts/BaseInputConnectionTest.java b/tests/inputmethod/src/android/view/inputmethod/cts/BaseInputConnectionTest.java
index d5ace2e..d3efc72 100644
--- a/tests/inputmethod/src/android/view/inputmethod/cts/BaseInputConnectionTest.java
+++ b/tests/inputmethod/src/android/view/inputmethod/cts/BaseInputConnectionTest.java
@@ -493,8 +493,8 @@
     public void testCloseConnection() {
         final CharSequence source = "0123456789";
         mConnection.commitText(source, source.length());
+        mConnection.setComposingRegion(2, 5);
         final Editable text = mConnection.getEditable();
-        BaseInputConnection.setComposingSpans(text, 2, 5);
         assertEquals(2, BaseInputConnection.getComposingSpanStart(text));
         assertEquals(5, BaseInputConnection.getComposingSpanEnd(text));
 
diff --git a/tests/inputmethod/src/android/view/inputmethod/cts/ExtractedTextTest.java b/tests/inputmethod/src/android/view/inputmethod/cts/ExtractedTextTest.java
index 41e3efa..9b03ed7 100644
--- a/tests/inputmethod/src/android/view/inputmethod/cts/ExtractedTextTest.java
+++ b/tests/inputmethod/src/android/view/inputmethod/cts/ExtractedTextTest.java
@@ -18,9 +18,14 @@
 
 import static org.junit.Assert.assertEquals;
 
+import android.graphics.Typeface;
 import android.os.Parcel;
 import android.support.test.filters.SmallTest;
 import android.support.test.runner.AndroidJUnit4;
+import android.text.Spannable;
+import android.text.SpannableStringBuilder;
+import android.text.Spanned;
+import android.text.style.StyleSpan;
 import android.view.inputmethod.ExtractedText;
 
 import org.junit.Test;
@@ -38,6 +43,9 @@
         extractedText.startOffset = 1;
         CharSequence text = "test";
         extractedText.text = text;
+        SpannableStringBuilder hint = new SpannableStringBuilder("hint");
+        hint.setSpan(new StyleSpan(Typeface.BOLD), 1, 3, Spanned.SPAN_INCLUSIVE_INCLUSIVE);
+        extractedText.hint = hint;
         Parcel p = Parcel.obtain();
         extractedText.writeToParcel(p, 0);
         p.setDataPosition(0);
@@ -49,6 +57,9 @@
         assertEquals(extractedText.partialStartOffset, target.partialStartOffset);
         assertEquals(extractedText.partialEndOffset, target.partialEndOffset);
         assertEquals(extractedText.text.toString(), target.text.toString());
+        assertEquals(extractedText.hint.toString(), target.hint.toString());
+        final Spannable hintText = (Spannable) extractedText.hint;
+        assertEquals(1, hintText.getSpans(0, hintText.length(), StyleSpan.class).length);
 
         assertEquals(0, extractedText.describeContents());
     }
diff --git a/tests/inputmethod/src/android/view/inputmethod/cts/FocusHandlingTest.java b/tests/inputmethod/src/android/view/inputmethod/cts/FocusHandlingTest.java
new file mode 100644
index 0000000..2e6267f
--- /dev/null
+++ b/tests/inputmethod/src/android/view/inputmethod/cts/FocusHandlingTest.java
@@ -0,0 +1,363 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.view.inputmethod.cts;
+
+import static android.view.WindowManager.LayoutParams.SOFT_INPUT_STATE_ALWAYS_VISIBLE;
+import static android.widget.PopupWindow.INPUT_METHOD_NOT_NEEDED;
+
+import static com.android.cts.mockime.ImeEventStreamTestUtils.expectBindInput;
+import static com.android.cts.mockime.ImeEventStreamTestUtils.expectCommand;
+import static com.android.cts.mockime.ImeEventStreamTestUtils.expectEvent;
+import static com.android.cts.mockime.ImeEventStreamTestUtils.notExpectEvent;
+
+import static org.junit.Assert.assertFalse;
+
+import android.app.Instrumentation;
+import android.content.Context;
+import android.os.Build;
+import android.os.IBinder;
+import android.os.Process;
+import android.support.test.InstrumentationRegistry;
+import android.support.test.filters.MediumTest;
+import android.support.test.runner.AndroidJUnit4;
+import android.text.TextUtils;
+import android.view.View;
+import android.view.inputmethod.EditorInfo;
+import android.view.inputmethod.InputMethodManager;
+import android.view.inputmethod.cts.util.EndToEndImeTestBase;
+import android.view.inputmethod.cts.util.TestActivity;
+import android.view.inputmethod.cts.util.TestUtils;
+import android.view.inputmethod.cts.util.WindowFocusStealer;
+import android.widget.EditText;
+import android.widget.LinearLayout;
+import android.widget.PopupWindow;
+import android.widget.TextView;
+
+import com.android.compatibility.common.util.CtsTouchUtils;
+import com.android.cts.mockime.ImeCommand;
+import com.android.cts.mockime.ImeEvent;
+import com.android.cts.mockime.ImeEventStream;
+import com.android.cts.mockime.ImeSettings;
+import com.android.cts.mockime.MockImeSession;
+
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+import java.util.concurrent.TimeUnit;
+import java.util.concurrent.atomic.AtomicReference;
+
+@MediumTest
+@RunWith(AndroidJUnit4.class)
+public class FocusHandlingTest extends EndToEndImeTestBase {
+    static final long TIMEOUT = TimeUnit.SECONDS.toMillis(5);
+    static final long NOT_EXPECT_TIMEOUT = TimeUnit.SECONDS.toMillis(1);
+
+    private final static String TEST_MARKER = "android.view.inputmethod.cts.FocusHandlingTest";
+
+    public EditText launchTestActivity() {
+        final AtomicReference<EditText> editTextRef = new AtomicReference<>();
+        TestActivity.startSync((TestActivity activity) -> {
+            final LinearLayout layout = new LinearLayout(activity);
+            layout.setOrientation(LinearLayout.VERTICAL);
+
+            final EditText editText = new EditText(activity);
+            editText.setPrivateImeOptions(TEST_MARKER);
+            editText.setHint("editText");
+            editTextRef.set(editText);
+
+            layout.addView(editText);
+            return layout;
+        });
+        return editTextRef.get();
+    }
+
+    @Test
+    public void testOnStartInputCalledOnceIme() throws Exception {
+        try(MockImeSession imeSession = MockImeSession.create(
+                InstrumentationRegistry.getContext(),
+                InstrumentationRegistry.getInstrumentation().getUiAutomation(),
+                new ImeSettings.Builder())) {
+            final ImeEventStream stream = imeSession.openEventStream();
+
+            final EditText editText = launchTestActivity();
+
+            // Wait until the MockIme gets bound to the TestActivity.
+            expectBindInput(stream, Process.myPid(), TIMEOUT);
+
+            // Emulate tap event
+            CtsTouchUtils.emulateTapOnViewCenter(
+                    InstrumentationRegistry.getInstrumentation(), editText);
+
+            // Wait until "onStartInput" gets called for the EditText.
+            final ImeEvent onStart = expectEvent(stream, event -> {
+                if (!TextUtils.equals("onStartInput", event.getEventName())) {
+                    return false;
+                }
+                final EditorInfo editorInfo = event.getArguments().getParcelable("editorInfo");
+                return TextUtils.equals(TEST_MARKER, editorInfo.privateImeOptions);
+            }, TIMEOUT);
+            assertFalse(stream.dump(), onStart.getEnterState().hasDummyInputConnection());
+            assertFalse(stream.dump(), onStart.getArguments().getBoolean("restarting"));
+
+            // There shouldn't be onStartInput any more.
+            notExpectEvent(stream, event -> "onStartInput".equals(event.getEventName()),
+                    NOT_EXPECT_TIMEOUT);
+        }
+    }
+
+    @Test
+    public void testSoftInputStateAlwaysVisibleWithoutFocusedEditorView() throws Exception {
+        try(MockImeSession imeSession = MockImeSession.create(
+                InstrumentationRegistry.getContext(),
+                InstrumentationRegistry.getInstrumentation().getUiAutomation(),
+                new ImeSettings.Builder())) {
+            final ImeEventStream stream = imeSession.openEventStream();
+
+            final TestActivity testActivity = TestActivity.startSync((TestActivity activity) -> {
+                final LinearLayout layout = new LinearLayout(activity);
+                layout.setOrientation(LinearLayout.VERTICAL);
+
+                final TextView textView = new TextView(activity) {
+                    @Override
+                    public boolean onCheckIsTextEditor() {
+                        return false;
+                    }
+                };
+                textView.setText("textView");
+                textView.requestFocus();
+
+                activity.getWindow().setSoftInputMode(SOFT_INPUT_STATE_ALWAYS_VISIBLE);
+                layout.addView(textView);
+                return layout;
+            });
+
+            // Wait until the MockIme gets bound to the TestActivity.
+            expectBindInput(stream, Process.myPid(), TIMEOUT);
+
+            if (testActivity.getApplicationInfo().targetSdkVersion >= Build.VERSION_CODES.P) {
+                // There shouldn't be onStartInput because the focused view is not an editor.
+                notExpectEvent(stream, event -> "showSoftInput".equals(event.getEventName()),
+                        TIMEOUT);
+            } else {
+                // For apps that target pre-P devices, onStartInput() should be called.
+                expectEvent(stream, event -> "showSoftInput".equals(event.getEventName()), TIMEOUT);
+            }
+        }
+    }
+
+    @Test
+    public void testSoftInputStateAlwaysVisibleFocusedEditorView() throws Exception {
+        try(MockImeSession imeSession = MockImeSession.create(
+                InstrumentationRegistry.getContext(),
+                InstrumentationRegistry.getInstrumentation().getUiAutomation(),
+                new ImeSettings.Builder())) {
+            final ImeEventStream stream = imeSession.openEventStream();
+
+            TestActivity.startSync((TestActivity activity) -> {
+                final LinearLayout layout = new LinearLayout(activity);
+                layout.setOrientation(LinearLayout.VERTICAL);
+
+                final EditText editText = new EditText(activity);
+                editText.setText("editText");
+                editText.requestFocus();
+
+                activity.getWindow().setSoftInputMode(SOFT_INPUT_STATE_ALWAYS_VISIBLE);
+                layout.addView(editText);
+                return layout;
+            });
+
+            // Wait until the MockIme gets bound to the TestActivity.
+            expectBindInput(stream, Process.myPid(), TIMEOUT);
+
+            expectEvent(stream, event -> "showSoftInput".equals(event.getEventName()), TIMEOUT);
+        }
+    }
+
+    /**
+     * Makes sure that an existing {@link android.view.inputmethod.InputConnection} will not be
+     * invalidated by showing a focusable {@link PopupWindow} with
+     * {@link PopupWindow#INPUT_METHOD_NOT_NEEDED}.
+     *
+     * <p>If {@link android.view.WindowManager.LayoutParams#FLAG_ALT_FOCUSABLE_IM} is set and
+     * {@link android.view.WindowManager.LayoutParams#FLAG_NOT_FOCUSABLE} is not set to a
+     * {@link android.view.Window}, showing that window must not invalidate an existing valid
+     * {@link android.view.inputmethod.InputConnection}.</p>
+     *
+     * @see android.view.WindowManager.LayoutParams#mayUseInputMethod(int)
+     */
+    @Test
+    public void testFocusableWindowDoesNotInvalidateExistingInputConnection() throws Exception {
+        final Instrumentation instrumentation = InstrumentationRegistry.getInstrumentation();
+        try(MockImeSession imeSession = MockImeSession.create(
+                InstrumentationRegistry.getContext(),
+                instrumentation.getUiAutomation(),
+                new ImeSettings.Builder())) {
+            final ImeEventStream stream = imeSession.openEventStream();
+
+            final EditText editText = launchTestActivity();
+            instrumentation.runOnMainSync(() -> editText.requestFocus());
+
+            // Wait until the MockIme gets bound to the TestActivity.
+            expectBindInput(stream, Process.myPid(), TIMEOUT);
+
+            expectEvent(stream, event -> {
+                if (!TextUtils.equals("onStartInput", event.getEventName())) {
+                    return false;
+                }
+                final EditorInfo editorInfo = event.getArguments().getParcelable("editorInfo");
+                return TextUtils.equals(TEST_MARKER, editorInfo.privateImeOptions);
+            }, TIMEOUT);
+
+            // Make sure that InputConnection#commitText() works.
+            final ImeCommand commit1 = imeSession.callCommitText("test commit", 1);
+            expectCommand(stream, commit1, TIMEOUT);
+            TestUtils.waitOnMainUntil(
+                    () -> TextUtils.equals(editText.getText(), "test commit"), TIMEOUT);
+            instrumentation.runOnMainSync(() -> editText.setText(""));
+
+            // Create a popup window that cannot be the IME target.
+            final PopupWindow popupWindow = TestUtils.getOnMainSync(() -> {
+                final Context context = instrumentation.getTargetContext();
+                final PopupWindow popup = new PopupWindow(context);
+                popup.setFocusable(true);
+                popup.setInputMethodMode(INPUT_METHOD_NOT_NEEDED);
+                final TextView textView = new TextView(context);
+                textView.setText("Test Text");
+                popup.setContentView(textView);
+                return popup;
+            });
+
+            // Show the popup window.
+            instrumentation.runOnMainSync(() -> popupWindow.showAsDropDown(editText));
+            instrumentation.waitForIdleSync();
+
+            // Make sure that the EditText no longer has window-focus
+            TestUtils.waitOnMainUntil(() -> !editText.hasWindowFocus(), TIMEOUT);
+
+            // Make sure that InputConnection#commitText() works.
+            final ImeCommand commit2 = imeSession.callCommitText("Hello!", 1);
+            expectCommand(stream, commit2, TIMEOUT);
+            TestUtils.waitOnMainUntil(
+                    () -> TextUtils.equals(editText.getText(), "Hello!"), TIMEOUT);
+            instrumentation.runOnMainSync(() -> editText.setText(""));
+
+            stream.skipAll();
+
+            // Call InputMethodManager#restartInput()
+            instrumentation.runOnMainSync(() -> {
+                editText.getContext()
+                        .getSystemService(InputMethodManager.class)
+                        .restartInput(editText);
+            });
+
+            // Make sure that onStartInput() is called with restarting == true.
+            expectEvent(stream, event -> {
+                if (!TextUtils.equals("onStartInput", event.getEventName())) {
+                    return false;
+                }
+                if (!event.getArguments().getBoolean("restarting")) {
+                    return false;
+                }
+                final EditorInfo editorInfo = event.getArguments().getParcelable("editorInfo");
+                return TextUtils.equals(TEST_MARKER, editorInfo.privateImeOptions);
+            }, TIMEOUT);
+
+            // Make sure that InputConnection#commitText() works.
+            final ImeCommand commit3 = imeSession.callCommitText("World!", 1);
+            expectCommand(stream, commit3, TIMEOUT);
+            TestUtils.waitOnMainUntil(
+                    () -> TextUtils.equals(editText.getText(), "World!"), TIMEOUT);
+            instrumentation.runOnMainSync(() -> editText.setText(""));
+
+            // Dismiss the popup window.
+            instrumentation.runOnMainSync(() -> popupWindow.dismiss());
+            instrumentation.waitForIdleSync();
+
+            // Make sure that the EditText now has window-focus again.
+            TestUtils.waitOnMainUntil(() -> editText.hasWindowFocus(), TIMEOUT);
+
+            // Make sure that InputConnection#commitText() works.
+            final ImeCommand commit4 = imeSession.callCommitText("Done!", 1);
+            expectCommand(stream, commit4, TIMEOUT);
+            TestUtils.waitOnMainUntil(
+                    () -> TextUtils.equals(editText.getText(), "Done!"), TIMEOUT);
+            instrumentation.runOnMainSync(() -> editText.setText(""));
+        }
+    }
+
+    /**
+     * Test case for Bug 70629102.
+     *
+     * {@link InputMethodManager#restartInput(View)} can be called even when another process
+     * temporarily owns focused window. {@link InputMethodManager} should continue to work after
+     * the IME target application gains window focus again.
+     */
+    @Test
+    public void testRestartInputWhileOtherProcessHasWindowFocus() throws Exception {
+        final Instrumentation instrumentation = InstrumentationRegistry.getInstrumentation();
+        try(MockImeSession imeSession = MockImeSession.create(
+                InstrumentationRegistry.getContext(),
+                instrumentation.getUiAutomation(),
+                new ImeSettings.Builder())) {
+            final ImeEventStream stream = imeSession.openEventStream();
+
+            final EditText editText = launchTestActivity();
+            instrumentation.runOnMainSync(() -> editText.requestFocus());
+
+            // Wait until the MockIme gets bound to the TestActivity.
+            expectBindInput(stream, Process.myPid(), TIMEOUT);
+
+            expectEvent(stream, event -> {
+                if (!TextUtils.equals("onStartInput", event.getEventName())) {
+                    return false;
+                }
+                final EditorInfo editorInfo = event.getArguments().getParcelable("editorInfo");
+                return TextUtils.equals(TEST_MARKER, editorInfo.privateImeOptions);
+            }, TIMEOUT);
+
+            // Get app window token
+            final IBinder appWindowToken = TestUtils.getOnMainSync(
+                    () -> editText.getApplicationWindowToken());
+
+            try (WindowFocusStealer focusStealer =
+                         WindowFocusStealer.connect(instrumentation.getTargetContext(), TIMEOUT)) {
+
+                focusStealer.stealWindowFocus(appWindowToken, TIMEOUT);
+
+                // Wait until the edit text loses window focus.
+                TestUtils.waitOnMainUntil(() -> !editText.hasWindowFocus(), TIMEOUT);
+
+                // Call InputMethodManager#restartInput()
+                instrumentation.runOnMainSync(() -> {
+                    editText.getContext()
+                            .getSystemService(InputMethodManager.class)
+                            .restartInput(editText);
+                });
+            }
+
+            // Wait until the edit text gains window focus again.
+            TestUtils.waitOnMainUntil(() -> editText.hasWindowFocus(), TIMEOUT);
+
+            // Make sure that InputConnection#commitText() still works.
+            final ImeCommand command = imeSession.callCommitText("test commit", 1);
+            expectCommand(stream, command, TIMEOUT);
+
+            TestUtils.waitOnMainUntil(
+                    () -> TextUtils.equals(editText.getText(), "test commit"), TIMEOUT);
+        }
+    }
+}
diff --git a/tests/inputmethod/src/android/view/inputmethod/cts/InputMethodInfoTest.java b/tests/inputmethod/src/android/view/inputmethod/cts/InputMethodInfoTest.java
index 2b5d7ed..b538d41 100644
--- a/tests/inputmethod/src/android/view/inputmethod/cts/InputMethodInfoTest.java
+++ b/tests/inputmethod/src/android/view/inputmethod/cts/InputMethodInfoTest.java
@@ -250,7 +250,7 @@
                     ApplicationInfo.FLAG_SYSTEM) {
                 continue;
             }
-            if (serviceInfo.encryptionAware) {
+            if (serviceInfo.directBootAware) {
                 hasEncryptionAwareInputMethod = true;
                 break;
             }
diff --git a/tests/inputmethod/src/android/view/inputmethod/cts/NavigationBarColorTest.java b/tests/inputmethod/src/android/view/inputmethod/cts/NavigationBarColorTest.java
new file mode 100644
index 0000000..a3c41bb
--- /dev/null
+++ b/tests/inputmethod/src/android/view/inputmethod/cts/NavigationBarColorTest.java
@@ -0,0 +1,339 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.view.inputmethod.cts;
+
+import static android.view.View.SYSTEM_UI_FLAG_LIGHT_NAVIGATION_BAR;
+import static android.view.WindowManager.LayoutParams.FLAG_ALT_FOCUSABLE_IM;
+import static android.view.WindowManager.LayoutParams.FLAG_DIM_BEHIND;
+import static android.view.WindowManager.LayoutParams.FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS;
+import static android.view.WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE;
+import static android.view.inputmethod.cts.util.LightNavigationBarVerifier.expectLightNavigationBarNotSupported;
+import static android.view.inputmethod.cts.util.NavigationBarColorVerifier.expectNavigationBarColorNotSupported;
+import static android.view.inputmethod.cts.util.NavigationBarColorVerifier.expectNavigationBarColorSupported;
+import static android.view.inputmethod.cts.util.TestUtils.getOnMainSync;
+
+import static com.android.cts.mockime.ImeEventStreamTestUtils.expectBindInput;
+import static com.android.cts.mockime.ImeEventStreamTestUtils.expectEvent;
+import static com.android.cts.mockime.ImeEventStreamTestUtils.waitForInputViewLayoutStable;
+
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assume.assumeTrue;
+
+import android.app.Activity;
+import android.app.AlertDialog;
+import android.app.UiAutomation;
+import android.graphics.Bitmap;
+import android.graphics.Color;
+import android.os.Process;
+import android.support.annotation.ColorInt;
+import android.support.annotation.NonNull;
+import android.support.test.InstrumentationRegistry;
+import android.support.test.filters.MediumTest;
+import android.support.test.runner.AndroidJUnit4;
+import android.text.TextUtils;
+import android.view.View;
+import android.view.ViewGroup;
+import android.view.inputmethod.EditorInfo;
+import android.view.inputmethod.cts.util.EndToEndImeTestBase;
+import android.view.inputmethod.cts.util.ImeAwareEditText;
+import android.view.inputmethod.cts.util.NavigationBarInfo;
+import android.view.inputmethod.cts.util.TestActivity;
+import android.widget.LinearLayout;
+import android.widget.TextView;
+
+import com.android.cts.mockime.ImeEventStream;
+import com.android.cts.mockime.ImeLayoutInfo;
+import com.android.cts.mockime.ImeSettings;
+import com.android.cts.mockime.MockImeSession;
+
+import org.junit.BeforeClass;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+import java.util.concurrent.TimeUnit;
+
+@MediumTest
+@RunWith(AndroidJUnit4.class)
+public class NavigationBarColorTest extends EndToEndImeTestBase {
+    private static final long TIMEOUT = TimeUnit.SECONDS.toMillis(5);
+    private static final long LAYOUT_STABLE_THRESHOLD = TimeUnit.SECONDS.toMillis(3);
+
+    private final static String TEST_MARKER = "android.view.inputmethod.cts.NavigationBarColorTest";
+
+    private static void updateSystemUiVisibility(@NonNull View view, int flags, int mask) {
+        final int currentFlags = view.getSystemUiVisibility();
+        final int newFlags = (currentFlags & ~mask) | (flags & mask);
+        if (currentFlags != newFlags) {
+            view.setSystemUiVisibility(newFlags);
+        }
+    }
+
+    @BeforeClass
+    public static void checkNavigationBar() throws Exception {
+        assumeTrue("This test does not make sense if there is no navigation bar",
+                NavigationBarInfo.getInstance().hasBottomNavigationBar());
+
+        assumeTrue("This test does not make sense if custom navigation bar color is not supported"
+                        + " even for typical Activity",
+                NavigationBarInfo.getInstance().supportsNavigationBarColor());
+    }
+
+    /**
+     * Represents test scenarios regarding how a {@link android.view.Window} that has
+     * {@link android.view.WindowManager.LayoutParams#FLAG_DIM_BEHIND} interacts with a different
+     * {@link android.view.Window} that has
+     * {@link android.view.View#SYSTEM_UI_FLAG_LIGHT_NAVIGATION_BAR}.
+     */
+    private enum DimmingTestMode {
+        /**
+         * No {@link AlertDialog} is shown when testing.
+         */
+        NO_DIMMING_DIALOG,
+        /**
+         * An {@link AlertDialog} that has dimming effect is shown above the IME window.
+         */
+        DIMMING_DIALOG_ABOVE_IME,
+        /**
+         * An {@link AlertDialog} that has dimming effect is shown behind the IME window.
+         */
+        DIMMING_DIALOG_BEHIND_IME,
+    }
+
+    @NonNull
+    public TestActivity launchTestActivity(@ColorInt int navigationBarColor,
+            boolean lightNavigationBar, @NonNull DimmingTestMode dimmingTestMode) {
+        return TestActivity.startSync(activity -> {
+            final View contentView;
+            switch (dimmingTestMode) {
+                case NO_DIMMING_DIALOG:
+                case DIMMING_DIALOG_ABOVE_IME: {
+                    final LinearLayout layout = new LinearLayout(activity);
+                    layout.setOrientation(LinearLayout.VERTICAL);
+                    final ImeAwareEditText editText = new ImeAwareEditText(activity);
+                    editText.setPrivateImeOptions(TEST_MARKER);
+                    editText.setHint("editText");
+                    editText.requestFocus();
+                    editText.scheduleShowSoftInput();
+                    layout.addView(editText);
+                    contentView = layout;
+                    break;
+                }
+                case DIMMING_DIALOG_BEHIND_IME: {
+                    final View view = new View(activity);
+                    view.setLayoutParams(new ViewGroup.LayoutParams(
+                            ViewGroup.LayoutParams.MATCH_PARENT,
+                            ViewGroup.LayoutParams.MATCH_PARENT));
+                    contentView = view;
+                    break;
+                }
+                default:
+                    throw new IllegalStateException("unknown mode=" + dimmingTestMode);
+            }
+            activity.getWindow().setNavigationBarColor(navigationBarColor);
+            updateSystemUiVisibility(contentView,
+                    lightNavigationBar ? SYSTEM_UI_FLAG_LIGHT_NAVIGATION_BAR : 0,
+                    SYSTEM_UI_FLAG_LIGHT_NAVIGATION_BAR);
+            return contentView;
+        });
+    }
+
+    private AutoCloseable showDialogIfNecessary(
+            @NonNull Activity activity, @NonNull DimmingTestMode dimmingTestMode) {
+        switch (dimmingTestMode) {
+            case NO_DIMMING_DIALOG:
+                // Dialog is not necessary.
+                return () -> {};
+            case DIMMING_DIALOG_ABOVE_IME: {
+                final AlertDialog alertDialog = getOnMainSync(() -> {
+                    final TextView textView = new TextView(activity);
+                    textView.setText("Dummy");
+                    textView.requestFocus();
+                    final AlertDialog dialog = new AlertDialog.Builder(activity)
+                            .setView(textView)
+                            .create();
+                    dialog.getWindow().setFlags(FLAG_DIM_BEHIND | FLAG_ALT_FOCUSABLE_IM,
+                            FLAG_DIM_BEHIND | FLAG_NOT_FOCUSABLE | FLAG_ALT_FOCUSABLE_IM);
+                    dialog.show();
+                    return dialog;
+                });
+                // Note: Dialog#dismiss() is a thread safe method so we don't need to call this from
+                // the UI thread.
+                return () -> alertDialog.dismiss();
+            }
+            case DIMMING_DIALOG_BEHIND_IME: {
+                final AlertDialog alertDialog = getOnMainSync(() -> {
+                    final ImeAwareEditText editText = new ImeAwareEditText(activity);
+                    editText.setPrivateImeOptions(TEST_MARKER);
+                    editText.setHint("editText");
+                    editText.requestFocus();
+                    editText.scheduleShowSoftInput();
+                    final AlertDialog dialog = new AlertDialog.Builder(activity)
+                            .setView(editText)
+                            .create();
+                    dialog.getWindow().setFlags(FLAG_DIM_BEHIND,
+                            FLAG_DIM_BEHIND | FLAG_NOT_FOCUSABLE | FLAG_ALT_FOCUSABLE_IM);
+                    dialog.show();
+                    return dialog;
+                });
+                // Note: Dialog#dismiss() is a thread safe method so we don't need to call this from
+                // the UI thread.
+                return () -> alertDialog.dismiss();
+            }
+            default:
+                throw new IllegalStateException("unknown mode=" + dimmingTestMode);
+        }
+    }
+
+    @NonNull
+    private ImeSettings.Builder imeSettingForSolidNavigationBar(@ColorInt int navigationBarColor,
+            boolean lightNavigationBar) {
+        final ImeSettings.Builder builder = new ImeSettings.Builder();
+        builder.setNavigationBarColor(navigationBarColor);
+        if (lightNavigationBar) {
+            builder.setInputViewSystemUiVisibility(SYSTEM_UI_FLAG_LIGHT_NAVIGATION_BAR);
+        }
+        return builder;
+    }
+
+    @NonNull
+    private ImeSettings.Builder imeSettingForFloatingIme(@ColorInt int navigationBarColor,
+            boolean lightNavigationBar) {
+        final ImeSettings.Builder builder = new ImeSettings.Builder();
+        builder.setWindowFlags(0, FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS);
+        // As documented, Window#setNavigationBarColor() is actually ignored when the IME window
+        // does not have FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS.  We are calling setNavigationBarColor()
+        // to ensure it.
+        builder.setNavigationBarColor(navigationBarColor);
+        if (lightNavigationBar) {
+            // Although the document says that Window#setNavigationBarColor() requires
+            // SYSTEM_UI_FLAG_LIGHT_NAVIGATION_BAR to work, currently it's not true for IME windows.
+            // TODO: Fix this anomaly
+            builder.setInputViewSystemUiVisibility(SYSTEM_UI_FLAG_LIGHT_NAVIGATION_BAR);
+        }
+        return builder;
+    }
+
+    @NonNull
+    private Bitmap getNavigationBarBitmap(@NonNull ImeSettings.Builder builder,
+            @ColorInt int appNavigationBarColor, boolean appLightNavigationBar,
+            int navigationBarHeight, @NonNull DimmingTestMode dimmingTestMode)
+            throws Exception {
+        final UiAutomation uiAutomation =
+                InstrumentationRegistry.getInstrumentation().getUiAutomation();
+        try(MockImeSession imeSession = MockImeSession.create(
+                InstrumentationRegistry.getContext(), uiAutomation, builder)) {
+            final ImeEventStream stream = imeSession.openEventStream();
+
+            final TestActivity activity = launchTestActivity(
+                    appNavigationBarColor, appLightNavigationBar, dimmingTestMode);
+
+            // Show AlertDialog if necessary, based on the dimming test mode.
+            try (AutoCloseable dialogCloser = showDialogIfNecessary(
+                    activity, dimmingTestMode)) {
+                // Wait until the MockIme gets bound to the TestActivity.
+                expectBindInput(stream, Process.myPid(), TIMEOUT);
+
+                // Wait until "onStartInput" gets called for the EditText.
+                expectEvent(stream, event -> {
+                    if (!TextUtils.equals("onStartInputView", event.getEventName())) {
+                        return false;
+                    }
+                    final EditorInfo editorInfo = event.getArguments().getParcelable("editorInfo");
+                    return TextUtils.equals(TEST_MARKER, editorInfo.privateImeOptions);
+                }, TIMEOUT);
+
+                // Wait until MockIme's layout becomes stable.
+                final ImeLayoutInfo lastLayout =
+                        waitForInputViewLayoutStable(stream, LAYOUT_STABLE_THRESHOLD);
+                assertNotNull(lastLayout);
+
+                final Bitmap bitmap = uiAutomation.takeScreenshot();
+                return Bitmap.createBitmap(bitmap, 0, bitmap.getHeight() - navigationBarHeight,
+                        bitmap.getWidth(), navigationBarHeight);
+            }
+        }
+    }
+
+    @Test
+    public void testSetNavigationBarColor() throws Exception {
+        final NavigationBarInfo info = NavigationBarInfo.getInstance();
+
+        // Make sure that Window#setNavigationBarColor() works for IMEs.
+        expectNavigationBarColorSupported(color ->
+                getNavigationBarBitmap(imeSettingForSolidNavigationBar(color, false),
+                        Color.BLACK, false, info.getBottomNavigationBerHeight(),
+                        DimmingTestMode.NO_DIMMING_DIALOG));
+
+        // Make sure that IME's navigation bar can be transparent
+        expectNavigationBarColorSupported(color ->
+                getNavigationBarBitmap(imeSettingForSolidNavigationBar(Color.TRANSPARENT, false),
+                        color, false, info.getBottomNavigationBerHeight(),
+                        DimmingTestMode.NO_DIMMING_DIALOG));
+
+        // Make sure that Window#setNavigationBarColor() is ignored when
+        // FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS is unset
+        expectNavigationBarColorNotSupported(color ->
+                getNavigationBarBitmap(imeSettingForFloatingIme(color, false),
+                        Color.BLACK, false, info.getBottomNavigationBerHeight(),
+                        DimmingTestMode.NO_DIMMING_DIALOG));
+    }
+
+    @Test
+    public void testLightNavigationBar() throws Exception {
+        final NavigationBarInfo info = NavigationBarInfo.getInstance();
+
+        assumeTrue("This test does not make sense if light navigation bar is not supported"
+                + " even for typical Activity", info.supportsLightNavigationBar());
+
+        // Currently SYSTEM_UI_FLAG_LIGHT_NAVIGATION_BAR is ignored for IME windows.
+        // TODO: Support SYSTEM_UI_FLAG_LIGHT_NAVIGATION_BAR for IME windows (Bug 69002467)
+        expectLightNavigationBarNotSupported((color, lightMode) ->
+                getNavigationBarBitmap(imeSettingForSolidNavigationBar(color, lightMode),
+                        Color.BLACK, false, info.getBottomNavigationBerHeight(),
+                        DimmingTestMode.NO_DIMMING_DIALOG));
+
+        // Currently there is no way for IMEs to opt-out dark/light navigation bar mode.
+        // TODO: Allows IMEs to opt out dark/light navigation bar mode (Bug 69111208).
+        expectLightNavigationBarNotSupported((color, lightMode) ->
+                getNavigationBarBitmap(imeSettingForFloatingIme(Color.BLACK, false),
+                        color, lightMode, info.getBottomNavigationBerHeight(),
+                        DimmingTestMode.NO_DIMMING_DIALOG));
+    }
+
+    @Test
+    public void testDimmingWindow() throws Exception {
+        final NavigationBarInfo info = NavigationBarInfo.getInstance();
+
+        assumeTrue("This test does not make sense if dimming windows do not affect light "
+                + " light navigation bar for typical Activities",
+                info.supportsDimmingWindowLightNavigationBarOverride());
+
+        // Currently SYSTEM_UI_FLAG_LIGHT_NAVIGATION_BAR is ignored for IME windows.
+        // TODO: Support SYSTEM_UI_FLAG_LIGHT_NAVIGATION_BAR for IME windows (Bug 69002467)
+        expectLightNavigationBarNotSupported((color, lightMode) ->
+                getNavigationBarBitmap(imeSettingForSolidNavigationBar(color, lightMode),
+                        Color.BLACK, false, info.getBottomNavigationBerHeight(),
+                        DimmingTestMode.DIMMING_DIALOG_BEHIND_IME));
+
+        // If a dimming window is shown above the IME window, IME window's
+        // SYSTEM_UI_FLAG_LIGHT_NAVIGATION_BAR should be canceled.
+        expectLightNavigationBarNotSupported((color, lightMode) ->
+                getNavigationBarBitmap(imeSettingForSolidNavigationBar(color, lightMode),
+                        Color.BLACK, false, info.getBottomNavigationBerHeight(),
+                        DimmingTestMode.DIMMING_DIALOG_ABOVE_IME));
+    }
+}
diff --git a/tests/inputmethod/src/android/view/inputmethod/cts/OnScreenPositionTest.java b/tests/inputmethod/src/android/view/inputmethod/cts/OnScreenPositionTest.java
new file mode 100644
index 0000000..014ef90
--- /dev/null
+++ b/tests/inputmethod/src/android/view/inputmethod/cts/OnScreenPositionTest.java
@@ -0,0 +1,134 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.view.inputmethod.cts;
+
+import static com.android.cts.mockime.ImeEventStreamTestUtils.expectBindInput;
+import static com.android.cts.mockime.ImeEventStreamTestUtils.expectEvent;
+import static com.android.cts.mockime.ImeEventStreamTestUtils.waitForInputViewLayoutStable;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertTrue;
+
+import android.graphics.Rect;
+import android.os.Process;
+import android.support.test.InstrumentationRegistry;
+import android.support.test.filters.MediumTest;
+import android.support.test.runner.AndroidJUnit4;
+import android.text.TextUtils;
+import android.view.inputmethod.EditorInfo;
+import android.view.inputmethod.cts.util.EndToEndImeTestBase;
+import android.view.inputmethod.cts.util.TestActivity;
+import android.widget.EditText;
+import android.widget.LinearLayout;
+
+import com.android.compatibility.common.util.CtsTouchUtils;
+import com.android.cts.mockime.ImeEventStream;
+import com.android.cts.mockime.ImeLayoutInfo;
+import com.android.cts.mockime.ImeSettings;
+import com.android.cts.mockime.MockImeSession;
+
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+import java.util.concurrent.TimeUnit;
+import java.util.concurrent.atomic.AtomicReference;
+
+@MediumTest
+@RunWith(AndroidJUnit4.class)
+public class OnScreenPositionTest extends EndToEndImeTestBase {
+    private static final long TIMEOUT = TimeUnit.SECONDS.toMillis(5);
+    private static final long LAYOUT_STABLE_THRESHOLD = TimeUnit.SECONDS.toMillis(3);
+
+    private final static String TEST_MARKER = "android.view.inputmethod.cts.OnScreenPositionTest";
+
+    public EditText launchTestActivity() {
+        final AtomicReference<EditText> editTextRef = new AtomicReference<>();
+        TestActivity.startSync((TestActivity activity) -> {
+            final LinearLayout layout = new LinearLayout(activity);
+            layout.setOrientation(LinearLayout.VERTICAL);
+
+            final EditText editText = new EditText(activity);
+            editText.setPrivateImeOptions(TEST_MARKER);
+            editText.setHint("editText");
+            editTextRef.set(editText);
+
+            layout.addView(editText);
+            return layout;
+        });
+        return editTextRef.get();
+    }
+
+    /**
+     * Regression test for Bug 33308065.
+     */
+    @Test
+    public void testImeIsNotBehindNavBar() throws Exception {
+        final int EXPECTED_KEYBOARD_HEIGHT = 100;
+
+        try(MockImeSession imeSession = MockImeSession.create(
+                InstrumentationRegistry.getContext(),
+                InstrumentationRegistry.getInstrumentation().getUiAutomation(),
+                new ImeSettings.Builder()
+                        .setInputViewHeightWithoutSystemWindowInset(EXPECTED_KEYBOARD_HEIGHT))) {
+            final ImeEventStream stream = imeSession.openEventStream();
+
+            final EditText editText = launchTestActivity();
+
+            // Wait until the MockIme gets bound to the TestActivity.
+            expectBindInput(stream, Process.myPid(), TIMEOUT);
+
+            // Emulate tap event
+            CtsTouchUtils.emulateTapOnViewCenter(
+                    InstrumentationRegistry.getInstrumentation(), editText);
+
+            // Wait until "onStartInput" gets called for the EditText.
+            expectEvent(stream, event -> {
+                if (!TextUtils.equals("onStartInputView", event.getEventName())) {
+                    return false;
+                }
+                final EditorInfo editorInfo = event.getArguments().getParcelable("editorInfo");
+                return TextUtils.equals(TEST_MARKER, editorInfo.privateImeOptions);
+            }, TIMEOUT);
+
+            // Wait until MockIme's layout becomes stable.
+            final ImeLayoutInfo lastLayout =
+                    waitForInputViewLayoutStable(stream, LAYOUT_STABLE_THRESHOLD);
+            assertNotNull(lastLayout);
+
+            // We consider that the screenRectWithoutNavBar is a union of those two rects.
+            // See the following methods for details.
+            //  - DecorView#getColorViewTopInset(int, int)
+            //  - DecorView#getColorViewBottomInset(int, int)
+            //  - DecorView#getColorViewRightInset(int, int)
+            //  - DecorView#getColorViewLeftInset(int, int)
+            final Rect screenRectWithoutNavBar = lastLayout.getScreenRectWithoutStableInset();
+            screenRectWithoutNavBar.union(lastLayout.getScreenRectWithoutSystemWindowInset());
+
+            final Rect keyboardViewBounds = lastLayout.getInputViewBoundsInScreen();
+            // By default, the above region must contain the keyboard view region.
+            assertTrue("screenRectWithoutNavBar(" + screenRectWithoutNavBar + ") must"
+                    + " contain keyboardViewBounds(" + keyboardViewBounds + ")",
+                    screenRectWithoutNavBar.contains(keyboardViewBounds));
+
+            // Make sure that the keyboard height is expected.  Here we assume that the expected
+            // height is small enough for all the Android-based devices to show.
+            assertEquals(EXPECTED_KEYBOARD_HEIGHT,
+                    lastLayout.getInputViewBoundsInScreen().height());
+        }
+    }
+}
diff --git a/tests/inputmethod/src/android/view/inputmethod/cts/SearchViewTest.java b/tests/inputmethod/src/android/view/inputmethod/cts/SearchViewTest.java
new file mode 100644
index 0000000..1512684
--- /dev/null
+++ b/tests/inputmethod/src/android/view/inputmethod/cts/SearchViewTest.java
@@ -0,0 +1,102 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.view.inputmethod.cts;
+
+import static com.android.cts.mockime.ImeEventStreamTestUtils.expectBindInput;
+import static com.android.cts.mockime.ImeEventStreamTestUtils.expectEvent;
+
+import android.os.Process;
+import android.support.test.InstrumentationRegistry;
+import android.support.test.filters.MediumTest;
+import android.support.test.runner.AndroidJUnit4;
+import android.text.InputType;
+import android.view.inputmethod.EditorInfo;
+import android.view.inputmethod.cts.util.EndToEndImeTestBase;
+import android.view.inputmethod.cts.util.TestActivity;
+import android.widget.EditText;
+import android.widget.LinearLayout;
+import android.widget.SearchView;
+
+import com.android.compatibility.common.util.CtsTouchUtils;
+import com.android.cts.mockime.ImeEventStream;
+import com.android.cts.mockime.ImeSettings;
+import com.android.cts.mockime.MockImeSession;
+
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+import java.util.concurrent.TimeUnit;
+import java.util.concurrent.atomic.AtomicReference;
+
+@MediumTest
+@RunWith(AndroidJUnit4.class)
+public class SearchViewTest extends EndToEndImeTestBase {
+    static final long TIMEOUT = TimeUnit.SECONDS.toMillis(5);
+
+    public SearchView launchTestActivity() {
+        final AtomicReference<SearchView> searchViewRef = new AtomicReference<>();
+        TestActivity.startSync((TestActivity activity) -> {
+            final LinearLayout layout = new LinearLayout(activity);
+            layout.setOrientation(LinearLayout.VERTICAL);
+
+            final EditText initialFocusedEditText = new EditText(activity);
+            initialFocusedEditText.setHint("initialFocusedTextView");
+
+            final SearchView searchView = new SearchView(activity);
+            searchViewRef.set(searchView);
+
+            searchView.setQueryHint("hint");
+            searchView.setIconifiedByDefault(false);
+            searchView.setInputType(InputType.TYPE_TEXT_FLAG_CAP_SENTENCES);
+            searchView.setImeOptions(EditorInfo.IME_ACTION_DONE);
+
+            layout.addView(initialFocusedEditText);
+            layout.addView(searchView);
+            return layout;
+        });
+        return searchViewRef.get();
+    }
+
+    @Test
+    public void testTapThenSetQuery() throws Exception {
+        try(MockImeSession imeSession = MockImeSession.create(
+                InstrumentationRegistry.getContext(),
+                InstrumentationRegistry.getInstrumentation().getUiAutomation(),
+                new ImeSettings.Builder())) {
+            final ImeEventStream stream = imeSession.openEventStream();
+
+            final SearchView searchView = launchTestActivity();
+
+            // Wait until the MockIme gets bound to the TestActivity.
+            expectBindInput(stream, Process.myPid(), TIMEOUT);
+
+            // Emulate tap event
+            CtsTouchUtils.emulateTapOnViewCenter(
+                    InstrumentationRegistry.getInstrumentation(), searchView);
+
+            // Wait until "showSoftInput" gets called with a real InputConnection
+            expectEvent(stream, event ->
+                    "showSoftInput".equals(event.getEventName())
+                            && !event.getExitState().hasDummyInputConnection(), TIMEOUT);
+
+            // Make sure that "setQuery" triggers "hideSoftInput" in the IME side.
+            InstrumentationRegistry.getInstrumentation().runOnMainSync(
+                    () -> searchView.setQuery("test", true /* submit */));
+            expectEvent(stream, event -> "hideSoftInput".equals(event.getEventName()), TIMEOUT);
+        }
+    }
+}
diff --git a/tests/inputmethod/src/android/view/inputmethod/cts/util/EndToEndImeTestBase.java b/tests/inputmethod/src/android/view/inputmethod/cts/util/EndToEndImeTestBase.java
new file mode 100644
index 0000000..e68a2c7
--- /dev/null
+++ b/tests/inputmethod/src/android/view/inputmethod/cts/util/EndToEndImeTestBase.java
@@ -0,0 +1,47 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License
+ */
+
+package android.view.inputmethod.cts.util;
+
+import static org.junit.Assume.assumeTrue;
+
+import android.content.Intent;
+import android.content.pm.PackageManager;
+import android.support.test.InstrumentationRegistry;
+
+import org.junit.Before;
+import org.junit.BeforeClass;
+
+public class EndToEndImeTestBase {
+
+    @BeforeClass
+    public static void assumeFeatureInputMethod() {
+        assumeTrue("MockIme cannot be used for devices that do not support installable IMEs",
+                InstrumentationRegistry.getContext().getPackageManager().hasSystemFeature(
+                        PackageManager.FEATURE_INPUT_METHODS));
+    }
+
+    @Before
+    public void showStateInitializeActivity() {
+        final Intent intent = new Intent()
+                .setAction(Intent.ACTION_MAIN)
+                .setClass(InstrumentationRegistry.getTargetContext(), StateInitializeActivity.class)
+                .addFlags(Intent.FLAG_ACTIVITY_NEW_TASK)
+                .addFlags(Intent.FLAG_ACTIVITY_NO_ANIMATION)
+                .addFlags(Intent.FLAG_ACTIVITY_CLEAR_TASK);
+        InstrumentationRegistry.getInstrumentation().startActivitySync(intent);
+    }
+}
diff --git a/tests/inputmethod/src/android/view/inputmethod/cts/util/IWindowFocusStealer.aidl b/tests/inputmethod/src/android/view/inputmethod/cts/util/IWindowFocusStealer.aidl
new file mode 100644
index 0000000..aef3bf3
--- /dev/null
+++ b/tests/inputmethod/src/android/view/inputmethod/cts/util/IWindowFocusStealer.aidl
@@ -0,0 +1,23 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License
+ */
+
+package android.view.inputmethod.cts.util;
+
+import android.os.ResultReceiver;
+
+interface IWindowFocusStealer {
+    void stealWindowFocus(in IBinder parentAppWindowToken, in ResultReceiver resultReceiver);
+}
diff --git a/tests/inputmethod/src/android/view/inputmethod/cts/util/ImeAwareEditText.java b/tests/inputmethod/src/android/view/inputmethod/cts/util/ImeAwareEditText.java
new file mode 100644
index 0000000..b48e750
--- /dev/null
+++ b/tests/inputmethod/src/android/view/inputmethod/cts/util/ImeAwareEditText.java
@@ -0,0 +1,92 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.view.inputmethod.cts.util;
+
+import android.content.Context;
+import android.util.AttributeSet;
+import android.view.View;
+import android.view.inputmethod.EditorInfo;
+import android.view.inputmethod.InputConnection;
+import android.view.inputmethod.InputMethodManager;
+import android.widget.EditText;
+
+public class ImeAwareEditText extends EditText {
+    private boolean mHasPendingShowSoftInputRequest;
+    final Runnable mRunShowSoftInputIfNecessary = () -> showSoftInputIfNecessary();
+
+    public ImeAwareEditText(Context context) {
+        super(context, null);
+    }
+
+    public ImeAwareEditText(Context context, AttributeSet attrs) {
+        super(context, attrs);
+    }
+
+    public ImeAwareEditText(Context context, AttributeSet attrs, int defStyleAttr) {
+        super(context, attrs, defStyleAttr);
+    }
+
+    public ImeAwareEditText(Context context, AttributeSet attrs, int defStyleAttr,
+            int defStyleRes) {
+        super(context, attrs, defStyleAttr, defStyleRes);
+    }
+
+    /**
+     * This method is called back by the system when the system is about to establish a connection
+     * to the current input method.
+     *
+     * <p>This is a good and reliable signal to schedule a pending task to call
+     * {@link InputMethodManager#showSoftInput(View, int)}.</p>
+     *
+     * @param editorInfo context about the text input field.
+     * @return {@link InputConnection} to be passed to the input method.
+     */
+    @Override
+    public InputConnection onCreateInputConnection(EditorInfo editorInfo) {
+        final InputConnection ic = super.onCreateInputConnection(editorInfo);
+        if (mHasPendingShowSoftInputRequest) {
+            removeCallbacks(mRunShowSoftInputIfNecessary);
+            post(mRunShowSoftInputIfNecessary);
+        }
+        return ic;
+    }
+
+    private void showSoftInputIfNecessary() {
+        if (mHasPendingShowSoftInputRequest) {
+            final InputMethodManager imm =
+                    getContext().getSystemService(InputMethodManager.class);
+            imm.showSoftInput(this, 0);
+            mHasPendingShowSoftInputRequest = false;
+        }
+    }
+
+    public void scheduleShowSoftInput() {
+        final InputMethodManager imm = getContext().getSystemService(InputMethodManager.class);
+        if (imm.isActive(this)) {
+            // This means that ImeAwareEditText is already connected to the IME.
+            // InputMethodManager#showSoftInput() is guaranteed to pass client-side focus check.
+            mHasPendingShowSoftInputRequest = false;
+            removeCallbacks(mRunShowSoftInputIfNecessary);
+            imm.showSoftInput(this, 0);
+            return;
+        }
+
+        // Otherwise, InputMethodManager#showSoftInput() should be deferred after
+        // onCreateInputConnection().
+        mHasPendingShowSoftInputRequest = true;
+    }
+}
diff --git a/tests/inputmethod/src/android/view/inputmethod/cts/util/LightNavigationBarVerifier.java b/tests/inputmethod/src/android/view/inputmethod/cts/util/LightNavigationBarVerifier.java
new file mode 100644
index 0000000..401caa3
--- /dev/null
+++ b/tests/inputmethod/src/android/view/inputmethod/cts/util/LightNavigationBarVerifier.java
@@ -0,0 +1,209 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License
+ */
+
+package android.view.inputmethod.cts.util;
+
+import static org.junit.Assert.assertEquals;
+
+import android.graphics.Bitmap;
+import android.graphics.Color;
+import android.support.annotation.ColorInt;
+import android.support.annotation.NonNull;
+import android.support.annotation.Nullable;
+import android.util.SparseIntArray;
+
+import java.util.Arrays;
+import java.util.OptionalDouble;
+import java.util.function.Supplier;
+
+/**
+ * A utility class to evaluate if {@link android.view.View#SYSTEM_UI_FLAG_LIGHT_NAVIGATION_BAR} is
+ * supported on the device or not.
+ */
+public class LightNavigationBarVerifier {
+
+    /** This value actually does not have strong rationale. */
+    private static float LIGHT_NAVBAR_SUPPORTED_THRESHOLD = 20.0f;
+
+    /** This value actually does not have strong rationale. */
+    private static float LIGHT_NAVBAR_NOT_SUPPORTED_THRESHOLD = 5.0f;
+
+    @FunctionalInterface
+    public interface ScreenshotSupplier {
+        @NonNull
+        Bitmap takeScreenshot(@ColorInt int navigationBarColor, boolean lightMode) throws Exception;
+    }
+
+    public enum ResultType {
+        /**
+         * {@link android.view.View#SYSTEM_UI_FLAG_LIGHT_NAVIGATION_BAR} seems to be not supported.
+         */
+        NOT_SUPPORTED,
+        /**
+         * {@link android.view.View#SYSTEM_UI_FLAG_LIGHT_NAVIGATION_BAR} seems to be supported.
+         */
+        SUPPORTED,
+        /**
+         * Not sure if {@link android.view.View#SYSTEM_UI_FLAG_LIGHT_NAVIGATION_BAR} is supported.
+         */
+        UNKNOWN,
+    }
+
+    static final class Result {
+        @NonNull
+        public final ResultType mResult;
+        @NonNull
+        public final Supplier<String> mAssertionMessageProvider;
+
+        public Result(@NonNull ResultType result,
+                @Nullable Supplier<String> assertionMessageProvider) {
+            mResult = result;
+            mAssertionMessageProvider = assertionMessageProvider;
+        }
+
+        @NonNull
+        public ResultType getResult() {
+            return mResult;
+        }
+
+        @NonNull
+        public String getAssertionMessage() {
+            return mAssertionMessageProvider.get();
+        }
+    }
+
+    /**
+     * Asserts that {@link android.view.View#SYSTEM_UI_FLAG_LIGHT_NAVIGATION_BAR} is supported on
+     * this device.
+     *
+     * @param screenshotSupplier callback to provide {@link Bitmap} of the navigation bar region
+     */
+    public static void expectLightNavigationBarSupported(
+            @NonNull ScreenshotSupplier screenshotSupplier) throws Exception {
+        final Result result = verify(screenshotSupplier);
+        assertEquals(result.getAssertionMessage(), ResultType.SUPPORTED, result.getResult());
+    }
+
+    /**
+     * Asserts that {@link android.view.View#SYSTEM_UI_FLAG_LIGHT_NAVIGATION_BAR} is not supported
+     * on this device.
+     *
+     * @param screenshotSupplier callback to provide {@link Bitmap} of the navigation bar region
+     */
+    public static void expectLightNavigationBarNotSupported(
+            @NonNull ScreenshotSupplier screenshotSupplier) throws Exception {
+        final Result result = verify(screenshotSupplier);
+        assertEquals(result.getAssertionMessage(), ResultType.NOT_SUPPORTED, result.getResult());
+    }
+
+    @FunctionalInterface
+    private interface ColorOperator {
+        int operate(@ColorInt int color1, @ColorInt int color2);
+    }
+
+    private static int[] operateColorArrays(@NonNull int[] pixels1, @NonNull int[] pixels2,
+            @NonNull ColorOperator operator) {
+        assertEquals(pixels1.length, pixels2.length);
+        final int numPixels = pixels1.length;
+        final int[] result = new int[numPixels];
+        for (int i = 0; i < numPixels; ++i) {
+            result[i] = operator.operate(pixels1[i], pixels2[i]);
+        }
+        return result;
+    }
+
+    @NonNull
+    private static int[] getPixels(@NonNull Bitmap bitmap) {
+        final int width = bitmap.getWidth();
+        final int height = bitmap.getHeight();
+        final int[] pixels = new int[width * height];
+        bitmap.getPixels(pixels, 0 /* offset */, width /* stride */, 0 /* x */, 0 /* y */,
+                width, height);
+        return pixels;
+    }
+
+    @NonNull
+    static Result verify(
+            @NonNull ScreenshotSupplier screenshotSupplier) throws Exception {
+        final int[] darkNavBarPixels = getPixels(
+                screenshotSupplier.takeScreenshot(Color.BLACK, false));
+        final int[] lightNavBarPixels = getPixels(
+                screenshotSupplier.takeScreenshot(Color.BLACK, true));
+
+        if (darkNavBarPixels.length != lightNavBarPixels.length) {
+            throw new IllegalStateException("Pixel count mismatch."
+                    + " dark=" + darkNavBarPixels.length + " light=" + lightNavBarPixels.length);
+        }
+
+        final int[][] channelDiffs = new int[][] {
+                operateColorArrays(darkNavBarPixels, lightNavBarPixels,
+                        (dark, light) -> Color.red(dark) - Color.red(light)),
+                operateColorArrays(darkNavBarPixels, lightNavBarPixels,
+                        (dark, light) -> Color.green(dark) - Color.green(light)),
+                operateColorArrays(darkNavBarPixels, lightNavBarPixels,
+                        (dark, light) -> Color.blue(dark) - Color.blue(light)),
+        };
+
+        if (Arrays.stream(channelDiffs).allMatch(
+                diffs -> Arrays.stream(diffs).allMatch(diff -> diff == 0))) {
+            // Exactly the same image.  Safe to conclude that light navigation bar is not supported.
+            return new Result(ResultType.NOT_SUPPORTED, () -> dumpDiffStreams(channelDiffs));
+        }
+
+        if (Arrays.stream(channelDiffs).anyMatch(diffs -> {
+            final OptionalDouble average = Arrays.stream(diffs).filter(diff -> diff != 0).average();
+            return average.isPresent() && average.getAsDouble() > LIGHT_NAVBAR_SUPPORTED_THRESHOLD;
+        })) {
+            // If darkNavBarPixels have brighter pixels in at least one color channel
+            // (red, green, blue), we consider that it is because light navigation bar takes effect.
+            // Keep in mind that with SYSTEM_UI_FLAG_LIGHT_NAVIGATION_BAR navigation bar buttons
+            // are expected to become darker.  So if everything works fine, darkNavBarPixels should
+            // have brighter pixels.
+            return new Result(ResultType.SUPPORTED, () -> dumpDiffStreams(channelDiffs));
+        }
+
+        if (Arrays.stream(channelDiffs).allMatch(diffs -> {
+            final OptionalDouble average = Arrays.stream(diffs).filter(diff -> diff != 0).average();
+            return average.isPresent()
+                    && Math.abs(average.getAsDouble()) < LIGHT_NAVBAR_NOT_SUPPORTED_THRESHOLD;
+        })) {
+            // If all color channels (red, green, blue) have diffs less than a certain threshold,
+            // consider light navigation bar is not supported.  For instance, some devices may
+            // intentionally add some fluctuations to the navigation bar button colors/positions.
+            return new Result(ResultType.NOT_SUPPORTED, () -> dumpDiffStreams(channelDiffs));
+        }
+
+        return new Result(ResultType.UNKNOWN, () -> dumpDiffStreams(channelDiffs));
+    }
+
+    @NonNull
+    private static String dumpDiffStreams(@NonNull int[][] diffStreams) {
+        final String[] channelNames = {"red", "green", "blue"};
+        final StringBuilder sb = new StringBuilder();
+        sb.append("diff histogram: ");
+        for (int i = 0; i < diffStreams.length; ++i) {
+            if (i != 0) {
+                sb.append(", ");
+            }
+            final int[] channel = diffStreams[i];
+            final SparseIntArray histogram = new SparseIntArray();
+            Arrays.stream(channel).sorted().forEachOrdered(
+                    diff -> histogram.put(diff, histogram.get(diff, 0) + 1));
+            sb.append(channelNames[i]).append(": ").append(histogram);
+        }
+        return sb.toString();
+    }
+}
diff --git a/tests/inputmethod/src/android/view/inputmethod/cts/util/NavigationBarColorVerifier.java b/tests/inputmethod/src/android/view/inputmethod/cts/util/NavigationBarColorVerifier.java
new file mode 100644
index 0000000..6cdb000
--- /dev/null
+++ b/tests/inputmethod/src/android/view/inputmethod/cts/util/NavigationBarColorVerifier.java
@@ -0,0 +1,172 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License
+ */
+
+package android.view.inputmethod.cts.util;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertTrue;
+
+import android.graphics.Bitmap;
+import android.graphics.Color;
+import android.support.annotation.ColorInt;
+import android.support.annotation.NonNull;
+
+import java.util.ArrayList;
+
+/**
+ * A utility class to evaluate if {@link android.view.Window#setNavigationBarColor(int)} is
+ * supported on the device or not.
+ */
+public class NavigationBarColorVerifier {
+
+    private static final class ScreenShot {
+        @ColorInt
+        public final int mBackgroundColor;
+        @NonNull
+        public final int[] mPixels;
+
+        public ScreenShot(@ColorInt int backgroundColor, @NonNull Bitmap bitmap) {
+            mBackgroundColor = backgroundColor;
+            final int width = bitmap.getWidth();
+            final int height = bitmap.getHeight();
+            mPixels = new int[width * height];
+            bitmap.getPixels(mPixels, 0 /* offset */, width /* stride */, 0 /* x */, 0 /* y */,
+                    width, height);
+        }
+    }
+
+    public enum ResultType {
+        /**
+         * {@link android.view.Window#setNavigationBarColor(int)} seems to be not supported.
+         */
+        NOT_SUPPORTED,
+        /**
+         * {@link android.view.Window#setNavigationBarColor(int)} seems to be supported.
+         */
+        SUPPORTED,
+    }
+
+    static final class Result {
+        @NonNull
+        public final ResultType mResult;
+        @NonNull
+        public final String mAssertionMessage;
+
+        public Result(@NonNull ResultType result, @NonNull String assertionMessage) {
+            mResult = result;
+            mAssertionMessage = assertionMessage;
+        }
+
+        @NonNull
+        public ResultType getResult() {
+            return mResult;
+        }
+
+        @NonNull
+        public String getAssertionMessage() {
+            return mAssertionMessage;
+        }
+    }
+
+    @FunctionalInterface
+    public interface ScreenshotSupplier {
+        @NonNull
+        Bitmap takeScreenshot(@ColorInt int navigationBarColor) throws Exception;
+    }
+
+    @NonNull
+    static Result verify(@NonNull ScreenshotSupplier screenshotSupplier) throws Exception {
+        final ArrayList<ScreenShot> screenShots = new ArrayList<>();
+        final int[] colors = new int[]{Color.RED, Color.GREEN, Color.BLUE};
+        for (int color : colors) {
+            screenShots.add(new ScreenShot(color, screenshotSupplier.takeScreenshot(color)));
+        }
+        return verifyInternal(screenShots);
+    }
+
+    /**
+     * Asserts that {@link android.view.Window#setNavigationBarColor(int)} is supported on this
+     * device.
+     *
+     * @param screenshotSupplier callback to provide {@link Bitmap} of the navigation bar region
+     */
+    public static void expectNavigationBarColorSupported(
+            @NonNull ScreenshotSupplier screenshotSupplier) throws Exception {
+        final Result result = verify(screenshotSupplier);
+        assertEquals(result.getAssertionMessage(), ResultType.SUPPORTED, result.getResult());
+    }
+
+    /**
+     * Asserts that {@link android.view.Window#setNavigationBarColor(int)} is not supported on this
+     * device.
+     *
+     * @param screenshotSupplier callback to provide {@link Bitmap} of the navigation bar region
+     */
+    public static void expectNavigationBarColorNotSupported(
+            @NonNull ScreenshotSupplier screenshotSupplier) throws Exception {
+        final Result result = verify(screenshotSupplier);
+        assertEquals(result.getAssertionMessage(), ResultType.NOT_SUPPORTED, result.getResult());
+    }
+
+    private static Result verifyInternal(@NonNull ArrayList<ScreenShot> screenShots) {
+        final int numScreenShots = screenShots.size();
+        assertTrue(
+                "This algorithm requires at least 3 screen shots. size=" + numScreenShots,
+                numScreenShots >= 3);
+        assertEquals("All screenshots must have different background colors",
+                numScreenShots,
+                screenShots.stream()
+                        .mapToInt(screenShot -> screenShot.mBackgroundColor)
+                        .distinct()
+                        .count());
+        assertEquals("All screenshots must have the same pixel count",
+                1,
+                screenShots.stream()
+                        .mapToInt(screenShot -> screenShot.mPixels.length)
+                        .distinct()
+                        .count());
+
+        long numCompletelyFixedColorPixels = 0;
+        long numColorChangedAsRequestedPixels = 0;
+        final int numPixels = screenShots.get(0).mPixels.length;
+        for (int pixelIndex = 0; pixelIndex < numPixels; ++pixelIndex) {
+            final int i = pixelIndex;
+            final long numFoundColors = screenShots.stream()
+                    .mapToInt(screenShot -> screenShot.mPixels[i])
+                    .distinct()
+                    .count();
+            if (numFoundColors == 1) {
+                numCompletelyFixedColorPixels++;
+            }
+            final long matchingScore =  screenShots.stream()
+                    .filter(screenShot -> screenShot.mPixels[i] == screenShot.mBackgroundColor)
+                    .count();
+            if (matchingScore == numScreenShots) {
+                numColorChangedAsRequestedPixels++;
+            }
+        }
+        final String assertionMessage = "numPixels=" + numPixels
+                + " numColorChangedAsRequestedPixels=" + numColorChangedAsRequestedPixels
+                + " numCompletelyFixedColorPixels=" + numCompletelyFixedColorPixels;
+
+        // OK, even 1 pixel is enough.
+        if (numColorChangedAsRequestedPixels > 0) {
+            return new Result(ResultType.SUPPORTED, assertionMessage);
+        }
+
+        return new Result(ResultType.NOT_SUPPORTED, assertionMessage);
+    }
+}
diff --git a/tests/inputmethod/src/android/view/inputmethod/cts/util/NavigationBarInfo.java b/tests/inputmethod/src/android/view/inputmethod/cts/util/NavigationBarInfo.java
new file mode 100644
index 0000000..3c71968
--- /dev/null
+++ b/tests/inputmethod/src/android/view/inputmethod/cts/util/NavigationBarInfo.java
@@ -0,0 +1,241 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License
+ */
+
+package android.view.inputmethod.cts.util;
+
+import static android.support.test.InstrumentationRegistry.getInstrumentation;
+import static android.view.WindowManager.LayoutParams.FLAG_DIM_BEHIND;
+import static android.view.inputmethod.cts.util.TestUtils.getOnMainSync;
+
+import android.app.AlertDialog;
+import android.app.Instrumentation;
+import android.content.Intent;
+import android.graphics.Bitmap;
+import android.graphics.Point;
+import android.graphics.Rect;
+import android.support.annotation.ColorInt;
+import android.support.annotation.NonNull;
+import android.support.annotation.Nullable;
+import android.support.test.InstrumentationRegistry;
+import android.util.Size;
+import android.view.View;
+import android.view.ViewGroup;
+import android.view.WindowInsets;
+import android.widget.TextView;
+
+import java.util.concurrent.TimeUnit;
+import java.util.concurrent.atomic.AtomicReference;
+
+/**
+ * A utility class to write tests that depend on some capabilities related to navigation bar.
+ */
+public class NavigationBarInfo {
+    private static final long BEFORE_SCREENSHOT_WAIT = TimeUnit.SECONDS.toMillis(1);
+
+    private final boolean mHasBottomNavigationBar;
+    private final int mBottomNavigationBerHeight;
+    private final boolean mSupportsNavigationBarColor;
+    private final boolean mSupportsLightNavigationBar;
+    private final boolean mSupportsDimmingWindowLightNavigationBarOverride;
+
+    private NavigationBarInfo(boolean hasBottomNavigationBar, int bottomNavigationBerHeight,
+            boolean supportsNavigationBarColor, boolean supportsLightNavigationBar,
+            boolean supportsDimmingWindowLightNavigationBarOverride) {
+        mHasBottomNavigationBar = hasBottomNavigationBar;
+        mBottomNavigationBerHeight = bottomNavigationBerHeight;
+        mSupportsNavigationBarColor = supportsNavigationBarColor;
+        mSupportsLightNavigationBar = supportsLightNavigationBar;
+        mSupportsDimmingWindowLightNavigationBarOverride =
+                supportsDimmingWindowLightNavigationBarOverride;
+    }
+
+    @Nullable
+    private static NavigationBarInfo sInstance;
+
+    /**
+     * Returns a {@link NavigationBarInfo} instance.
+     *
+     * <p>As a performance optimizations, this method internally caches the previous result and
+     * returns the same result if this gets called multiple times.</p>
+     *
+     * <p>Note: The caller should be aware that this method may launch {@link TestActivity}
+     * internally.</p>
+     *
+     * @return {@link NavigationBarInfo} obtained with {@link TestActivity}.
+     */
+    @NonNull
+    public static NavigationBarInfo getInstance() throws Exception {
+        if (sInstance != null) {
+            return sInstance;
+        }
+
+        final int actualBottomInset;
+        {
+            final AtomicReference<View> viewRef = new AtomicReference<>();
+            TestActivity.startSync((TestActivity activity) -> {
+                final View view = new View(activity);
+                view.setLayoutParams(new ViewGroup.LayoutParams(
+                        ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.MATCH_PARENT));
+                viewRef.set(view);
+                return view;
+            }, Intent.FLAG_ACTIVITY_NO_ANIMATION);
+
+            final View view = viewRef.get();
+
+            final WindowInsets windowInsets = getOnMainSync(() -> view.getRootWindowInsets());
+            if (!windowInsets.hasStableInsets() || windowInsets.getStableInsetBottom() <= 0) {
+                return new NavigationBarInfo(false, 0, false, false, false);
+            }
+            final Size displaySize = getOnMainSync(() -> {
+                final Point size = new Point();
+                view.getDisplay().getRealSize(size);
+                return new Size(size.x, size.y);
+            });
+
+            final Rect viewBoundsOnScreen = getOnMainSync(() -> {
+                final int[] xy = new int[2];
+                view.getLocationOnScreen(xy);
+                final int x = xy[0];
+                final int y = xy[1];
+                return new Rect(x, y, x + view.getWidth(), y + view.getHeight());
+            });
+            actualBottomInset = displaySize.getHeight() - viewBoundsOnScreen.bottom;
+            if (actualBottomInset != windowInsets.getStableInsetBottom()) {
+                sInstance = new NavigationBarInfo(false, 0, false, false, false);
+                return sInstance;
+            }
+        }
+
+        final boolean colorSupported = NavigationBarColorVerifier.verify(
+                color -> getBottomNavigationBarBitmapForActivity(
+                        color, false /* lightNavigationBar */, actualBottomInset,
+                        false /* showDimmingDialog */)).getResult()
+                == NavigationBarColorVerifier.ResultType.SUPPORTED;
+
+        final boolean lightModeSupported = LightNavigationBarVerifier.verify(
+                (color, lightNavigationBar) -> getBottomNavigationBarBitmapForActivity(
+                        color, lightNavigationBar, actualBottomInset,
+                        false /* showDimmingDialog */)).getResult()
+                == LightNavigationBarVerifier.ResultType.SUPPORTED;
+
+        final boolean dimmingSupported = lightModeSupported && LightNavigationBarVerifier.verify(
+                (color, lightNavigationBar) -> getBottomNavigationBarBitmapForActivity(
+                        color, lightNavigationBar, actualBottomInset,
+                        true /* showDimmingDialog */)).getResult()
+                == LightNavigationBarVerifier.ResultType.NOT_SUPPORTED;
+
+        sInstance = new NavigationBarInfo(
+                true, actualBottomInset, colorSupported, lightModeSupported, dimmingSupported);
+        return sInstance;
+    }
+
+    @NonNull
+    private static Bitmap getBottomNavigationBarBitmapForActivity(
+            @ColorInt int navigationBarColor, boolean lightNavigationBar,
+            int bottomNavigationBarHeight, boolean showDimmingDialog) throws Exception {
+        final Instrumentation instrumentation = InstrumentationRegistry.getInstrumentation();
+
+
+        final TestActivity testActivity = TestActivity.startSync(activity -> {
+            final View view = new View(activity);
+            activity.getWindow().setNavigationBarColor(navigationBarColor);
+
+            // Set/unset SYSTEM_UI_FLAG_LIGHT_NAVIGATION_BAR if necessary.
+            final int currentVis = view.getSystemUiVisibility();
+            final int newVis = (currentVis & ~View.SYSTEM_UI_FLAG_LIGHT_NAVIGATION_BAR)
+                    | (lightNavigationBar ? View.SYSTEM_UI_FLAG_LIGHT_NAVIGATION_BAR : 0);
+            if (currentVis != newVis) {
+                view.setSystemUiVisibility(newVis);
+            }
+
+            view.setLayoutParams(new ViewGroup.LayoutParams(
+                    ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.MATCH_PARENT));
+            return view;
+        });
+        instrumentation.waitForIdleSync();
+
+        final AlertDialog dialog;
+        if (showDimmingDialog) {
+            dialog = getOnMainSync(() -> {
+                final TextView textView = new TextView(testActivity);
+                textView.setText("Dimming Window");
+                final AlertDialog alertDialog = new AlertDialog.Builder(testActivity)
+                        .setView(textView)
+                        .create();
+                alertDialog.getWindow().setFlags(FLAG_DIM_BEHIND, FLAG_DIM_BEHIND);
+                alertDialog.show();
+                return alertDialog;
+            });
+        } else {
+            dialog = null;
+        }
+
+        Thread.sleep(BEFORE_SCREENSHOT_WAIT);
+
+        final Bitmap fullBitmap = getInstrumentation().getUiAutomation().takeScreenshot();
+        final Bitmap bottomNavBarBitmap = Bitmap.createBitmap(fullBitmap, 0,
+                fullBitmap.getHeight() - bottomNavigationBarHeight, fullBitmap.getWidth(),
+                bottomNavigationBarHeight);
+        if (dialog != null) {
+            // Dialog#dismiss() is a thread safe method so we don't need to call this from the UI
+            // thread.
+            dialog.dismiss();
+        }
+        return bottomNavBarBitmap;
+    }
+
+    /**
+     * @return {@code true} if this device seems to have bottom navigation bar.
+     */
+    public boolean hasBottomNavigationBar() {
+        return mHasBottomNavigationBar;
+    }
+
+    /**
+     * @return height of the bottom navigation bar. Valid only when
+     *         {@link #hasBottomNavigationBar()} returns {@code true}
+     */
+    public int getBottomNavigationBerHeight() {
+        return mBottomNavigationBerHeight;
+    }
+
+    /**
+     * @return {@code true} if {@link android.view.Window#setNavigationBarColor(int)} seem to take
+     *         effect on this device. Valid only when {@link #hasBottomNavigationBar()} returns
+     *         {@code true}
+     */
+    public boolean supportsNavigationBarColor() {
+        return mSupportsNavigationBarColor;
+    }
+
+    /**
+     * @return {@code true} if {@link android.view.View#SYSTEM_UI_FLAG_LIGHT_NAVIGATION_BAR} seem to
+     *         take effect on this device. Valid only when {@link #hasBottomNavigationBar()} returns
+     *         {@code true}
+     */
+    public boolean supportsLightNavigationBar() {
+        return mSupportsLightNavigationBar;
+    }
+
+    /**
+     * @return {@code true} if {@link android.view.View#SYSTEM_UI_FLAG_LIGHT_NAVIGATION_BAR} will be
+     *         canceled when a {@link android.view.Window} with
+     *         {@link android.view.WindowManager.LayoutParams#FLAG_DIM_BEHIND} is shown.
+     */
+    public boolean supportsDimmingWindowLightNavigationBarOverride() {
+        return mSupportsDimmingWindowLightNavigationBarOverride;
+    }
+}
diff --git a/tests/inputmethod/src/android/view/inputmethod/cts/util/StateInitializeActivity.java b/tests/inputmethod/src/android/view/inputmethod/cts/util/StateInitializeActivity.java
new file mode 100644
index 0000000..e9c15cc
--- /dev/null
+++ b/tests/inputmethod/src/android/view/inputmethod/cts/util/StateInitializeActivity.java
@@ -0,0 +1,55 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License
+ */
+
+package android.view.inputmethod.cts.util;
+
+import static android.view.WindowManager.LayoutParams.SOFT_INPUT_STATE_ALWAYS_HIDDEN;
+
+import android.app.Activity;
+import android.graphics.Color;
+import android.os.Bundle;
+import android.view.View;
+import android.view.ViewGroup;
+
+/**
+ * A nop {@link Activity} to make sure that the test starts from a deterministic state.
+ *
+ * <p>Currently this {@link Activity} makes sure the following things</p>
+ * <li>
+ *     <ul>Hide the software keyboard with
+ *     {@link android.view.WindowManager.LayoutParams#SOFT_INPUT_STATE_ALWAYS_HIDDEN}</ul>
+ *     <ul>Make sure that the navigation bar (if supported) is rendered with {@link Color#BLACK}.
+ *     </ul>
+ * </li>
+ */
+public class StateInitializeActivity extends Activity {
+    @Override
+    protected void onCreate(Bundle bundle){
+        super.onCreate(bundle);
+        final View view = new View(this);
+        view.setBackgroundColor(Color.WHITE);
+        view.setLayoutParams(new ViewGroup.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT,
+                ViewGroup.LayoutParams.MATCH_PARENT));
+
+        // Make sure that IME is hidden in the initial state
+        getWindow().setSoftInputMode(SOFT_INPUT_STATE_ALWAYS_HIDDEN);
+
+        // Make sure that navigation bar is rendered with black (if supported).
+        getWindow().setNavigationBarColor(Color.BLACK);
+
+        setContentView(view);
+    }
+}
diff --git a/tests/inputmethod/src/android/view/inputmethod/cts/util/TestActivity.java b/tests/inputmethod/src/android/view/inputmethod/cts/util/TestActivity.java
new file mode 100644
index 0000000..85c3e66
--- /dev/null
+++ b/tests/inputmethod/src/android/view/inputmethod/cts/util/TestActivity.java
@@ -0,0 +1,86 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License
+ */
+
+package android.view.inputmethod.cts.util;
+
+import android.app.Activity;
+import android.content.Intent;
+import android.os.Bundle;
+import android.support.annotation.NonNull;
+import android.support.test.InstrumentationRegistry;
+import android.view.View;
+
+import java.util.concurrent.atomic.AtomicReference;
+import java.util.function.Function;
+
+public final class TestActivity extends Activity {
+
+    private static final AtomicReference<Function<TestActivity, View>> sInitializer =
+            new AtomicReference<>();
+
+    private Function<TestActivity, View> mInitializer = null;
+
+    @Override
+    protected void onCreate(Bundle savedInstanceState) {
+        super.onCreate(savedInstanceState);
+        if (mInitializer == null) {
+            mInitializer = sInitializer.get();
+        }
+        setContentView(mInitializer.apply(this));
+    }
+
+    /**
+     * Launches {@link TestActivity} with the given initialization logic for content view.
+     *
+     * <p>As long as you are using {@link android.support.test.runner.AndroidJUnitRunner}, the test
+     * runner automatically calls {@link Activity#finish()} for the {@link Activity} launched when
+     * the test finished.  You do not need to explicitly call {@link Activity#finish()}.</p>
+     *
+     * @param activityInitializer initializer to supply {@link View} to be passed to
+     *                           {@link Activity#setContentView(View)}
+     * @return {@link TestActivity} launched
+     */
+    public static TestActivity startSync(
+            @NonNull Function<TestActivity, View> activityInitializer) {
+        return startSync(activityInitializer, 0 /* noAnimation */);
+    }
+
+    /**
+     * Launches {@link TestActivity} with the given initialization logic for content view.
+     *
+     * <p>As long as you are using {@link android.support.test.runner.AndroidJUnitRunner}, the test
+     * runner automatically calls {@link Activity#finish()} for the {@link Activity} launched when
+     * the test finished.  You do not need to explicitly call {@link Activity#finish()}.</p>
+     *
+     * @param activityInitializer initializer to supply {@link View} to be passed to
+     *                           {@link Activity#setContentView(View)}
+     * @param additionalFlags flags to be set to {@link Intent#setFlags(int)}
+     * @return {@link TestActivity} launched
+     */
+    public static TestActivity startSync(
+            @NonNull Function<TestActivity, View> activityInitializer,
+            int additionalFlags) {
+        sInitializer.set(activityInitializer);
+        final Intent intent = new Intent()
+                .setAction(Intent.ACTION_MAIN)
+                .setClass(InstrumentationRegistry.getContext(), TestActivity.class)
+                .addFlags(Intent.FLAG_ACTIVITY_NEW_TASK)
+                .addFlags(Intent.FLAG_ACTIVITY_CLEAR_TASK)
+                .addFlags(additionalFlags);
+        return (TestActivity) InstrumentationRegistry
+                .getInstrumentation().startActivitySync(intent);
+    }
+}
diff --git a/tests/inputmethod/src/android/view/inputmethod/cts/util/TestUtils.java b/tests/inputmethod/src/android/view/inputmethod/cts/util/TestUtils.java
new file mode 100644
index 0000000..b5f9c9a
--- /dev/null
+++ b/tests/inputmethod/src/android/view/inputmethod/cts/util/TestUtils.java
@@ -0,0 +1,78 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.view.inputmethod.cts.util;
+
+import android.app.Instrumentation;
+import android.support.annotation.NonNull;
+import android.support.test.InstrumentationRegistry;
+
+import java.util.concurrent.TimeoutException;
+import java.util.concurrent.atomic.AtomicBoolean;
+import java.util.concurrent.atomic.AtomicReference;
+import java.util.function.BooleanSupplier;
+import java.util.function.Supplier;
+
+public final class TestUtils {
+    private static final long TIME_SLICE = 100;  // msec
+
+    /**
+     * Retrieves a value that needs to be obtained on the main thread.
+     *
+     * <p>A simple utility method that helps to return an object from the UI thread.</p>
+     *
+     * @param supplier callback to be called on the UI thread to return a value
+     * @param <T> Type of the value to be returned
+     * @return Value returned from {@code supplier}
+     */
+    public static <T> T getOnMainSync(@NonNull Supplier<T> supplier) {
+        final AtomicReference<T> result = new AtomicReference<>();
+        final Instrumentation instrumentation = InstrumentationRegistry.getInstrumentation();
+        instrumentation.runOnMainSync(() -> result.set(supplier.get()));
+        return result.get();
+    }
+
+    /**
+     * Does polling loop on the UI thread to wait until the given condition is met.
+     *
+     * @param condition Condition to be satisfied. This is guaranteed to run on the UI thread.
+     * @param timeout timeout in millisecond
+     * @throws TimeoutException when the no event is matched to the given condition within
+     *                          {@code timeout}
+     */
+    public static void waitOnMainUntil(@NonNull BooleanSupplier condition, long timeout)
+            throws TimeoutException {
+        final AtomicBoolean result = new AtomicBoolean();
+
+        final Instrumentation instrumentation = InstrumentationRegistry.getInstrumentation();
+        while (!result.get()) {
+            if (timeout < 0) {
+                throw new TimeoutException();
+            }
+            instrumentation.runOnMainSync(() -> {
+                if (condition.getAsBoolean()) {
+                    result.set(true);
+                }
+            });
+            try {
+                Thread.sleep(TIME_SLICE);
+            } catch (InterruptedException e) {
+                throw new IllegalStateException(e);
+            }
+            timeout -= TIME_SLICE;
+        }
+    }
+}
diff --git a/tests/inputmethod/src/android/view/inputmethod/cts/util/WindowFocusStealer.java b/tests/inputmethod/src/android/view/inputmethod/cts/util/WindowFocusStealer.java
new file mode 100644
index 0000000..657420f
--- /dev/null
+++ b/tests/inputmethod/src/android/view/inputmethod/cts/util/WindowFocusStealer.java
@@ -0,0 +1,142 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.view.inputmethod.cts.util;
+
+import android.content.ComponentName;
+import android.content.Context;
+import android.content.Intent;
+import android.content.ServiceConnection;
+import android.os.Bundle;
+import android.os.IBinder;
+import android.os.RemoteException;
+import android.os.ResultReceiver;
+import android.support.annotation.NonNull;
+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;
+
+/**
+ * Helper class to trigger the situation where window in a different process gains focus.
+ */
+public class WindowFocusStealer implements AutoCloseable {
+
+    private final static class MyResultReceiver extends ResultReceiver {
+        final BlockingQueue<Integer> mQueue = new ArrayBlockingQueue<>(1);
+
+        public MyResultReceiver() {
+            super(null);
+        }
+
+        @Override
+        protected void onReceiveResult(int resultCode, Bundle resultData) {
+            mQueue.add(resultCode);
+        }
+
+        public void waitResult(long timeout) throws TimeoutException {
+            final Object result;
+            try {
+                result = mQueue.poll(timeout, TimeUnit.MILLISECONDS);
+            } catch (InterruptedException e) {
+                throw new IllegalStateException(e);
+            }
+            if (result == null) {
+                throw new TimeoutException();
+            }
+        }
+    }
+
+    @NonNull
+    private final IWindowFocusStealer mService;
+    @NonNull
+    private final Runnable mCloser;
+
+    /**
+     * Let a window in a different process gain the window focus.
+     * @param parentAppWindowToken Token returned from
+     *                             {@link android.view.View#getApplicationWindowToken()}
+     * @param timeout timeout in millisecond
+     * @throws TimeoutException when failed to have the window focused within {@code timeout}
+     */
+    public void stealWindowFocus(IBinder parentAppWindowToken, long timeout)
+            throws TimeoutException {
+        final MyResultReceiver resultReceiver = new MyResultReceiver();
+        try {
+            mService.stealWindowFocus(parentAppWindowToken, resultReceiver);
+        } catch (RemoteException e) {
+            throw new IllegalStateException(e);
+        }
+        resultReceiver.waitResult(timeout);
+    }
+
+    private WindowFocusStealer(@NonNull IWindowFocusStealer service, @NonNull Runnable closer) {
+        mService = service;
+        mCloser = closer;
+    }
+
+    /**
+     * Establishes a connection to the service.
+     *
+     * @param context {@link Context} to which {@link WindowFocusStealerService} belongs
+     * @param timeout timeout in millisecond
+     * @return
+     * @throws TimeoutException when failed to establish the connection within {@code timeout}
+     */
+    public static WindowFocusStealer connect(Context context, long timeout)
+            throws TimeoutException {
+        final BlockingQueue<IWindowFocusStealer> queue = new ArrayBlockingQueue<>(1);
+
+        final ServiceConnection connection = new ServiceConnection() {
+            public void onServiceConnected(ComponentName className, IBinder service) {
+                queue.add(IWindowFocusStealer.Stub.asInterface(service));
+            }
+            public void onServiceDisconnected(ComponentName className) {
+            }
+        };
+
+        final Intent intent = new Intent();
+        intent.setClass(context, WindowFocusStealerService.class);
+        context.bindService(intent, connection, Context.BIND_AUTO_CREATE);
+
+        final IWindowFocusStealer focusStealer;
+        try {
+            focusStealer = queue.poll(timeout, TimeUnit.MILLISECONDS);
+        } catch (InterruptedException e) {
+            throw new IllegalStateException(e);
+        }
+
+        if (focusStealer == null) {
+            throw new TimeoutException();
+        }
+
+        final AtomicBoolean closed = new AtomicBoolean(false);
+        return new WindowFocusStealer(focusStealer, () -> {
+            if (closed.compareAndSet(false, true)) {
+                context.unbindService(connection);
+            }
+        });
+    }
+
+    /**
+     * Removes the temporary window and clean up everything.
+     */
+    @Override
+    public void close() throws Exception {
+        mCloser.run();
+    }
+}
diff --git a/tests/inputmethod/src/android/view/inputmethod/cts/util/WindowFocusStealerService.java b/tests/inputmethod/src/android/view/inputmethod/cts/util/WindowFocusStealerService.java
new file mode 100644
index 0000000..ecca4c2
--- /dev/null
+++ b/tests/inputmethod/src/android/view/inputmethod/cts/util/WindowFocusStealerService.java
@@ -0,0 +1,109 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.view.inputmethod.cts.util;
+
+import android.app.Service;
+import android.content.Intent;
+import android.graphics.Color;
+import android.graphics.PixelFormat;
+import android.graphics.drawable.ColorDrawable;
+import android.os.Handler;
+import android.os.IBinder;
+import android.os.Looper;
+import android.os.ResultReceiver;
+import android.support.annotation.BinderThread;
+import android.support.annotation.MainThread;
+import android.support.annotation.Nullable;
+import android.view.WindowManager;
+import android.widget.TextView;
+
+public final class WindowFocusStealerService extends Service {
+
+    @Nullable
+    private TextView mCustomView;
+
+    private final class BinderService extends IWindowFocusStealer.Stub {
+        @BinderThread
+        @Override
+        public void stealWindowFocus(IBinder parentAppWindowToken, ResultReceiver resultReceiver) {
+            mMainHandler.post(() -> handleStealWindowFocus(parentAppWindowToken, resultReceiver));
+        }
+    }
+
+    private final Handler mMainHandler = new Handler(Looper.getMainLooper());
+
+    @Override
+    public IBinder onBind(Intent intent) {
+        return new BinderService();
+    }
+
+    @Override
+    public boolean onUnbind(Intent intent) {
+        handleReset();
+        return false;
+    }
+
+    @MainThread
+    public void handleStealWindowFocus(
+            IBinder parentAppWindowToken, ResultReceiver resultReceiver) {
+        handleReset();
+
+        mCustomView = new TextView(this) {
+            private boolean mWindowInitiallyFocused = false;
+            /**
+             * {@inheritDoc}
+             */
+            @Override
+            public void onWindowFocusChanged(boolean hasWindowFocus) {
+                if (!mWindowInitiallyFocused && hasWindowFocus) {
+                    mWindowInitiallyFocused = true;
+                    resultReceiver.send(0, null);
+                }
+            }
+        };
+        mCustomView.setText("Popup");
+        mCustomView.setBackground(new ColorDrawable(Color.CYAN));
+        mCustomView.setElevation(0.5f);
+
+        WindowManager mWm = getSystemService(WindowManager.class);
+        WindowManager.LayoutParams params = new WindowManager.LayoutParams(
+                150, 150, 10, 10,
+                WindowManager.LayoutParams.TYPE_APPLICATION_PANEL,
+                WindowManager.LayoutParams.FLAG_ALT_FOCUSABLE_IM |
+                        WindowManager.LayoutParams.FLAG_NOT_TOUCH_MODAL,
+                PixelFormat.OPAQUE);
+        params.packageName = getPackageName();
+        params.token = parentAppWindowToken;
+
+        mWm.addView(mCustomView, params);
+    }
+
+    @MainThread
+    public void handleReset() {
+        if (mCustomView != null) {
+            getSystemService(WindowManager.class).removeView(mCustomView);
+            mCustomView = null;
+        }
+    }
+
+    @MainThread
+    public void onDestroy() {
+        super.onDestroy();
+        handleReset();
+    }
+
+}
diff --git a/tests/jank/Android.mk b/tests/jank/Android.mk
index 16954f2..230f3e8 100644
--- a/tests/jank/Android.mk
+++ b/tests/jank/Android.mk
@@ -32,7 +32,8 @@
     ctstestrunner \
     ub-uiautomator \
     ub-janktesthelper \
-    junit \
-    legacy-android-test
+    junit
+
+LOCAL_JAVA_LIBRARIES := android.test.base
 
 include $(BUILD_CTS_PACKAGE)
diff --git a/tests/jdwp/AndroidTest.xml b/tests/jdwp/AndroidTest.xml
index 72d26b1..66b0f94 100644
--- a/tests/jdwp/AndroidTest.xml
+++ b/tests/jdwp/AndroidTest.xml
@@ -14,6 +14,7 @@
      limitations under the License.
 -->
 <configuration description="Config for CTS JDWP test cases">
+    <option name="test-suite-tag" value="cts" />
     <option name="config-descriptor:metadata" key="component" value="art" />
     <target_preparer class="com.android.tradefed.targetprep.RunCommandTargetPreparer">
         <option name="run-command" value="mkdir -p /data/local/tmp/ctsjdwp/java.io.tmpdir" />
diff --git a/tests/leanbackjank/Android.mk b/tests/leanbackjank/Android.mk
index ec114f3..0c6ff9d 100644
--- a/tests/leanbackjank/Android.mk
+++ b/tests/leanbackjank/Android.mk
@@ -32,11 +32,9 @@
     compatibility-device-util \
     ctstestrunner \
     ub-uiautomator \
-    ub-janktesthelper \
-    android-support-v17-leanback \
-    android-support-v7-recyclerview \
-    android-support-v4 \
-    legacy-android-test
+    ub-janktesthelper
+
+LOCAL_JAVA_LIBRARIES := android.test.base
 
 include $(BUILD_CTS_PACKAGE)
 
diff --git a/tests/leanbackjank/AndroidTest.xml b/tests/leanbackjank/AndroidTest.xml
index 31504b6..d941325 100644
--- a/tests/leanbackjank/AndroidTest.xml
+++ b/tests/leanbackjank/AndroidTest.xml
@@ -14,6 +14,7 @@
      limitations under the License.
 -->
 <configuration description="Config for CTS LeanbackJank test cases">
+    <option name="test-suite-tag" value="cts" />
     <option name="config-descriptor:metadata" key="component" value="tv" />
     <option name="not-shardable" value="true" />
     <target_preparer class="com.android.tradefed.targetprep.suite.SuiteApkInstaller">
diff --git a/tests/leanbackjank/app/Android.mk b/tests/leanbackjank/app/Android.mk
index 3fc685d..04c3d3f 100644
--- a/tests/leanbackjank/app/Android.mk
+++ b/tests/leanbackjank/app/Android.mk
@@ -27,25 +27,21 @@
 # Tag this module as a cts test artifact
 LOCAL_COMPATIBILITY_SUITE := cts vts general-tests
 
-LOCAL_RESOURCE_DIR := \
-    $(TOP)/frameworks/support/v17/leanback/res \
-    $(TOP)/frameworks/support/v7/recyclerview/res \
-    $(LOCAL_PATH)/res
+LOCAL_USE_AAPT2 := true
+
+LOCAL_RESOURCE_DIR := $(LOCAL_PATH)/res
 
 LOCAL_STATIC_JAVA_LIBRARIES := \
     compatibility-device-util \
     ctstestrunner \
     ub-uiautomator \
     ub-janktesthelper \
-    android-support-v4 \
-    android-support-v7-recyclerview \
-    android-support-v17-leanback \
     glide
 
-LOCAL_AAPT_FLAGS := \
-        --auto-add-overlay \
-        --extra-packages android.support.v17.leanback \
-        --extra-packages android.support.v7.recyclerview
+LOCAL_STATIC_ANDROID_LIBRARIES := \
+    android-support-v4 \
+    android-support-v7-recyclerview \
+    android-support-v17-leanback
 
 LOCAL_PROGUARD_FLAG_FILES := proguard.flags
 
diff --git a/tests/leanbackjank/app/AndroidManifest.xml b/tests/leanbackjank/app/AndroidManifest.xml
index 1e1ff3b..1ea3f3f 100644
--- a/tests/leanbackjank/app/AndroidManifest.xml
+++ b/tests/leanbackjank/app/AndroidManifest.xml
@@ -40,6 +40,8 @@
         android:label="@string/app_name"
         android:logo="@drawable/videos_by_google_banner"
         android:theme="@style/Theme.Example.Leanback" >
+        <uses-library android:name="android.test.runner" />
+
         <activity
             android:name=".ui.MainActivity"
             android:icon="@drawable/videos_by_google_banner"
diff --git a/tests/libcore/javautilcollections/AndroidTest.xml b/tests/libcore/javautilcollections/AndroidTest.xml
index 75be288..7ea7634 100644
--- a/tests/libcore/javautilcollections/AndroidTest.xml
+++ b/tests/libcore/javautilcollections/AndroidTest.xml
@@ -14,6 +14,7 @@
      limitations under the License.
 -->
 <configuration description="Config for CTS Libcore java.util Collection test cases">
+    <option name="test-suite-tag" value="cts" />
     <option name="not-shardable" value="true" />
     <option name="config-descriptor:metadata" key="component" value="libcore" />
     <target_preparer class="com.android.tradefed.targetprep.suite.SuiteApkInstaller">
diff --git a/tests/libcore/jsr166/AndroidManifest.xml b/tests/libcore/jsr166/AndroidManifest.xml
index baae488..5fd3f6e 100644
--- a/tests/libcore/jsr166/AndroidManifest.xml
+++ b/tests/libcore/jsr166/AndroidManifest.xml
@@ -22,6 +22,9 @@
 
     <instrumentation android:name="android.support.test.runner.AndroidJUnitRunner"
                      android:targetPackage="android.libcore.cts.jsr166"
-                     android:label="CTS Libcore JSR166 test cases" />
+                     android:label="CTS Libcore JSR166 test cases">
+        <meta-data android:name="listener"
+                   android:value="com.android.cts.runner.CtsTestRunListener" />
+    </instrumentation>
 
 </manifest>
diff --git a/tests/libcore/jsr166/AndroidTest.xml b/tests/libcore/jsr166/AndroidTest.xml
index 2a3e5d2..9926f57 100644
--- a/tests/libcore/jsr166/AndroidTest.xml
+++ b/tests/libcore/jsr166/AndroidTest.xml
@@ -14,6 +14,7 @@
      limitations under the License.
 -->
 <configuration description="Config for CTS Libcore JSR166 test cases">
+    <option name="test-suite-tag" value="cts" />
     <option name="config-descriptor:metadata" key="component" value="libcore" />
     <target_preparer class="com.android.tradefed.targetprep.RunCommandTargetPreparer">
         <option name="run-command" value="mkdir -p /data/local/tmp/ctslibcore/java.io.tmpdir" />
@@ -27,8 +28,6 @@
     </target_preparer>
     <test class="com.android.compatibility.testtype.LibcoreTest" >
         <option name="package" value="android.libcore.cts.jsr166" />
-        <option name="instrumentation-arg" key="listener"
-                value="com.android.cts.runner.CtsTestRunListener" />
         <option name="instrumentation-arg" key="filter"
                 value="com.android.cts.core.runner.ExpectationBasedFilter" />
         <option name="core-expectation" value="/knownfailures.txt" />
diff --git a/tests/libcore/luni/AndroidManifest.xml b/tests/libcore/luni/AndroidManifest.xml
index 0389be6..4df4b93 100644
--- a/tests/libcore/luni/AndroidManifest.xml
+++ b/tests/libcore/luni/AndroidManifest.xml
@@ -23,6 +23,9 @@
 
     <instrumentation android:name="android.support.test.runner.AndroidJUnitRunner"
                      android:targetPackage="android.libcore.cts"
-                     android:label="CTS Libcore test cases" />
+                     android:label="CTS Libcore test cases">
+        <meta-data android:name="listener"
+                   android:value="com.android.cts.runner.CtsTestRunListener" />
+    </instrumentation>
 
 </manifest>
diff --git a/tests/libcore/luni/AndroidTest.xml b/tests/libcore/luni/AndroidTest.xml
index a8c4142..5a397f4 100644
--- a/tests/libcore/luni/AndroidTest.xml
+++ b/tests/libcore/luni/AndroidTest.xml
@@ -14,6 +14,7 @@
      limitations under the License.
 -->
 <configuration description="Config for CTS Libcore test cases">
+    <option name="test-suite-tag" value="cts" />
     <option name="config-descriptor:metadata" key="component" value="libcore" />
     <target_preparer class="com.android.tradefed.targetprep.RunCommandTargetPreparer">
         <option name="run-command" value="mkdir -p /data/local/tmp/ctslibcore/java.io.tmpdir" />
@@ -27,8 +28,6 @@
     </target_preparer>
     <test class="com.android.compatibility.testtype.LibcoreTest" >
         <option name="package" value="android.libcore.cts" />
-        <option name="instrumentation-arg" key="listener"
-                value="com.android.cts.runner.CtsTestRunListener" />
         <option name="instrumentation-arg" key="filter"
                 value="com.android.cts.core.runner.ExpectationBasedFilter" />
         <option name="core-expectation" value="/knownfailures.txt" />
diff --git a/tests/libcore/ojluni/AndroidManifest.xml b/tests/libcore/ojluni/AndroidManifest.xml
index c010a32..8c45c30 100644
--- a/tests/libcore/ojluni/AndroidManifest.xml
+++ b/tests/libcore/ojluni/AndroidManifest.xml
@@ -21,6 +21,9 @@
     <!-- important: instrument another package which actually contains AJUR -->
     <instrumentation android:name="android.support.test.runner.AndroidJUnitRunner"
                      android:targetPackage="android.libcore.runner"
-                     android:label="CTS Libcore OJ test cases" />
+                     android:label="CTS Libcore OJ test cases">
+        <meta-data android:name="listener"
+                   android:value="com.android.cts.runner.CtsTestRunListener" />
+    </instrumentation>
 
 </manifest>
diff --git a/tests/libcore/ojluni/AndroidTest.xml b/tests/libcore/ojluni/AndroidTest.xml
index 1cebd4e..a222270 100644
--- a/tests/libcore/ojluni/AndroidTest.xml
+++ b/tests/libcore/ojluni/AndroidTest.xml
@@ -14,6 +14,7 @@
      limitations under the License.
 -->
 <configuration description="Config for CTS Libcore OJ test cases">
+    <option name="test-suite-tag" value="cts" />
     <option name="config-descriptor:metadata" key="component" value="libcore" />
     <target_preparer class="com.android.tradefed.targetprep.RunCommandTargetPreparer">
         <option name="run-command" value="mkdir -p /data/local/tmp/ctslibcore/java.io.tmpdir" />
@@ -22,7 +23,7 @@
     </target_preparer>
     <target_preparer class="com.android.tradefed.targetprep.suite.SuiteApkInstaller">
         <option name="cleanup-apks" value="true" />
-        <!-- this has the CoreTestRunner which needs to be in a separate APK -->
+        <!-- this has the AJUR which needs to be in a separate APK -->
         <option name="test-file-name" value="CtsLibcoreTestRunner.apk" />
         <!-- this has just the instrumentation which acts as the tests we want to run -->
         <option name="test-file-name" value="CtsLibcoreOjTestCases.apk" />
@@ -31,8 +32,6 @@
         <option name="package" value="android.libcore.cts.oj" />
         <option name="instrumentation-arg" key="runnerBuilder"
                 value="com.android.cts.core.runner.support.TestNgRunnerBuilder"/>
-        <option name="instrumentation-arg" key="listener"
-                value="com.android.cts.runner.CtsTestRunListener"/>
         <option name="instrumentation-arg" key="filter"
                 value="com.android.cts.core.runner.ExpectationBasedFilter" />
         <option name="core-expectation" value="/knownfailures.txt" />
diff --git a/tests/libcore/okhttp/AndroidManifest.xml b/tests/libcore/okhttp/AndroidManifest.xml
index 151fda9..dfff563 100644
--- a/tests/libcore/okhttp/AndroidManifest.xml
+++ b/tests/libcore/okhttp/AndroidManifest.xml
@@ -17,12 +17,15 @@
 
 <manifest xmlns:android="http://schemas.android.com/apk/res/android" package="android.libcore.cts.okhttp">
     <uses-permission android:name="android.permission.INTERNET" />
-    <application>
+    <application android:usesCleartextTraffic="true">
         <uses-library android:name="android.test.runner" />
     </application>
 
     <instrumentation android:name="android.support.test.runner.AndroidJUnitRunner"
                      android:targetPackage="android.libcore.cts.okhttp"
-                     android:label="CTS Libcore OkHttp test cases" />
+                     android:label="CTS Libcore OkHttp test cases">
+        <meta-data android:name="listener"
+                   android:value="com.android.cts.runner.CtsTestRunListener" />
+    </instrumentation>
 
 </manifest>
diff --git a/tests/libcore/okhttp/AndroidTest.xml b/tests/libcore/okhttp/AndroidTest.xml
index 4e79b80..1445c20 100644
--- a/tests/libcore/okhttp/AndroidTest.xml
+++ b/tests/libcore/okhttp/AndroidTest.xml
@@ -14,6 +14,7 @@
      limitations under the License.
 -->
 <configuration description="Config for CTS Libcore OkHttp test cases">
+    <option name="test-suite-tag" value="cts" />
     <option name="config-descriptor:metadata" key="component" value="libcore" />
     <target_preparer class="com.android.tradefed.targetprep.RunCommandTargetPreparer">
         <option name="run-command" value="mkdir -p /data/local/tmp/ctslibcore/java.io.tmpdir" />
@@ -27,8 +28,6 @@
     </target_preparer>
     <test class="com.android.compatibility.testtype.LibcoreTest" >
         <option name="package" value="android.libcore.cts.okhttp" />
-        <option name="instrumentation-arg" key="listener"
-                value="com.android.cts.runner.CtsTestRunListener" />
         <option name="instrumentation-arg" key="filter"
                 value="com.android.cts.core.runner.ExpectationBasedFilter" />
         <option name="core-expectation" value="/knownfailures.txt" />
diff --git a/tests/libcore/wycheproof-bc/AndroidManifest.xml b/tests/libcore/wycheproof-bc/AndroidManifest.xml
index 15c5fd5..fb53977 100644
--- a/tests/libcore/wycheproof-bc/AndroidManifest.xml
+++ b/tests/libcore/wycheproof-bc/AndroidManifest.xml
@@ -23,6 +23,9 @@
 
     <instrumentation android:name="android.support.test.runner.AndroidJUnitRunner"
                      android:targetPackage="android.libcore.cts.wycheproof.bouncycastle"
-                     android:label="CTS Libcore Wycheproof Bouncy Castle test cases" />
+                     android:label="CTS Libcore Wycheproof Bouncy Castle test cases">
+        <meta-data android:name="listener"
+                   android:value="com.android.cts.runner.CtsTestRunListener" />
+    </instrumentation>
 
 </manifest>
diff --git a/tests/libcore/wycheproof-bc/AndroidTest.xml b/tests/libcore/wycheproof-bc/AndroidTest.xml
index 9c3a50a..e5280a4 100644
--- a/tests/libcore/wycheproof-bc/AndroidTest.xml
+++ b/tests/libcore/wycheproof-bc/AndroidTest.xml
@@ -14,6 +14,7 @@
      limitations under the License.
 -->
 <configuration description="Config for CTS Libcore Wycheproof Bouncy Castle test cases">
+    <option name="test-suite-tag" value="cts" />
     <option name="config-descriptor:metadata" key="component" value="libcore" />
     <target_preparer class="com.android.tradefed.targetprep.suite.SuiteApkInstaller">
         <option name="cleanup-apks" value="true" />
@@ -26,8 +27,6 @@
              context of one of the suites, so we have to limit the test
              infrastructure to only running the test suites. -->
         <option name="test-package" value="android.libcore.cts.wycheproof" />
-        <option name="instrumentation-arg" key="listener"
-                value="com.android.cts.runner.CtsTestRunListener" />
         <option name="instrumentation-arg" key="filter"
                 value="com.android.cts.core.runner.ExpectationBasedFilter" />
         <option name="core-expectation" value="/knownfailures.txt" />
diff --git a/tests/libcore/wycheproof/AndroidManifest.xml b/tests/libcore/wycheproof/AndroidManifest.xml
index 765c677..5e8058f 100644
--- a/tests/libcore/wycheproof/AndroidManifest.xml
+++ b/tests/libcore/wycheproof/AndroidManifest.xml
@@ -23,6 +23,9 @@
 
     <instrumentation android:name="android.support.test.runner.AndroidJUnitRunner"
                      android:targetPackage="android.libcore.cts.wycheproof.conscrypt"
-                     android:label="CTS Libcore Wycheproof Conscrypt test cases" />
+                     android:label="CTS Libcore Wycheproof Conscrypt test cases">
+        <meta-data android:name="listener"
+                   android:value="com.android.cts.runner.CtsTestRunListener" />
+    </instrumentation>
 
 </manifest>
diff --git a/tests/libcore/wycheproof/AndroidTest.xml b/tests/libcore/wycheproof/AndroidTest.xml
index b5874fd..2a1f2b5 100644
--- a/tests/libcore/wycheproof/AndroidTest.xml
+++ b/tests/libcore/wycheproof/AndroidTest.xml
@@ -14,6 +14,7 @@
      limitations under the License.
 -->
 <configuration description="Config for CTS Libcore Wycheproof Conscrypt test cases">
+    <option name="test-suite-tag" value="cts" />
     <option name="config-descriptor:metadata" key="component" value="libcore" />
     <target_preparer class="com.android.tradefed.targetprep.suite.SuiteApkInstaller">
         <option name="cleanup-apks" value="true" />
@@ -26,8 +27,6 @@
              context of one of the suites, so we have to limit the test
              infrastructure to only running the test suites. -->
         <option name="test-package" value="android.libcore.cts.wycheproof" />
-        <option name="instrumentation-arg" key="listener"
-                value="com.android.cts.runner.CtsTestRunListener" />
         <option name="instrumentation-arg" key="filter"
                 value="com.android.cts.core.runner.ExpectationBasedFilter" />
         <option name="core-expectation" value="/knownfailures.txt" />
diff --git a/tests/mocking/AndroidTest.xml b/tests/mocking/AndroidTest.xml
index 2741eba..58340c5 100644
--- a/tests/mocking/AndroidTest.xml
+++ b/tests/mocking/AndroidTest.xml
@@ -15,6 +15,7 @@
   ~ limitations under the License.
   -->
 <configuration description="Config for Mockito mocking test cases">
+   <option name="test-suite-tag" value="cts" />
     <option name="config-descriptor:metadata" key="component" value="framework" />
 
     <target_preparer class="com.android.tradefed.targetprep.suite.SuiteApkInstaller">
diff --git a/tests/mocking/inline/AndroidTest.xml b/tests/mocking/inline/AndroidTest.xml
index e642329..d65d6f5 100644
--- a/tests/mocking/inline/AndroidTest.xml
+++ b/tests/mocking/inline/AndroidTest.xml
@@ -15,6 +15,7 @@
   ~ limitations under the License.
   -->
 <configuration description="Config for Mockito inline mocking test cases">
+    <option name="test-suite-tag" value="cts" />
     <option name="config-descriptor:metadata" key="component" value="framework" />
 
     <target_preparer class="com.android.tradefed.targetprep.suite.SuiteApkInstaller">
diff --git a/tests/netlegacy22.api/Android.mk b/tests/netlegacy22.api/Android.mk
index 8639514..5a330e5 100644
--- a/tests/netlegacy22.api/Android.mk
+++ b/tests/netlegacy22.api/Android.mk
@@ -27,7 +27,7 @@
 
 LOCAL_SDK_VERSION := 22
 
-LOCAL_STATIC_JAVA_LIBRARIES := ctstestrunner legacy-android-test
+LOCAL_STATIC_JAVA_LIBRARIES := ctstestrunner
 
 # Tag this module as a cts test artifact
 LOCAL_COMPATIBILITY_SUITE := cts vts general-tests
diff --git a/tests/netlegacy22.permission/Android.mk b/tests/netlegacy22.permission/Android.mk
index 262e600..f5cc38b 100644
--- a/tests/netlegacy22.permission/Android.mk
+++ b/tests/netlegacy22.permission/Android.mk
@@ -27,7 +27,7 @@
 
 LOCAL_SDK_VERSION := 22
 
-LOCAL_STATIC_JAVA_LIBRARIES := ctstestrunner legacy-android-test
+LOCAL_STATIC_JAVA_LIBRARIES := ctstestrunner
 
 # Tag this module as a cts test artifact
 LOCAL_COMPATIBILITY_SUITE := cts vts general-tests
diff --git a/tests/netlegacy22.permission/AndroidTest.xml b/tests/netlegacy22.permission/AndroidTest.xml
index db6f294..83983a7 100644
--- a/tests/netlegacy22.permission/AndroidTest.xml
+++ b/tests/netlegacy22.permission/AndroidTest.xml
@@ -14,6 +14,7 @@
      limitations under the License.
 -->
 <configuration description="Config for CTS Legacy android.net Permission test cases">
+    <option name="test-suite-tag" value="cts" />
     <option name="config-descriptor:metadata" key="component" value="networking" />
     <option name="not-shardable" value="true" />
     <target_preparer class="com.android.tradefed.targetprep.suite.SuiteApkInstaller">
diff --git a/tests/openglperf2/AndroidTest.xml b/tests/openglperf2/AndroidTest.xml
index 5d8f9d1..4ac0455 100644
--- a/tests/openglperf2/AndroidTest.xml
+++ b/tests/openglperf2/AndroidTest.xml
@@ -14,6 +14,7 @@
      limitations under the License.
 -->
 <configuration description="Config for CTS OpenGL test cases">
+    <option name="test-suite-tag" value="cts" />
     <option name="config-descriptor:metadata" key="component" value="graphics" />
     <target_preparer class="com.android.tradefed.targetprep.suite.SuiteApkInstaller">
         <option name="cleanup-apks" value="true" />
diff --git a/tests/pdf/Android.mk b/tests/pdf/Android.mk
index 2b301af..53ec66c 100644
--- a/tests/pdf/Android.mk
+++ b/tests/pdf/Android.mk
@@ -28,8 +28,7 @@
     compatibility-device-util \
     ctstestrunner \
     android-support-annotations \
-    junit \
-    legacy-android-test
+    junit
 
 LOCAL_SRC_FILES := $(call all-java-files-under, src)
 
diff --git a/tests/pdf/AndroidTest.xml b/tests/pdf/AndroidTest.xml
index ab88726..e237461 100644
--- a/tests/pdf/AndroidTest.xml
+++ b/tests/pdf/AndroidTest.xml
@@ -14,6 +14,7 @@
      limitations under the License.
 -->
 <configuration description="Config for CTS Pdf test cases">
+    <option name="test-suite-tag" value="cts" />
     <option name="not-shardable" value="true" />
     <option name="config-descriptor:metadata" key="component" value="framework" />
 
diff --git a/tests/pdf/res/raw/protected_pdf.pdf b/tests/pdf/res/raw/protected_pdf.pdf
new file mode 100644
index 0000000..e4092b9
--- /dev/null
+++ b/tests/pdf/res/raw/protected_pdf.pdf
Binary files differ
diff --git a/tests/pdf/src/android/graphics/pdf/cts/PdfRendererTest.java b/tests/pdf/src/android/graphics/pdf/cts/PdfRendererTest.java
index 2f0158e..2ce69d1 100644
--- a/tests/pdf/src/android/graphics/pdf/cts/PdfRendererTest.java
+++ b/tests/pdf/src/android/graphics/pdf/cts/PdfRendererTest.java
@@ -62,6 +62,7 @@
     private static final int A5_PORTRAIT_PRINTSCALING_NONE =
             R.raw.a5_portrait_rgbb_1_6_printscaling_none;
     private static final int TWO_PAGES = R.raw.two_pages;
+    private static final int PROTECTED_PDF = R.raw.protected_pdf;
 
     private Context mContext;
 
@@ -76,12 +77,28 @@
     }
 
     @Test
-    @Ignore("Makes all subsequent tests fail")
     public void constructRendererFromNonPDF() throws Exception {
         // Open jpg as if it was a PDF
-        ParcelFileDescriptor fd = mContext.getResources().openRawResourceFd(R.raw.testimage)
-                .getParcelFileDescriptor();
-        verifyException(() -> new PdfRenderer(fd), IOException.class);
+        verifyException(() -> createRenderer(R.raw.testimage, mContext), IOException.class);
+    }
+
+    @Test
+    public void constructRendererFromProtectedPDF() throws Exception {
+        verifyException(() -> createRenderer(PROTECTED_PDF, mContext), SecurityException.class);
+    }
+
+    @Test
+    public void rendererRecoversAfterFailure() throws Exception {
+        // Create rendered to prevent lib from being unloaded
+        PdfRenderer firstRenderer = createRenderer(A4_PORTRAIT, mContext);
+
+        verifyException(() -> createRenderer(PROTECTED_PDF, mContext), SecurityException.class);
+
+        // We can create new renderers after we failed to create one
+        PdfRenderer renderer = createRenderer(TWO_PAGES, mContext);
+        renderer.close();
+
+        firstRenderer.close();
     }
 
     @Test
diff --git a/tests/sample/Android.mk b/tests/sample/Android.mk
index f6d8760..5602e7e 100755
--- a/tests/sample/Android.mk
+++ b/tests/sample/Android.mk
@@ -27,8 +27,9 @@
 
 LOCAL_STATIC_JAVA_LIBRARIES := \
     compatibility-device-util \
-    android-support-test \
-    legacy-android-test
+    android-support-test
+
+LOCAL_JAVA_LIBRARIES := android.test.runner.stubs android.test.base.stubs
 
 LOCAL_SRC_FILES := $(call all-java-files-under, src)
 
diff --git a/tests/sample/AndroidTest.xml b/tests/sample/AndroidTest.xml
index c8d1949..8a7d2d9 100644
--- a/tests/sample/AndroidTest.xml
+++ b/tests/sample/AndroidTest.xml
@@ -14,6 +14,7 @@
      limitations under the License.
 -->
 <configuration description="Config for CTS Sample 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" />
diff --git a/tests/security/Android.mk b/tests/security/Android.mk
new file mode 100755
index 0000000..aecc076
--- /dev/null
+++ b/tests/security/Android.mk
@@ -0,0 +1,26 @@
+# 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)
+
+# Don't include this package in any target.
+LOCAL_MODULE_TAGS := optional
+
+LOCAL_STATIC_JAVA_LIBRARIES := bouncycastle bouncycastle-bcpkix guava
+LOCAL_SRC_FILES := $(call all-java-files-under, src)
+LOCAL_MODULE := cts-security-test-support-library
+
+include $(BUILD_STATIC_JAVA_LIBRARY)
diff --git a/tests/tests/keystore/src/android/keystore/cts/Asn1Utils.java b/tests/security/src/android/keystore/cts/Asn1Utils.java
similarity index 100%
rename from tests/tests/keystore/src/android/keystore/cts/Asn1Utils.java
rename to tests/security/src/android/keystore/cts/Asn1Utils.java
diff --git a/tests/tests/keystore/src/android/keystore/cts/Attestation.java b/tests/security/src/android/keystore/cts/Attestation.java
similarity index 100%
rename from tests/tests/keystore/src/android/keystore/cts/Attestation.java
rename to tests/security/src/android/keystore/cts/Attestation.java
diff --git a/tests/tests/keystore/src/android/keystore/cts/AttestationApplicationId.java b/tests/security/src/android/keystore/cts/AttestationApplicationId.java
similarity index 100%
rename from tests/tests/keystore/src/android/keystore/cts/AttestationApplicationId.java
rename to tests/security/src/android/keystore/cts/AttestationApplicationId.java
diff --git a/tests/security/src/android/keystore/cts/AttestationPackageInfo.java b/tests/security/src/android/keystore/cts/AttestationPackageInfo.java
new file mode 100644
index 0000000..3c3e2bd
--- /dev/null
+++ b/tests/security/src/android/keystore/cts/AttestationPackageInfo.java
@@ -0,0 +1,83 @@
+/*
+ * 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.keystore.cts;
+
+import com.android.org.bouncycastle.asn1.ASN1Encodable;
+import com.android.org.bouncycastle.asn1.ASN1Sequence;
+
+import java.security.cert.CertificateParsingException;
+
+import java.io.UnsupportedEncodingException;
+
+public class AttestationPackageInfo implements java.lang.Comparable<AttestationPackageInfo> {
+    private static final int PACKAGE_NAME_INDEX = 0;
+    private static final int VERSION_INDEX = 1;
+
+    private final String packageName;
+    private final long version;
+
+    public AttestationPackageInfo(String packageName, long version) {
+        this.packageName = packageName;
+        this.version = version;
+    }
+
+    public AttestationPackageInfo(ASN1Encodable asn1Encodable) throws CertificateParsingException {
+        if (!(asn1Encodable instanceof ASN1Sequence)) {
+            throw new CertificateParsingException(
+                    "Expected sequence for AttestationPackageInfo, found "
+                            + asn1Encodable.getClass().getName());
+        }
+
+        ASN1Sequence sequence = (ASN1Sequence) asn1Encodable;
+        try {
+            packageName = Asn1Utils.getStringFromAsn1OctetStreamAssumingUTF8(
+                    sequence.getObjectAt(PACKAGE_NAME_INDEX));
+        } catch (UnsupportedEncodingException e) {
+            throw new CertificateParsingException(
+                    "Converting octet stream to String triggered an UnsupportedEncodingException",
+                    e);
+        }
+        version = Asn1Utils.getLongFromAsn1(sequence.getObjectAt(VERSION_INDEX));
+    }
+
+    public String getPackageName() {
+        return packageName;
+    }
+
+    public long getVersion() {
+        return version;
+    }
+
+    @Override
+    public String toString() {
+        return new StringBuilder().append("Package name: ").append(getPackageName())
+                .append("\nVersion: " + getVersion()).toString();
+    }
+
+    @Override
+    public int compareTo(AttestationPackageInfo other) {
+        int res = packageName.compareTo(other.packageName);
+        if (res != 0) return res;
+        res = Long.compare(version, other.version);
+        if (res != 0) return res;
+        return res;
+    }
+
+    @Override
+    public boolean equals(Object o) {
+        return (o instanceof AttestationPackageInfo)
+                && (0 == compareTo((AttestationPackageInfo) o));
+    }
+}
diff --git a/tests/tests/keystore/src/android/keystore/cts/AuthorizationList.java b/tests/security/src/android/keystore/cts/AuthorizationList.java
similarity index 100%
rename from tests/tests/keystore/src/android/keystore/cts/AuthorizationList.java
rename to tests/security/src/android/keystore/cts/AuthorizationList.java
diff --git a/tests/security/src/android/keystore/cts/CertificateUtils.java b/tests/security/src/android/keystore/cts/CertificateUtils.java
new file mode 100644
index 0000000..68e936e
--- /dev/null
+++ b/tests/security/src/android/keystore/cts/CertificateUtils.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.keystore.cts;
+
+import com.android.org.bouncycastle.asn1.x500.X500Name;
+import com.android.org.bouncycastle.asn1.x509.SubjectPublicKeyInfo;
+import com.android.org.bouncycastle.cert.X509CertificateHolder;
+import com.android.org.bouncycastle.cert.X509v3CertificateBuilder;
+import com.android.org.bouncycastle.operator.jcajce.JcaContentSignerBuilder;
+import java.io.ByteArrayInputStream;
+import java.math.BigInteger;
+import java.security.KeyPair;
+import java.security.SecureRandom;
+import java.security.cert.CertificateFactory;
+import java.security.cert.X509Certificate;
+import java.util.Date;
+import javax.security.auth.x500.X500Principal;
+
+public class CertificateUtils {
+    /** Creates a self-signed X.509 certificate, given a key pair, subject and issuer. */
+    public static X509Certificate createCertificate(
+            KeyPair keyPair, X500Principal subject, X500Principal issuer) throws Exception {
+        // Make the certificate valid for two days.
+        long millisPerDay = 24 * 60 * 60 * 1000;
+        long now = System.currentTimeMillis();
+        Date start = new Date(now - millisPerDay);
+        Date end = new Date(now + millisPerDay);
+
+        // Assign a random serial number.
+        byte[] serialBytes = new byte[16];
+        new SecureRandom().nextBytes(serialBytes);
+        BigInteger serialNumber = new BigInteger(1, serialBytes);
+
+        // Create the certificate builder
+        X509v3CertificateBuilder x509cg =
+                new X509v3CertificateBuilder(
+                        X500Name.getInstance(issuer.getEncoded()),
+                        serialNumber,
+                        start,
+                        end,
+                        X500Name.getInstance(subject.getEncoded()),
+                        SubjectPublicKeyInfo.getInstance(keyPair.getPublic().getEncoded()));
+
+        // Choose a signature algorithm matching the key format.
+        String keyAlgorithm = keyPair.getPrivate().getAlgorithm();
+        String signatureAlgorithm;
+        if (keyAlgorithm.equals("RSA")) {
+            signatureAlgorithm = "SHA256withRSA";
+        } else if (keyAlgorithm.equals("EC")) {
+            signatureAlgorithm = "SHA256withECDSA";
+        } else {
+            throw new IllegalArgumentException("Unknown key algorithm " + keyAlgorithm);
+        }
+
+        // Sign the certificate and generate it.
+        X509CertificateHolder x509holder =
+                x509cg.build(
+                        new JcaContentSignerBuilder(signatureAlgorithm)
+                                .build(keyPair.getPrivate()));
+        CertificateFactory certFactory = CertificateFactory.getInstance("X.509");
+        X509Certificate x509c =
+                (X509Certificate)
+                        certFactory.generateCertificate(
+                                new ByteArrayInputStream(x509holder.getEncoded()));
+        return x509c;
+    }
+}
diff --git a/tests/tests/keystore/src/android/keystore/cts/RootOfTrust.java b/tests/security/src/android/keystore/cts/RootOfTrust.java
similarity index 100%
rename from tests/tests/keystore/src/android/keystore/cts/RootOfTrust.java
rename to tests/security/src/android/keystore/cts/RootOfTrust.java
diff --git a/tests/sensor/Android.mk b/tests/sensor/Android.mk
index dcf9015..e636af8 100644
--- a/tests/sensor/Android.mk
+++ b/tests/sensor/Android.mk
@@ -27,7 +27,7 @@
 
 LOCAL_STATIC_JAVA_LIBRARIES := compatibility-device-util
 
-LOCAL_JAVA_LIBRARIES := platform-test-annotations
+LOCAL_JAVA_LIBRARIES := platform-test-annotations android.test.base.stubs
 
 LOCAL_SDK_VERSION := current
 
@@ -89,7 +89,7 @@
 
 LOCAL_SDK_VERSION := current
 
-LOCAL_JAVA_LIBRARIES := android.test.runner
+LOCAL_JAVA_LIBRARIES := android.test.runner.stubs android.test.base.stubs
 
 LOCAL_NDK_STL_VARIANT := c++_shared
 
diff --git a/tests/sensor/AndroidTest.xml b/tests/sensor/AndroidTest.xml
index 3eff64c..7137323 100644
--- a/tests/sensor/AndroidTest.xml
+++ b/tests/sensor/AndroidTest.xml
@@ -14,6 +14,7 @@
      limitations under the License.
 -->
 <configuration description="Config for CTS Sensor test cases">
+    <option name="test-suite-tag" value="cts" />
     <option name="config-descriptor:metadata" key="component" value="location" />
     <target_preparer class="com.android.compatibility.common.tradefed.targetprep.LocationCheck" />
     <target_preparer class="com.android.tradefed.targetprep.suite.SuiteApkInstaller">
diff --git a/tests/signature/api-check/Android.mk b/tests/signature/api-check/Android.mk
index 1d616cb..15fa178 100644
--- a/tests/signature/api-check/Android.mk
+++ b/tests/signature/api-check/Android.mk
@@ -30,7 +30,7 @@
 
 LOCAL_STATIC_JAVA_LIBRARIES := \
     cts-signature-common \
-    repackaged-legacy-test \
+    repackaged.android.test.base \
     repackaged.android.test.runner \
     repackaged.android.test.mock \
 
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
new file mode 100644
index 0000000..1adcb12c
--- /dev/null
+++ b/tests/signature/api-check/android-test-base-27-api/Android.mk
@@ -0,0 +1,24 @@
+# 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_PACKAGE_NAME := CtsAndroidTestBase27ApiSignatureTestCases
+
+LOCAL_SIGNATURE_API_FILES := \
+    android-test-base-current.api \
+
+include $(LOCAL_PATH)/../build_signature_apk.mk
diff --git a/tests/signature/api-check/android-test-base-27-api/AndroidManifest.xml b/tests/signature/api-check/android-test-base-27-api/AndroidManifest.xml
new file mode 100644
index 0000000..c65bfdf
--- /dev/null
+++ b/tests/signature/api-check/android-test-base-27-api/AndroidManifest.xml
@@ -0,0 +1,31 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT 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.signature.cts.api.android_test_base_26">
+    <uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />
+    <uses-permission android:name="android.permission.READ_PHONE_STATE"/>
+
+    <uses-sdk android:minSdkVersion="25" android:targetSdkVersion="27"/>
+
+    <application/>
+
+    <instrumentation android:name="repackaged.android.test.InstrumentationTestRunner"
+                     android:targetPackage="android.signature.cts.api.android_test_base_27"
+                     android:label="Android Test Base 27 API Signature Test"/>
+
+</manifest>
diff --git a/tests/signature/api-check/android-test-base-27-api/AndroidTest.xml b/tests/signature/api-check/android-test-base-27-api/AndroidTest.xml
new file mode 100644
index 0000000..0362d58
--- /dev/null
+++ b/tests/signature/api-check/android-test-base-27-api/AndroidTest.xml
@@ -0,0 +1,36 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2017 The Android Open Source Project
+
+     Licensed under the Apache License, Version 2.0 (the "License");
+     you may not use this file except in compliance with the License.
+     You may obtain a copy of the License at
+
+          http://www.apache.org/licenses/LICENSE-2.0
+
+     Unless required by applicable law or agreed to in writing, software
+     distributed under the License is distributed on an "AS IS" BASIS,
+     WITHOUT 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 Android Test Base 27 API Signature test cases">
+    <option name="test-suite-tag" value="cts" />
+    <option name="config-descriptor:metadata" key="component" value="systems" />
+    <target_preparer class="com.android.tradefed.targetprep.RunCommandTargetPreparer">
+        <option name="run-command" value="mkdir -p /data/local/tmp/signature-test" />
+        <option name="teardown-command" value="rm -rf /data/local/tmp/signature-test" />
+    </target_preparer>
+    <target_preparer class="com.android.compatibility.common.tradefed.targetprep.FilePusher">
+        <option name="push" value="android-test-base-current.api->/data/local/tmp/signature-test/android-test-base-current.api" />
+    </target_preparer>
+    <target_preparer class="com.android.tradefed.targetprep.suite.SuiteApkInstaller">
+        <option name="cleanup-apks" value="true" />
+        <option name="test-file-name" value="CtsAndroidTestBase27ApiSignatureTestCases.apk" />
+    </target_preparer>
+    <test class="com.android.tradefed.testtype.AndroidJUnitTest" >
+        <option name="package" value="android.signature.cts.api.android_test_base_27" />
+        <option name="runner" value="repackaged.android.test.InstrumentationTestRunner" />
+        <option name="instrumentation-arg" key="expected-api-files" value="android-test-base-current.api" />
+        <option name="runtime-hint" value="5s" />
+    </test>
+</configuration>
diff --git a/tests/signature/api-check/android-test-mock-current-api/AndroidTest.xml b/tests/signature/api-check/android-test-mock-current-api/AndroidTest.xml
index 16b29a8..a180ad0 100644
--- a/tests/signature/api-check/android-test-mock-current-api/AndroidTest.xml
+++ b/tests/signature/api-check/android-test-mock-current-api/AndroidTest.xml
@@ -14,6 +14,7 @@
      limitations under the License.
 -->
 <configuration description="Config for CTS Android Test Mock Current API Signature test cases">
+    <option name="test-suite-tag" value="cts" />
     <option name="config-descriptor:metadata" key="component" value="systems" />
     <target_preparer class="com.android.tradefed.targetprep.RunCommandTargetPreparer">
         <option name="run-command" value="mkdir -p /data/local/tmp/signature-test" />
diff --git a/tests/signature/api-check/android-test-runner-current-api/AndroidTest.xml b/tests/signature/api-check/android-test-runner-current-api/AndroidTest.xml
index ecf7055..dc16296 100644
--- a/tests/signature/api-check/android-test-runner-current-api/AndroidTest.xml
+++ b/tests/signature/api-check/android-test-runner-current-api/AndroidTest.xml
@@ -14,6 +14,7 @@
      limitations under the License.
 -->
 <configuration description="Config for CTS Android Test Runner Current API Signature test cases">
+    <option name="test-suite-tag" value="cts" />
     <option name="config-descriptor:metadata" key="component" value="systems" />
     <target_preparer class="com.android.tradefed.targetprep.RunCommandTargetPreparer">
         <option name="run-command" value="mkdir -p /data/local/tmp/signature-test" />
diff --git a/tests/signature/api-check/apache-http-legacy-current-api/Android.mk b/tests/signature/api-check/apache-http-legacy-current-api/Android.mk
index df69004..b382698 100644
--- a/tests/signature/api-check/apache-http-legacy-current-api/Android.mk
+++ b/tests/signature/api-check/apache-http-legacy-current-api/Android.mk
@@ -19,6 +19,6 @@
 LOCAL_PACKAGE_NAME := CtsApacheHttpLegacyCurrentApiSignatureTestCases
 
 LOCAL_SIGNATURE_API_FILES := \
-    apache-http-legacy-current.api \
+    apache-http-legacy-minus-current.api \
 
 include $(LOCAL_PATH)/../build_signature_apk.mk
diff --git a/tests/signature/api-check/apache-http-legacy-current-api/AndroidTest.xml b/tests/signature/api-check/apache-http-legacy-current-api/AndroidTest.xml
index a5e69a9..4ab686e 100644
--- a/tests/signature/api-check/apache-http-legacy-current-api/AndroidTest.xml
+++ b/tests/signature/api-check/apache-http-legacy-current-api/AndroidTest.xml
@@ -14,13 +14,14 @@
      limitations under the License.
 -->
 <configuration description="Config for CTS Apache Http Legacy Current API Signature test cases">
+    <option name="test-suite-tag" value="cts" />
     <option name="config-descriptor:metadata" key="component" value="systems" />
     <target_preparer class="com.android.tradefed.targetprep.RunCommandTargetPreparer">
         <option name="run-command" value="mkdir -p /data/local/tmp/signature-test" />
         <option name="teardown-command" value="rm -rf /data/local/tmp/signature-test" />
     </target_preparer>
     <target_preparer class="com.android.compatibility.common.tradefed.targetprep.FilePusher">
-        <option name="push" value="apache-http-legacy-current.api->/data/local/tmp/signature-test/apache-http-legacy-current.api" />
+        <option name="push" value="apache-http-legacy-minus-current.api->/data/local/tmp/signature-test/apache-http-legacy-minus-current.api" />
     </target_preparer>
     <target_preparer class="com.android.tradefed.targetprep.suite.SuiteApkInstaller">
         <option name="cleanup-apks" value="true" />
@@ -29,7 +30,7 @@
     <test class="com.android.tradefed.testtype.AndroidJUnitTest" >
         <option name="package" value="android.signature.cts.api.apache_http_legacy_current" />
         <option name="runner" value="repackaged.android.test.InstrumentationTestRunner" />
-        <option name="instrumentation-arg" key="expected-api-files" value="apache-http-legacy-current.api" />
+        <option name="instrumentation-arg" key="expected-api-files" value="apache-http-legacy-minus-current.api" />
         <option name="runtime-hint" value="5s" />
     </test>
 </configuration>
diff --git a/tests/signature/api-check/current-api/AndroidTest.xml b/tests/signature/api-check/current-api/AndroidTest.xml
index 29dfe6d..eac4cae 100644
--- a/tests/signature/api-check/current-api/AndroidTest.xml
+++ b/tests/signature/api-check/current-api/AndroidTest.xml
@@ -14,6 +14,7 @@
      limitations under the License.
 -->
 <configuration description="Config for CTS Current API Signature test cases">
+    <option name="test-suite-tag" value="cts" />
     <option name="config-descriptor:metadata" key="component" value="systems" />
     <option name="not-shardable" value="true" />
     <target_preparer class="com.android.tradefed.targetprep.RunCommandTargetPreparer">
diff --git a/tests/signature/api-check/legacy-test-26-api/Android.mk b/tests/signature/api-check/legacy-test-26-api/Android.mk
deleted file mode 100644
index 699bd0c..0000000
--- a/tests/signature/api-check/legacy-test-26-api/Android.mk
+++ /dev/null
@@ -1,24 +0,0 @@
-# Copyright (C) 2017 The Android Open Source Project
-#
-# Licensed under the Apache License, Version 2.0 (the "License");
-# you may not use this file except in compliance with the License.
-# You may obtain a copy of the License at
-#
-#      http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing, software
-# distributed under the License is distributed on an "AS IS" BASIS,
-# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-# See the License for the specific language governing permissions and
-# limitations under the License.
-
-LOCAL_PATH := $(call my-dir)
-
-include $(CLEAR_VARS)
-
-LOCAL_PACKAGE_NAME := CtsLegacyTest26ApiSignatureTestCases
-
-LOCAL_SIGNATURE_API_FILES := \
-    legacy-test-current.api \
-
-include $(LOCAL_PATH)/../build_signature_apk.mk
diff --git a/tests/signature/api-check/legacy-test-26-api/AndroidManifest.xml b/tests/signature/api-check/legacy-test-26-api/AndroidManifest.xml
deleted file mode 100644
index 636dfd3..0000000
--- a/tests/signature/api-check/legacy-test-26-api/AndroidManifest.xml
+++ /dev/null
@@ -1,31 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
- * Copyright (C) 2017 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT 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.signature.cts.api.legacy_test_26">
-    <uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />
-    <uses-permission android:name="android.permission.READ_PHONE_STATE"/>
-
-    <uses-sdk android:minSdkVersion="25" android:targetSdkVersion="26"/>
-
-    <application/>
-
-    <instrumentation android:name="repackaged.android.test.InstrumentationTestRunner"
-                     android:targetPackage="android.signature.cts.api.legacy_test_26"
-                     android:label="Legacy Test 26 API Signature Test"/>
-
-</manifest>
diff --git a/tests/signature/api-check/legacy-test-26-api/AndroidTest.xml b/tests/signature/api-check/legacy-test-26-api/AndroidTest.xml
deleted file mode 100644
index ecb3299..0000000
--- a/tests/signature/api-check/legacy-test-26-api/AndroidTest.xml
+++ /dev/null
@@ -1,35 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!-- Copyright (C) 2017 The Android Open Source Project
-
-     Licensed under the Apache License, Version 2.0 (the "License");
-     you may not use this file except in compliance with the License.
-     You may obtain a copy of the License at
-
-          http://www.apache.org/licenses/LICENSE-2.0
-
-     Unless required by applicable law or agreed to in writing, software
-     distributed under the License is distributed on an "AS IS" BASIS,
-     WITHOUT 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 Legacy Test 26 API Signature test cases">
-    <option name="config-descriptor:metadata" key="component" value="systems" />
-    <target_preparer class="com.android.tradefed.targetprep.RunCommandTargetPreparer">
-        <option name="run-command" value="mkdir -p /data/local/tmp/signature-test" />
-        <option name="teardown-command" value="rm -rf /data/local/tmp/signature-test" />
-    </target_preparer>
-    <target_preparer class="com.android.compatibility.common.tradefed.targetprep.FilePusher">
-        <option name="push" value="legacy-test-current.api->/data/local/tmp/signature-test/legacy-test-current.api" />
-    </target_preparer>
-    <target_preparer class="com.android.tradefed.targetprep.suite.SuiteApkInstaller">
-        <option name="cleanup-apks" value="true" />
-        <option name="test-file-name" value="CtsLegacyTest26ApiSignatureTestCases.apk" />
-    </target_preparer>
-    <test class="com.android.tradefed.testtype.AndroidJUnitTest" >
-        <option name="package" value="android.signature.cts.api.legacy_test_26" />
-        <option name="runner" value="repackaged.android.test.InstrumentationTestRunner" />
-        <option name="instrumentation-arg" key="expected-api-files" value="legacy-test-current.api" />
-        <option name="runtime-hint" value="5s" />
-    </test>
-</configuration>
diff --git a/tests/signature/api-check/src/android/signature/cts/api/BootClassPathClassesProvider.java b/tests/signature/api-check/src/android/signature/cts/api/BootClassPathClassesProvider.java
new file mode 100644
index 0000000..79b6374
--- /dev/null
+++ b/tests/signature/api-check/src/android/signature/cts/api/BootClassPathClassesProvider.java
@@ -0,0 +1,59 @@
+/*
+ * 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.signature.cts.api;
+
+import java.io.IOException;
+import java.util.Enumeration;
+import java.util.stream.Stream;
+
+import android.signature.cts.ClassProvider;
+import dalvik.system.DexFile;
+
+@SuppressWarnings("deprecation")
+public class BootClassPathClassesProvider extends ClassProvider {
+    private Stream<Class<?>> allClasses = null;
+
+    @Override
+    public Stream<Class<?>> getAllClasses() {
+        Stream.Builder<Class<?>> builder = Stream.builder();
+        if (allClasses == null) {
+            for (String file : getBootJarPaths()) {
+                try {
+                    DexFile dexFile = new DexFile(file);
+                    Enumeration<String> entries = dexFile.entries();
+                    while (entries.hasMoreElements()) {
+                        String className = entries.nextElement();
+                        Class<?> clazz = getClass(className);
+                        if (clazz != null) {
+                            builder.add(clazz);
+                        }
+                    }
+                } catch (IOException e) {
+                    throw new RuntimeException("Failed to parse dex in " + file, e);
+                } catch (ClassNotFoundException e) {
+                    throw new RuntimeException("Error while loading class in " + file, e);
+                }
+            }
+            allClasses = builder.build();
+        }
+        return allClasses;
+    }
+
+    private String[] getBootJarPaths() {
+        return System.getProperty("java.boot.class.path").split(":");
+    }
+}
\ No newline at end of file
diff --git a/tests/signature/api-check/src/android/signature/cts/api/SignatureTest.java b/tests/signature/api-check/src/android/signature/cts/api/SignatureTest.java
index 3473cfd..37ca114 100644
--- a/tests/signature/api-check/src/android/signature/cts/api/SignatureTest.java
+++ b/tests/signature/api-check/src/android/signature/cts/api/SignatureTest.java
@@ -16,13 +16,8 @@
 
 package android.signature.cts.api;
 
-import android.os.Bundle;
-import android.signature.cts.ApiDocumentParser;
-import android.signature.cts.ApiComplianceChecker;
-import android.signature.cts.FailureType;
-import android.signature.cts.JDiffClassDescription;
-import android.signature.cts.ReflectionHelper;
-import android.signature.cts.ResultObserver;
+import static android.signature.cts.CurrentApi.API_FILE_DIRECTORY;
+
 import java.io.File;
 import java.io.FileInputStream;
 import java.io.IOException;
@@ -30,12 +25,21 @@
 import java.util.HashSet;
 import java.util.Set;
 import java.util.TreeSet;
+
 import org.xmlpull.v1.XmlPullParserException;
+
+import android.os.Bundle;
+import android.signature.cts.ApiComplianceChecker;
+import android.signature.cts.ApiDocumentParser;
+import android.signature.cts.ClassProvider;
+import android.signature.cts.ExcludingClassProvider;
+import android.signature.cts.FailureType;
+import android.signature.cts.JDiffClassDescription;
+import android.signature.cts.ReflectionHelper;
+import android.signature.cts.ResultObserver;
 import repackaged.android.test.InstrumentationTestCase;
 import repackaged.android.test.InstrumentationTestRunner;
 
-import static android.signature.cts.CurrentApi.API_FILE_DIRECTORY;
-
 /**
  * Performs the signature check via a JUnit test.
  */
@@ -58,12 +62,16 @@
         KNOWN_INACCESSIBLE_CLASSES.add("android.os.UserManager.UserRestrictionSource");
         KNOWN_INACCESSIBLE_CLASSES.add(
                 "android.service.persistentdata.PersistentDataBlockManager.FlashLockState");
+        KNOWN_INACCESSIBLE_CLASSES.add("android.hardware.radio.ProgramSelector.IdentifierType");
+        KNOWN_INACCESSIBLE_CLASSES.add("android.hardware.radio.ProgramSelector.ProgramType");
+        KNOWN_INACCESSIBLE_CLASSES.add("android.hardware.radio.RadioManager.Band");
     }
 
     private TestResultObserver mResultObserver;
 
     private String[] expectedApiFiles;
     private String[] unexpectedApiFiles;
+    private String annotationForExactMatch;
 
     private class TestResultObserver implements ResultObserver {
 
@@ -94,6 +102,7 @@
 
         expectedApiFiles = getCommaSeparatedList(instrumentationArgs, "expected-api-files");
         unexpectedApiFiles = getCommaSeparatedList(instrumentationArgs, "unexpected-api-files");
+        annotationForExactMatch = instrumentationArgs.getString("annotation-for-exact-match");
     }
 
     private String[] getCommaSeparatedList(Bundle instrumentationArgs, String key) {
@@ -111,9 +120,19 @@
      */
     public void testSignature() {
         try {
+
+            // Prepare for a class provider that loads classes from bootclasspath but filters
+            // out known inaccessible classes.
+            // Note that com.android.internal.R.* inner classes are also excluded as they are
+            // not part of API though exist in the runtime.
+            ClassProvider classProvider = new ExcludingClassProvider(
+                    new BootClassPathClassesProvider(),
+                    name -> KNOWN_INACCESSIBLE_CLASSES.contains(name)
+                            || (name != null && name.startsWith("com.android.internal.R.")));
+
             Set<JDiffClassDescription> unexpectedClasses = loadUnexpectedClasses();
             for (JDiffClassDescription classDescription : unexpectedClasses) {
-                Class<?> unexpectedClass = findUnexpectedClass(classDescription);
+                Class<?> unexpectedClass = findUnexpectedClass(classDescription, classProvider);
                 if (unexpectedClass != null) {
                     mResultObserver.notifyFailure(
                             FailureType.UNEXPECTED_CLASS,
@@ -122,7 +141,9 @@
                 }
             }
 
-            ApiComplianceChecker complianceChecker = new ApiComplianceChecker(mResultObserver);
+            ApiComplianceChecker complianceChecker = new ApiComplianceChecker(mResultObserver,
+                    classProvider);
+            complianceChecker.setAnnotationForExactMatch(annotationForExactMatch);
             ApiDocumentParser apiDocumentParser = new ApiDocumentParser(
                     TAG, new ApiDocumentParser.Listener() {
                 @Override
@@ -143,6 +164,9 @@
                 File file = new File(API_FILE_DIRECTORY + "/" + expectedApiFile);
                 apiDocumentParser.parse(new FileInputStream(file));
             }
+
+            // After done parsing all expected API files, check for the exact match.
+            complianceChecker.checkExactMatch();
         } catch (Exception e) {
             mResultObserver.notifyFailure(FailureType.CAUGHT_EXCEPTION, e.getMessage(),
                     e.getMessage());
@@ -159,9 +183,10 @@
         }
     }
 
-    private Class<?> findUnexpectedClass(JDiffClassDescription classDescription) {
+    private Class<?> findUnexpectedClass(JDiffClassDescription classDescription,
+            ClassProvider classProvider) {
         try {
-            return ReflectionHelper.findMatchingClass(classDescription);
+            return ReflectionHelper.findMatchingClass(classDescription, classProvider);
         } catch (ClassNotFoundException e) {
             return null;
         }
diff --git a/tests/signature/api-check/system-current-api/Android.mk b/tests/signature/api-check/system-current-api/Android.mk
index 1c10d2c..34c6c22 100644
--- a/tests/signature/api-check/system-current-api/Android.mk
+++ b/tests/signature/api-check/system-current-api/Android.mk
@@ -20,6 +20,7 @@
 
 LOCAL_SIGNATURE_API_FILES := \
     system-current.api \
+    system-removed.api \
     android-test-mock-current.api \
     android-test-runner-current.api \
 
diff --git a/tests/signature/api-check/system-current-api/AndroidTest.xml b/tests/signature/api-check/system-current-api/AndroidTest.xml
index 6bf641a..f958e3d 100644
--- a/tests/signature/api-check/system-current-api/AndroidTest.xml
+++ b/tests/signature/api-check/system-current-api/AndroidTest.xml
@@ -14,6 +14,7 @@
      limitations under the License.
 -->
 <configuration description="Config for CTS System Current API Signature test cases">
+    <option name="test-suite-tag" value="cts" />
     <option name="config-descriptor:metadata" key="component" value="systems" />
     <target_preparer class="com.android.tradefed.targetprep.RunCommandTargetPreparer">
         <option name="run-command" value="mkdir -p /data/local/tmp/signature-test" />
@@ -23,6 +24,9 @@
         <option name="push" value="system-current.api->/data/local/tmp/signature-test/system-current.api" />
     </target_preparer>
     <target_preparer class="com.android.compatibility.common.tradefed.targetprep.FilePusher">
+        <option name="push" value="system-removed.api->/data/local/tmp/signature-test/system-removed.api" />
+    </target_preparer>
+    <target_preparer class="com.android.compatibility.common.tradefed.targetprep.FilePusher">
         <option name="push" value="android-test-mock-current.api->/data/local/tmp/signature-test/android-test-mock-current.api" />
     </target_preparer>
     <target_preparer class="com.android.compatibility.common.tradefed.targetprep.FilePusher">
@@ -35,8 +39,9 @@
     <test class="com.android.tradefed.testtype.AndroidJUnitTest" >
         <option name="package" value="android.signature.cts.api.system_current" />
         <option name="runner" value="repackaged.android.test.InstrumentationTestRunner" />
-        <option name="instrumentation-arg" key="expected-api-files" value="system-current.api" />
+        <option name="instrumentation-arg" key="expected-api-files" value="system-current.api,system-removed.api" />
         <option name="instrumentation-arg" key="unexpected-api-files" value="android-test-mock-current.api,android-test-runner-current.api" />
+        <option name="instrumentation-arg" key="annotation-for-exact-match" value="android.annotation.SystemApi" />
         <option name="runtime-hint" value="30s" />
     </test>
 </configuration>
diff --git a/tests/signature/api/Android.mk b/tests/signature/api/Android.mk
index 9be5e06..abeae54 100644
--- a/tests/signature/api/Android.mk
+++ b/tests/signature/api/Android.mk
@@ -48,12 +48,12 @@
 
 include $(LOCAL_PATH)/build_xml_api_file.mk
 
-# current legacy-test api, in XML format.
+# current android-test-base api, in XML format.
 # ============================================================
 include $(CLEAR_VARS)
-LOCAL_MODULE := cts-legacy-test-current-api
-LOCAL_MODULE_STEM := legacy-test-current.api
-LOCAL_SRC_FILES := frameworks/base/legacy-test/api/legacy-test-current.txt
+LOCAL_MODULE := cts-android-test-base-current-api
+LOCAL_MODULE_STEM := android-test-base-current.api
+LOCAL_SRC_FILES := frameworks/base/test-base/api/android-test-base-current.txt
 
 include $(LOCAL_PATH)/build_xml_api_file.mk
 
@@ -75,11 +75,28 @@
 
 include $(LOCAL_PATH)/build_xml_api_file.mk
 
-# current apache-http-legacy api, in XML format.
-# ==============================================
+# current apache-http-legacy minus current api, in text format.
+# =============================================================
+# Removes any classes from the org.apache.http.legacy API description that are
+# also part of the Android API description.
 include $(CLEAR_VARS)
-LOCAL_MODULE := cts-apache-http-legacy-current-api
-LOCAL_MODULE_STEM := apache-http-legacy-current.api
-LOCAL_SRC_FILES := external/apache-http/api/apache-http-legacy-current.txt
+LOCAL_MODULE_CLASS := ETC
+LOCAL_MODULE_PATH := $(TARGET_OUT_DATA_ETC)
 
-include $(LOCAL_PATH)/build_xml_api_file.mk
+# Tag this module as a cts test artifact
+LOCAL_COMPATIBILITY_SUITE := cts vts general-tests
+
+LOCAL_MODULE := cts-apache-http-legacy-minus-current-api
+LOCAL_MODULE_STEM := apache-http-legacy-minus-current.api
+
+include $(BUILD_SYSTEM)/base_rules.mk
+$(LOCAL_BUILT_MODULE) : \
+        frameworks/base/api/current.txt \
+        external/apache-http/api/apache-http-legacy-current.txt \
+        | $(APICHECK)
+	@echo "Generate unique Apache Http Legacy API file -> $@"
+	@mkdir -p $(dir $@)
+	$(hide) $(APICHECK_COMMAND) -new_api_no_strip \
+	        frameworks/base/api/current.txt \
+            external/apache-http/api/apache-http-legacy-current.txt \
+            $@
diff --git a/tests/signature/intent-check/AndroidTest.xml b/tests/signature/intent-check/AndroidTest.xml
index da6895b..134a175 100644
--- a/tests/signature/intent-check/AndroidTest.xml
+++ b/tests/signature/intent-check/AndroidTest.xml
@@ -14,6 +14,7 @@
      limitations under the License.
 -->
 <configuration description="Config for CTS Intent Signature test cases">
+    <option name="test-suite-tag" value="cts" />
     <option name="config-descriptor:metadata" key="component" value="systems" />
     <option name="not-shardable" value="true" />
     <target_preparer class="com.android.compatibility.common.tradefed.targetprep.DynamicConfigPusher">
diff --git a/tests/signature/runSignatureTests.sh b/tests/signature/runSignatureTests.sh
index 623973d..e23d4fa 100755
--- a/tests/signature/runSignatureTests.sh
+++ b/tests/signature/runSignatureTests.sh
@@ -15,7 +15,7 @@
 CtsSystemCurrentApiSignatureTestCases
 CtsAndroidTestMockCurrentApiSignatureTestCases
 CtsAndroidTestRunnerCurrentApiSignatureTestCases
-CtsLegacyTest26ApiSignatureTestCases
+CtsAndroidTestBase27ApiSignatureTestCases
 CtsApacheHttpLegacyCurrentApiSignatureTestCases
 "
 else
diff --git a/tests/signature/src/android/signature/cts/ApiComplianceChecker.java b/tests/signature/src/android/signature/cts/ApiComplianceChecker.java
index 8dbf5e7..57ad676 100644
--- a/tests/signature/src/android/signature/cts/ApiComplianceChecker.java
+++ b/tests/signature/src/android/signature/cts/ApiComplianceChecker.java
@@ -15,6 +15,7 @@
  */
 package android.signature.cts;
 
+import java.lang.annotation.Annotation;
 import java.lang.reflect.Constructor;
 import java.lang.reflect.Field;
 import java.lang.reflect.Method;
@@ -71,9 +72,80 @@
 
 
     private final ResultObserver resultObserver;
+    private final ClassProvider classProvider;
 
-    public ApiComplianceChecker(ResultObserver resultObserver) {
+    private Class<? extends Annotation> annotationClass = null;
+
+    private Map<String, Class<?>> annotatedClassesMap = new HashMap<>();
+    private Map<String, Set<Constructor<?>>> annotatedConstructorsMap = new HashMap<>();
+    private Map<String, Set<Method>> annotatedMethodsMap = new HashMap<>();
+    private Map<String, Set<Field>> annotatedFieldsMap = new HashMap<>();
+
+    public ApiComplianceChecker(ResultObserver resultObserver, ClassProvider classProvider) {
         this.resultObserver = resultObserver;
+        this.classProvider = classProvider;
+    }
+
+    /**
+     * Optionally let ApiComplianceCheker to ensure runtime classes do not have more API members
+     * than the documented API ones.
+     *
+     * @param annotationName name of the annotation class for the API type (e.g.
+     *            android.annotation.SystemApi)
+     */
+    public void setAnnotationForExactMatch(String annotationName) {
+        annotationClass = ReflectionHelper.getAnnotationClass(annotationName);
+        classProvider.getAllClasses().forEach(clazz -> {
+            if (clazz.isAnnotationPresent(annotationClass)) {
+                annotatedClassesMap.put(clazz.getName(), clazz);
+            }
+            Set<Constructor<?>> constructors = ReflectionHelper.getAnnotatedConstructors(clazz,
+                    annotationClass);
+            if (!constructors.isEmpty()) {
+                annotatedConstructorsMap.put(clazz.getName(), constructors);
+            }
+            Set<Method> methods = ReflectionHelper.getAnnotatedMethods(clazz, annotationClass);
+            if (!methods.isEmpty()) {
+                annotatedMethodsMap.put(clazz.getName(), methods);
+            }
+            Set<Field> fields = ReflectionHelper.getAnnotatedFields(clazz, annotationClass);
+            if (!fields.isEmpty()) {
+                annotatedFieldsMap.put(clazz.getName(), fields);
+            }
+        });
+    }
+
+    /**
+     * If an annotated API (class, method, constructor, fields) found in the device is not in the
+     * documented API then trigger an error.
+     */
+    public void checkExactMatch() {
+        for (Class<?> clazz : annotatedClassesMap.values()) {
+            resultObserver.notifyFailure(FailureType.EXTRA_CLASS, clazz.getName(),
+                    "Class annotated with " + annotationClass.getName()
+                            + " does not exist in the documented API");
+        }
+        for (Set<Constructor<?>> set : annotatedConstructorsMap.values()) {
+            for (Constructor<?> c : set) {
+                resultObserver.notifyFailure(FailureType.EXTRA_METHOD, c.toString(),
+                        "Constructor annotated with " + annotationClass.getName()
+                                + " does not exist in the API");
+            }
+        }
+        for (Set<Method> set : annotatedMethodsMap.values()) {
+            for (Method m : set) {
+                resultObserver.notifyFailure(FailureType.EXTRA_METHOD, m.toString(),
+                        "Method annotated with " + annotationClass.getName()
+                                + " does not exist in the API");
+            }
+        }
+        for (Set<Field> set : annotatedFieldsMap.values()) {
+            for (Field f : set) {
+                resultObserver.notifyFailure(FailureType.EXTRA_FIELD, f.toString(),
+                        "Field annotated with " + annotationClass.getName()
+                                + " does not exist in the API");
+            }
+        }
     }
 
     private static void loge(String message, Exception exception) {
@@ -100,6 +172,9 @@
     public void checkSignatureCompliance(JDiffClassDescription classDescription) {
         Class<?> runtimeClass = checkClassCompliance(classDescription);
         if (runtimeClass != null) {
+            // remove the class from the set if found
+            annotatedClassesMap.remove(runtimeClass.getName());
+
             checkFieldsCompliance(classDescription, runtimeClass);
             checkConstructorCompliance(classDescription, runtimeClass);
             checkMethodCompliance(classDescription, runtimeClass);
@@ -188,7 +263,7 @@
 
     private Class<?> findRequiredClass(JDiffClassDescription classDescription) {
         try {
-            return ReflectionHelper.findMatchingClass(classDescription);
+            return ReflectionHelper.findMatchingClass(classDescription, classProvider);
         } catch (ClassNotFoundException e) {
             loge("ClassNotFoundException for " + classDescription.getAbsoluteClassName(), e);
             return null;
@@ -357,9 +432,11 @@
             Class<?> runtimeClass) {
         // A map of field name to field of the fields contained in runtimeClass.
         Map<String, Field> classFieldMap = buildFieldMap(runtimeClass);
+        Set<Field> annotatedFields = annotatedFieldsMap.get(runtimeClass.getName());
         for (JDiffClassDescription.JDiffField field : classDescription.getFields()) {
             try {
                 Field f = classFieldMap.get(field.mName);
+                boolean found = false;
                 if (f == null) {
                     resultObserver.notifyFailure(FailureType.MISSING_FIELD,
                             field.toReadableString(classDescription.getAbsoluteClassName()),
@@ -390,8 +467,25 @@
                                 "Non-compatible field type found when looking for " +
                                         field.toSignatureString());
                     }
+                    found = true;
+                } else {
+                    found = true;
                 }
+                if (found) {
+                    // make sure that the field (or its declaring class) is annotated
+                    if (annotationClass != null) {
+                        if (!ReflectionHelper.isAnnotatedOrInAnnotatedClass(f, annotationClass)) {
+                            resultObserver.notifyFailure(FailureType.MISSING_ANNOTATION,
+                                    f.toString(),
+                                    "Annotation " + annotationClass.getName() + " is missing");
+                        }
+                    }
 
+                    // remove it from the set if found in the API doc
+                    if (annotatedFields != null) {
+                        annotatedFields.remove(f);
+                    }
+                }
             } catch (Exception e) {
                 loge("Got exception when checking field compliance", e);
                 resultObserver.notifyFailure(
@@ -623,6 +717,8 @@
     @SuppressWarnings("unchecked")
     private void checkConstructorCompliance(JDiffClassDescription classDescription,
             Class<?> runtimeClass) {
+        Set<Constructor<?>> annotatedConstructors = annotatedConstructorsMap
+                .get(runtimeClass.getName());
         for (JDiffClassDescription.JDiffConstructor con : classDescription.getConstructors()) {
             try {
                 Constructor<?> c = ReflectionHelper.findMatchingConstructor(runtimeClass, con);
@@ -642,6 +738,19 @@
                                 "Non-compatible method found when looking for " +
                                         con.toSignatureString());
                     }
+                    // make sure that the constructor (or its declaring class) is annotated
+                    if (annotationClass != null) {
+                        if (!ReflectionHelper.isAnnotatedOrInAnnotatedClass(c, annotationClass)) {
+                            resultObserver.notifyFailure(FailureType.MISSING_ANNOTATION,
+                                    c.toString(),
+                                    "Annotation " + annotationClass.getName() + " is missing");
+                        }
+                    }
+
+                    // remove it from the set if found in the API doc
+                    if (annotatedConstructors != null) {
+                        annotatedConstructors.remove(c);
+                    }
                 }
             } catch (Exception e) {
                 loge("Got exception when checking constructor compliance", e);
@@ -661,6 +770,7 @@
      */
     private void checkMethodCompliance(JDiffClassDescription classDescription,
             Class<?> runtimeClass) {
+        Set<Method> annotatedMethods = annotatedMethodsMap.get(runtimeClass.getName());
         for (JDiffClassDescription.JDiffMethod method : classDescription.getMethods()) {
             try {
 
@@ -692,6 +802,22 @@
                                 "Non-compatible method found when looking for " +
                                         method.toSignatureString());
                     }
+                    // make sure that the method (or its declaring class) is annotated or overriding
+                    // annotated method.
+                    if (annotationClass != null) {
+                        if (!ReflectionHelper.isAnnotatedOrInAnnotatedClass(m, annotationClass)
+                                && !ReflectionHelper.isOverridingAnnotatedMethod(m,
+                                        annotationClass)) {
+                            resultObserver.notifyFailure(FailureType.MISSING_ANNOTATION,
+                                    m.toString(),
+                                    "Annotation " + annotationClass.getName() + " is missing");
+                        }
+                    }
+
+                    // remove it from the set if found in the API doc
+                    if (annotatedMethods != null) {
+                        annotatedMethods.remove(m);
+                    }
                 }
             } catch (Exception e) {
                 loge("Got exception when checking method compliance", e);
diff --git a/tests/signature/src/android/signature/cts/ClassProvider.java b/tests/signature/src/android/signature/cts/ClassProvider.java
new file mode 100644
index 0000000..2461f26
--- /dev/null
+++ b/tests/signature/src/android/signature/cts/ClassProvider.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.signature.cts;
+
+import java.util.stream.Stream;
+
+/**
+ * Generic class for getting runtime classes that will be matched against
+ * {@link JDiffClassDescription}. {@link ApiComplianceChecker} is using this
+ * when it needs runtime classes.
+ */
+public abstract class ClassProvider {
+    /**
+     * Get a specific class with the given name.
+     *
+     * @throws ClassNotFoundException
+     */
+    public Class<?> getClass(String name) throws ClassNotFoundException {
+        return Class.forName(name, false, this.getClass().getClassLoader());
+    }
+
+    /**
+     * Gets all classes available to this provider.
+     */
+    public abstract Stream<Class<?>> getAllClasses();
+}
diff --git a/tests/signature/src/android/signature/cts/ExcludingClassProvider.java b/tests/signature/src/android/signature/cts/ExcludingClassProvider.java
new file mode 100644
index 0000000..290cbf5
--- /dev/null
+++ b/tests/signature/src/android/signature/cts/ExcludingClassProvider.java
@@ -0,0 +1,48 @@
+/*
+ * 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.signature.cts;
+
+import java.util.function.Predicate;
+import java.util.stream.Stream;
+
+/**
+ * A filtered class provider which excludes classes by their canonical names
+ */
+public class ExcludingClassProvider extends ClassProvider {
+    private final ClassProvider base;
+    private final Predicate<String> testForExclusion;
+
+    public ExcludingClassProvider(ClassProvider base,
+            Predicate<String> testForExclusion) {
+        this.base = base;
+        this.testForExclusion = testForExclusion;
+    }
+
+    @Override
+    public Class<?> getClass(String name) throws ClassNotFoundException {
+        if (!testForExclusion.test(name)) {
+            return base.getClass(name);
+        }
+        // a filtered-out class is the same as non-existing class
+        throw new ClassNotFoundException("Cannot find class " + name);
+    }
+
+    @Override
+    public Stream<Class<?>> getAllClasses() {
+        return base.getAllClasses()
+                .filter(clazz -> !testForExclusion.test(clazz.getCanonicalName()));
+    }
+}
diff --git a/tests/signature/src/android/signature/cts/FailureType.java b/tests/signature/src/android/signature/cts/FailureType.java
index 77820eb..6f538f4 100644
--- a/tests/signature/src/android/signature/cts/FailureType.java
+++ b/tests/signature/src/android/signature/cts/FailureType.java
@@ -4,6 +4,7 @@
  * Define the type of the signature check failures.
  */
 public enum FailureType {
+    MISSING_ANNOTATION,
     MISSING_CLASS,
     MISSING_INTERFACE,
     MISSING_METHOD,
@@ -14,5 +15,9 @@
     MISMATCH_METHOD,
     MISMATCH_FIELD,
     UNEXPECTED_CLASS,
+    EXTRA_CLASS,
+    EXTRA_INTERFACE,
+    EXTRA_METHOD,
+    EXTRA_FIELD,
     CAUGHT_EXCEPTION,
 }
diff --git a/tests/signature/src/android/signature/cts/JDiffClassDescription.java b/tests/signature/src/android/signature/cts/JDiffClassDescription.java
index df91ddb..0718393 100644
--- a/tests/signature/src/android/signature/cts/JDiffClassDescription.java
+++ b/tests/signature/src/android/signature/cts/JDiffClassDescription.java
@@ -66,7 +66,7 @@
         return mPackageName;
     }
 
-    String getShortClassName() {
+    public String getShortClassName() {
         return mShortClassName;
     }
 
diff --git a/tests/signature/src/android/signature/cts/ReflectionHelper.java b/tests/signature/src/android/signature/cts/ReflectionHelper.java
index 54640a3..dc2ef5a 100644
--- a/tests/signature/src/android/signature/cts/ReflectionHelper.java
+++ b/tests/signature/src/android/signature/cts/ReflectionHelper.java
@@ -15,8 +15,14 @@
  */
 package android.signature.cts;
 
+import java.lang.annotation.Annotation;
+import java.lang.reflect.AnnotatedElement;
+import java.lang.reflect.Array;
 import java.lang.reflect.Constructor;
+import java.lang.reflect.Field;
 import java.lang.reflect.GenericArrayType;
+import java.lang.reflect.InvocationTargetException;
+import java.lang.reflect.Member;
 import java.lang.reflect.Method;
 import java.lang.reflect.Modifier;
 import java.lang.reflect.ParameterizedType;
@@ -24,7 +30,10 @@
 import java.lang.reflect.TypeVariable;
 import java.lang.reflect.WildcardType;
 import java.util.ArrayList;
+import java.util.Enumeration;
+import java.util.HashSet;
 import java.util.List;
+import java.util.Set;
 
 /**
  * Uses reflection to obtain runtime representations of elements in the API.
@@ -42,7 +51,7 @@
      * @return the reflected class, or null if not found.
      */
     @SuppressWarnings("unchecked")
-    public static Class<?> findMatchingClass(JDiffClassDescription classDescription)
+    public static Class<?> findMatchingClass(JDiffClassDescription classDescription, ClassProvider classProvider)
             throws ClassNotFoundException {
         // even if there are no . in the string, split will return an
         // array of length 1
@@ -51,8 +60,7 @@
         String packageName = classDescription.getPackageName();
         String currentName = packageName + "." + classNameParts[0];
 
-        Class<?> clz = Class.forName(
-                currentName, false, ReflectionHelper.class.getClassLoader());
+        Class<?> clz = classProvider.getClass(currentName);
         String absoluteClassName = classDescription.getAbsoluteClassName();
         if (clz.getCanonicalName().equals(absoluteClassName)) {
             return clz;
@@ -330,4 +338,138 @@
             throw new RuntimeException("Got an unknown java.lang.Type");
         }
     }
+
+    /**
+     * Returns a Class representing an annotation type of the given name.
+     */
+    @SuppressWarnings("unchecked")
+    public static Class<? extends Annotation> getAnnotationClass(String name) {
+        try {
+            Class<?> clazz = Class.forName(
+                    name, false, ReflectionHelper.class.getClassLoader());
+            if (clazz.isAnnotation()) {
+                return (Class<? extends Annotation>) clazz;
+            } else {
+                return null;
+            }
+        } catch (ClassNotFoundException e) {
+            return null;
+        }
+    }
+
+    /**
+     * Returns a list of constructors which are annotated with the given annotation class.
+     */
+    public static Set<Constructor<?>> getAnnotatedConstructors(Class<?> clazz,
+            Class<? extends Annotation> annotation) {
+        Set<Constructor<?>> result = new HashSet<>();
+        if (annotation != null) {
+            for (Constructor<?> c : clazz.getDeclaredConstructors()) {
+                if (c.isAnnotationPresent(annotation)) {
+                    // TODO(b/71630695): currently, some API members are not annotated, because
+                    // a member is automatically added to the API set if it is in a class with
+                    // annotation and it is not @hide. <member>.getDeclaringClass().
+                    // isAnnotationPresent(annotationClass) won't help because it will then
+                    // incorrectly include non-API members which are marked as @hide;
+                    // @hide isn't visible at runtime. Until the issue is fixed, we should
+                    // omit those automatically added API members from the test.
+                    result.add(c);
+                }
+            }
+        }
+        return result;
+    }
+
+    /**
+     * Returns a list of methods which are annotated with the given annotation class.
+     */
+    public static Set<Method> getAnnotatedMethods(Class<?> clazz,
+            Class<? extends Annotation> annotation) {
+        Set<Method> result = new HashSet<>();
+        if (annotation != null) {
+            for (Method m : clazz.getDeclaredMethods()) {
+                if (m.isAnnotationPresent(annotation)) {
+                    // TODO(b/71630695): see getAnnotatedConstructors for details
+                    result.add(m);
+                }
+            }
+        }
+        return result;
+    }
+
+    /**
+     * Returns a list of fields which are annotated with the given annotation class.
+     */
+    public static Set<Field> getAnnotatedFields(Class<?> clazz,
+            Class<? extends Annotation> annotation) {
+        Set<Field> result = new HashSet<>();
+        if (annotation != null) {
+            for (Field f : clazz.getDeclaredFields()) {
+                if (f.isAnnotationPresent(annotation)) {
+                    // TODO(b/71630695): see getAnnotatedConstructors for details
+                    result.add(f);
+                }
+            }
+        }
+        return result;
+    }
+
+    private static boolean isInAnnotatedClass(Member m,
+            Class<? extends Annotation> annotationClass) {
+        Class<?> clazz = m.getDeclaringClass();
+        do {
+            if (clazz.isAnnotationPresent(annotationClass)) {
+                return true;
+            }
+        } while ((clazz = clazz.getDeclaringClass()) != null);
+        return false;
+    }
+
+    public static boolean isAnnotatedOrInAnnotatedClass(Field field,
+            Class<? extends Annotation> annotationClass) {
+        if (annotationClass == null) {
+            return true;
+        }
+        return field.isAnnotationPresent(annotationClass)
+                || isInAnnotatedClass(field, annotationClass);
+    }
+
+    public static boolean isAnnotatedOrInAnnotatedClass(Constructor<?> constructor,
+            Class<? extends Annotation> annotationClass) {
+        if (annotationClass == null) {
+            return true;
+        }
+        return constructor.isAnnotationPresent(annotationClass)
+                || isInAnnotatedClass(constructor, annotationClass);
+    }
+
+    public static boolean isAnnotatedOrInAnnotatedClass(Method method,
+            Class<? extends Annotation> annotationClass) {
+        if (annotationClass == null) {
+            return true;
+        }
+        return method.isAnnotationPresent(annotationClass)
+                || isInAnnotatedClass(method, annotationClass);
+    }
+
+    public static boolean isOverridingAnnotatedMethod(Method method,
+            Class<? extends Annotation> annotationClass) {
+        Class<?> clazz = method.getDeclaringClass();
+        while (!(clazz = clazz.getSuperclass()).equals(Object.class)) {
+            try {
+                Method overriddenMethod;
+                overriddenMethod = clazz.getDeclaredMethod(method.getName(),
+                        method.getParameterTypes());
+                if (overriddenMethod != null) {
+                    return isAnnotatedOrInAnnotatedClass(overriddenMethod, annotationClass);
+                }
+            } catch (NoSuchMethodException e) {
+                continue;
+            } catch (SecurityException e) {
+                throw new RuntimeException(
+                        "Error while searching for overridden method. " + method.toString(), e);
+            }
+        }
+        return false;
+    }
 }
diff --git a/tests/signature/tests/src/android/signature/cts/tests/ApiComplianceCheckerTest.java b/tests/signature/tests/src/android/signature/cts/tests/ApiComplianceCheckerTest.java
index 71d2e37..abdef7b 100644
--- a/tests/signature/tests/src/android/signature/cts/tests/ApiComplianceCheckerTest.java
+++ b/tests/signature/tests/src/android/signature/cts/tests/ApiComplianceCheckerTest.java
@@ -16,19 +16,23 @@
 
 package android.signature.cts.tests;
 
+import java.lang.reflect.Modifier;
+import java.util.Arrays;
+
 import android.signature.cts.ApiComplianceChecker;
+import android.signature.cts.ClassProvider;
+import android.signature.cts.ExcludingClassProvider;
 import android.signature.cts.FailureType;
 import android.signature.cts.JDiffClassDescription;
 import android.signature.cts.ResultObserver;
-
+import android.signature.cts.tests.data.ApiAnnotation;
 import junit.framework.Assert;
 import junit.framework.TestCase;
 
-import java.lang.reflect.Modifier;
-
 /**
  * Test class for JDiffClassDescription.
  */
+@SuppressWarnings("deprecation")
 public class ApiComplianceCheckerTest extends TestCase {
 
     private static final String VALUE = "VALUE";
@@ -36,7 +40,8 @@
     private class NoFailures implements ResultObserver {
         @Override
         public void notifyFailure(FailureType type, String name, String errmsg) {
-            Assert.fail("Saw unexpected test failure: " + name + " failure type: " + type);
+            Assert.fail("Saw unexpected test failure: " + name + " failure type: " + type
+                    + " error message: " + errmsg);
         }
     }
 
@@ -68,14 +73,38 @@
     }
 
     private void checkSignatureCompliance(JDiffClassDescription classDescription) {
+        checkSignatureCompliance(classDescription, false);
+    }
+
+    private void checkSignatureCompliance(JDiffClassDescription classDescription,
+            boolean doExactMatch, String... excludedRuntimeClassNames) {
         ResultObserver resultObserver = new NoFailures();
-        checkSignatureCompliance(classDescription, resultObserver);
+        checkSignatureCompliance(classDescription, resultObserver, doExactMatch,
+                excludedRuntimeClassNames);
     }
 
     private void checkSignatureCompliance(JDiffClassDescription classDescription,
             ResultObserver resultObserver) {
-        ApiComplianceChecker complianceChecker = new ApiComplianceChecker(resultObserver);
+        checkSignatureCompliance(classDescription, resultObserver, false);
+    }
+
+    private void checkSignatureCompliance(JDiffClassDescription classDescription,
+            ResultObserver resultObserver, boolean doExactMatch, String... excludedRuntimeClasses) {
+        ClassProvider provider = new TestClassesProvider();
+        if (excludedRuntimeClasses.length != 0) {
+            provider = new ExcludingClassProvider(provider,
+                    name -> Arrays.stream(excludedRuntimeClasses)
+                            .anyMatch(myname -> myname.equals(name)));
+        }
+        ApiComplianceChecker complianceChecker = new ApiComplianceChecker(resultObserver,
+                provider);
+        if (doExactMatch) {
+            complianceChecker.setAnnotationForExactMatch(ApiAnnotation.class.getName());
+        }
         complianceChecker.checkSignatureCompliance(classDescription);
+        if (doExactMatch) {
+            complianceChecker.checkExactMatch();
+        }
     }
 
     /**
@@ -501,4 +530,274 @@
         checkSignatureCompliance(clz, observer);
         observer.validate();
     }
+
+    private static JDiffClassDescription createClass(String name) {
+        JDiffClassDescription clz = new JDiffClassDescription(
+                "android.signature.cts.tests.data", name);
+        clz.setType(JDiffClassDescription.JDiffType.CLASS);
+        clz.setModifier(Modifier.PUBLIC);
+        return clz;
+    }
+
+    private static void addConstructor(JDiffClassDescription clz, String... paramTypes) {
+        JDiffClassDescription.JDiffConstructor constructor = new JDiffClassDescription.JDiffConstructor(
+                clz.getShortClassName(), Modifier.PUBLIC);
+        if (paramTypes != null) {
+            for (String type : paramTypes) {
+                constructor.addParam(type);
+            }
+        }
+        clz.addConstructor(constructor);
+    }
+
+    private static void addPublicVoidMethod(JDiffClassDescription clz, String name) {
+        JDiffClassDescription.JDiffMethod method = new JDiffClassDescription.JDiffMethod(
+                name, Modifier.PUBLIC, "void");
+        clz.addMethod(method);
+    }
+
+    private static void addPublicBooleanField(JDiffClassDescription clz, String name) {
+        JDiffClassDescription.JDiffField field = new JDiffClassDescription.JDiffField(
+                name, "boolean", Modifier.PUBLIC, VALUE);
+        clz.addField(field);
+    }
+
+    /**
+     * Documented API and runtime classes are exactly matched.
+     */
+    public void testExactApiMatch() {
+        JDiffClassDescription clz = createClass("SystemApiClass");
+        addConstructor(clz);
+        addPublicVoidMethod(clz, "apiMethod");
+        addPublicBooleanField(clz, "apiField");
+
+        checkSignatureCompliance(clz, true,
+                "android.signature.cts.tests.data.PublicApiClass",
+                "android.signature.cts.tests.data.ForciblyPublicizedPrivateClass");
+
+        clz = createClass("PublicApiClass");
+        addConstructor(clz);
+        addPublicVoidMethod(clz, "apiMethod");
+        addPublicBooleanField(clz, "apiField");
+
+        checkSignatureCompliance(clz, true,
+                "android.signature.cts.tests.data.SystemApiClass",
+                "android.signature.cts.tests.data.ForciblyPublicizedPrivateClass");
+    }
+
+    /**
+     * A constructor is found in the runtime class, but not in the documented API
+     */
+    public void testDetectUnauthorizedConstructorApi() {
+        ExpectFailure observer = new ExpectFailure(FailureType.EXTRA_METHOD);
+
+        JDiffClassDescription clz = createClass("SystemApiClass");
+        // (omitted) addConstructor(clz);
+        addPublicVoidMethod(clz, "apiMethod");
+        addPublicBooleanField(clz, "apiField");
+
+        checkSignatureCompliance(clz, observer, true,
+                "android.signature.cts.tests.data.PublicApiClass",
+                "android.signature.cts.tests.data.ForciblyPublicizedPrivateClass");
+        observer.validate();
+
+        observer = new ExpectFailure(FailureType.EXTRA_METHOD);
+
+        clz = createClass("PublicApiClass");
+        // (omitted) addConstructor(clz);
+        addPublicVoidMethod(clz, "apiMethod");
+        addPublicBooleanField(clz, "apiField");
+
+        checkSignatureCompliance(clz, observer, true,
+                "android.signature.cts.tests.data.SystemApiClass",
+                "android.signature.cts.tests.data.ForciblyPublicizedPrivateClass");
+        observer.validate();
+    }
+
+    /**
+     * A method is found in the runtime class, but not in the documented API
+     */
+    public void testDetectUnauthorizedMethodApi() {
+        ExpectFailure observer = new ExpectFailure(FailureType.EXTRA_METHOD);
+
+        JDiffClassDescription clz = createClass("SystemApiClass");
+        addConstructor(clz);
+        // (omitted) addPublicVoidMethod(clz, "apiMethod");
+        addPublicBooleanField(clz, "apiField");
+
+        checkSignatureCompliance(clz, observer, true,
+                "android.signature.cts.tests.data.PublicApiClass",
+                "android.signature.cts.tests.data.ForciblyPublicizedPrivateClass");
+        observer.validate();
+
+        observer = new ExpectFailure(FailureType.EXTRA_METHOD);
+
+        clz = createClass("PublicApiClass");
+        addConstructor(clz);
+        // (omitted) addPublicVoidMethod(clz, "apiMethod");
+        addPublicBooleanField(clz, "apiField");
+
+        checkSignatureCompliance(clz, observer, true,
+                "android.signature.cts.tests.data.SystemApiClass",
+                "android.signature.cts.tests.data.ForciblyPublicizedPrivateClass");
+        observer.validate();
+    }
+
+    /**
+     * A field is found in the runtime class, but not in the documented API
+     */
+    public void testDetectUnauthorizedFieldApi() {
+        ExpectFailure observer = new ExpectFailure(FailureType.EXTRA_FIELD);
+
+        JDiffClassDescription clz = createClass("SystemApiClass");
+        addConstructor(clz);
+        addPublicVoidMethod(clz, "apiMethod");
+        // (omitted) addPublicBooleanField(clz, "apiField");
+
+        checkSignatureCompliance(clz, observer, true,
+                "android.signature.cts.tests.data.PublicApiClass",
+                "android.signature.cts.tests.data.ForciblyPublicizedPrivateClass");
+        observer.validate();
+
+        observer = new ExpectFailure(FailureType.EXTRA_FIELD);
+
+        clz = createClass("PublicApiClass");
+        addConstructor(clz);
+        addPublicVoidMethod(clz, "apiMethod");
+        // (omitted) addPublicBooleanField(clz, "apiField");
+
+        checkSignatureCompliance(clz, observer, true,
+                "android.signature.cts.tests.data.SystemApiClass",
+                "android.signature.cts.tests.data.ForciblyPublicizedPrivateClass");
+        observer.validate();
+    }
+
+    /**
+     * A class is found in the runtime classes, but not in the documented API
+     */
+    public void testDetectUnauthorizedClassApi() {
+        ExpectFailure observer = new ExpectFailure(FailureType.EXTRA_CLASS);
+        JDiffClassDescription clz = createClass("SystemApiClass");
+        addConstructor(clz);
+        addPublicVoidMethod(clz, "apiMethod");
+        addPublicBooleanField(clz, "apiField");
+
+        checkSignatureCompliance(clz, observer, true,
+                "android.signature.cts.tests.data.PublicApiClass");
+        // Note that ForciblyPublicizedPrivateClass is now included in the runtime classes
+        observer.validate();
+
+        observer = new ExpectFailure(FailureType.EXTRA_CLASS);
+
+        clz = createClass("PublicApiClass");
+        addConstructor(clz);
+        addPublicVoidMethod(clz, "apiMethod");
+        addPublicBooleanField(clz, "apiField");
+
+        checkSignatureCompliance(clz, observer, true,
+                "android.signature.cts.tests.data.SystemApiClass");
+        // Note that ForciblyPublicizedPrivateClass is now included in the runtime classes
+        observer.validate();
+    }
+
+    /**
+     * A member which is declared in an annotated class is currently recognized as an API.
+     */
+    public void testB71630695() {
+        // TODO(b/71630695): currently, some API members are not annotated, because
+        // a member is automatically added to the API set if it is in a class with
+        // annotation and it is not @hide. This should be fixed, but until then,
+        // CTS should respect the existing behavior.
+        JDiffClassDescription clz = createClass("SystemApiClass");
+        addConstructor(clz);
+        addPublicVoidMethod(clz, "apiMethod");
+        addPublicBooleanField(clz, "apiField");
+        addConstructor(clz, "float"); // this is not annotated
+
+        checkSignatureCompliance(clz, true,
+                "android.signature.cts.tests.data.PublicApiClass",
+                "android.signature.cts.tests.data.ForciblyPublicizedPrivateClass");
+
+        clz = createClass("SystemApiClass");
+        addConstructor(clz);
+        addPublicVoidMethod(clz, "apiMethod");
+        addPublicBooleanField(clz, "apiField");
+        addPublicVoidMethod(clz, "unannotatedApiMethod"); // this is not annotated
+
+        checkSignatureCompliance(clz, true,
+                "android.signature.cts.tests.data.PublicApiClass",
+                "android.signature.cts.tests.data.ForciblyPublicizedPrivateClass");
+
+        clz = createClass("SystemApiClass");
+        addConstructor(clz);
+        addPublicVoidMethod(clz, "apiMethod");
+        addPublicBooleanField(clz, "apiField");
+        addPublicBooleanField(clz, "unannotatedApiField"); // this is not annotated
+
+        checkSignatureCompliance(clz, true,
+                "android.signature.cts.tests.data.PublicApiClass",
+                "android.signature.cts.tests.data.ForciblyPublicizedPrivateClass");
+    }
+
+    /**
+     * An API is documented, but isn't annotated in the runtime class. But, due to b/71630695, this
+     * test can only be done for public API classes.
+     */
+    public void testDetectMissingAnnotation() {
+        ExpectFailure observer = new ExpectFailure(FailureType.MISSING_ANNOTATION);
+
+        JDiffClassDescription clz = createClass("PublicApiClass");
+        addConstructor(clz);
+        addPublicVoidMethod(clz, "apiMethod");
+        addPublicBooleanField(clz, "apiField");
+        addConstructor(clz, "int"); // this is not annotated
+
+        checkSignatureCompliance(clz, observer, true,
+                "android.signature.cts.tests.data.SystemApiClass",
+                "android.signature.cts.tests.data.ForciblyPublicizedPrivateClass");
+        observer.validate();
+
+        observer = new ExpectFailure(FailureType.MISSING_ANNOTATION);
+
+        clz = createClass("PublicApiClass");
+        addConstructor(clz);
+        addPublicVoidMethod(clz, "apiMethod");
+        addPublicBooleanField(clz, "apiField");
+        addPublicVoidMethod(clz, "privateMethod"); // this is not annotated
+
+        checkSignatureCompliance(clz, observer, true,
+                "android.signature.cts.tests.data.SystemApiClass",
+                "android.signature.cts.tests.data.ForciblyPublicizedPrivateClass");
+        observer.validate();
+
+        observer = new ExpectFailure(FailureType.MISSING_ANNOTATION);
+
+        clz = createClass("PublicApiClass");
+        addConstructor(clz);
+        addPublicVoidMethod(clz, "apiMethod");
+        addPublicBooleanField(clz, "apiField");
+        addPublicBooleanField(clz, "privateField"); // this is not annotated
+
+        checkSignatureCompliance(clz, observer, true,
+                "android.signature.cts.tests.data.SystemApiClass",
+                "android.signature.cts.tests.data.ForciblyPublicizedPrivateClass");
+        observer.validate();
+    }
+
+    /**
+     * A <code>@hide</code> method should be recognized as API though it is not annotated, if it is
+     * overriding a method which is already an API.
+     */
+    public void testOverriddenHidenMethodIsApi() {
+        JDiffClassDescription clz = createClass("PublicApiClass");
+        addConstructor(clz);
+        addPublicVoidMethod(clz, "apiMethod");
+        addPublicBooleanField(clz, "apiField");
+        addPublicVoidMethod(clz, "anOverriddenMethod"); // not annotated and @hide, but is API
+
+        checkSignatureCompliance(clz, true,
+                "android.signature.cts.tests.data.SystemApiClass",
+                "android.signature.cts.tests.data.ForciblyPublicizedPrivateClass");
+
+    }
 }
diff --git a/tests/signature/tests/src/android/signature/cts/tests/TestClassesProvider.java b/tests/signature/tests/src/android/signature/cts/tests/TestClassesProvider.java
new file mode 100644
index 0000000..80176de
--- /dev/null
+++ b/tests/signature/tests/src/android/signature/cts/tests/TestClassesProvider.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.signature.cts.tests;
+
+import java.util.stream.Stream;
+
+import android.signature.cts.ClassProvider;
+import android.signature.cts.tests.data.AbstractClass;
+import android.signature.cts.tests.data.SystemApiClass;
+import android.signature.cts.tests.data.FinalClass;
+import android.signature.cts.tests.data.PrivateClass;
+import android.signature.cts.tests.data.PublicApiClass;
+import android.signature.cts.tests.data.ForciblyPublicizedPrivateClass;
+import android.signature.cts.tests.data.NormalClass;
+import android.signature.cts.tests.data.NormalException;
+import android.signature.cts.tests.data.NormalInterface;
+import android.signature.cts.tests.data.ApiAnnotation;
+
+public class TestClassesProvider extends ClassProvider {
+    @Override
+    public Stream<Class<?>> getAllClasses() {
+        Stream.Builder<Class<?>> builder = Stream.builder();
+        builder.add(AbstractClass.class);
+        builder.add(FinalClass.class);
+        builder.add(NormalClass.class);
+        builder.add(NormalException.class);
+        builder.add(NormalInterface.class);
+        builder.add(ApiAnnotation.class);
+        builder.add(PublicApiClass.class);
+        builder.add(SystemApiClass.class);
+        builder.add(PrivateClass.class);
+        builder.add(ForciblyPublicizedPrivateClass.class);
+        return builder.build();
+    }
+
+}
diff --git a/tests/signature/tests/src/android/signature/cts/tests/data/ApiAnnotation.java b/tests/signature/tests/src/android/signature/cts/tests/data/ApiAnnotation.java
new file mode 100644
index 0000000..17a49b2
--- /dev/null
+++ b/tests/signature/tests/src/android/signature/cts/tests/data/ApiAnnotation.java
@@ -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.
+ */
+package android.signature.cts.tests.data;
+
+import static java.lang.annotation.ElementType.ANNOTATION_TYPE;
+import static java.lang.annotation.ElementType.CONSTRUCTOR;
+import static java.lang.annotation.ElementType.FIELD;
+import static java.lang.annotation.ElementType.METHOD;
+import static java.lang.annotation.ElementType.PACKAGE;
+import static java.lang.annotation.ElementType.TYPE;
+
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.lang.annotation.Target;
+
+@Target({
+    TYPE, FIELD, METHOD, CONSTRUCTOR, ANNOTATION_TYPE, PACKAGE
+})
+@Retention(RetentionPolicy.RUNTIME)
+public @interface ApiAnnotation {
+}
diff --git a/tests/signature/tests/src/android/signature/cts/tests/data/ForciblyPublicizedPrivateClass.java b/tests/signature/tests/src/android/signature/cts/tests/data/ForciblyPublicizedPrivateClass.java
new file mode 100644
index 0000000..1533544
--- /dev/null
+++ b/tests/signature/tests/src/android/signature/cts/tests/data/ForciblyPublicizedPrivateClass.java
@@ -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 android.signature.cts.tests.data;
+
+/**
+ * This mocks a private class, but someone has forcibly added the annotation to make it as an API.
+ */
+@ApiAnnotation
+public class ForciblyPublicizedPrivateClass {
+
+}
diff --git a/tests/signature/tests/src/android/signature/cts/tests/data/PrivateClass.java b/tests/signature/tests/src/android/signature/cts/tests/data/PrivateClass.java
new file mode 100644
index 0000000..8f0884c
--- /dev/null
+++ b/tests/signature/tests/src/android/signature/cts/tests/data/PrivateClass.java
@@ -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.
+ */
+
+package android.signature.cts.tests.data;
+
+/**
+ * This mocks an internal private class
+ */
+public class PrivateClass {
+    public void privateMethod() {
+    }
+
+    public boolean privateField;
+}
diff --git a/tests/signature/tests/src/android/signature/cts/tests/data/PublicApiClass.java b/tests/signature/tests/src/android/signature/cts/tests/data/PublicApiClass.java
new file mode 100644
index 0000000..e27d070
--- /dev/null
+++ b/tests/signature/tests/src/android/signature/cts/tests/data/PublicApiClass.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 android.signature.cts.tests.data;
+
+/**
+ * A public API class, with some members are marked as system API
+ */
+public class PublicApiClass extends PublicApiClassParent {
+    public PublicApiClass(boolean foo) {
+    }
+
+    public void publicApiMethod() {
+    }
+
+    public boolean publicApiField;
+
+    /** @hide */
+    @ApiAnnotation
+    public PublicApiClass() {
+    }
+
+    /** @hide */
+    @ApiAnnotation
+    public void apiMethod() {
+    }
+
+    /** @hide */
+    @ApiAnnotation
+    public boolean apiField;
+
+    /** @hide */
+    public PublicApiClass(int foo) {
+    }
+
+    /** @hide */
+    public void privateMethod() {
+    }
+
+    /** @hide */
+    public boolean privateField;
+
+    /** @hide */
+    @Override
+    public void anOverriddenMethod() {
+        // This is @hide but should be recognized as an API because the exact same method is defined
+        // as API in one of the ancestor classes.
+    }
+}
diff --git a/tests/signature/tests/src/android/signature/cts/tests/data/PublicApiClassParent.java b/tests/signature/tests/src/android/signature/cts/tests/data/PublicApiClassParent.java
new file mode 100644
index 0000000..7e29d03
--- /dev/null
+++ b/tests/signature/tests/src/android/signature/cts/tests/data/PublicApiClassParent.java
@@ -0,0 +1,29 @@
+/*
+ * 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.signature.cts.tests.data;
+
+/**
+ * Parent class of PublicApiClass.
+ */
+public abstract class PublicApiClassParent {
+    public void publicMethod() {
+    }
+
+    /** @hide */
+    @ApiAnnotation
+    public abstract void anOverriddenMethod();
+}
diff --git a/tests/signature/tests/src/android/signature/cts/tests/data/SystemApiClass.java b/tests/signature/tests/src/android/signature/cts/tests/data/SystemApiClass.java
new file mode 100644
index 0000000..2636d4d
--- /dev/null
+++ b/tests/signature/tests/src/android/signature/cts/tests/data/SystemApiClass.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 android.signature.cts.tests.data;
+
+/**
+ * Not a public API, but a system API.
+ *
+ * @hide
+ */
+@ApiAnnotation
+public class SystemApiClass {
+    @ApiAnnotation
+    public SystemApiClass() {
+    }
+
+    @ApiAnnotation
+    public void apiMethod() {
+    }
+
+    @ApiAnnotation
+    public boolean apiField;
+
+    // TODO(b/71630695) this shouldn't be recognized
+    // as an API, as it is not annotated.
+    public SystemApiClass(float foo) {
+
+    }
+
+    // TODO(b/71630695) this shouldn't be recognized
+    // as an API, as it is not annotated.
+    public void unannotatedApiMethod() {
+    }
+
+    // TODO(b/71630695) this shouldn't be recognized
+    // as an API, as it is not annotated.
+    public boolean unannotatedApiField;
+
+    /** @hide */
+    public SystemApiClass(boolean foo) {
+    }
+
+    /** @hide */
+    public void privateMethod() {
+    }
+
+    /** @hide */
+    public boolean privateField;
+}
diff --git a/tests/simplecpu/AndroidTest.xml b/tests/simplecpu/AndroidTest.xml
index 9192e8b..916da50 100644
--- a/tests/simplecpu/AndroidTest.xml
+++ b/tests/simplecpu/AndroidTest.xml
@@ -14,6 +14,7 @@
      limitations under the License.
 -->
 <configuration description="Config for CTS CPU test cases">
+    <option name="test-suite-tag" value="cts" />
     <option name="config-descriptor:metadata" key="component" value="systems" />
     <target_preparer class="com.android.tradefed.targetprep.suite.SuiteApkInstaller">
         <option name="cleanup-apks" value="true" />
diff --git a/tests/tests/accounts/Android.mk b/tests/tests/accounts/Android.mk
index f5ac4fa..18ab37f 100644
--- a/tests/tests/accounts/Android.mk
+++ b/tests/tests/accounts/Android.mk
@@ -22,9 +22,9 @@
 LOCAL_MODULE_PATH := $(TARGET_OUT_DATA_APPS)
 
 LOCAL_STATIC_JAVA_LIBRARIES := \
-    CtsAccountTestsCommon ctstestrunner legacy-android-test
+    CtsAccountTestsCommon ctstestrunner
 
-LOCAL_JAVA_LIBRARIES := legacy-android-test
+LOCAL_JAVA_LIBRARIES := android.test.runner.stubs android.test.base.stubs
 
 LOCAL_SRC_FILES := $(call all-java-files-under, src)
 
diff --git a/tests/tests/accounts/AndroidTest.xml b/tests/tests/accounts/AndroidTest.xml
index b6ad758..77118d7 100644
--- a/tests/tests/accounts/AndroidTest.xml
+++ b/tests/tests/accounts/AndroidTest.xml
@@ -14,6 +14,7 @@
      limitations under the License.
 -->
 <configuration description="Config for CTS Accounts test cases">
+    <option name="test-suite-tag" value="cts" />
     <option name="config-descriptor:metadata" key="component" value="framework" />
     <target_preparer class="com.android.tradefed.targetprep.suite.SuiteApkInstaller">
         <option name="cleanup-apks" value="true" />
diff --git a/tests/tests/alarmclock/Android.mk b/tests/tests/alarmclock/Android.mk
index adfd51f..5d5c4e4 100644
--- a/tests/tests/alarmclock/Android.mk
+++ b/tests/tests/alarmclock/Android.mk
@@ -23,6 +23,8 @@
 
 LOCAL_STATIC_JAVA_LIBRARIES := CtsAlarmClockCommon ctstestrunner compatibility-device-util
 
+LOCAL_JAVA_LIBRARIES := android.test.runner.stubs android.test.base.stubs
+
 LOCAL_SRC_FILES := $(call all-java-files-under, src)
 
 LOCAL_PACKAGE_NAME := CtsAlarmClockTestCases
diff --git a/tests/tests/alarmclock/common/src/android/alarmclock/common/Utils.java b/tests/tests/alarmclock/common/src/android/alarmclock/common/Utils.java
index d469592..8f04d2a 100644
--- a/tests/tests/alarmclock/common/src/android/alarmclock/common/Utils.java
+++ b/tests/tests/alarmclock/common/src/android/alarmclock/common/Utils.java
@@ -24,8 +24,10 @@
 
     public enum TestcaseType {
         DISMISS_ALARM,
+        DISMISS_TIMER,
         SET_ALARM,
         SET_ALARM_FOR_DISMISSAL,
+        SET_TIMER_FOR_DISMISSAL,
         SNOOZE_ALARM,
     }
     public static final String TESTCASE_TYPE = "Testcase_type";
diff --git a/tests/tests/alarmclock/service/src/android/alarmclock/service/MainInteractionSession.java b/tests/tests/alarmclock/service/src/android/alarmclock/service/MainInteractionSession.java
index ede762a..547203e 100644
--- a/tests/tests/alarmclock/service/src/android/alarmclock/service/MainInteractionSession.java
+++ b/tests/tests/alarmclock/service/src/android/alarmclock/service/MainInteractionSession.java
@@ -78,12 +78,21 @@
                         AlarmClock.ALARM_SEARCH_MODE_NEXT);
                 break;
 
+            case DISMISS_TIMER:
+                intent = new Intent(AlarmClock.ACTION_DISMISS_TIMER);
+                break;
+
             case SET_ALARM_FOR_DISMISSAL:
             case SET_ALARM:
                 intent = new Intent(AlarmClock.ACTION_SET_ALARM);
                 intent.putExtra(AlarmClock.EXTRA_HOUR, 14);
                 break;
 
+            case SET_TIMER_FOR_DISMISSAL:
+                intent = new Intent(AlarmClock.ACTION_SET_TIMER);
+                intent.putExtra(AlarmClock.EXTRA_LENGTH, 10);
+                break;
+
             case SNOOZE_ALARM:
                 intent = new Intent(AlarmClock.ACTION_SNOOZE_ALARM);
                 break;
diff --git a/tests/tests/alarmclock/src/android/alarmclock/cts/AlarmClockTestBase.java b/tests/tests/alarmclock/src/android/alarmclock/cts/AlarmClockTestBase.java
index 224c8ab..69f19b9 100644
--- a/tests/tests/alarmclock/src/android/alarmclock/cts/AlarmClockTestBase.java
+++ b/tests/tests/alarmclock/src/android/alarmclock/cts/AlarmClockTestBase.java
@@ -89,11 +89,19 @@
               intent = new Intent(AlarmClock.ACTION_DISMISS_ALARM);
               break;
 
+          case DISMISS_TIMER:
+              intent = new Intent(AlarmClock.ACTION_DISMISS_TIMER);
+              break;
+
           case SET_ALARM:
           case SET_ALARM_FOR_DISMISSAL:
               intent = new Intent(AlarmClock.ACTION_SET_ALARM);
               break;
 
+          case SET_TIMER_FOR_DISMISSAL:
+              intent = new Intent(AlarmClock.ACTION_SET_TIMER);
+              break;
+
           case SNOOZE_ALARM:
               intent = new Intent(AlarmClock.ACTION_SNOOZE_ALARM);
               break;
diff --git a/tests/tests/alarmclock/src/android/alarmclock/cts/DismissTimerTest.java b/tests/tests/alarmclock/src/android/alarmclock/cts/DismissTimerTest.java
new file mode 100644
index 0000000..dff8835
--- /dev/null
+++ b/tests/tests/alarmclock/src/android/alarmclock/cts/DismissTimerTest.java
@@ -0,0 +1,11 @@
+package android.alarmclock.cts;
+
+import android.alarmclock.common.Utils;
+
+public class DismissTimerTest extends AlarmClockTestBase {
+
+    public void testAll() throws Exception {
+        assertEquals(Utils.COMPLETION_RESULT, runTest(Utils.TestcaseType.SET_TIMER_FOR_DISMISSAL));
+        assertEquals(Utils.COMPLETION_RESULT, runTest(Utils.TestcaseType.DISMISS_TIMER));
+    }
+}
diff --git a/tests/tests/animation/Android.mk b/tests/tests/animation/Android.mk
index c625cbe..2311683 100644
--- a/tests/tests/animation/Android.mk
+++ b/tests/tests/animation/Android.mk
@@ -30,8 +30,7 @@
     android-common \
     compatibility-device-util \
     ctstestrunner \
-    platform-test-annotations \
-    legacy-android-test
+    platform-test-annotations
 
 LOCAL_JAVA_LIBRARIES := android.test.runner
 
diff --git a/tests/tests/app.usage/Android.mk b/tests/tests/app.usage/Android.mk
index dbef83c..4233c0b 100644
--- a/tests/tests/app.usage/Android.mk
+++ b/tests/tests/app.usage/Android.mk
@@ -28,9 +28,10 @@
     android-support-test \
     ctstestrunner \
     junit \
-    legacy-android-test \
     ub-uiautomator
 
+LOCAL_JAVA_LIBRARIES := android.test.base android.test.runner
+
 LOCAL_SRC_FILES := $(call all-java-files-under, src)
 
 # Tag this module as a cts test artifact
diff --git a/tests/tests/app.usage/AndroidManifest.xml b/tests/tests/app.usage/AndroidManifest.xml
index 462736a..e02e19d 100644
--- a/tests/tests/app.usage/AndroidManifest.xml
+++ b/tests/tests/app.usage/AndroidManifest.xml
@@ -26,7 +26,7 @@
     <uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
     <uses-permission android:name="android.permission.READ_PHONE_STATE" />
 
-    <application>
+    <application android:usesCleartextTraffic="true">
         <uses-library android:name="android.test.runner" />
 
         <activity android:name=".Activities$ActivityOne" />
diff --git a/tests/tests/app.usage/AndroidTest.xml b/tests/tests/app.usage/AndroidTest.xml
index 8868c17..cd48c16 100644
--- a/tests/tests/app.usage/AndroidTest.xml
+++ b/tests/tests/app.usage/AndroidTest.xml
@@ -14,6 +14,7 @@
      limitations under the License.
 -->
 <configuration description="Configuration for app.usage Tests">
+    <option name="test-suite-tag" value="cts" />
     <option name="config-descriptor:metadata" key="component" value="framework" />
     <target_preparer class="com.android.tradefed.targetprep.suite.SuiteApkInstaller">
         <option name="cleanup-apks" value="true" />
diff --git a/tests/tests/app/Android.mk b/tests/tests/app/Android.mk
index bd679a4..4c0e217 100644
--- a/tests/tests/app/Android.mk
+++ b/tests/tests/app/Android.mk
@@ -24,13 +24,12 @@
 # and when built explicitly put it in the data partition
 LOCAL_MODULE_PATH := $(TARGET_OUT_DATA_APPS)
 
-LOCAL_JAVA_LIBRARIES := android.test.runner
+LOCAL_JAVA_LIBRARIES := android.test.runner android.test.base
 
 LOCAL_STATIC_JAVA_LIBRARIES := \
     ctstestrunner \
     android-support-test \
-    junit \
-    legacy-android-test
+    junit
 
 LOCAL_SRC_FILES := $(call all-java-files-under, src)
 
diff --git a/tests/tests/app/AndroidTest.xml b/tests/tests/app/AndroidTest.xml
index c9ee968..adebf03 100644
--- a/tests/tests/app/AndroidTest.xml
+++ b/tests/tests/app/AndroidTest.xml
@@ -14,6 +14,7 @@
      limitations under the License.
 -->
 <configuration description="Configuration for app Tests">
+    <option name="test-suite-tag" value="cts" />
     <option name="config-descriptor:metadata" key="component" value="misc" />
     <option name="not-shardable" value="true" />
     <target_preparer class="com.android.tradefed.targetprep.suite.SuiteApkInstaller">
diff --git a/tests/tests/appwidget/Android.mk b/tests/tests/appwidget/Android.mk
index 5df6046..0857835 100644
--- a/tests/tests/appwidget/Android.mk
+++ b/tests/tests/appwidget/Android.mk
@@ -28,8 +28,9 @@
 LOCAL_STATIC_JAVA_LIBRARIES := \
     mockito-target-minus-junit4 \
     ctstestrunner \
-    junit \
-    legacy-android-test
+    junit
+
+LOCAL_JAVA_LIBRARIES := android.test.base
 
 # Tag this module as a cts test artifact
 LOCAL_COMPATIBILITY_SUITE := cts vts general-tests
diff --git a/tests/tests/appwidget/AndroidManifest.xml b/tests/tests/appwidget/AndroidManifest.xml
index 6f7d053..e7e7944 100644
--- a/tests/tests/appwidget/AndroidManifest.xml
+++ b/tests/tests/appwidget/AndroidManifest.xml
@@ -38,6 +38,30 @@
               android:resource="@xml/second_appwidget_info" />
       </receiver>
 
+      <receiver android:name="android.appwidget.cts.provider.AppWidgetProviderWithFeatures$Provider1" >
+          <intent-filter>
+              <action android:name="android.appwidget.action.APPWIDGET_UPDATE" />
+          </intent-filter>
+          <meta-data android:name="android.appwidget.provider"
+              android:resource="@xml/appwidget_info_with_feature1" />
+      </receiver>
+
+      <receiver android:name="android.appwidget.cts.provider.AppWidgetProviderWithFeatures$Provider2" >
+          <intent-filter>
+              <action android:name="android.appwidget.action.APPWIDGET_UPDATE" />
+          </intent-filter>
+          <meta-data android:name="android.appwidget.provider"
+              android:resource="@xml/appwidget_info_with_feature2" />
+      </receiver>
+
+      <receiver android:name="android.appwidget.cts.provider.AppWidgetProviderWithFeatures$Provider3" >
+          <intent-filter>
+              <action android:name="android.appwidget.action.APPWIDGET_UPDATE" />
+          </intent-filter>
+          <meta-data android:name="android.appwidget.provider"
+              android:resource="@xml/appwidget_info_with_feature3" />
+      </receiver>
+
       <service android:name="android.appwidget.cts.service.MyAppWidgetService"
           android:permission="android.permission.BIND_REMOTEVIEWS">
       </service>
diff --git a/tests/tests/appwidget/AndroidTest.xml b/tests/tests/appwidget/AndroidTest.xml
index f1d2df2..cc4fe42 100644
--- a/tests/tests/appwidget/AndroidTest.xml
+++ b/tests/tests/appwidget/AndroidTest.xml
@@ -13,6 +13,7 @@
      limitations under the License.
 -->
 <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" />
     <target_preparer class="com.android.tradefed.targetprep.suite.SuiteApkInstaller">
         <option name="cleanup-apks" value="true" />
@@ -21,6 +22,26 @@
         <option name="test-file-name" value="CtsAppWidgetLauncher3.apk" />
         <option name="test-file-name" value="CtsAppWidgetTestCases.apk" />
     </target_preparer>
+
+    <target_preparer class="com.android.tradefed.targetprep.RunCommandTargetPreparer">
+        <option name="run-command" value="mkdir -p /data/local/tmp/cts/widgetprovider/" />
+        <option name="teardown-command" value="rm -rf /data/local/tmp/cts/widgetprovider"/>
+    </target_preparer>
+
+    <target_preparer class="com.android.compatibility.common.tradefed.targetprep.FilePusher">
+        <option name="cleanup" value="true" />
+        <option name="push" value="CtsAppWidgetProvider1.apk->/data/local/tmp/cts/widgetprovider/CtsAppWidgetProvider1.apk" />
+    </target_preparer>
+    <target_preparer class="com.android.compatibility.common.tradefed.targetprep.FilePusher">
+        <option name="cleanup" value="true" />
+        <option name="push" value="CtsAppWidgetProvider2.apk->/data/local/tmp/cts/widgetprovider/CtsAppWidgetProvider2.apk" />
+    </target_preparer>
+    <target_preparer class="com.android.compatibility.common.tradefed.targetprep.FilePusher">
+        <option name="cleanup" value="true" />
+        <option name="push" value="CtsAppWidgetProvider3.apk->/data/local/tmp/cts/widgetprovider/CtsAppWidgetProvider3.apk" />
+    </target_preparer>
+
+
     <test class="com.android.tradefed.testtype.AndroidJUnitTest" >
         <option name="package" value="android.appwidget.cts" />
         <option name="runtime-hint" value="9m30s" />
diff --git a/tests/tests/appwidget/common/src/android/appwidget/cts/common/Constants.java b/tests/tests/appwidget/common/src/android/appwidget/cts/common/Constants.java
index 954a81b..786b2eb 100644
--- a/tests/tests/appwidget/common/src/android/appwidget/cts/common/Constants.java
+++ b/tests/tests/appwidget/common/src/android/appwidget/cts/common/Constants.java
@@ -27,4 +27,6 @@
     public static final String EXTRA_PACKAGE = "PACKAGE_NAME";
     public static final String EXTRA_REQUEST = "REQUEST";
 
+    public static final String ACTION_APPLY_OVERRIDE =
+            "android.appwidget.cts.widgetprovider.APPLY_OVERRIDE";
 }
diff --git a/tests/tests/appwidget/packages/src/android/appwidget/cts/packages/SimpleProvider.java b/tests/tests/appwidget/packages/src/android/appwidget/cts/packages/SimpleProvider.java
new file mode 100644
index 0000000..3c29746
--- /dev/null
+++ b/tests/tests/appwidget/packages/src/android/appwidget/cts/packages/SimpleProvider.java
@@ -0,0 +1,25 @@
+package android.appwidget.cts.packages;
+
+import android.app.Activity;
+import android.appwidget.AppWidgetManager;
+import android.appwidget.AppWidgetProvider;
+import android.appwidget.cts.common.Constants;
+import android.content.ComponentName;
+import android.content.Context;
+import android.content.Intent;
+
+public class SimpleProvider extends AppWidgetProvider {
+
+    @Override
+    public void onReceive(Context context, Intent intent) {
+        super.onReceive(context, intent);
+
+        if (Constants.ACTION_APPLY_OVERRIDE.equals(intent.getAction())) {
+            String request = intent.getStringExtra(Constants.EXTRA_REQUEST);
+            AppWidgetManager.getInstance(context).updateAppWidgetProviderInfo(
+                    new ComponentName(context, SimpleProvider.class),
+                    request);
+            setResultCode(Activity.RESULT_OK);
+        }
+    }
+}
diff --git a/tests/tests/appwidget/packages/widgetprovider/Android.mk b/tests/tests/appwidget/packages/widgetprovider/Android.mk
new file mode 100644
index 0000000..733a4a8
--- /dev/null
+++ b/tests/tests/appwidget/packages/widgetprovider/Android.mk
@@ -0,0 +1,78 @@
+# 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_PACKAGE_NAME := CtsAppWidgetProvider1
+
+LOCAL_MODULE_TAGS := optional
+
+LOCAL_MODULE_PATH := $(TARGET_OUT_DATA_APPS)
+
+LOCAL_SRC_FILES := $(call all-java-files-under, ../src) \
+        $(call all-java-files-under, ../../common/src)
+LOCAL_FULL_LIBS_MANIFEST_FILES := \
+    $(LOCAL_PATH)/AndroidManifestV1.xml
+
+LOCAL_SDK_VERSION := current
+
+# tag this module as a cts test artifact
+LOCAL_COMPATIBILITY_SUITE := cts vts general-tests
+
+include $(BUILD_CTS_PACKAGE)
+
+#-----------------------------------------------------------
+include $(CLEAR_VARS)
+
+LOCAL_PACKAGE_NAME := CtsAppWidgetProvider2
+
+LOCAL_MODULE_TAGS := optional
+
+LOCAL_MODULE_PATH := $(TARGET_OUT_DATA_APPS)
+
+LOCAL_SRC_FILES := $(call all-java-files-under, ../src) \
+        $(call all-java-files-under, ../../common/src)
+LOCAL_FULL_LIBS_MANIFEST_FILES := \
+    $(LOCAL_PATH)/AndroidManifestV2.xml
+
+LOCAL_SDK_VERSION := current
+
+# tag this module as a cts test artifact
+LOCAL_COMPATIBILITY_SUITE := cts vts general-tests
+
+include $(BUILD_CTS_PACKAGE)
+
+#-----------------------------------------------------------
+include $(CLEAR_VARS)
+
+LOCAL_PACKAGE_NAME := CtsAppWidgetProvider3
+
+LOCAL_MODULE_TAGS := optional
+
+LOCAL_MODULE_PATH := $(TARGET_OUT_DATA_APPS)
+
+LOCAL_SRC_FILES := $(call all-java-files-under, ../src) \
+        $(call all-java-files-under, ../../common/src)
+LOCAL_FULL_LIBS_MANIFEST_FILES := \
+    $(LOCAL_PATH)/AndroidManifestV3.xml
+
+LOCAL_SDK_VERSION := current
+
+# tag this module as a cts test artifact
+LOCAL_COMPATIBILITY_SUITE := cts vts general-tests
+
+include $(BUILD_CTS_PACKAGE)
diff --git a/tests/tests/appwidget/packages/widgetprovider/AndroidManifest.xml b/tests/tests/appwidget/packages/widgetprovider/AndroidManifest.xml
new file mode 100644
index 0000000..77dfc5b
--- /dev/null
+++ b/tests/tests/appwidget/packages/widgetprovider/AndroidManifest.xml
@@ -0,0 +1,20 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT 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 package="android.appwidget.cts.widgetprovider" >
+
+</manifest>
diff --git a/tests/tests/appwidget/packages/widgetprovider/AndroidManifestV1.xml b/tests/tests/appwidget/packages/widgetprovider/AndroidManifestV1.xml
new file mode 100644
index 0000000..ea23176
--- /dev/null
+++ b/tests/tests/appwidget/packages/widgetprovider/AndroidManifestV1.xml
@@ -0,0 +1,32 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT 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" >
+
+    <application>
+        <receiver android:name="android.appwidget.cts.packages.SimpleProvider">
+            <intent-filter>
+                <action android:name="android.appwidget.action.APPWIDGET_UPDATE" />
+                <action android:name="android.appwidget.cts.widgetprovider.APPLY_OVERRIDE" />
+            </intent-filter>
+            <meta-data android:name="android.appwidget.provider"
+                       android:resource="@xml/widget_no_config" />
+            <meta-data android:name="my_custom_info"
+                       android:resource="@xml/widget_config" />
+        </receiver>
+    </application>
+</manifest>
diff --git a/tests/tests/appwidget/packages/widgetprovider/AndroidManifestV2.xml b/tests/tests/appwidget/packages/widgetprovider/AndroidManifestV2.xml
new file mode 100644
index 0000000..e46e160
--- /dev/null
+++ b/tests/tests/appwidget/packages/widgetprovider/AndroidManifestV2.xml
@@ -0,0 +1,32 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT 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" >
+
+    <application>
+        <receiver android:name="android.appwidget.cts.packages.SimpleProvider">
+            <intent-filter>
+                <action android:name="android.appwidget.action.APPWIDGET_UPDATE" />
+                <action android:name="android.appwidget.cts.widgetprovider.APPLY_OVERRIDE" />
+            </intent-filter>
+            <meta-data android:name="android.appwidget.provider"
+                       android:resource="@xml/widget_no_config" />
+            <meta-data android:name="my_custom_info"
+                       android:resource="@xml/widget_config_no_resize" />
+        </receiver>
+    </application>
+</manifest>
diff --git a/tests/tests/appwidget/packages/widgetprovider/AndroidManifestV3.xml b/tests/tests/appwidget/packages/widgetprovider/AndroidManifestV3.xml
new file mode 100644
index 0000000..6da740d
--- /dev/null
+++ b/tests/tests/appwidget/packages/widgetprovider/AndroidManifestV3.xml
@@ -0,0 +1,30 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT 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" >
+
+    <application>
+        <receiver android:name="android.appwidget.cts.packages.SimpleProvider">
+            <intent-filter>
+                <action android:name="android.appwidget.action.APPWIDGET_UPDATE" />
+                <action android:name="android.appwidget.cts.widgetprovider.APPLY_OVERRIDE" />
+            </intent-filter>
+            <meta-data android:name="android.appwidget.provider"
+                       android:resource="@xml/widget_no_config" />
+        </receiver>
+    </application>
+</manifest>
diff --git a/tests/tests/appwidget/packages/widgetprovider/res/layout/simple_widget.xml b/tests/tests/appwidget/packages/widgetprovider/res/layout/simple_widget.xml
new file mode 100644
index 0000000..b9db450
--- /dev/null
+++ b/tests/tests/appwidget/packages/widgetprovider/res/layout/simple_widget.xml
@@ -0,0 +1,22 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ -->
+<FrameLayout
+    xmlns:android="http://schemas.android.com/apk/res/android"
+    android:layout_width="match_parent"
+    android:layout_height="match_parent"
+    android:background="#ff333333"
+    android:elevation="3dp" />
\ No newline at end of file
diff --git a/tests/tests/appwidget/packages/widgetprovider/res/xml/widget_config.xml b/tests/tests/appwidget/packages/widgetprovider/res/xml/widget_config.xml
new file mode 100644
index 0000000..f45c476
--- /dev/null
+++ b/tests/tests/appwidget/packages/widgetprovider/res/xml/widget_config.xml
@@ -0,0 +1,26 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ -->
+<appwidget-provider
+    xmlns:android="http://schemas.android.com/apk/res/android"
+    android:minWidth="180dp"
+    android:minHeight="110dp"
+    android:updatePeriodMillis="86400000"
+    android:initialLayout="@layout/simple_widget"
+    android:resizeMode="horizontal|vertical"
+    android:configure="android.appwidget.cts.widgetprovider.ExampleAppWidgetConfigure"
+    android:widgetCategory="home_screen">
+</appwidget-provider>
\ No newline at end of file
diff --git a/tests/tests/appwidget/packages/widgetprovider/res/xml/widget_config_no_resize.xml b/tests/tests/appwidget/packages/widgetprovider/res/xml/widget_config_no_resize.xml
new file mode 100644
index 0000000..428a780
--- /dev/null
+++ b/tests/tests/appwidget/packages/widgetprovider/res/xml/widget_config_no_resize.xml
@@ -0,0 +1,26 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ -->
+<appwidget-provider
+    xmlns:android="http://schemas.android.com/apk/res/android"
+    android:minWidth="180dp"
+    android:minHeight="110dp"
+    android:updatePeriodMillis="86400000"
+    android:initialLayout="@layout/simple_widget"
+    android:resizeMode="none"
+    android:configure="android.appwidget.cts.widgetprovider.ExampleAppWidgetConfigure"
+    android:widgetCategory="home_screen">
+</appwidget-provider>
\ No newline at end of file
diff --git a/tests/tests/appwidget/packages/widgetprovider/res/xml/widget_no_config.xml b/tests/tests/appwidget/packages/widgetprovider/res/xml/widget_no_config.xml
new file mode 100644
index 0000000..0dacd1c
--- /dev/null
+++ b/tests/tests/appwidget/packages/widgetprovider/res/xml/widget_no_config.xml
@@ -0,0 +1,25 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ -->
+<appwidget-provider
+    xmlns:android="http://schemas.android.com/apk/res/android"
+    android:minWidth="180dp"
+    android:minHeight="110dp"
+    android:updatePeriodMillis="86400000"
+    android:initialLayout="@layout/simple_widget"
+    android:resizeMode="horizontal|vertical"
+    android:widgetCategory="home_screen">
+</appwidget-provider>
\ No newline at end of file
diff --git a/tests/tests/appwidget/res/xml/appwidget_info_with_feature1.xml b/tests/tests/appwidget/res/xml/appwidget_info_with_feature1.xml
new file mode 100644
index 0000000..cadb283
--- /dev/null
+++ b/tests/tests/appwidget/res/xml/appwidget_info_with_feature1.xml
@@ -0,0 +1,32 @@
+<?xml version="1.0" encoding="utf-8"?>
+
+<!--
+     Copyright (C) 2017 The Android Open Source Project
+
+     Licensed under the Apache License, Version 2.0 (the "License");
+     you may not use this file except in compliance with the License.
+     You may obtain a copy of the License at
+
+          http://www.apache.org/licenses/LICENSE-2.0
+
+     Unless required by applicable law or agreed to in writing, software
+     distributed under the License is distributed on an "AS IS" BASIS,
+     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+     See the License for the specific language governing permissions and
+     limitations under the License.
+-->
+<appwidget-provider xmlns:android="http://schemas.android.com/apk/res/android"
+    android:minWidth="@dimen/first_min_appwidget_size"
+    android:minHeight="@dimen/first_min_appwidget_size"
+    android:minResizeWidth="@dimen/first_min_resize_appwidget_size"
+    android:minResizeHeight="@dimen/first_min_resize_appwidget_size"
+    android:updatePeriodMillis="@integer/first_update_period_millis"
+    android:configure="android.appwidget.cts.provider.FirstAppWidgetConfigureActivity"
+    android:resizeMode="horizontal|vertical"
+    android:widgetCategory="home_screen|keyguard"
+    android:widgetFeatures="reconfigurable"
+    android:initialLayout="@layout/first_initial_layout"
+    android:initialKeyguardLayout="@layout/first_initial_keyguard_layout"
+    android:previewImage="@drawable/first_android_icon"
+    android:autoAdvanceViewId="@id/first_auto_advance_view_id">
+</appwidget-provider>
diff --git a/tests/tests/appwidget/res/xml/appwidget_info_with_feature2.xml b/tests/tests/appwidget/res/xml/appwidget_info_with_feature2.xml
new file mode 100644
index 0000000..89d60eb
--- /dev/null
+++ b/tests/tests/appwidget/res/xml/appwidget_info_with_feature2.xml
@@ -0,0 +1,32 @@
+<?xml version="1.0" encoding="utf-8"?>
+
+<!--
+     Copyright (C) 2017 The Android Open Source Project
+
+     Licensed under the Apache License, Version 2.0 (the "License");
+     you may not use this file except in compliance with the License.
+     You may obtain a copy of the License at
+
+          http://www.apache.org/licenses/LICENSE-2.0
+
+     Unless required by applicable law or agreed to in writing, software
+     distributed under the License is distributed on an "AS IS" BASIS,
+     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+     See the License for the specific language governing permissions and
+     limitations under the License.
+-->
+<appwidget-provider xmlns:android="http://schemas.android.com/apk/res/android"
+    android:minWidth="@dimen/first_min_appwidget_size"
+    android:minHeight="@dimen/first_min_appwidget_size"
+    android:minResizeWidth="@dimen/first_min_resize_appwidget_size"
+    android:minResizeHeight="@dimen/first_min_resize_appwidget_size"
+    android:updatePeriodMillis="@integer/first_update_period_millis"
+    android:configure="android.appwidget.cts.provider.FirstAppWidgetConfigureActivity"
+    android:resizeMode="horizontal|vertical"
+    android:widgetCategory="home_screen|keyguard"
+    android:widgetFeatures="hide_from_picker"
+    android:initialLayout="@layout/first_initial_layout"
+    android:initialKeyguardLayout="@layout/first_initial_keyguard_layout"
+    android:previewImage="@drawable/first_android_icon"
+    android:autoAdvanceViewId="@id/first_auto_advance_view_id">
+</appwidget-provider>
diff --git a/tests/tests/appwidget/res/xml/appwidget_info_with_feature3.xml b/tests/tests/appwidget/res/xml/appwidget_info_with_feature3.xml
new file mode 100644
index 0000000..25936ab
--- /dev/null
+++ b/tests/tests/appwidget/res/xml/appwidget_info_with_feature3.xml
@@ -0,0 +1,32 @@
+<?xml version="1.0" encoding="utf-8"?>
+
+<!--
+     Copyright (C) 2017 The Android Open Source Project
+
+     Licensed under the Apache License, Version 2.0 (the "License");
+     you may not use this file except in compliance with the License.
+     You may obtain a copy of the License at
+
+          http://www.apache.org/licenses/LICENSE-2.0
+
+     Unless required by applicable law or agreed to in writing, software
+     distributed under the License is distributed on an "AS IS" BASIS,
+     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+     See the License for the specific language governing permissions and
+     limitations under the License.
+-->
+<appwidget-provider xmlns:android="http://schemas.android.com/apk/res/android"
+    android:minWidth="@dimen/first_min_appwidget_size"
+    android:minHeight="@dimen/first_min_appwidget_size"
+    android:minResizeWidth="@dimen/first_min_resize_appwidget_size"
+    android:minResizeHeight="@dimen/first_min_resize_appwidget_size"
+    android:updatePeriodMillis="@integer/first_update_period_millis"
+    android:configure="android.appwidget.cts.provider.FirstAppWidgetConfigureActivity"
+    android:resizeMode="horizontal|vertical"
+    android:widgetCategory="home_screen|keyguard"
+    android:widgetFeatures="reconfigurable|hide_from_picker"
+    android:initialLayout="@layout/first_initial_layout"
+    android:initialKeyguardLayout="@layout/first_initial_keyguard_layout"
+    android:previewImage="@drawable/first_android_icon"
+    android:autoAdvanceViewId="@id/first_auto_advance_view_id">
+</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 747a8bd..c0870f2 100644
--- a/tests/tests/appwidget/src/android/appwidget/cts/AppWidgetTest.java
+++ b/tests/tests/appwidget/src/android/appwidget/cts/AppWidgetTest.java
@@ -33,6 +33,7 @@
 import android.appwidget.AppWidgetManager;
 import android.appwidget.AppWidgetProviderInfo;
 import android.appwidget.cts.provider.AppWidgetProviderCallbacks;
+import android.appwidget.cts.provider.AppWidgetProviderWithFeatures;
 import android.appwidget.cts.provider.FirstAppWidgetProvider;
 import android.appwidget.cts.provider.SecondAppWidgetProvider;
 import android.appwidget.cts.service.MyAppWidgetService;
@@ -85,10 +86,6 @@
 
 
     public void testGetAppInstalledProvidersForCurrentUserLegacy() throws Exception {
-        if (!hasAppWidgets()) {
-            return;
-        }
-
         // By default we should get only providers for the current user.
         List<AppWidgetProviderInfo> providers = getAppWidgetManager().getInstalledProviders();
 
@@ -97,10 +94,6 @@
     }
 
     public void testGetAppInstalledProvidersForCurrentUserNewCurrentProfile() throws Exception {
-        if (!hasAppWidgets()) {
-            return;
-        }
-
         // We ask only for providers for the current user.
         List<AppWidgetProviderInfo> providers = getAppWidgetManager()
                 .getInstalledProvidersForProfile(Process.myUserHandle());
@@ -110,10 +103,6 @@
     }
 
     public void testGetAppInstalledProvidersForCurrentUserNewAllProfiles() throws Exception {
-        if (!hasAppWidgets()) {
-            return;
-        }
-
         // We ask only for providers for all current user's profiles
         UserManager userManager = (UserManager) getInstrumentation()
                 .getTargetContext().getSystemService(Context.USER_SERVICE);
@@ -134,10 +123,6 @@
     }
 
     public void testBindAppWidget() throws Exception {
-        if (!hasAppWidgets()) {
-            return;
-        }
-
         // Create a host and start listening.
         AppWidgetHost host = new AppWidgetHost(getInstrumentation().getTargetContext(), 0);
         host.deleteHost();
@@ -175,9 +160,6 @@
     }
 
     public void testGetAppWidgetIdsForHost() throws Exception {
-        if (!hasAppWidgets()) {
-            return;
-        }
         AppWidgetHost host1 = new AppWidgetHost(getInstrumentation().getTargetContext(), 1);
         AppWidgetHost host2 = new AppWidgetHost(getInstrumentation().getTargetContext(), 2);
 
@@ -207,10 +189,6 @@
     }
 
     public void testAppWidgetProviderCallbacks() throws Exception {
-        if (!hasAppWidgets()) {
-            return;
-        }
-
         AtomicInteger invocationCounter = new AtomicInteger();
 
         // Set a mock to intercept provider callbacks.
@@ -318,10 +296,6 @@
     }
 
     public void testTwoAppWidgetProviderCallbacks() throws Exception {
-        if (!hasAppWidgets()) {
-            return;
-        }
-
         AtomicInteger invocationCounter = new AtomicInteger();
 
         // Set a mock to intercept first provider callbacks.
@@ -413,10 +387,6 @@
     }
 
     public void testGetAppWidgetIdsForProvider() throws Exception {
-        if (!hasAppWidgets()) {
-            return;
-        }
-
         // We want to bind widgets.
         grantBindAppWidgetPermission();
 
@@ -464,10 +434,6 @@
     }
 
     public void testGetAppWidgetInfo() throws Exception {
-        if (!hasAppWidgets()) {
-            return;
-        }
-
         // We want to bind widgets.
         grantBindAppWidgetPermission();
 
@@ -520,10 +486,6 @@
     }
 
     public void testGetAppWidgetOptions() throws Exception {
-        if (!hasAppWidgets()) {
-            return;
-        }
-
         // We want to bind widgets.
         grantBindAppWidgetPermission();
 
@@ -569,10 +531,6 @@
     }
 
     public void testDeleteHost() throws Exception {
-        if (!hasAppWidgets()) {
-            return;
-        }
-
         // We want to bind widgets.
         grantBindAppWidgetPermission();
 
@@ -614,10 +572,6 @@
     }
 
     public void testDeleteHosts() throws Exception {
-        if (!hasAppWidgets()) {
-            return;
-        }
-
         // We want to bind widgets.
         grantBindAppWidgetPermission();
 
@@ -674,10 +628,6 @@
     }
 
     public void testOnProvidersChanged() throws Exception {
-        if (!hasAppWidgets()) {
-            return;
-        }
-
         // We want to bind widgets.
         grantBindAppWidgetPermission();
 
@@ -740,10 +690,6 @@
     }
 
     public void testUpdateAppWidgetViaComponentName() throws Exception {
-        if (!hasAppWidgets()) {
-            return;
-        }
-
         // We want to bind widgets.
         grantBindAppWidgetPermission();
 
@@ -834,10 +780,6 @@
     }
 
     public void testUpdateAppWidgetViaWidgetId() throws Exception {
-        if (!hasAppWidgets()) {
-            return;
-        }
-
         // We want to bind widgets.
         grantBindAppWidgetPermission();
 
@@ -908,10 +850,6 @@
     }
 
     public void testUpdateAppWidgetViaWidgetIds() throws Exception {
-        if (!hasAppWidgets()) {
-            return;
-        }
-
         // We want to bind widgets.
         grantBindAppWidgetPermission();
 
@@ -1004,10 +942,6 @@
     }
 
     public void testPartiallyUpdateAppWidgetViaWidgetId() throws Exception {
-        if (!hasAppWidgets()) {
-            return;
-        }
-
         // We want to bind widgets.
         grantBindAppWidgetPermission();
 
@@ -1082,10 +1016,6 @@
     }
 
     public void testPartiallyUpdateAppWidgetViaWidgetIds() throws Exception {
-        if (!hasAppWidgets()) {
-            return;
-        }
-
         // We want to bind widgets.
         grantBindAppWidgetPermission();
 
@@ -1196,10 +1126,6 @@
     }
 
     public void testCollectionWidgets() throws Exception {
-        if (!hasAppWidgets()) {
-            return;
-        }
-
         // We want to bind widgets.
         grantBindAppWidgetPermission();
 
@@ -1308,6 +1234,22 @@
         }
     }
 
+    public void testWidgetFeaturesParsed() throws Exception {
+        assertEquals(0, getFirstAppWidgetProviderInfo().widgetFeatures);
+        String packageName = getInstrumentation().getTargetContext().getPackageName();
+
+        assertEquals(AppWidgetProviderInfo.WIDGET_FEATURE_RECONFIGURABLE,
+                getProviderInfo(new ComponentName(packageName,
+                AppWidgetProviderWithFeatures.Provider1.class.getName())).widgetFeatures);
+        assertEquals(AppWidgetProviderInfo.WIDGET_FEATURE_HIDE_FROM_PICKER,
+                getProviderInfo(new ComponentName(packageName,
+                        AppWidgetProviderWithFeatures.Provider2.class.getName())).widgetFeatures);
+        assertEquals(AppWidgetProviderInfo.WIDGET_FEATURE_RECONFIGURABLE
+                | AppWidgetProviderInfo.WIDGET_FEATURE_HIDE_FROM_PICKER,
+                getProviderInfo(new ComponentName(packageName,
+                        AppWidgetProviderWithFeatures.Provider3.class.getName())).widgetFeatures);
+    }
+
     private void waitForCallCount(AtomicInteger counter, int expectedCount) {
         synchronized (mLock) {
             final long startTimeMillis = SystemClock.uptimeMillis();
diff --git a/tests/tests/appwidget/src/android/appwidget/cts/AppWidgetTestCase.java b/tests/tests/appwidget/src/android/appwidget/cts/AppWidgetTestCase.java
index fb0dbd1..b98973a 100644
--- a/tests/tests/appwidget/src/android/appwidget/cts/AppWidgetTestCase.java
+++ b/tests/tests/appwidget/src/android/appwidget/cts/AppWidgetTestCase.java
@@ -16,6 +16,8 @@
 
 package android.appwidget.cts;
 
+import static org.junit.Assume.assumeTrue;
+
 import android.appwidget.AppWidgetProviderInfo;
 import android.appwidget.cts.provider.FirstAppWidgetProvider;
 import android.appwidget.cts.provider.SecondAppWidgetProvider;
@@ -36,6 +38,12 @@
     private static final String SECOND_APP_WIDGET_CONFIGURE_ACTIVITY =
             "android.appwidget.cts.provider.SecondAppWidgetConfigureActivity";
 
+    @Override
+    protected void setUp() throws Exception {
+        super.setUp();
+        assumeTrue(hasAppWidgets());
+    }
+
     public boolean hasAppWidgets() {
         return getInstrumentation().getTargetContext().getPackageManager()
                 .hasSystemFeature(PackageManager.FEATURE_APP_WIDGETS);
diff --git a/tests/tests/appwidget/src/android/appwidget/cts/RequestPinAppWidgetTest.java b/tests/tests/appwidget/src/android/appwidget/cts/RequestPinAppWidgetTest.java
index 019fb66..4f6c657 100644
--- a/tests/tests/appwidget/src/android/appwidget/cts/RequestPinAppWidgetTest.java
+++ b/tests/tests/appwidget/src/android/appwidget/cts/RequestPinAppWidgetTest.java
@@ -52,9 +52,6 @@
     }
 
     private void runPinWidgetTest(final String launcherPkg) throws Exception {
-        if (!hasAppWidgets()) {
-            return;
-        }
         setLauncher(launcherPkg + "/" + LAUNCHER_CLASS);
 
         Context context = getInstrumentation().getContext();
@@ -109,9 +106,6 @@
 
     public void verifyIsRequestPinAppWidgetSupported(String launcherPkg, boolean expectedSupport)
         throws Exception {
-        if (!hasAppWidgets()) {
-            return;
-        }
         setLauncher(launcherPkg + "/" + LAUNCHER_CLASS);
 
         Context context = getInstrumentation().getContext();
diff --git a/tests/tests/appwidget/src/android/appwidget/cts/UpdateProviderInfoTest.java b/tests/tests/appwidget/src/android/appwidget/cts/UpdateProviderInfoTest.java
new file mode 100644
index 0000000..2ec8cf7
--- /dev/null
+++ b/tests/tests/appwidget/src/android/appwidget/cts/UpdateProviderInfoTest.java
@@ -0,0 +1,174 @@
+/*
+ * 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.appwidget.cts;
+
+import android.appwidget.AppWidgetHost;
+import android.appwidget.AppWidgetManager;
+import android.appwidget.AppWidgetProviderInfo;
+import android.appwidget.cts.common.Constants;
+import android.content.ComponentName;
+import android.content.Intent;
+import android.os.Process;
+
+import java.util.List;
+import java.util.concurrent.CountDownLatch;
+import java.util.concurrent.TimeUnit;
+
+public class UpdateProviderInfoTest extends AppWidgetTestCase {
+
+    private static final String PROVIDER_PACKAGE = "android.appwidget.cts.widgetprovider";
+    private static final String PROVIDER_CLASS = "android.appwidget.cts.packages.SimpleProvider";
+
+    private static final String APK_PATH = "data/local/tmp/cts/widgetprovider/";
+    private static final String APK_V1 = APK_PATH + "CtsAppWidgetProvider1.apk";
+    private static final String APK_V2 = APK_PATH + "CtsAppWidgetProvider2.apk";
+    private static final String APK_V3 = APK_PATH + "CtsAppWidgetProvider3.apk";
+
+    private static final String EXTRA_CUSTOM_INFO = "my_custom_info";
+
+    private static final int HOST_ID = 42;
+
+    private static final int RETRY_COUNT = 3;
+
+    private CountDownLatch mProviderChangeNotifier;
+    AppWidgetHost mHost;
+
+    @Override
+    protected void setUp() throws Exception {
+        super.setUp();
+
+        uninstallProvider();
+        createHost();
+    }
+
+    @Override
+    protected void tearDown() throws Exception {
+        super.tearDown();
+        uninstallProvider();
+
+        if (mHost != null) {
+            mHost.deleteHost();
+        }
+    }
+
+    public void testInfoOverrides() throws Throwable {
+        // On first install the provider does not have any config activity.
+        installApk(APK_V1);
+        assertNull(getProviderInfo().configure);
+
+        // The provider info is updated
+        updateInfo(EXTRA_CUSTOM_INFO);
+        assertNotNull(getProviderInfo().configure);
+
+        // The provider info is updated
+        updateInfo(null);
+        assertNull(getProviderInfo().configure);
+    }
+
+    public void testOverridesPersistedOnUpdate() throws Exception {
+        installApk(APK_V1);
+        assertNull(getProviderInfo().configure);
+
+        updateInfo(EXTRA_CUSTOM_INFO);
+        assertNotNull(getProviderInfo().configure);
+        assertEquals((AppWidgetProviderInfo.RESIZE_BOTH & getProviderInfo().resizeMode),
+                AppWidgetProviderInfo.RESIZE_BOTH);
+
+        // Apk updated, the info is also updated
+        installApk(APK_V2);
+        assertNotNull(getProviderInfo().configure);
+        assertEquals((AppWidgetProviderInfo.RESIZE_BOTH & getProviderInfo().resizeMode), 0);
+
+        // The provider info is reverted
+        updateInfo(null);
+        assertNull(getProviderInfo().configure);
+    }
+
+    public void testOverrideClearedWhenMissingInfo() throws Exception {
+        installApk(APK_V1);
+        assertNull(getProviderInfo().configure);
+
+        updateInfo(EXTRA_CUSTOM_INFO);
+        assertNotNull(getProviderInfo().configure);
+
+        // V3 does not have the custom info definition
+        installApk(APK_V3);
+        assertNull(getProviderInfo().configure);
+    }
+
+    private void createHost() throws Exception {
+        try {
+            runTestOnUiThread(() -> {
+                mHost = new AppWidgetHost(getInstrumentation().getTargetContext(), HOST_ID) {
+
+                    @Override
+                    protected void onProvidersChanged() {
+                        super.onProvidersChanged();
+
+                        if (mProviderChangeNotifier != null) {
+                            mProviderChangeNotifier.countDown();
+                        }
+                    }
+                };
+                mHost.startListening();
+            });
+        } catch (Throwable t) {
+            throw new RuntimeException(t);
+        }
+    }
+
+    private void updateInfo(String key) throws Exception {
+        mProviderChangeNotifier = new CountDownLatch(1);
+        Intent intent = new Intent(Constants.ACTION_APPLY_OVERRIDE)
+                .setComponent(new ComponentName(PROVIDER_PACKAGE, PROVIDER_CLASS))
+                .addFlags(Intent.FLAG_RECEIVER_FOREGROUND)
+                .putExtra(Constants.EXTRA_REQUEST, key);
+        getInstrumentation().getTargetContext().sendBroadcast(intent);
+
+        // Wait until the app widget manager is notified
+        mProviderChangeNotifier.await();
+    }
+
+    private void uninstallProvider() throws Exception {
+        runShellCommand("pm uninstall " + PROVIDER_PACKAGE);
+    }
+
+    private void installApk(String path) throws Exception {
+        mProviderChangeNotifier = new CountDownLatch(1);
+        runShellCommand("pm install -r -d " + path);
+
+        // Wait until the app widget manager is notified
+        mProviderChangeNotifier.await();
+    }
+
+    private AppWidgetProviderInfo getProviderInfo() throws Exception {
+        for (int i = 0; i < RETRY_COUNT; i++) {
+            mProviderChangeNotifier = new CountDownLatch(1);
+            List<AppWidgetProviderInfo> providers = AppWidgetManager.getInstance(getInstrumentation()
+                    .getTargetContext()).getInstalledProvidersForPackage(
+                    PROVIDER_PACKAGE, Process.myUserHandle());
+
+            if (providers != null && !providers.isEmpty()) {
+                return providers.get(0);
+            }
+
+            // Sometimes it could take time for the info to appear after the apk is just installed
+            mProviderChangeNotifier.await(2, TimeUnit.SECONDS);
+        }
+        return null;
+    }
+}
diff --git a/tests/tests/appwidget/src/android/appwidget/cts/provider/AppWidgetProviderWithFeatures.java b/tests/tests/appwidget/src/android/appwidget/cts/provider/AppWidgetProviderWithFeatures.java
new file mode 100644
index 0000000..d1f848a
--- /dev/null
+++ b/tests/tests/appwidget/src/android/appwidget/cts/provider/AppWidgetProviderWithFeatures.java
@@ -0,0 +1,45 @@
+/*
+ * 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.appwidget.cts.provider;
+
+public abstract class AppWidgetProviderWithFeatures extends StubbableAppWidgetProvider {
+    private static final Object sLock = new Object();
+
+    private static AppWidgetProviderCallbacks sCallbacks;
+
+    public static void setCallbacks(AppWidgetProviderCallbacks callbacks) {
+        synchronized (sLock) {
+            sCallbacks = callbacks;
+        }
+    }
+
+    @Override
+    protected AppWidgetProviderCallbacks getCallbacks() {
+        synchronized (sLock) {
+            if (sCallbacks != null) {
+                sCallbacks.setProvider(this);
+            }
+            return sCallbacks;
+        }
+    }
+
+    public static final class Provider1 extends AppWidgetProviderWithFeatures { }
+
+    public static final class Provider2 extends AppWidgetProviderWithFeatures { }
+
+    public static final class Provider3 extends AppWidgetProviderWithFeatures { }
+}
diff --git a/tests/tests/assist/Android.mk b/tests/tests/assist/Android.mk
index 6aa818e..f9bc56d 100644
--- a/tests/tests/assist/Android.mk
+++ b/tests/tests/assist/Android.mk
@@ -26,6 +26,8 @@
 
 LOCAL_STATIC_JAVA_LIBRARIES := CtsAssistCommon ctstestrunner compatibility-device-util
 
+LOCAL_JAVA_LIBRARIES := android.test.runner.stubs android.test.base.stubs
+
 LOCAL_SRC_FILES := $(call all-java-files-under, src)
 
 LOCAL_PACKAGE_NAME := CtsAssistTestCases
diff --git a/tests/tests/assist/AndroidTest.xml b/tests/tests/assist/AndroidTest.xml
index d8f31e7..13b1847 100644
--- a/tests/tests/assist/AndroidTest.xml
+++ b/tests/tests/assist/AndroidTest.xml
@@ -14,6 +14,7 @@
      limitations under the License.
 -->
 <configuration description="Config for CTS Assist test cases">
+    <option name="test-suite-tag" value="cts" />
     <option name="config-descriptor:metadata" key="component" value="framework" />
     <target_preparer class="com.android.tradefed.targetprep.suite.SuiteApkInstaller">
         <option name="cleanup-apks" value="true" />
diff --git a/tests/tests/background/AndroidTest.xml b/tests/tests/background/AndroidTest.xml
index 33e20ea..c54940c 100644
--- a/tests/tests/background/AndroidTest.xml
+++ b/tests/tests/background/AndroidTest.xml
@@ -14,6 +14,7 @@
      limitations under the License.
 -->
 <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="not-shardable" value="true" />
     <target_preparer class="com.android.tradefed.targetprep.suite.SuiteApkInstaller">
diff --git a/tests/tests/bionic/AndroidTest.xml b/tests/tests/bionic/AndroidTest.xml
index ce13307..f6d2b09 100644
--- a/tests/tests/bionic/AndroidTest.xml
+++ b/tests/tests/bionic/AndroidTest.xml
@@ -14,6 +14,7 @@
      limitations under the License.
 -->
 <configuration description="Config for CTS Bionic test cases">
+    <option name="test-suite-tag" value="cts" />
     <option name="config-descriptor:metadata" key="component" value="bionic" />
     <target_preparer class="com.android.compatibility.common.tradefed.targetprep.FilePusher">
         <option name="cleanup" value="true" />
diff --git a/tests/tests/bluetooth/Android.mk b/tests/tests/bluetooth/Android.mk
index 1af4a3f..4c0b04d 100644
--- a/tests/tests/bluetooth/Android.mk
+++ b/tests/tests/bluetooth/Android.mk
@@ -24,8 +24,9 @@
 # When built, explicitly put it in the data partition.
 LOCAL_MODULE_PATH := $(TARGET_OUT_DATA_APPS)
 
-LOCAL_STATIC_JAVA_LIBRARIES := ctstestrunner legacy-android-test
+LOCAL_STATIC_JAVA_LIBRARIES := ctstestrunner
 LOCAL_JAVA_LIBRARIES += android.test.runner
+LOCAL_JAVA_LIBRARIES += android.test.base.stubs
 
 LOCAL_SRC_FILES := $(call all-java-files-under, src)
 
diff --git a/tests/tests/bluetooth/src/android/bluetooth/cts/BluetoothLeScanTest.java b/tests/tests/bluetooth/src/android/bluetooth/cts/BluetoothLeScanTest.java
index 634b033..8aabe9c 100644
--- a/tests/tests/bluetooth/src/android/bluetooth/cts/BluetoothLeScanTest.java
+++ b/tests/tests/bluetooth/src/android/bluetooth/cts/BluetoothLeScanTest.java
@@ -19,7 +19,6 @@
 import android.app.PendingIntent;
 import android.bluetooth.BluetoothAdapter;
 import android.bluetooth.BluetoothManager;
-import android.bluetooth.cts.BluetoothScanReceiver;
 import android.bluetooth.le.BluetoothLeScanner;
 import android.bluetooth.le.ScanCallback;
 import android.bluetooth.le.ScanFilter;
@@ -61,8 +60,10 @@
 
     private static final String TAG = "BluetoothLeScanTest";
 
-    private static final int SCAN_DURATION_MILLIS = 5000;
+    private static final int SCAN_DURATION_MILLIS = 10000;
     private static final int BATCH_SCAN_REPORT_DELAY_MILLIS = 20000;
+    private static final int SCAN_STOP_TIMEOUT = 2000;
+    private static final int ADAPTER_ENABLE_TIMEOUT = 3000;
     private CountDownLatch mFlushBatchScanLatch;
 
     private BluetoothAdapter mBluetoothAdapter;
@@ -82,7 +83,7 @@
             // Note it's not reliable to listen for Adapter.ACTION_STATE_CHANGED broadcast and check
             // bluetooth state.
             mBluetoothAdapter.enable();
-            sleep(3000);
+            sleep(ADAPTER_ENABLE_TIMEOUT);
         }
         mScanner = mBluetoothAdapter.getBluetoothLeScanner();
         mLocationOn = TestUtils.isLocationOn(getContext());
@@ -99,6 +100,8 @@
         if (!mLocationOn) {
             TestUtils.disableLocation(getContext());
         }
+        mBluetoothAdapter.disable();
+        sleep(ADAPTER_ENABLE_TIMEOUT);
     }
 
     /**
@@ -112,6 +115,7 @@
         long scanStartMillis = SystemClock.elapsedRealtime();
         Collection<ScanResult> scanResults = scan();
         long scanEndMillis = SystemClock.elapsedRealtime();
+        Log.d(TAG, "scan result size:" + scanResults.size());
         assertTrue("Scan results shouldn't be empty", !scanResults.isEmpty());
         verifyTimestamp(scanResults, scanStartMillis, scanEndMillis);
     }
@@ -140,7 +144,7 @@
         mScanner.startScan(filters, settings, filterLeScanCallback);
         sleep(SCAN_DURATION_MILLIS);
         mScanner.stopScan(filterLeScanCallback);
-        sleep(1000);
+        sleep(SCAN_STOP_TIMEOUT);
         Collection<ScanResult> scanResults = filterLeScanCallback.getScanResults();
         for (ScanResult result : scanResults) {
             assertTrue(filter.matches(result));
@@ -178,51 +182,58 @@
         return null;
     }
 
-    /**
-     * Test of opportunistic BLE scans.
-     */
-    @MediumTest
-    public void testOpportunisticScan() {
-        if (!isBleSupported()) {
-            return;
-        }
-        ScanSettings opportunisticScanSettings = new ScanSettings.Builder()
-                .setScanMode(ScanSettings.SCAN_MODE_OPPORTUNISTIC)
-                .build();
-        BleScanCallback emptyScanCallback = new BleScanCallback();
-
-        // No scans are really started with opportunistic scans only.
-        mScanner.startScan(Collections.<ScanFilter>emptyList(), opportunisticScanSettings,
-                emptyScanCallback);
-        sleep(SCAN_DURATION_MILLIS);
-        assertTrue(emptyScanCallback.getScanResults().isEmpty());
-
-        BleScanCallback regularScanCallback = new BleScanCallback();
-        ScanSettings regularScanSettings = new ScanSettings.Builder()
-                .setScanMode(ScanSettings.SCAN_MODE_LOW_LATENCY).build();
-        List<ScanFilter> filters = new ArrayList<>();
-        ScanFilter filter = createScanFilter();
-        if (filter != null) {
-            filters.add(filter);
-        } else {
-            Log.d(TAG, "no appropriate filter can be set");
-        }
-        mScanner.startScan(filters, regularScanSettings, regularScanCallback);
-        sleep(SCAN_DURATION_MILLIS);
-        // With normal BLE scan client, opportunistic scan client will get scan results.
-        assertTrue("opportunistic scan results shouldn't be empty",
-                !emptyScanCallback.getScanResults().isEmpty());
-
-        // No more scan results for opportunistic scan clients once the normal BLE scan clients
-        // stops.
-        mScanner.stopScan(regularScanCallback);
-        // In case we got scan results before scan was completely stopped.
-        sleep(1000);
-        emptyScanCallback.clear();
-        sleep(SCAN_DURATION_MILLIS);
-        assertTrue("opportunistic scan shouldn't have scan results",
-                emptyScanCallback.getScanResults().isEmpty());
-    }
+//    /**
+//     * Test of opportunistic BLE scans.
+//     * Temporarily disable this test because it is interfered by the GmsCore;
+//     * it fails when it obtains results from GmsCore explicit scan.
+//     * TODO(b/70865144): re-enable this test.
+//     */
+//    @MediumTest
+//    public void testOpportunisticScan() {
+//        if (!isBleSupported()) {
+//            return;
+//        }
+//        ScanSettings opportunisticScanSettings = new ScanSettings.Builder()
+//                .setScanMode(ScanSettings.SCAN_MODE_OPPORTUNISTIC)
+//                .build();
+//        BleScanCallback emptyScanCallback = new BleScanCallback();
+//        assertTrue("opportunistic scan shouldn't have scan results",
+//                emptyScanCallback.getScanResults().isEmpty());
+//
+//        // No scans are really started with opportunistic scans only.
+//        mScanner.startScan(Collections.<ScanFilter>emptyList(), opportunisticScanSettings,
+//                emptyScanCallback);
+//        sleep(SCAN_DURATION_MILLIS);
+//        Log.d(TAG, "result: " + emptyScanCallback.getScanResults());
+//        assertTrue("opportunistic scan shouldn't have scan results",
+//                emptyScanCallback.getScanResults().isEmpty());
+//
+//        BleScanCallback regularScanCallback = new BleScanCallback();
+//        ScanSettings regularScanSettings = new ScanSettings.Builder()
+//                .setScanMode(ScanSettings.SCAN_MODE_LOW_LATENCY).build();
+//        List<ScanFilter> filters = new ArrayList<>();
+//        ScanFilter filter = createScanFilter();
+//        if (filter != null) {
+//            filters.add(filter);
+//        } else {
+//            Log.d(TAG, "no appropriate filter can be set");
+//        }
+//        mScanner.startScan(filters, regularScanSettings, regularScanCallback);
+//        sleep(SCAN_DURATION_MILLIS);
+//        // With normal BLE scan client, opportunistic scan client will get scan results.
+//        assertTrue("opportunistic scan results shouldn't be empty",
+//                !emptyScanCallback.getScanResults().isEmpty());
+//
+//        // No more scan results for opportunistic scan clients once the normal BLE scan clients
+//        // stops.
+//        mScanner.stopScan(regularScanCallback);
+//        // In case we got scan results before scan was completely stopped.
+//        sleep(SCAN_STOP_TIMEOUT);
+//        emptyScanCallback.clear();
+//        sleep(SCAN_DURATION_MILLIS);
+//        assertTrue("opportunistic scan shouldn't have scan results",
+//                emptyScanCallback.getScanResults().isEmpty());
+//    }
 
     /**
      * Test case for BLE Batch scan.
@@ -370,7 +381,7 @@
         mScanner.startScan(regularLeScanCallback);
         sleep(SCAN_DURATION_MILLIS);
         mScanner.stopScan(regularLeScanCallback);
-        sleep(1000);
+        sleep(SCAN_STOP_TIMEOUT);
         return regularLeScanCallback.getScanResults();
     }
 
diff --git a/tests/tests/calendarcommon/Android.mk b/tests/tests/calendarcommon/Android.mk
index 18ae49e..bfd9f26 100644
--- a/tests/tests/calendarcommon/Android.mk
+++ b/tests/tests/calendarcommon/Android.mk
@@ -25,9 +25,9 @@
 # When built, explicitly put it in the data partition.
 LOCAL_MODULE_PATH := $(TARGET_OUT_DATA_APPS)
 
-LOCAL_STATIC_JAVA_LIBRARIES := ctstestrunner legacy-android-test
+LOCAL_STATIC_JAVA_LIBRARIES := ctstestrunner
 
-LOCAL_JAVA_LIBRARIES := legacy-android-test
+LOCAL_JAVA_LIBRARIES := android.test.base.stubs
 
 LOCAL_SRC_FILES := $(call all-java-files-under, src)
 
diff --git a/tests/tests/calendarcommon/AndroidManifest.xml b/tests/tests/calendarcommon/AndroidManifest.xml
index 4f21b18..951404a 100644
--- a/tests/tests/calendarcommon/AndroidManifest.xml
+++ b/tests/tests/calendarcommon/AndroidManifest.xml
@@ -30,7 +30,7 @@
             android:value="com.android.cts.runner.CtsTestRunListener" />
     </instrumentation>
 
-    <uses-sdk android:minSdkVersion="15" android:targetSdkVersion="15"></uses-sdk>
+    <uses-sdk android:minSdkVersion="15" android:targetSdkVersion="17"></uses-sdk>
 
 </manifest>
 
diff --git a/tests/tests/calendarcommon/AndroidTest.xml b/tests/tests/calendarcommon/AndroidTest.xml
index 9169b79..76efb9b 100644
--- a/tests/tests/calendarcommon/AndroidTest.xml
+++ b/tests/tests/calendarcommon/AndroidTest.xml
@@ -14,6 +14,7 @@
      limitations under the License.
 -->
 <configuration description="Config for CTS Calendar test cases">
+    <option name="test-suite-tag" value="cts" />
     <option name="config-descriptor:metadata" key="component" value="framework" />
     <option name="not-shardable" value="true" />
     <target_preparer class="com.android.tradefed.targetprep.suite.SuiteApkInstaller">
diff --git a/tests/tests/car/Android.mk b/tests/tests/car/Android.mk
index db12143..07cbd83 100644
--- a/tests/tests/car/Android.mk
+++ b/tests/tests/car/Android.mk
@@ -24,9 +24,9 @@
 # When built, explicitly put it in the data partition.
 LOCAL_MODULE_PATH := $(TARGET_OUT_DATA_APPS)
 
-LOCAL_STATIC_JAVA_LIBRARIES := ctstestrunner android-support-test legacy-android-test
+LOCAL_STATIC_JAVA_LIBRARIES := ctstestrunner android-support-test
 
-LOCAL_JAVA_LIBRARIES := android.car
+LOCAL_JAVA_LIBRARIES := android.car android.test.base.stubs
 
 LOCAL_SRC_FILES := $(call all-java-files-under, src)
 
diff --git a/tests/tests/carrierapi/Android.mk b/tests/tests/carrierapi/Android.mk
index 954ae14..4969e8e 100644
--- a/tests/tests/carrierapi/Android.mk
+++ b/tests/tests/carrierapi/Android.mk
@@ -25,8 +25,7 @@
 LOCAL_STATIC_JAVA_LIBRARIES := \
     ctstestrunner \
     compatibility-device-util \
-    junit \
-    legacy-android-test
+    junit
 
 LOCAL_SRC_FILES := $(call all-java-files-under, src)
 
@@ -36,5 +35,6 @@
 LOCAL_COMPATIBILITY_SUITE := cts vts general-tests
 
 LOCAL_JAVA_LIBRARIES += android.test.runner telephony-common
+LOCAL_JAVA_LIBRARIES += android.test.base
 
 include $(BUILD_CTS_PACKAGE)
diff --git a/tests/tests/carrierapi/AndroidTest.xml b/tests/tests/carrierapi/AndroidTest.xml
index e46d53a..204d339 100644
--- a/tests/tests/carrierapi/AndroidTest.xml
+++ b/tests/tests/carrierapi/AndroidTest.xml
@@ -14,6 +14,7 @@
      limitations under the License.
 -->
 <configuration description="Config for CTS Carrier APIs test cases">
+    <option name="test-suite-tag" value="cts" />
     <option name="config-descriptor:metadata" key="component" value="telecom" />
     <option name="not-shardable" value="true" />
     <target_preparer class="com.android.compatibility.common.tradefed.targetprep.TokenRequirement">
diff --git a/tests/tests/colormode/Android.mk b/tests/tests/colormode/Android.mk
index a506fa5..d589a4f 100644
--- a/tests/tests/colormode/Android.mk
+++ b/tests/tests/colormode/Android.mk
@@ -24,7 +24,7 @@
 
 LOCAL_PROGUARD_ENABLED := disabled
 
-LOCAL_STATIC_JAVA_LIBRARIES := ctstestrunner legacy-android-test
+LOCAL_STATIC_JAVA_LIBRARIES := ctstestrunner
 
 LOCAL_SRC_FILES := $(call all-java-files-under, src)
 
diff --git a/tests/tests/colormode/AndroidTest.xml b/tests/tests/colormode/AndroidTest.xml
index 9c09038..eb491c2 100644
--- a/tests/tests/colormode/AndroidTest.xml
+++ b/tests/tests/colormode/AndroidTest.xml
@@ -14,6 +14,7 @@
      limitations under the License.
 -->
 <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="not-shardable" value="true" />
     <target_preparer class="com.android.tradefed.targetprep.suite.SuiteApkInstaller">
diff --git a/tests/tests/contactsproviderwipe/Android.mk b/tests/tests/contactsproviderwipe/Android.mk
index d843256..ad06530 100644
--- a/tests/tests/contactsproviderwipe/Android.mk
+++ b/tests/tests/contactsproviderwipe/Android.mk
@@ -28,7 +28,7 @@
     ctstestrunner \
     ub-uiautomator
 
-LOCAL_JAVA_LIBRARIES := android.test.runner
+LOCAL_JAVA_LIBRARIES := android.test.runner android.test.base.stubs
 
 LOCAL_SRC_FILES := $(call all-java-files-under, src)
 
diff --git a/tests/tests/contactsproviderwipe/AndroidTest.xml b/tests/tests/contactsproviderwipe/AndroidTest.xml
index b12398b..3a03a1e 100644
--- a/tests/tests/contactsproviderwipe/AndroidTest.xml
+++ b/tests/tests/contactsproviderwipe/AndroidTest.xml
@@ -14,6 +14,7 @@
      limitations under the License.
 -->
 <configuration description="Config for CTS Provider test cases">
+    <option name="test-suite-tag" value="cts" />
     <option name="config-descriptor:metadata" key="component" value="framework" />
     <option name="not-shardable" value="true" />
     <target_preparer class="com.android.tradefed.targetprep.suite.SuiteApkInstaller">
diff --git a/tests/tests/content/Android.mk b/tests/tests/content/Android.mk
index 3b64fa1..1fa78d3 100644
--- a/tests/tests/content/Android.mk
+++ b/tests/tests/content/Android.mk
@@ -24,15 +24,15 @@
 
 LOCAL_JNI_SHARED_LIBRARIES := libnativecursorwindow_jni libnativehelper_compat_libc++
 
-LOCAL_JAVA_LIBRARIES := android.test.runner
+LOCAL_JAVA_LIBRARIES := android.test.runner android.test.base android.test.mock
 
 LOCAL_STATIC_JAVA_LIBRARIES :=  \
     compatibility-device-util \
     ctstestrunner \
     services.core \
     junit \
-    legacy-android-test \
-    truth-prebuilt
+    truth-prebuilt \
+    accountaccesslib
 
 LOCAL_STATIC_ANDROID_LIBRARIES := android-support-v4
 
diff --git a/tests/tests/content/AndroidManifest.xml b/tests/tests/content/AndroidManifest.xml
index b191a11..eb6fecb 100644
--- a/tests/tests/content/AndroidManifest.xml
+++ b/tests/tests/content/AndroidManifest.xml
@@ -29,6 +29,7 @@
     <uses-permission android:name="android.permission.SET_WALLPAPER" />
     <uses-permission android:name="android.permission.BROADCAST_STICKY" />
     <uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />
+    <uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
     <uses-permission android:name="android.content.cts.permission.TEST_GRANTED" />
 
     <!-- Used for PackageManager test, don't delete this INTERNET permission -->
@@ -288,6 +289,16 @@
                 android:process=":providerProcess">
         </provider>
 
+        <activity android:name="com.android.cts.content.StubActivity"/>
+
+        <service android:name="com.android.cts.content.SyncService">
+            <intent-filter>
+                <action android:name="android.content.SyncAdapter"/>
+            </intent-filter>
+            <meta-data android:name="android.content.SyncAdapter"
+                android:resource="@xml/accountaccesssyncadapter" />
+        </service>
+
     </application>
 
     <instrumentation android:name="android.support.test.runner.AndroidJUnitRunner"
diff --git a/tests/tests/content/AndroidTest.xml b/tests/tests/content/AndroidTest.xml
index 5cab0ac..1fd262f 100644
--- a/tests/tests/content/AndroidTest.xml
+++ b/tests/tests/content/AndroidTest.xml
@@ -31,6 +31,7 @@
     <target_preparer class="com.android.tradefed.targetprep.suite.SuiteApkInstaller">
         <option name="cleanup-apks" value="true" />
         <option name="test-file-name" value="CtsContentTestCases.apk" />
+        <option name="test-file-name" value="CtsSyncAccountAccessStubs.apk" />
     </target_preparer>
 
     <test class="com.android.tradefed.testtype.AndroidJUnitTest" >
diff --git a/tests/tests/content/CtsSyncAccountAccessOtherCertTests/Android.mk b/tests/tests/content/CtsSyncAccountAccessOtherCertTests/Android.mk
new file mode 100644
index 0000000..09523ba
--- /dev/null
+++ b/tests/tests/content/CtsSyncAccountAccessOtherCertTests/Android.mk
@@ -0,0 +1,42 @@
+#
+# Copyright (C) 2016 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#      http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+#
+
+LOCAL_PATH := $(call my-dir)
+
+include $(CLEAR_VARS)
+
+LOCAL_MODULE_TAGS := tests
+
+LOCAL_STATIC_JAVA_LIBRARIES := \
+    android-support-test \
+    ctstestrunner \
+    ub-uiautomator \
+    compatibility-device-util \
+    accountaccesslib
+
+LOCAL_SRC_FILES := $(call all-java-files-under, src)
+
+LOCAL_PACKAGE_NAME := CtsSyncAccountAccessOtherCertTestCases
+
+LOCAL_CERTIFICATE := cts/hostsidetests/appsecurity/certs/cts-testkey2
+
+LOCAL_COMPATIBILITY_SUITE := cts vts general-tests
+
+LOCAL_PROGUARD_ENABLED := disabled
+
+LOCAL_DEX_PREOPT := false
+
+include $(BUILD_CTS_PACKAGE)
diff --git a/hostsidetests/content/test-apps/CtsSyncAccountAccessOtherCertTests/AndroidManifest.xml b/tests/tests/content/CtsSyncAccountAccessOtherCertTests/AndroidManifest.xml
similarity index 100%
rename from hostsidetests/content/test-apps/CtsSyncAccountAccessOtherCertTests/AndroidManifest.xml
rename to tests/tests/content/CtsSyncAccountAccessOtherCertTests/AndroidManifest.xml
diff --git a/tests/tests/content/CtsSyncAccountAccessOtherCertTests/AndroidTest.xml b/tests/tests/content/CtsSyncAccountAccessOtherCertTests/AndroidTest.xml
new file mode 100644
index 0000000..cc6da02
--- /dev/null
+++ b/tests/tests/content/CtsSyncAccountAccessOtherCertTests/AndroidTest.xml
@@ -0,0 +1,30 @@
+<?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 tests of sync adapters with different certs as the authenticator">
+    <option name="test-suite-tag" value="cts" />
+    <option name="config-descriptor:metadata" key="component" value="framework" />
+
+    <target_preparer class="com.android.tradefed.targetprep.suite.SuiteApkInstaller">
+        <option name="cleanup-apks" value="true" />
+        <option name="test-file-name" value="CtsSyncAccountAccessOtherCertTestCases.apk" />
+        <option name="test-file-name" value="CtsSyncAccountAccessStubs.apk" />
+    </target_preparer>
+
+    <test class="com.android.tradefed.testtype.AndroidJUnitTest" >
+        <option name="package" value="com.android.cts.content" />
+        <option name="runtime-hint" value="30s" />
+    </test>
+</configuration>
diff --git a/hostsidetests/content/test-apps/CtsSyncAccountAccessOtherCertTests/res/xml/syncadapter.xml b/tests/tests/content/CtsSyncAccountAccessOtherCertTests/res/xml/syncadapter.xml
similarity index 100%
rename from hostsidetests/content/test-apps/CtsSyncAccountAccessOtherCertTests/res/xml/syncadapter.xml
rename to tests/tests/content/CtsSyncAccountAccessOtherCertTests/res/xml/syncadapter.xml
diff --git a/tests/tests/content/CtsSyncAccountAccessOtherCertTests/src/com/android/cts/content/CtsSyncAccountAccessOtherCertTestCases.java b/tests/tests/content/CtsSyncAccountAccessOtherCertTests/src/com/android/cts/content/CtsSyncAccountAccessOtherCertTestCases.java
new file mode 100644
index 0000000..95bf60c
--- /dev/null
+++ b/tests/tests/content/CtsSyncAccountAccessOtherCertTests/src/com/android/cts/content/CtsSyncAccountAccessOtherCertTestCases.java
@@ -0,0 +1,228 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.cts.content;
+
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.Mockito.timeout;
+import static org.mockito.Mockito.verify;
+
+import android.accounts.Account;
+import android.accounts.AccountManager;
+import android.app.Activity;
+import android.content.AbstractThreadedSyncAdapter;
+import android.content.ContentResolver;
+import android.content.Context;
+import android.content.Intent;
+import android.content.SyncRequest;
+import android.content.pm.PackageManager;
+import android.content.res.Configuration;
+import android.net.ConnectivityManager;
+import android.net.NetworkInfo;
+import android.os.Bundle;
+import android.os.Process;
+import android.os.SystemClock;
+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.UiObject2;
+import android.support.test.uiautomator.Until;
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.rules.TestRule;
+import org.junit.runner.RunWith;
+
+import java.io.IOException;
+import java.util.regex.Pattern;
+
+import static org.junit.Assume.assumeTrue;
+
+import com.android.compatibility.common.util.SystemUtil;
+
+/**
+ * Tests whether a sync adapter can access accounts.
+ */
+@RunWith(AndroidJUnit4.class)
+public class CtsSyncAccountAccessOtherCertTestCases {
+    private static final long SYNC_TIMEOUT_MILLIS = 20000; // 20 sec
+    private static final long UI_TIMEOUT_MILLIS = 5000; // 5 sec
+
+    private static final Pattern PERMISSION_REQUESTED = Pattern.compile(
+            "Permission Requested|Permission requested");
+    private static final Pattern ALLOW_SYNC = Pattern.compile("ALLOW|Allow");
+    public static final String TOKEN_TYPE_REMOVE_ACCOUNTS = "TOKEN_TYPE_REMOVE_ACCOUNTS";
+
+    @Rule
+    public final TestRule mFlakyTestRule = new FlakyTestRule(3);
+
+    @Before
+    public void setUp() throws Exception {
+        allowSyncAdapterRunInBackgroundAndDataInBackground();
+    }
+
+    @After
+    public void tearDown() throws Exception {
+        disallowSyncAdapterRunInBackgroundAndDataInBackground();
+    }
+
+    @Test
+    public void testAccountAccess_otherCertAsAuthenticatorCanNotSeeAccount() throws Exception {
+        assumeTrue(hasDataConnection());
+        assumeTrue(hasNotificationSupport());
+
+        Intent intent = new Intent(getContext(), StubActivity.class);
+        Activity activity = InstrumentationRegistry.getInstrumentation().startActivitySync(intent);
+
+        AccountManager accountManager = getContext().getSystemService(AccountManager.class);
+        Bundle result = accountManager.addAccount("com.stub", null, null, null, activity,
+                null, null).getResult();
+
+        Account addedAccount = new Account(
+                result.getString(AccountManager.KEY_ACCOUNT_NAME),
+                result.getString(AccountManager.KEY_ACCOUNT_TYPE));
+
+        waitForSyncManagerAccountChangeUpdate();
+
+        try {
+            AbstractThreadedSyncAdapter adapter = SyncAdapter.setNewDelegate();
+
+            Bundle extras = new Bundle();
+            extras.putBoolean(ContentResolver.SYNC_EXTRAS_DO_NOT_RETRY, true);
+            extras.putBoolean(ContentResolver.SYNC_EXTRAS_PRIORITY, true);
+            extras.getBoolean(ContentResolver.SYNC_EXTRAS_IGNORE_SETTINGS, true);
+            SyncRequest request = new SyncRequest.Builder()
+                    .setSyncAdapter(null, "com.android.cts.stub.provider")
+                    .syncOnce()
+                    .setExtras(extras)
+                    .setExpedited(true)
+                    .setManual(true)
+                    .build();
+            ContentResolver.requestSync(request);
+
+            verify(adapter, timeout(SYNC_TIMEOUT_MILLIS).times(0)).onPerformSync(any(), any(),
+                    any(), any(), any());
+
+            UiDevice uiDevice = getUiDevice();
+            if (isWatch()) {
+                UiObject2 notification = findPermissionNotificationInStream(uiDevice);
+                notification.click();
+            } else {
+                uiDevice.openNotification();
+                uiDevice.wait(Until.hasObject(By.text(PERMISSION_REQUESTED)),
+                        UI_TIMEOUT_MILLIS);
+
+                uiDevice.findObject(By.text(PERMISSION_REQUESTED)).click();
+            }
+
+            uiDevice.wait(Until.hasObject(By.text(ALLOW_SYNC)),
+                    UI_TIMEOUT_MILLIS);
+
+            uiDevice.findObject(By.text(ALLOW_SYNC)).click();
+
+            ContentResolver.requestSync(request);
+
+            verify(adapter, timeout(SYNC_TIMEOUT_MILLIS)).onPerformSync(any(), any(), any(), any(),
+                    any());
+        } finally {
+            // Ask the differently signed authenticator to drop all accounts
+            accountManager.getAuthToken(addedAccount, TOKEN_TYPE_REMOVE_ACCOUNTS,
+                    null, false, null, null);
+            activity.finish();
+        }
+    }
+
+    private UiObject2 findPermissionNotificationInStream(UiDevice uiDevice) {
+        uiDevice.pressHome();
+        swipeUp(uiDevice);
+        if (uiDevice.hasObject(By.text(PERMISSION_REQUESTED))) {
+          return uiDevice.findObject(By.text(PERMISSION_REQUESTED));
+        }
+        for (int i = 0; i < 100; i++) {
+          if (!swipeUp(uiDevice)) {
+            // We have reached the end of the stream and not found the target.
+            break;
+          }
+          if (uiDevice.hasObject(By.text(PERMISSION_REQUESTED))) {
+            return uiDevice.findObject(By.text(PERMISSION_REQUESTED));
+          }
+        }
+        return null;
+    }
+
+    private boolean swipeUp(UiDevice uiDevice) {
+        int width = uiDevice.getDisplayWidth();
+        int height = uiDevice.getDisplayHeight();
+        return uiDevice.swipe(
+            width / 2 /* startX */,
+            height - 1 /* startY */,
+            width / 2 /* endX */,
+            1 /* endY */,
+            50 /* numberOfSteps */);
+    }
+
+    private boolean isWatch() {
+        return (getContext().getResources().getConfiguration().uiMode
+                & Configuration.UI_MODE_TYPE_MASK) == Configuration.UI_MODE_TYPE_WATCH;
+    }
+
+    private Context getContext() {
+        return InstrumentationRegistry.getInstrumentation().getContext();
+    }
+
+    private UiDevice getUiDevice() {
+        return UiDevice.getInstance(InstrumentationRegistry.getInstrumentation());
+    }
+
+    private void waitForSyncManagerAccountChangeUpdate() {
+        // Wait for the sync manager to be notified for the new account.
+        // Unfortunately, there is no way to detect this event, sigh...
+        SystemClock.sleep(SYNC_TIMEOUT_MILLIS);
+    }
+
+    private boolean hasDataConnection() {
+        ConnectivityManager connectivityManager = getContext().getSystemService(
+                ConnectivityManager.class);
+        NetworkInfo activeNetwork = connectivityManager.getActiveNetworkInfo();
+        return activeNetwork != null && activeNetwork.isConnectedOrConnecting();
+    }
+
+    private boolean hasNotificationSupport() {
+        final PackageManager manager = getContext().getPackageManager();
+        return !manager.hasSystemFeature(PackageManager.FEATURE_LEANBACK)
+                && !manager.hasSystemFeature(PackageManager.FEATURE_EMBEDDED);
+    }
+
+    private void allowSyncAdapterRunInBackgroundAndDataInBackground() throws IOException {
+        // Allow us to run in the background
+        SystemUtil.runShellCommand(InstrumentationRegistry.getInstrumentation(),
+                "cmd deviceidle whitelist +" + getContext().getPackageName());
+        // Allow us to use data in the background
+        SystemUtil.runShellCommand(InstrumentationRegistry.getInstrumentation(),
+                "cmd netpolicy add restrict-background-whitelist " + Process.myUid());
+    }
+
+    private void disallowSyncAdapterRunInBackgroundAndDataInBackground() throws IOException {
+        // Allow us to run in the background
+        SystemUtil.runShellCommand(InstrumentationRegistry.getInstrumentation(),
+                "cmd deviceidle whitelist -" + getContext().getPackageName());
+        // Allow us to use data in the background
+        SystemUtil.runShellCommand(InstrumentationRegistry.getInstrumentation(),
+                "cmd netpolicy remove restrict-background-whitelist " + Process.myUid());
+    }
+}
diff --git a/hostsidetests/content/test-apps/SyncAccountAccessStubs/Android.mk b/tests/tests/content/SyncAccountAccessStubs/Android.mk
similarity index 100%
rename from hostsidetests/content/test-apps/SyncAccountAccessStubs/Android.mk
rename to tests/tests/content/SyncAccountAccessStubs/Android.mk
diff --git a/hostsidetests/content/test-apps/SyncAccountAccessStubs/AndroidManifest.xml b/tests/tests/content/SyncAccountAccessStubs/AndroidManifest.xml
similarity index 100%
rename from hostsidetests/content/test-apps/SyncAccountAccessStubs/AndroidManifest.xml
rename to tests/tests/content/SyncAccountAccessStubs/AndroidManifest.xml
diff --git a/hostsidetests/content/test-apps/SyncAccountAccessStubs/res/xml/authenticator.xml b/tests/tests/content/SyncAccountAccessStubs/res/xml/authenticator.xml
similarity index 100%
rename from hostsidetests/content/test-apps/SyncAccountAccessStubs/res/xml/authenticator.xml
rename to tests/tests/content/SyncAccountAccessStubs/res/xml/authenticator.xml
diff --git a/hostsidetests/content/test-apps/SyncAccountAccessStubs/src/com/android/cts/stub/StubAuthenticator.java b/tests/tests/content/SyncAccountAccessStubs/src/com/android/cts/stub/StubAuthenticator.java
similarity index 100%
rename from hostsidetests/content/test-apps/SyncAccountAccessStubs/src/com/android/cts/stub/StubAuthenticator.java
rename to tests/tests/content/SyncAccountAccessStubs/src/com/android/cts/stub/StubAuthenticator.java
diff --git a/hostsidetests/content/test-apps/SyncAccountAccessStubs/src/com/android/cts/stub/StubProvider.java b/tests/tests/content/SyncAccountAccessStubs/src/com/android/cts/stub/StubProvider.java
similarity index 100%
rename from hostsidetests/content/test-apps/SyncAccountAccessStubs/src/com/android/cts/stub/StubProvider.java
rename to tests/tests/content/SyncAccountAccessStubs/src/com/android/cts/stub/StubProvider.java
diff --git a/tests/tests/content/fonts_readme.txt b/tests/tests/content/fonts_readme.txt
new file mode 100644
index 0000000..f0de576
--- /dev/null
+++ b/tests/tests/content/fonts_readme.txt
@@ -0,0 +1,15 @@
+All fonts included in this project follow the below copyright and licensing:
+
+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.
\ No newline at end of file
diff --git a/tests/tests/content/lib/Android.mk b/tests/tests/content/lib/Android.mk
new file mode 100644
index 0000000..9aaa6ac
--- /dev/null
+++ b/tests/tests/content/lib/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/tests/tests/content/lib/accountaccess/Android.mk b/tests/tests/content/lib/accountaccess/Android.mk
new file mode 100644
index 0000000..559dc90
--- /dev/null
+++ b/tests/tests/content/lib/accountaccess/Android.mk
@@ -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.
+
+LOCAL_PATH:= $(call my-dir)
+
+include $(CLEAR_VARS)
+
+LOCAL_MODULE_TAGS := tests
+
+LOCAL_SRC_FILES := $(call all-java-files-under, src)
+
+LOCAL_MODULE := accountaccesslib
+
+LOCAL_STATIC_JAVA_LIBRARIES := mockito-target
+
+include $(BUILD_STATIC_JAVA_LIBRARY)
\ No newline at end of file
diff --git a/tests/tests/content/lib/accountaccess/src/com.android.cts.content/FlakyTestRule.java b/tests/tests/content/lib/accountaccess/src/com.android.cts.content/FlakyTestRule.java
new file mode 100644
index 0000000..2608b97
--- /dev/null
+++ b/tests/tests/content/lib/accountaccess/src/com.android.cts.content/FlakyTestRule.java
@@ -0,0 +1,52 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ */
+
+package com.android.cts.content;
+
+import org.junit.rules.TestRule;
+import org.junit.runner.Description;
+import org.junit.runners.model.Statement;
+
+/**
+ * Rule for running flaky tests that runs the test up to attempt
+ * count and if one run succeeds reports the tests as passing.
+ */
+// TODO: Move this puppy in a common place, so ppl can use it.
+public class FlakyTestRule implements TestRule {
+    private final int mAttemptCount;
+
+    public FlakyTestRule(int attemptCount) {
+        mAttemptCount = attemptCount;
+    }
+
+    @Override
+    public Statement apply(Statement statement, Description description) {
+        return new Statement() {
+            @Override
+            public void evaluate() throws Throwable {
+                Throwable throwable = null;
+                for (int i = 0; i < mAttemptCount; i++) {
+                    try {
+                        statement.evaluate();
+                        return;
+                    } catch (Throwable t) {
+                        throwable = t;
+                    }
+                }
+                throw throwable;
+            };
+        };
+    }
+}
diff --git a/hostsidetests/content/test-apps/CtsSyncAccountAccessSameCertTests/src/com/android/cts/content/StubActivity.java b/tests/tests/content/lib/accountaccess/src/com.android.cts.content/StubActivity.java
similarity index 100%
rename from hostsidetests/content/test-apps/CtsSyncAccountAccessSameCertTests/src/com/android/cts/content/StubActivity.java
rename to tests/tests/content/lib/accountaccess/src/com.android.cts.content/StubActivity.java
diff --git a/tests/tests/content/lib/accountaccess/src/com.android.cts.content/SyncAdapter.java b/tests/tests/content/lib/accountaccess/src/com.android.cts.content/SyncAdapter.java
new file mode 100644
index 0000000..19fb8ee
--- /dev/null
+++ b/tests/tests/content/lib/accountaccess/src/com.android.cts.content/SyncAdapter.java
@@ -0,0 +1,62 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.cts.content;
+
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.when;
+
+import android.accounts.Account;
+import android.content.AbstractThreadedSyncAdapter;
+import android.content.ContentProviderClient;
+import android.content.Context;
+import android.content.SyncResult;
+import android.os.Bundle;
+
+public class SyncAdapter extends AbstractThreadedSyncAdapter {
+    private static final Object sLock = new Object();
+    private static AbstractThreadedSyncAdapter sDelegate;
+
+    public static AbstractThreadedSyncAdapter setNewDelegate() {
+        AbstractThreadedSyncAdapter delegate = mock(AbstractThreadedSyncAdapter.class);
+
+        synchronized (sLock) {
+            sDelegate = delegate;
+        }
+
+        return delegate;
+    }
+
+    public SyncAdapter(Context context, boolean autoInitialize) {
+        super(context, autoInitialize);
+    }
+
+    private AbstractThreadedSyncAdapter getCopyOfDelegate() {
+        synchronized (sLock) {
+            return sDelegate;
+        }
+    }
+
+    @Override
+    public void onPerformSync(Account account, Bundle extras, String authority,
+            ContentProviderClient provider, SyncResult syncResult) {
+        AbstractThreadedSyncAdapter delegate = getCopyOfDelegate();
+
+        if (delegate != null) {
+            delegate.onPerformSync(account, extras, authority, provider, syncResult);
+        }
+    }
+}
diff --git a/hostsidetests/content/test-apps/CtsSyncAccountAccessSameCertTests/src/com/android/cts/content/SyncService.java b/tests/tests/content/lib/accountaccess/src/com.android.cts.content/SyncService.java
similarity index 100%
rename from hostsidetests/content/test-apps/CtsSyncAccountAccessSameCertTests/src/com/android/cts/content/SyncService.java
rename to tests/tests/content/lib/accountaccess/src/com.android.cts.content/SyncService.java
diff --git a/tests/tests/content/res/font/sample_font_collection.ttc b/tests/tests/content/res/font/sample_font_collection.ttc
new file mode 100644
index 0000000..9252f3d
--- /dev/null
+++ b/tests/tests/content/res/font/sample_font_collection.ttc
Binary files differ
diff --git a/tests/tests/content/res/font/sample_ttc_family.xml b/tests/tests/content/res/font/sample_ttc_family.xml
new file mode 100644
index 0000000..ae9b63f
--- /dev/null
+++ b/tests/tests/content/res/font/sample_ttc_family.xml
@@ -0,0 +1,5 @@
+<?xml version="1.0" encoding="utf-8"?>
+<font-family xmlns:android="http://schemas.android.com/apk/res/android">
+    <font android:fontStyle="normal" android:font="@font/sample_font_collection" android:ttcIndex="0" />
+    <font android:fontStyle="italic" android:font="@font/sample_font_collection" android:ttcIndex="1" />
+</font-family>
\ No newline at end of file
diff --git a/tests/tests/content/res/font/sample_variation_settings_family1.xml b/tests/tests/content/res/font/sample_variation_settings_family1.xml
new file mode 100644
index 0000000..c719fa9
--- /dev/null
+++ b/tests/tests/content/res/font/sample_variation_settings_family1.xml
@@ -0,0 +1,4 @@
+<?xml version="1.0" encoding="utf-8"?>
+<font-family xmlns:android="http://schemas.android.com/apk/res/android">
+    <font android:font="@font/variable_width_dash_font" android:fontVariationSettings="'wdth' 100.0" />
+</font-family>
diff --git a/tests/tests/content/res/font/sample_variation_settings_family2.xml b/tests/tests/content/res/font/sample_variation_settings_family2.xml
new file mode 100644
index 0000000..94d4d79
--- /dev/null
+++ b/tests/tests/content/res/font/sample_variation_settings_family2.xml
@@ -0,0 +1,4 @@
+<?xml version="1.0" encoding="utf-8"?>
+<font-family xmlns:android="http://schemas.android.com/apk/res/android">
+    <font android:font="@font/variable_width_dash_font" android:fontVariationSettings="'wdth' 500.0" />
+</font-family>
diff --git a/tests/tests/content/res/font/variable_width_dash_font.ttf b/tests/tests/content/res/font/variable_width_dash_font.ttf
new file mode 100644
index 0000000..f7a256a
--- /dev/null
+++ b/tests/tests/content/res/font/variable_width_dash_font.ttf
Binary files differ
diff --git a/tests/tests/content/res/font/variable_width_dash_font_source.ttx b/tests/tests/content/res/font/variable_width_dash_font_source.ttx
new file mode 100644
index 0000000..401fe0f
--- /dev/null
+++ b/tests/tests/content/res/font/variable_width_dash_font_source.ttx
@@ -0,0 +1,241 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- Copyright (C) 2017 The Android Open Source Project
+
+     Licensed under the Apache License, Version 2.0 (the "License");
+     you may not use this file except in compliance with the License.
+     You may obtain a copy of the License at
+
+          http://www.apache.org/licenses/LICENSE-2.0
+
+     Unless required by applicable law or agreed to in writing, software
+     distributed under the License is distributed on an "AS IS" BASIS
+     WITHOUT 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.9">
+
+  <GlyphOrder>
+    <!-- The 'id' attribute is only for humans; it is ignored when parsed. -->
+    <GlyphID id="0" name=".notdef"/>
+    <GlyphID id="1" name="dash"/>
+  </GlyphOrder>
+
+  <head>
+    <!-- Most of this table will be recalculated by the compiler -->
+    <tableVersion value="1.0"/>
+    <fontRevision value="1.0"/>
+    <checkSumAdjustment value="0x81ee73dd"/>
+    <magicNumber value="0x5f0f3cf5"/>
+    <flags value="00000000 00000011"/>
+    <unitsPerEm value="1000"/>
+    <created value="Wed Sep 9 08:01:17 2015"/>
+    <modified value="Thu Mar 9 22:22:31 2017"/>
+    <xMin value="0"/>
+    <yMin value="0"/>
+    <xMax value="0"/>
+    <yMax value="0"/>
+    <macStyle value="00000000 00000000"/>
+    <lowestRecPPEM value="7"/>
+    <fontDirectionHint value="2"/>
+    <indexToLocFormat value="0"/>
+    <glyphDataFormat value="0"/>
+  </head>
+
+  <hhea>
+    <tableVersion value="0x00010000"/>
+    <ascent value="1000"/>
+    <descent value="-200"/>
+    <lineGap value="0"/>
+    <advanceWidthMax value="500"/>
+    <minLeftSideBearing value="0"/>
+    <minRightSideBearing value="0"/>
+    <xMaxExtent 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"/>
+    <numberOfHMetrics value="1"/>
+  </hhea>
+
+  <maxp>
+    <!-- Most of this table will be recalculated by the compiler -->
+    <tableVersion value="0x10000"/>
+    <numGlyphs value="2"/>
+    <maxPoints value="0"/>
+    <maxContours value="0"/>
+    <maxCompositePoints value="0"/>
+    <maxCompositeContours value="0"/>
+    <maxZones value="2"/>
+    <maxTwilightPoints value="12"/>
+    <maxStorage value="28"/>
+    <maxFunctionDefs value="119"/>
+    <maxInstructionDefs value="0"/>
+    <maxStackElements value="61"/>
+    <maxSizeOfInstructions value="2967"/>
+    <maxComponentElements value="0"/>
+    <maxComponentDepth 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="97"/>
+    <usLastCharIndex value="97"/>
+    <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="dash" width="500" lsb="93"/>
+  </hmtx>
+
+  <cmap>
+    <tableVersion version="0"/>
+    <cmap_format_4 platformID="3" platEncID="10" language="0">
+      <map code="0x2D" name="dash"/><!-- ASCII DASH -->
+    </cmap_format_4>
+  </cmap>
+
+  <loca>
+    <!-- The 'loca' table will be calculated by the compiler -->
+  </loca>
+
+  <glyf>
+
+    <!-- The xMin, yMin, xMax and yMax values
+         will be recalculated by the compiler. -->
+    <TTGlyph name=".notdef" xMin="0" yMin="0" xMax="0" yMax="0">
+      <contour>
+        <pt x="100" y="-500" on="1"/>
+        <pt x="900" y="-500" on="1"/>
+        <pt x="900" y="1000" on="1"/>
+        <pt x="100" y="1000" on="1"/>
+      </contour>
+      <instructions/>
+    </TTGlyph>
+
+    <TTGlyph name="dash">
+      <contour>
+        <pt x="0" y="200" on="1"/>
+        <pt x="100" y="200" on="1"/>
+        <pt x="100" y="300" on="1"/>
+        <pt x="0" y="300" on="1"/>
+      </contour>
+    <instructions/>
+    </TTGlyph>
+  </glyf>
+
+  <name>
+    <namerecord nameID="1" platformID="1" platEncID="0" langID="0x0" unicode="True">
+      MultiAxisFont Test
+    </namerecord>
+    <namerecord nameID="2" platformID="1" platEncID="0" langID="0x0" unicode="True">
+      Regular
+    </namerecord>
+    <namerecord nameID="4" platformID="1" platEncID="0" langID="0x0" unicode="True">
+      MultiAxisFont Test
+    </namerecord>
+    <namerecord nameID="6" platformID="1" platEncID="0" langID="0x0" unicode="True">
+      MultiAxisFontTest-Regular
+    </namerecord>
+    <namerecord nameID="1" platformID="3" platEncID="1" langID="0x409">
+      MultiAxisFont Test
+    </namerecord>
+    <namerecord nameID="2" platformID="3" platEncID="1" langID="0x409">
+      Regular
+    </namerecord>
+    <namerecord nameID="4" platformID="3" platEncID="1" langID="0x409">
+      MultiAxisFont Test
+    </namerecord>
+    <namerecord nameID="6" platformID="3" platEncID="1" langID="0x409">
+      MultiAxisFontTest-Regular
+    </namerecord>
+  </name>
+
+  <post>
+    <formatType value="3.0"/>
+    <italicAngle value="0.0"/>
+    <underlinePosition value="-200"/>
+    <underlineThickness value="20"/>
+    <isFixedPitch value="0"/>
+    <minMemType42 value="0"/>
+    <maxMemType42 value="0"/>
+    <minMemType1 value="0"/>
+    <maxMemType1 value="0"/>
+  </post>
+
+  <fvar>
+    <Axis>
+      <AxisTag>wdth</AxisTag>
+      <MinValue>100.0</MinValue>
+      <DefaultValue>100.0</DefaultValue>
+      <MaxValue>500.0</MaxValue>
+      <AxisNameID>256</AxisNameID>
+    </Axis>
+  </fvar>
+
+  <gvar>
+    <glyphVariations glyph="dash">
+      <tuple>
+        <coord axis="wdth" value="1.0"/>
+        <delta pt="0" x="0" y="0"/>
+        <delta pt="1" x="400" y="0"/>
+        <delta pt="2" x="400" y="0"/>
+        <delta pt="3" x="0" y="0"/>
+        <delta pt="4" x="-200" y="0"/>
+        <delta pt="5" x="200" y="0"/>
+      </tuple>
+    </glyphVariations>
+  </gvar>
+
+</ttFont>
diff --git a/tests/tests/content/res/mipmap/icon_background.png b/tests/tests/content/res/mipmap/icon_background.png
new file mode 100644
index 0000000..72a065c
--- /dev/null
+++ b/tests/tests/content/res/mipmap/icon_background.png
Binary files differ
diff --git a/tests/tests/content/res/mipmap/icon_recursive.xml b/tests/tests/content/res/mipmap/icon_recursive.xml
new file mode 100644
index 0000000..b44fe19
--- /dev/null
+++ b/tests/tests/content/res/mipmap/icon_recursive.xml
@@ -0,0 +1,21 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ -->
+
+<adaptive-icon xmlns:android="http://schemas.android.com/apk/res/android">
+    <background android:drawable="@mipmap/icon_background" />
+    <foreground android:drawable="@mipmap/icon_recursive" />
+</adaptive-icon>
diff --git a/hostsidetests/content/test-apps/CtsSyncAccountAccessOtherCertTests/res/xml/syncadapter.xml b/tests/tests/content/res/xml/accountaccesssyncadapter.xml
similarity index 100%
copy from hostsidetests/content/test-apps/CtsSyncAccountAccessOtherCertTests/res/xml/syncadapter.xml
copy to tests/tests/content/res/xml/accountaccesssyncadapter.xml
diff --git a/tests/tests/content/src/android/content/cts/AccountAccessSameCertTest.java b/tests/tests/content/src/android/content/cts/AccountAccessSameCertTest.java
new file mode 100644
index 0000000..72785b5
--- /dev/null
+++ b/tests/tests/content/src/android/content/cts/AccountAccessSameCertTest.java
@@ -0,0 +1,156 @@
+/*
+ * 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.content.cts;
+
+import static org.junit.Assume.assumeTrue;
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.Mockito.timeout;
+import static org.mockito.Mockito.verify;
+
+import android.accounts.Account;
+import android.accounts.AccountManager;
+import android.app.Activity;
+import android.content.AbstractThreadedSyncAdapter;
+import android.content.ContentResolver;
+import android.content.Context;
+import android.content.Intent;
+import android.content.SyncRequest;
+import android.content.pm.PackageManager;
+import android.net.ConnectivityManager;
+import android.net.NetworkInfo;
+import android.os.Bundle;
+import android.os.Process;
+import android.os.SystemClock;
+import android.support.test.InstrumentationRegistry;
+import android.support.test.runner.AndroidJUnit4;
+
+import com.android.compatibility.common.util.SystemUtil;
+import com.android.cts.content.FlakyTestRule;
+import com.android.cts.content.StubActivity;
+import com.android.cts.content.SyncAdapter;
+
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.rules.TestRule;
+import org.junit.runner.RunWith;
+
+import java.io.IOException;
+
+/**
+ * Tests whether a sync adapter can access accounts.
+ */
+@RunWith(AndroidJUnit4.class)
+public class AccountAccessSameCertTest {
+    private static final long SYNC_TIMEOUT_MILLIS = 20000; // 20 sec
+
+    @Rule
+    public final TestRule mFlakyTestTRule = new FlakyTestRule(3);
+
+    @Before
+    public void setUp() throws Exception {
+        allowSyncAdapterRunInBackgroundAndDataInBackground();
+    }
+
+    @After
+    public void tearDown() throws Exception {
+        disallowSyncAdapterRunInBackgroundAndDataInBackground();
+    }
+
+    @Test
+    public void testAccountAccess_sameCertAsAuthenticatorCanSeeAccount() throws Exception {
+        assumeTrue(hasDataConnection());
+        assumeTrue(hasNotificationSupport());
+
+        Intent intent = new Intent(getContext(), StubActivity.class);
+        Activity activity = InstrumentationRegistry.getInstrumentation().startActivitySync(intent);
+
+        AccountManager accountManager = getContext().getSystemService(AccountManager.class);
+        Bundle result = accountManager.addAccount("com.stub", null, null, null, activity,
+                null, null).getResult();
+
+        Account addedAccount = new Account(
+                result.getString(AccountManager.KEY_ACCOUNT_NAME),
+                        result.getString(AccountManager.KEY_ACCOUNT_TYPE));
+
+        waitForSyncManagerAccountChangeUpdate();
+
+        try {
+            AbstractThreadedSyncAdapter adapter = SyncAdapter.setNewDelegate();
+
+            Bundle extras = new Bundle();
+            extras.putBoolean(ContentResolver.SYNC_EXTRAS_DO_NOT_RETRY, true);
+            extras.putBoolean(ContentResolver.SYNC_EXTRAS_PRIORITY, true);
+            extras.getBoolean(ContentResolver.SYNC_EXTRAS_IGNORE_SETTINGS, true);
+            SyncRequest request = new SyncRequest.Builder()
+                    .setSyncAdapter(null, "com.android.cts.stub.provider")
+                    .syncOnce()
+                    .setExtras(extras)
+                    .setExpedited(true)
+                    .setManual(true)
+                    .build();
+            ContentResolver.requestSync(request);
+
+            verify(adapter, timeout(SYNC_TIMEOUT_MILLIS)).onPerformSync(any(), any(), any(), any(),
+                    any());
+        } finally {
+            accountManager.removeAccount(addedAccount, activity, null, null);
+            activity.finish();
+        }
+    }
+
+    private Context getContext() {
+        return InstrumentationRegistry.getInstrumentation().getContext();
+    }
+
+    private void waitForSyncManagerAccountChangeUpdate() {
+        // Wait for the sync manager to be notified for the new account.
+        // Unfortunately, there is no way to detect this event, sigh...
+        SystemClock.sleep(SYNC_TIMEOUT_MILLIS);
+    }
+
+    private boolean hasDataConnection() {
+        ConnectivityManager connectivityManager = getContext().getSystemService(
+                ConnectivityManager.class);
+        NetworkInfo activeNetwork = connectivityManager.getActiveNetworkInfo();
+        return activeNetwork != null && activeNetwork.isConnectedOrConnecting();
+    }
+
+    private boolean hasNotificationSupport() {
+        return !getContext().getPackageManager()
+                .hasSystemFeature(PackageManager.FEATURE_LEANBACK);
+    }
+
+    private void allowSyncAdapterRunInBackgroundAndDataInBackground() throws IOException {
+        // Allow us to run in the background
+        SystemUtil.runShellCommand(InstrumentationRegistry.getInstrumentation(),
+                "cmd deviceidle whitelist +" + getContext().getPackageName());
+        // Allow us to use data in the background
+        SystemUtil.runShellCommand(InstrumentationRegistry.getInstrumentation(),
+                "cmd netpolicy add restrict-background-whitelist " + Process.myUid());
+    }
+
+    private void disallowSyncAdapterRunInBackgroundAndDataInBackground() throws IOException {
+        // Allow us to run in the background
+        SystemUtil.runShellCommand(InstrumentationRegistry.getInstrumentation(),
+                "cmd deviceidle whitelist -" + getContext().getPackageName());
+        // Allow us to use data in the background
+        SystemUtil.runShellCommand(InstrumentationRegistry.getInstrumentation(),
+                "cmd netpolicy remove restrict-background-whitelist " + Process.myUid());
+    }
+}
diff --git a/tests/tests/content/src/android/content/cts/AvailableIntentsTest.java b/tests/tests/content/src/android/content/cts/AvailableIntentsTest.java
index dbebbc0..2695fac 100644
--- a/tests/tests/content/src/android/content/cts/AvailableIntentsTest.java
+++ b/tests/tests/content/src/android/content/cts/AvailableIntentsTest.java
@@ -353,4 +353,18 @@
             assertDefaultHandlerValidPriority(intent);
         }
     }
+
+    public void testFingerprintEnrollStart() {
+        PackageManager packageManager = mContext.getPackageManager();
+        if (packageManager.hasSystemFeature(PackageManager.FEATURE_FINGERPRINT)) {
+            assertCanBeHandled(new Intent(Settings.ACTION_FINGERPRINT_ENROLL));
+        }
+    }
+
+    public void testPictureInPictureSettings() {
+        PackageManager packageManager = mContext.getPackageManager();
+        if (packageManager.hasSystemFeature(PackageManager.FEATURE_PICTURE_IN_PICTURE)) {
+            assertCanBeHandled(new Intent(Settings.ACTION_PICTURE_IN_PICTURE_SETTINGS));
+        }
+    }
 }
diff --git a/tests/tests/content/src/android/content/cts/ContextTest.java b/tests/tests/content/src/android/content/cts/ContextTest.java
index 0d9c56d..4d608ab 100644
--- a/tests/tests/content/src/android/content/cts/ContextTest.java
+++ b/tests/tests/content/src/android/content/cts/ContextTest.java
@@ -25,6 +25,8 @@
 import android.content.res.XmlResourceParser;
 import android.database.Cursor;
 import android.database.sqlite.SQLiteDatabase;
+import android.os.Handler;
+import android.os.Looper;
 import android.test.AndroidTestCase;
 import android.util.AttributeSet;
 import android.util.Log;
@@ -335,6 +337,21 @@
         assertValidFile(longFile);
     }
 
+    public void testMainLooper() throws Exception {
+        final Thread mainThread = Looper.getMainLooper().getThread();
+        final Handler handler = new Handler(mContext.getMainLooper());
+        handler.post(() -> {
+            assertEquals(mainThread, Thread.currentThread());
+        });
+    }
+
+    public void testMainExecutor() throws Exception {
+        final Thread mainThread = Looper.getMainLooper().getThread();
+        mContext.getMainExecutor().execute(() -> {
+            assertEquals(mainThread, Thread.currentThread());
+        });
+    }
+
     private void assertValidFile(File file) throws Exception {
         Log.d(TAG, "Checking " + file);
         assertTrue("Failed to create " + file, file.createNewFile());
diff --git a/tests/tests/content/src/android/content/cts/ContextWrapperTest.java b/tests/tests/content/src/android/content/cts/ContextWrapperTest.java
index 5f48260..871107c 100644
--- a/tests/tests/content/src/android/content/cts/ContextWrapperTest.java
+++ b/tests/tests/content/src/android/content/cts/ContextWrapperTest.java
@@ -347,11 +347,11 @@
 
         // Test openOrCreateDatabase with null and actual factory
         mDatabase = mContextWrapper.openOrCreateDatabase(DATABASE_NAME1,
-                ContextWrapper.MODE_PRIVATE, factory);
+                ContextWrapper.MODE_ENABLE_WRITE_AHEAD_LOGGING, factory);
         assertNotNull(mDatabase);
         mDatabase.close();
         mDatabase = mContextWrapper.openOrCreateDatabase(DATABASE_NAME2,
-                ContextWrapper.MODE_PRIVATE, factory);
+                ContextWrapper.MODE_ENABLE_WRITE_AHEAD_LOGGING, factory);
         assertNotNull(mDatabase);
         mDatabase.close();
 
@@ -360,7 +360,7 @@
 
         // Test databaseList()
         List<String> list = Arrays.asList(mContextWrapper.databaseList());
-        assertEquals(4, list.size()); // Each database has a journal
+        assertEquals(2, list.size());
         assertTrue("1) database list: " + list, list.contains(DATABASE_NAME1));
         assertTrue("2) database list: " + list, list.contains(DATABASE_NAME2));
 
diff --git a/tests/tests/content/src/android/content/cts/SharedPreferencesTest.java b/tests/tests/content/src/android/content/cts/SharedPreferencesTest.java
index b4fcb31..7c9e538 100644
--- a/tests/tests/content/src/android/content/cts/SharedPreferencesTest.java
+++ b/tests/tests/content/src/android/content/cts/SharedPreferencesTest.java
@@ -22,6 +22,8 @@
 import android.content.pm.ApplicationInfo;
 import android.content.pm.PackageManager;
 import android.os.StrictMode;
+import android.os.StrictMode.ViolationInfo;
+import android.os.StrictMode.ViolationLogger;
 import android.preference.PreferenceManager;
 import android.test.AndroidTestCase;
 import android.util.Log;
@@ -32,6 +34,8 @@
 import java.util.HashMap;
 import java.util.Map;
 import java.util.Random;
+import java.util.concurrent.CountDownLatch;
+import java.util.concurrent.TimeUnit;
 
 /**
  * Test {@link SharedPreferences}.
@@ -322,28 +326,27 @@
         }
     }
 
-    public void testModeMultiProcess() {
+    public void testModeMultiProcess() throws InterruptedException {
         // Pre-load it.
         mContext.getSharedPreferences("multiprocessTest", 0);
 
         final StrictMode.ThreadPolicy oldPolicy = StrictMode.getThreadPolicy();
         try {
             StrictMode.ThreadPolicy diskReadDeath =
-                    new StrictMode.ThreadPolicy.Builder().detectDiskReads().penaltyDeath().build();
+                    new StrictMode.ThreadPolicy.Builder().detectDiskReads().penaltyLog().build();
             StrictMode.setThreadPolicy(diskReadDeath);
+            final CountDownLatch latch = new CountDownLatch(1);
+            StrictMode.setViolationLogger(info -> latch.countDown());
 
             // This shouldn't hit disk.  (it was already pre-loaded above)
             mContext.getSharedPreferences("multiprocessTest", 0);
+            boolean triggered = latch.await(1, TimeUnit.SECONDS);
+            assertFalse(triggered);
 
-            boolean didRead = false;
             // This SHOULD hit disk.  (multi-process flag is set)
-            try {
-                mContext.getSharedPreferences("multiprocessTest", Context.MODE_MULTI_PROCESS);
-                fail();  // we shouldn't get here.
-            } catch (StrictMode.StrictModeViolation e) {
-                didRead = true;
-            }
-            assertTrue(didRead);
+            mContext.getSharedPreferences("multiprocessTest", Context.MODE_MULTI_PROCESS);
+            triggered = latch.await(1, TimeUnit.SECONDS);
+            assertTrue(triggered);
         } finally {
             StrictMode.setThreadPolicy(oldPolicy);
         }
diff --git a/tests/tests/content/src/android/content/pm/cts/PackageInfoTest.java b/tests/tests/content/src/android/content/pm/cts/PackageInfoTest.java
index 886b4f5..9e4fc5c 100644
--- a/tests/tests/content/src/android/content/pm/cts/PackageInfoTest.java
+++ b/tests/tests/content/src/android/content/pm/cts/PackageInfoTest.java
@@ -63,7 +63,7 @@
 
     private void checkPkgInfoSame(PackageInfo expected, PackageInfo actual) {
         assertEquals(expected.packageName, actual.packageName);
-        assertEquals(expected.versionCode, actual.versionCode);
+        assertEquals(expected.getLongVersionCode(), actual.getLongVersionCode());
         assertEquals(expected.versionName, actual.versionName);
         assertEquals(expected.sharedUserId, actual.sharedUserId);
         assertEquals(expected.sharedUserLabel, actual.sharedUserLabel);
diff --git a/tests/tests/content/src/android/content/res/cts/ResourcesTest.java b/tests/tests/content/src/android/content/res/cts/ResourcesTest.java
index 7f5d6ce..6c02845 100644
--- a/tests/tests/content/src/android/content/res/cts/ResourcesTest.java
+++ b/tests/tests/content/src/android/content/res/cts/ResourcesTest.java
@@ -31,6 +31,7 @@
 import android.content.res.Resources.NotFoundException;
 import android.content.res.TypedArray;
 import android.content.res.XmlResourceParser;
+import android.graphics.Paint;
 import android.graphics.Typeface;
 import android.graphics.drawable.AdaptiveIconDrawable;
 import android.graphics.drawable.ColorDrawable;
@@ -324,6 +325,15 @@
         }
     }
 
+    public void testGetDrawable_StackOverflowErrorDrawable_mipmap() {
+        try {
+            mResources.getDrawable(R.mipmap.icon_recursive);
+            fail("Failed at testGetDrawable_StackOverflowErrorDrawable_mipmap");
+        } catch (NotFoundException e) {
+            //expected
+        }
+    }
+
     public void testGetDrawableForDensity() {
         final Drawable ldpi = mResources.getDrawableForDensity(
                 R.drawable.density_test, DisplayMetrics.DENSITY_LOW);
@@ -788,6 +798,55 @@
         assertNotSame(Typeface.DEFAULT, font);
     }
 
+    private Typeface getLargerTypeface(String text, Typeface typeface1, Typeface typeface2) {
+        Paint p1 = new Paint();
+        p1.setTypeface(typeface1);
+        float width1 = p1.measureText(text);
+        Paint p2 = new Paint();
+        p2.setTypeface(typeface2);
+        float width2 = p2.measureText(text);
+
+        if (width1 > width2) {
+            return typeface1;
+        } else if (width1 < width2) {
+            return typeface2;
+        } else {
+            fail("The widths of the text should not be the same");
+            return null;
+        }
+    }
+
+    public void testGetFont_xmlFileWithTtc() {
+        // Here we test that building typefaces by indexing in font collections works correctly.
+        // We want to ensure that the built typefaces correspond to the fonts with the right index.
+        // sample_font_collection.ttc contains two fonts (with indices 0 and 1). The first one has
+        // glyph "a" of 3em width, and all the other glyphs 1em. The second one has glyph "b" of
+        // 3em width, and all the other glyphs 1em. Hence, we can compare the width of these
+        // glyphs to assert that ttc indexing works.
+        Typeface normalFont = mResources.getFont(R.font.sample_ttc_family);
+        assertNotNull(normalFont);
+        Typeface italicFont = Typeface.create(normalFont, Typeface.ITALIC);
+        assertNotNull(italicFont);
+
+        assertEquals(getLargerTypeface("a", normalFont, italicFont), normalFont);
+        assertEquals(getLargerTypeface("b", normalFont, italicFont), italicFont);
+    }
+
+    public void testGetFont_xmlFileWithVariationSettings() {
+        // Here we test that specifying variation settings for fonts in XMLs works.
+        // We build typefaces from two families containing one font each, using the same font
+        // resource, but having different values for the 'wdth' tag. Then we measure the painted
+        // text to ensure that the tag affects the text width. The font resource used supports
+        // the 'wdth' axis for the dash (-) character.
+        Typeface typeface1 = mResources.getFont(R.font.sample_variation_settings_family1);
+        assertNotNull(typeface1);
+        Typeface typeface2 = mResources.getFont(R.font.sample_variation_settings_family2);
+        assertNotNull(typeface2);
+
+        assertNotSame(typeface1, typeface2);
+        assertEquals(getLargerTypeface("-", typeface1, typeface2), typeface2);
+    }
+
     public void testGetFont_invalidXmlFile() {
         try {
             assertNull(mResources.getFont(R.font.invalid_xmlfamily));
diff --git a/tests/tests/database/Android.mk b/tests/tests/database/Android.mk
index 0e24c15..d8c5987 100644
--- a/tests/tests/database/Android.mk
+++ b/tests/tests/database/Android.mk
@@ -26,10 +26,9 @@
     ctstestrunner \
     ctstestrunner \
     ub-uiautomator \
-    junit \
-    legacy-android-test
+    junit
 
-LOCAL_JAVA_LIBRARIES := android.test.runner
+LOCAL_JAVA_LIBRARIES := android.test.runner android.test.base.stubs
 
 LOCAL_SRC_FILES := $(call all-java-files-under, src)
 
diff --git a/tests/tests/database/AndroidTest.xml b/tests/tests/database/AndroidTest.xml
index 62338f1..bab4a44 100644
--- a/tests/tests/database/AndroidTest.xml
+++ b/tests/tests/database/AndroidTest.xml
@@ -14,6 +14,7 @@
      limitations under the License.
 -->
 <configuration description="Config for CTS Database test cases">
+    <option name="test-suite-tag" value="cts" />
     <option name="config-descriptor:metadata" key="component" value="framework" />
     <target_preparer class="com.android.tradefed.targetprep.suite.SuiteApkInstaller">
         <option name="cleanup-apks" value="true" />
diff --git a/tests/tests/database/src/android/database/cts/CursorWindowTest.java b/tests/tests/database/src/android/database/cts/CursorWindowTest.java
index 65ce3fd..b0dc266 100644
--- a/tests/tests/database/src/android/database/cts/CursorWindowTest.java
+++ b/tests/tests/database/src/android/database/cts/CursorWindowTest.java
@@ -21,16 +21,31 @@
 import android.database.MatrixCursor;
 import android.database.sqlite.SQLiteException;
 import android.os.Parcel;
-import android.test.AndroidTestCase;
+import android.support.test.runner.AndroidJUnit4;
+import android.test.suitebuilder.annotation.SmallTest;
+
+import org.junit.Assert;
+import org.junit.Test;
+import org.junit.runner.RunWith;
 
 import java.util.ArrayList;
 import java.util.Arrays;
 import java.util.Random;
 
-public class CursorWindowTest extends AndroidTestCase {
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertNull;
+import static org.junit.Assert.assertTrue;
+import static org.junit.Assert.fail;
+
+@RunWith(AndroidJUnit4.class)
+@SmallTest
+public class CursorWindowTest {
 
     private static final String TEST_STRING = "Test String";
 
+    @Test
     public void testWriteCursorToWindow() throws Exception {
         // create cursor
         String[] colNames = new String[]{"_id", "name", "number", "profit"};
@@ -75,6 +90,7 @@
         assertEquals(0, window.getNumRows());
     }
 
+    @Test
     public void testNull() {
         CursorWindow window = getOneByOneWindow();
 
@@ -82,10 +98,11 @@
         assertTrue(window.putNull(0, 0));
         assertNull(window.getString(0, 0));
         assertEquals(0, window.getLong(0, 0));
-        assertEquals(0.0, window.getDouble(0, 0));
+        assertEquals(0.0, window.getDouble(0, 0), 0.0);
         assertNull(window.getBlob(0, 0));
     }
 
+    @Test
     public void testEmptyString() {
         CursorWindow window = getOneByOneWindow();
 
@@ -93,9 +110,10 @@
         assertTrue(window.putString("", 0, 0));
         assertEquals("", window.getString(0, 0));
         assertEquals(0, window.getLong(0, 0));
-        assertEquals(0.0, window.getDouble(0, 0));
+        assertEquals(0.0, window.getDouble(0, 0), 0.0);
     }
 
+    @Test
     public void testConstructors() {
         int TEST_NUMBER = 5;
         CursorWindow cursorWindow;
@@ -123,6 +141,7 @@
         assertEquals(TEST_STRING, cursorWindow.getString(TEST_NUMBER, 0));
     }
 
+    @Test
     public void testDataStructureOperations() {
         CursorWindow cursorWindow = new CursorWindow(true);
 
@@ -175,6 +194,7 @@
         }
     }
 
+    @Test
     public void testAccessDataValues() {
         final long NUMBER_LONG_INTEGER = (long) 0xaabbccddffL;
         final long NUMBER_INTEGER = (int) NUMBER_LONG_INTEGER;
@@ -214,8 +234,8 @@
         assertEquals(0, cursorWindow.getLong(0, 0));
         assertEquals(0, cursorWindow.getInt(0, 0));
         assertEquals(0, cursorWindow.getShort(0, 0));
-        assertEquals(0.0, cursorWindow.getDouble(0, 0));
-        assertEquals(0.0f, cursorWindow.getFloat(0, 0), 0.00000001f);
+        assertEquals(0.0, cursorWindow.getDouble(0, 0), 0.0);
+        assertEquals(0.0f, cursorWindow.getFloat(0, 0), 0.0);
         assertFalse(cursorWindow.isNull(0, 0));
         assertFalse(cursorWindow.isBlob(0, 0));
 
@@ -226,8 +246,8 @@
         assertEquals(0, cursorWindow.getLong(0, 1));
         assertEquals(0, cursorWindow.getInt(0, 1));
         assertEquals(0, cursorWindow.getShort(0, 1));
-        assertEquals(0.0, cursorWindow.getDouble(0, 1));
-        assertEquals(0.0f, cursorWindow.getFloat(0, 1), 0.00000001f);
+        assertEquals(0.0, cursorWindow.getDouble(0, 1), 0.0);
+        assertEquals(0.0f, cursorWindow.getFloat(0, 1), 0.0);
         assertNull(cursorWindow.getBlob(0, 1));
         assertTrue(cursorWindow.isNull(0, 1));
         // If the field is null, isBlob will return true.
@@ -239,8 +259,8 @@
         assertEquals(NUMBER_INTEGER, cursorWindow.getInt(0, 2));
         assertEquals(Long.toString(NUMBER_LONG_INTEGER), cursorWindow.getString(0, 2));
         assertEquals(NUMBER_SHORT, cursorWindow.getShort(0, 2));
-        assertEquals(NUMBER_FLOAT_SCIENCE, cursorWindow.getFloat(0, 2), 0.00000001f);
-        assertEquals(NUMBER_DOUBLE_SCIENCE, cursorWindow.getDouble(0, 2), 0.00000001);
+        assertEquals(NUMBER_FLOAT_SCIENCE, cursorWindow.getFloat(0, 2), 0.0);
+        assertEquals(NUMBER_DOUBLE_SCIENCE, cursorWindow.getDouble(0, 2), 0.0);
         try {
             cursorWindow.getBlob(0, 2);
             fail("Can't get Blob from a Integer value.");
@@ -259,8 +279,8 @@
         assertEquals(NUMBER_FLOAT_SCIENCE_STRING2.substring(0, 6), cursorWindow.getString(0, 3)
                 .substring(0, 6));
         assertEquals(NUMBER_SHORT, cursorWindow.getShort(0, 3));
-        assertEquals(NUMBER_FLOAT_SCIENCE, cursorWindow.getFloat(0, 3), 0.00000001f);
-        assertEquals(NUMBER_DOUBLE_SCIENCE, cursorWindow.getDouble(0, 3), 0.00000001);
+        assertEquals(NUMBER_FLOAT_SCIENCE, cursorWindow.getFloat(0, 3), 0.0);
+        assertEquals(NUMBER_DOUBLE_SCIENCE, cursorWindow.getDouble(0, 3), 0.0);
         try {
             cursorWindow.getBlob(0, 3);
             fail("Can't get Blob from a Double value.");
@@ -279,6 +299,7 @@
         assertTrue(cursorWindow.isBlob(0, 4));
     }
 
+    @Test
     public void testCopyStringToBuffer() {
         int DEFAULT_ARRAY_LENGTH = 64;
         String baseString = "0123456789";
@@ -314,6 +335,7 @@
         assertEquals(expectedString.length(), charArrayBuffer.data.length);
     }
 
+    @Test
     public void testAccessStartPosition() {
         final int TEST_POSITION_1 = 0;
         final int TEST_POSITION_2 = 3;
@@ -343,6 +365,7 @@
         }
     }
 
+    @Test
     public void testClearAndOnAllReferencesReleased() {
         MockCursorWindow cursorWindow = new MockCursorWindow(true);
 
@@ -369,11 +392,43 @@
         assertTrue(cursorWindow.hasReleasedAllReferences());
     }
 
+    @Test
     public void testDescribeContents() {
         CursorWindow cursorWindow = new CursorWindow(true);
         assertEquals(0, cursorWindow.describeContents());
     }
 
+    @Test
+    public void testDefaultCursorWindowSize() {
+        CursorWindow cursorWindow = new CursorWindow("test");
+        cursorWindow.setNumColumns(1);
+        byte[] bytes = new byte[1024];
+        Arrays.fill(bytes, (byte) 1);
+        // Ensure that the default is not too small and it's possible to fill CursorWindow
+        // with ~2Mb of data
+        int testRowCount = 2016;
+        for (int i = 0; i < testRowCount; i++) {
+            assertTrue(cursorWindow.allocRow());
+            assertTrue("Allocation failed for row " + i, cursorWindow.putBlob(bytes, i, 0));
+        }
+        assertTrue(cursorWindow.allocRow());
+        assertFalse("Allocation should fail for row " + testRowCount,
+                cursorWindow.putBlob(bytes, testRowCount, 0));
+    }
+
+    @Test
+    public void testCustomSize() {
+        // Allocate CursorWindow with max size 10KB and test that restriction is enforced
+        CursorWindow cursorWindow = new CursorWindow("test", 10000);
+        cursorWindow.setNumColumns(1);
+        byte[] bytes = new byte[8000];
+        Arrays.fill(bytes, (byte) 1);
+        assertTrue(cursorWindow.allocRow());
+        assertTrue("Allocation of 1 row should succeed", cursorWindow.putBlob(bytes, 0, 0));
+        assertTrue(cursorWindow.allocRow());
+        assertFalse("Allocation of 2nd row should fail", cursorWindow.putBlob(bytes, 1, 0));
+    }
+
     private class MockCursorWindow extends CursorWindow {
         private boolean mHasReleasedAllReferences = false;
 
diff --git a/tests/tests/database/src/android/database/sqlite/cts/SQLiteCursorTest.java b/tests/tests/database/src/android/database/sqlite/cts/SQLiteCursorTest.java
index a506de5..a11abf6 100644
--- a/tests/tests/database/src/android/database/sqlite/cts/SQLiteCursorTest.java
+++ b/tests/tests/database/src/android/database/sqlite/cts/SQLiteCursorTest.java
@@ -19,10 +19,13 @@
 
 import android.content.Context;
 import android.database.AbstractCursor;
+import android.database.AbstractWindowedCursor;
 import android.database.Cursor;
 import android.database.CursorWindow;
 import android.database.DataSetObserver;
+import android.database.SQLException;
 import android.database.StaleDataException;
+import android.database.sqlite.SQLiteBlobTooBigException;
 import android.database.sqlite.SQLiteCursor;
 import android.database.sqlite.SQLiteDatabase;
 import android.database.sqlite.SQLiteDirectCursorDriver;
@@ -210,6 +213,46 @@
         assertEquals(TEST_COUNT - TEST_ARG2, cursor.getCount());
     }
 
+    public void testRowTooBig() {
+        mDatabase.execSQL("CREATE TABLE Tst (Txt BLOB NOT NULL);");
+        byte[] testArr = new byte[10000];
+        Arrays.fill(testArr, (byte) 1);
+        for (int i = 0; i < 10; i++) {
+            mDatabase.execSQL("INSERT INTO Tst VALUES (?)", new Object[]{testArr});
+        }
+
+        // Now reduce window size, so that no rows can fit
+        Cursor cursor = mDatabase.rawQuery("SELECT * FROM TST", null);
+        CursorWindow cw = new CursorWindow("test", 5000);
+        AbstractWindowedCursor ac = (AbstractWindowedCursor) cursor;
+        ac.setWindow(cw);
+
+        try {
+            ac.moveToNext();
+            fail("Exception is expected when row exceeds CursorWindow size");
+        } catch (SQLiteBlobTooBigException expected) {
+        }
+    }
+
+    public void testFillWindowForwardOnly() {
+        mDatabase.execSQL("CREATE TABLE Tst (Num Integer NOT NULL);");
+        mDatabase.beginTransaction();
+        for (int i = 0; i < 100; i++) {
+            mDatabase.execSQL("INSERT INTO Tst VALUES (?)", new Object[]{i});
+        }
+        mDatabase.setTransactionSuccessful();
+        mDatabase.endTransaction();
+        Cursor cursor = mDatabase.rawQuery("SELECT * FROM TST", null);
+        SQLiteCursor ac = (SQLiteCursor) cursor;
+        CursorWindow window = new CursorWindow("test", 1000);
+        ac.setFillWindowForwardOnly(true);
+        ac.setWindow(window);
+        assertTrue(ac.moveToFirst());
+        // Now skip 70 rows and check that the window start position corresponds to row 70
+        ac.move(70);
+        assertEquals(70, window.getStartPosition());
+    }
+
     public void testOnMove() {
         // Do not test this API. It is callback which:
         // 1. The callback mechanism has been tested in super class
diff --git a/tests/tests/database/src/android/database/sqlite/cts/SQLiteDatabaseTest.java b/tests/tests/database/src/android/database/sqlite/cts/SQLiteDatabaseTest.java
index 26f8794..56a815b 100644
--- a/tests/tests/database/src/android/database/sqlite/cts/SQLiteDatabaseTest.java
+++ b/tests/tests/database/src/android/database/sqlite/cts/SQLiteDatabaseTest.java
@@ -33,6 +33,7 @@
 import android.database.sqlite.SQLiteDatabase;
 import android.database.sqlite.SQLiteDatabase.CursorFactory;
 import android.database.sqlite.SQLiteDebug;
+import android.database.sqlite.SQLiteGlobal;
 import android.database.sqlite.SQLiteQuery;
 import android.database.sqlite.SQLiteStatement;
 import android.database.sqlite.SQLiteTransactionListener;
@@ -1390,6 +1391,16 @@
         assertFalse(mDatabase.isWriteAheadLoggingEnabled());
     }
 
+    public void testDisableWriteAheadLogging() {
+        assertFalse(mDatabase.isWriteAheadLoggingEnabled());
+        mDatabase.disableWriteAheadLogging();
+        assertFalse(mDatabase.isWriteAheadLoggingEnabled());
+        // Verify that default journal mode is set if WAL is disabled
+        String defaultJournalMode = SQLiteGlobal.getDefaultJournalMode();
+        assertTrue(DatabaseUtils.stringForQuery(mDatabase, "PRAGMA journal_mode", null)
+                .equalsIgnoreCase(defaultJournalMode));
+    }
+
     public void testEnableThenDisableWriteAheadLoggingUsingOpenFlag() {
         closeAndDeleteDatabase();
         mDatabase = SQLiteDatabase.openDatabase(mDatabaseFile.getPath(), null,
@@ -1584,4 +1595,64 @@
         } catch (IllegalArgumentException expected) {
         }
     }
+
+    public void testDefaultJournalModeNotWAL() {
+        String defaultJournalMode = SQLiteGlobal.getDefaultJournalMode();
+        assertFalse("Default journal mode should not be WAL",
+                DatabaseUtils.stringForQuery(mDatabase, "PRAGMA journal_mode", null)
+                        .equalsIgnoreCase(defaultJournalMode));
+    }
+
+    public void testCompatibilityWALIsDefaultWhenSupported() {
+        if (!SQLiteGlobal.isCompatibilityWalSupported()) {
+            Log.i(TAG, "Compatibility WAL not supported. "
+                    + "Skipping testCompatibilityWALIsDefaultWhenSupported");
+            return;
+        }
+
+        assertTrue("Journal mode should be WAL if compatibility WAL is supported",
+                DatabaseUtils.stringForQuery(mDatabase, "PRAGMA journal_mode", null)
+                        .equalsIgnoreCase("WAL"));
+    }
+
+    /**
+     * Test that app can specify journal mode/synchronous mode
+     */
+    public void testJournalModeSynchronousModeOverride() {
+        mDatabase.close();
+        SQLiteDatabase.OpenParams params = new SQLiteDatabase.OpenParams.Builder()
+                .setJournalMode("DELETE").setSynchronousMode("OFF").build();
+        mDatabase = SQLiteDatabase.openDatabase(mDatabaseFile, params);
+
+        String journalMode = DatabaseUtils
+                .stringForQuery(mDatabase, "PRAGMA journal_mode", null);
+
+        assertEquals("DELETE", journalMode.toUpperCase());
+        String syncMode = DatabaseUtils
+                .stringForQuery(mDatabase, "PRAGMA synchronous", null);
+
+        assertEquals("0", syncMode);
+    }
+
+    /**
+     * Test that enableWriteAheadLogging is not affected by app's journal mode/synchronous mode
+     * settings
+     */
+    public void testEnableWalOverridesJournalModeSynchronousMode() {
+        mDatabase.close();
+        SQLiteDatabase.OpenParams params = new SQLiteDatabase.OpenParams.Builder()
+                .setJournalMode("DELETE").setSynchronousMode("OFF").build();
+        mDatabase = SQLiteDatabase.openDatabase(mDatabaseFile, params);
+        mDatabase.enableWriteAheadLogging();
+
+        String journalMode = DatabaseUtils
+                .stringForQuery(mDatabase, "PRAGMA journal_mode", null);
+
+        assertEquals("WAL", journalMode.toUpperCase());
+        String syncMode = DatabaseUtils
+                .stringForQuery(mDatabase, "PRAGMA synchronous", null);
+
+        assertEquals("2" /* FULL */, syncMode);
+    }
+
 }
diff --git a/tests/tests/database/src/android/database/sqlite/cts/SQLiteOpenHelperTest.java b/tests/tests/database/src/android/database/sqlite/cts/SQLiteOpenHelperTest.java
index 853f24b..20c9a0d 100644
--- a/tests/tests/database/src/android/database/sqlite/cts/SQLiteOpenHelperTest.java
+++ b/tests/tests/database/src/android/database/sqlite/cts/SQLiteOpenHelperTest.java
@@ -18,6 +18,7 @@
 
 import android.app.ActivityManager;
 import android.content.Context;
+import android.database.Cursor;
 import android.database.sqlite.SQLiteCursor;
 import android.database.sqlite.SQLiteCursorDriver;
 import android.database.sqlite.SQLiteDatabase;
@@ -31,6 +32,9 @@
 import android.test.AndroidTestCase;
 import android.util.Log;
 
+import java.io.File;
+import java.util.Arrays;
+
 import static android.database.sqlite.cts.DatabaseTestUtils.getDbInfoOutput;
 import static android.database.sqlite.cts.DatabaseTestUtils.waitForConnectionToClose;
 
@@ -55,6 +59,7 @@
     @Override
     protected void tearDown() throws Exception {
         mOpenHelper.close();
+        SQLiteDatabase.deleteDatabase(mContext.getDatabasePath(TEST_DATABASE_NAME));
         super.tearDown();
     }
 
@@ -204,17 +209,73 @@
                 output.contains("Connection #0:"));
     }
 
+    public void testOpenParamsConstructor() {
+        SQLiteDatabase.OpenParams params = new SQLiteDatabase.OpenParams.Builder()
+                .build();
+
+        MockOpenHelper helper = new MockOpenHelper(mContext, null, 1, params);
+        SQLiteDatabase database = helper.getWritableDatabase();
+        assertNotNull(database);
+        helper.close();
+    }
+
+    /**
+     * Tests a scenario in WAL mode with multiple connections, when a connection should see schema
+     * changes made from another connection.
+     */
+    public void testWalSchemaChangeVisibilityOnUpgrade() {
+        File dbPath = mContext.getDatabasePath(TEST_DATABASE_NAME);
+        SQLiteDatabase.deleteDatabase(dbPath);
+        SQLiteDatabase db = SQLiteDatabase.openOrCreateDatabase(dbPath, null);
+        db.execSQL("CREATE TABLE test_table (_id INTEGER PRIMARY KEY AUTOINCREMENT)");
+        db.setVersion(1);
+        db.close();
+        mOpenHelper = new MockOpenHelper(mContext, TEST_DATABASE_NAME, null, 2) {
+            {
+                setWriteAheadLoggingEnabled(true);
+            }
+
+            @Override
+            public void onCreate(SQLiteDatabase db) {
+            }
+
+            @Override
+            public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {
+                if (oldVersion == 1) {
+                    db.execSQL("ALTER TABLE test_table ADD column2 INT DEFAULT 1234");
+                    db.execSQL("CREATE TABLE test_table2 (_id INTEGER PRIMARY KEY AUTOINCREMENT)");
+                }
+            }
+        };
+        // Check if can see the new column
+        try (Cursor cursor = mOpenHelper.getReadableDatabase()
+                .rawQuery("select * from test_table", null)) {
+            assertEquals("Newly added column should be visible. Returned columns: " + Arrays
+                    .toString(cursor.getColumnNames()), 2, cursor.getColumnCount());
+        }
+        // Check if can see the new table
+        try (Cursor cursor = mOpenHelper.getReadableDatabase()
+                .rawQuery("select * from test_table2", null)) {
+            assertEquals(1, cursor.getColumnCount());
+        }
+    }
+
     private MockOpenHelper getOpenHelper() {
         return new MockOpenHelper(mContext, TEST_DATABASE_NAME, mFactory, TEST_VERSION);
     }
 
-    private class MockOpenHelper extends SQLiteOpenHelper {
+    private static class MockOpenHelper extends SQLiteOpenHelper {
         private boolean mHasCalledOnOpen = false;
 
-        public MockOpenHelper(Context context, String name, CursorFactory factory, int version) {
+        MockOpenHelper(Context context, String name, CursorFactory factory, int version) {
             super(context, name, factory, version);
         }
 
+        MockOpenHelper(Context context, String name, int version,
+                SQLiteDatabase.OpenParams openParams) {
+            super(context, name, version, openParams);
+        }
+
         @Override
         public void onCreate(SQLiteDatabase db) {
         }
@@ -237,8 +298,8 @@
         }
     }
 
-    private class MockCursor extends SQLiteCursor {
-        public MockCursor(SQLiteDatabase db, SQLiteCursorDriver driver, String editTable,
+    private static class MockCursor extends SQLiteCursor {
+        MockCursor(SQLiteDatabase db, SQLiteCursorDriver driver, String editTable,
                 SQLiteQuery query) {
             super(db, driver, editTable, query);
         }
diff --git a/tests/tests/debug/Android.mk b/tests/tests/debug/Android.mk
index f1b9b27..c715d07 100644
--- a/tests/tests/debug/Android.mk
+++ b/tests/tests/debug/Android.mk
@@ -30,7 +30,7 @@
 # Tag this module as a cts test artifact
 LOCAL_COMPATIBILITY_SUITE := cts vts general-tests
 
-LOCAL_STATIC_JAVA_LIBRARIES := ctstestrunner
+LOCAL_STATIC_JAVA_LIBRARIES := ctstestrunner nativetesthelper
 
 LOCAL_JNI_SHARED_LIBRARIES := libdebugtest
 
diff --git a/tests/tests/debug/AndroidManifest.xml b/tests/tests/debug/AndroidManifest.xml
index 4b3254a..091e778 100644
--- a/tests/tests/debug/AndroidManifest.xml
+++ b/tests/tests/debug/AndroidManifest.xml
@@ -26,8 +26,6 @@
     <instrumentation android:name="android.support.test.runner.AndroidJUnitRunner"
                      android:targetPackage="android.debug.cts"
                      android:label="CTS tests of native debugging API">
-        <meta-data android:name="listener"
-            android:value="com.android.cts.runner.CtsTestRunListener" />
     </instrumentation>
 
 </manifest>
diff --git a/tests/tests/debug/libdebugtest/Android.mk b/tests/tests/debug/libdebugtest/Android.mk
index 80eb256..d3db70f 100644
--- a/tests/tests/debug/libdebugtest/Android.mk
+++ b/tests/tests/debug/libdebugtest/Android.mk
@@ -31,6 +31,7 @@
 	android_debug_cts.cpp
 
 LOCAL_SHARED_LIBRARIES := liblog
+LOCAL_WHOLE_STATIC_LIBRARIES := libnativetesthelper_jni
 
 LOCAL_SDK_VERSION := 23
 LOCAL_NDK_STL_VARIANT := c++_static
diff --git a/tests/tests/debug/libdebugtest/android_debug_cts.cpp b/tests/tests/debug/libdebugtest/android_debug_cts.cpp
index fb87a28..3aa4318 100644
--- a/tests/tests/debug/libdebugtest/android_debug_cts.cpp
+++ b/tests/tests/debug/libdebugtest/android_debug_cts.cpp
@@ -15,6 +15,7 @@
  */
 
 #include <jni.h>
+#include <gtest/gtest.h>
 #include <android/log.h>
 
 #include <errno.h>
@@ -29,6 +30,7 @@
 
 #define LOG_TAG "Cts-DebugTest"
 
+// Used by child processes only
 #define assert_or_exit(x)                                                                         \
     do {                                                                                          \
         if(x) break;                                                                              \
@@ -36,29 +38,21 @@
                 errno, strerror(errno));                                                          \
         _exit(1);                                                                                 \
     } while (0)
-#define assert_or_return(x)                                                                       \
-    do {                                                                                          \
-        if(x) break;                                                                              \
-        __android_log_print(ANDROID_LOG_ERROR, LOG_TAG, "Assertion " #x " failed. errno(%d): %s", \
-                errno, strerror(errno));                                                          \
-        return false;                                                                             \
-    } while (0)
 
-static bool parent(pid_t child) {
+static void parent(pid_t child) {
     int status;
     int wpid = waitpid(child, &status, 0);
-    assert_or_return(wpid == child);
-    assert_or_return(WIFEXITED(status));
-    assert_or_return(WEXITSTATUS(status ) == 0);
-    return true;
+    ASSERT_EQ(child, wpid);
+    ASSERT_TRUE(WIFEXITED(status));
+    ASSERT_EQ(0, WEXITSTATUS(status));
 }
 
-static bool run_test(const std::function<void(pid_t)> &test) {
+static void run_test(const std::function<void(pid_t)> &test) {
     pid_t pid = fork();
-    assert_or_return(pid >= 0);
-    if (pid != 0)
-        return parent(pid);
-    else {
+    ASSERT_NE(-1, pid) << "fork() failed with " << strerror(errno);
+    if (pid != 0) {
+        parent(pid);
+    } else {
         // child
         test(getppid());
         _exit(0);
@@ -75,12 +69,10 @@
     assert_or_exit(ptrace(PTRACE_DETACH, parent, nullptr, nullptr) == 0);
 }
 
-// public static native boolean ptraceAttach();
-extern "C" jboolean Java_android_debug_cts_DebugTest_ptraceAttach(JNIEnv *, jclass) {
-    return run_test(ptraceAttach);
+TEST(DebugTest, ptraceAttach) {
+    run_test(ptraceAttach);
 }
 
-
 static void processVmReadv(pid_t parent, const std::vector<long *> &addresses) {
     long destination;
     iovec local = { &destination, sizeof destination };
@@ -99,11 +91,11 @@
 
 static long global_variable = 0x47474747;
 // public static native boolean processVmReadv();
-extern "C" jboolean Java_android_debug_cts_DebugTest_processVmReadv(JNIEnv *, jclass) {
+TEST(DebugTest, processVmReadv) {
     long stack_variable = 0x42424242;
     // This runs the test with a selection of different kinds of addresses and
     // makes sure the child process (simulating a debugger) can read them.
-    return run_test([&](pid_t parent) {
+    run_test([&](pid_t parent) {
         processVmReadv(parent, std::vector<long *>{
                                    &global_variable, &stack_variable,
                                    reinterpret_cast<long *>(&processVmReadv)});
@@ -111,9 +103,9 @@
 }
 
 // public static native boolean processVmReadvNullptr();
-extern "C" jboolean Java_android_debug_cts_DebugTest_processVmReadvNullptr(JNIEnv *, jclass) {
+TEST(DebugTest, processVmReadvNullptr) {
     // Make sure reading unallocated memory behaves reasonably.
-    return run_test([](pid_t parent) {
+    run_test([](pid_t parent) {
         long destination;
         iovec local = {&destination, sizeof destination};
         iovec remote = {nullptr, sizeof(long)};
diff --git a/tests/tests/debug/src/android/debug/cts/DebugTest.java b/tests/tests/debug/src/android/debug/cts/DebugTest.java
index ca55d9c..993f02b 100644
--- a/tests/tests/debug/src/android/debug/cts/DebugTest.java
+++ b/tests/tests/debug/src/android/debug/cts/DebugTest.java
@@ -16,26 +16,10 @@
 
 package android.debug.cts;
 
-import junit.framework.TestCase;
+import org.junit.runner.RunWith;
+import com.android.gtestrunner.GtestRunner;
+import com.android.gtestrunner.TargetLibrary;
 
-public class DebugTest extends TestCase {
-
-    static {
-        System.loadLibrary("debugtest");
-    }
-
-    public static native boolean ptraceAttach();
-    public void test_ptraceAttach() {
-        assertEquals(true, ptraceAttach());
-    }
-
-    public static native boolean processVmReadv();
-    public void test_processVmReadv() {
-        assertEquals(true, processVmReadv());
-    }
-
-    public static native boolean processVmReadvNullptr();
-    public void test_processVmReadvNullptr() {
-        assertEquals(true, processVmReadvNullptr());
-    }
-}
+@RunWith(GtestRunner.class)
+@TargetLibrary("debugtest")
+public class DebugTest {}
diff --git a/tests/tests/display/Android.mk b/tests/tests/display/Android.mk
index 53ae177..a62d0ee 100644
--- a/tests/tests/display/Android.mk
+++ b/tests/tests/display/Android.mk
@@ -27,7 +27,9 @@
 
 LOCAL_SRC_FILES := $(call all-java-files-under, src)
 
-LOCAL_STATIC_JAVA_LIBRARIES := android-support-test legacy-android-test
+LOCAL_STATIC_JAVA_LIBRARIES := android-support-test
+
+LOCAL_JAVA_LIBRARIES := android.test.base.stubs
 
 # Tag this module as a cts test artifact
 LOCAL_COMPATIBILITY_SUITE := cts vts general-tests
diff --git a/tests/tests/display/AndroidTest.xml b/tests/tests/display/AndroidTest.xml
index 8b174f1..3e1b38d 100644
--- a/tests/tests/display/AndroidTest.xml
+++ b/tests/tests/display/AndroidTest.xml
@@ -14,6 +14,7 @@
      limitations under the License.
 -->
 <configuration description="Config for CTS Display test cases">
+    <option name="test-suite-tag" value="cts" />
     <option name="config-descriptor:metadata" key="component" value="framework" />
     <target_preparer class="com.android.tradefed.targetprep.suite.SuiteApkInstaller">
         <option name="cleanup-apks" value="true" />
diff --git a/tests/tests/dpi/Android.mk b/tests/tests/dpi/Android.mk
index 8bb7d64..46deccb 100644
--- a/tests/tests/dpi/Android.mk
+++ b/tests/tests/dpi/Android.mk
@@ -17,7 +17,9 @@
 
 include $(CLEAR_VARS)
 
-LOCAL_STATIC_JAVA_LIBRARIES := ctstestrunner junit legacy-android-test
+LOCAL_STATIC_JAVA_LIBRARIES := ctstestrunner junit
+
+LOCAL_JAVA_LIBRARIES := android.test.runner.stubs android.test.base.stubs
 
 LOCAL_SRC_FILES := $(call all-java-files-under, src)
 
@@ -41,7 +43,9 @@
 # CTS tests, so drop it into a library that other tests can use.
 include $(CLEAR_VARS)
 
-LOCAL_STATIC_JAVA_LIBRARIES := junit legacy-android-test
+LOCAL_STATIC_JAVA_LIBRARIES := junit
+
+LOCAL_JAVA_LIBRARIES := android.test.base
 
 LOCAL_SRC_FILES := src/android/dpi/cts/DefaultManifestAttributesTest.java
 
diff --git a/tests/tests/dpi/AndroidTest.xml b/tests/tests/dpi/AndroidTest.xml
index e12b36d..5e07b7c 100644
--- a/tests/tests/dpi/AndroidTest.xml
+++ b/tests/tests/dpi/AndroidTest.xml
@@ -14,6 +14,7 @@
      limitations under the License.
 -->
 <configuration description="Config for CTS DPI test cases">
+    <option name="test-suite-tag" value="cts" />
     <option name="config-descriptor:metadata" key="component" value="framework" />
     <target_preparer class="com.android.tradefed.targetprep.suite.SuiteApkInstaller">
         <option name="cleanup-apks" value="true" />
diff --git a/tests/tests/dpi2/Android.mk b/tests/tests/dpi2/Android.mk
index dcd2c0f..f366781 100644
--- a/tests/tests/dpi2/Android.mk
+++ b/tests/tests/dpi2/Android.mk
@@ -20,7 +20,7 @@
 # We use the DefaultManifestAttributesTest from the android.cts.dpi package.
 LOCAL_STATIC_JAVA_LIBRARIES := android.cts.dpi ctstestrunner junit
 
-LOCAL_JAVA_LIBRARIES := legacy-android-test
+LOCAL_JAVA_LIBRARIES := android.test.base.stubs
 
 LOCAL_SRC_FILES := $(call all-java-files-under, src)
 
diff --git a/tests/tests/dpi2/AndroidManifest.xml b/tests/tests/dpi2/AndroidManifest.xml
index 689be29..f3d5be0 100644
--- a/tests/tests/dpi2/AndroidManifest.xml
+++ b/tests/tests/dpi2/AndroidManifest.xml
@@ -25,7 +25,7 @@
 
     <!-- target cupcake so we can test the default attributes get set
          properly for the screen size attributes. -->
-    <uses-sdk android:minSdkVersion="3" android:targetSdkVersion="3" />
+    <uses-sdk android:minSdkVersion="3" android:targetSdkVersion="17" />
 
     <instrumentation android:name="android.support.test.runner.AndroidJUnitRunner"
                      android:targetPackage="android.dpi2.cts"
diff --git a/tests/tests/dpi2/AndroidTest.xml b/tests/tests/dpi2/AndroidTest.xml
index 589935d..0c2f6dc 100644
--- a/tests/tests/dpi2/AndroidTest.xml
+++ b/tests/tests/dpi2/AndroidTest.xml
@@ -14,6 +14,7 @@
      limitations under the License.
 -->
 <configuration description="Config for CTS DPI test cases">
+    <option name="test-suite-tag" value="cts" />
     <option name="config-descriptor:metadata" key="component" value="framework" />
     <target_preparer class="com.android.tradefed.targetprep.suite.SuiteApkInstaller">
         <option name="cleanup-apks" value="true" />
diff --git a/tests/tests/dpi2/src/android/dpi2/cts/DefaultManifestAttributesCupcakeTest.java b/tests/tests/dpi2/src/android/dpi2/cts/DefaultManifestAttributesCupcakeTest.java
index 04db412..abb8d66 100644
--- a/tests/tests/dpi2/src/android/dpi2/cts/DefaultManifestAttributesCupcakeTest.java
+++ b/tests/tests/dpi2/src/android/dpi2/cts/DefaultManifestAttributesCupcakeTest.java
@@ -34,6 +34,6 @@
 
     // This is a sanity test to make sure that we're instrumenting the proper package
     public void testPackageHasExpectedSdkVersion() {
-        assertEquals(3, getAppInfo().targetSdkVersion);
+        assertEquals(3, getAppInfo().minSdkVersion);
     }
 }
diff --git a/tests/tests/dreams/Android.mk b/tests/tests/dreams/Android.mk
index 383c9c3..50c4d7f 100644
--- a/tests/tests/dreams/Android.mk
+++ b/tests/tests/dreams/Android.mk
@@ -24,9 +24,9 @@
 # When built, explicitly put it in the data partition.
 LOCAL_MODULE_PATH := $(TARGET_OUT_DATA_APPS)
 
-LOCAL_STATIC_JAVA_LIBRARIES := ctstestrunner junit legacy-android-test
+LOCAL_STATIC_JAVA_LIBRARIES := ctstestrunner junit
 
-LOCAL_JAVA_LIBRARIES :=  android.test.runner
+LOCAL_JAVA_LIBRARIES :=  android.test.runner android.test.base
 
 LOCAL_SRC_FILES := $(call all-java-files-under, src)
 
diff --git a/tests/tests/dreams/AndroidTest.xml b/tests/tests/dreams/AndroidTest.xml
index 7274740..6f77cca 100644
--- a/tests/tests/dreams/AndroidTest.xml
+++ b/tests/tests/dreams/AndroidTest.xml
@@ -14,6 +14,7 @@
      limitations under the License.
 -->
 <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="not-shardable" value="true" />
     <target_preparer class="com.android.tradefed.targetprep.suite.SuiteApkInstaller">
diff --git a/tests/tests/drm/Android.mk b/tests/tests/drm/Android.mk
index 13ac8d6..b1f2aac 100644
--- a/tests/tests/drm/Android.mk
+++ b/tests/tests/drm/Android.mk
@@ -24,7 +24,9 @@
 # and when built explicitly put it in the data partition
 LOCAL_MODULE_PATH := $(TARGET_OUT_DATA_APPS)
 
-LOCAL_STATIC_JAVA_LIBRARIES := ctstestrunner compatibility-device-util legacy-android-test
+LOCAL_STATIC_JAVA_LIBRARIES := ctstestrunner compatibility-device-util
+
+LOCAL_JAVA_LIBRARIES := android.test.base.stubs
 
 LOCAL_SRC_FILES := $(call all-java-files-under, src)
 
diff --git a/tests/tests/drm/AndroidTest.xml b/tests/tests/drm/AndroidTest.xml
index 1a9d0b7..996f3f1 100644
--- a/tests/tests/drm/AndroidTest.xml
+++ b/tests/tests/drm/AndroidTest.xml
@@ -14,6 +14,7 @@
      limitations under the License.
 -->
 <configuration description="Config for CTS DRM test cases">
+    <option name="test-suite-tag" value="cts" />
     <option name="config-descriptor:metadata" key="component" value="media" />
     <target_preparer class="com.android.tradefed.targetprep.suite.SuiteApkInstaller">
         <option name="cleanup-apks" value="true" />
diff --git a/tests/tests/dynamic_linker/Android.mk b/tests/tests/dynamic_linker/Android.mk
index 97518bd..ef122ca 100644
--- a/tests/tests/dynamic_linker/Android.mk
+++ b/tests/tests/dynamic_linker/Android.mk
@@ -18,6 +18,7 @@
 LOCAL_MODULE := libdynamiclinker_native_lib_a
 LOCAL_MODULE_TAGS := optional
 LOCAL_SRC_FILES := native_lib_a.cpp
+LOCAL_CFLAGS := -Wall -Werror
 LOCAL_C_INCLUDES := $(JNI_H_INCLUDE)
 LOCAL_SDK_VERSION := 25
 LOCAL_NDK_STL_VARIANT := c++_static
@@ -28,6 +29,7 @@
 LOCAL_MODULE := libdynamiclinker_native_lib_b
 LOCAL_MODULE_TAGS := optional
 LOCAL_SRC_FILES := native_lib_b.cpp
+LOCAL_CFLAGS := -Wall -Werror
 LOCAL_C_INCLUDES := $(JNI_H_INCLUDE)
 LOCAL_SDK_VERSION := 25
 LOCAL_NDK_STL_VARIANT := c++_static
@@ -37,7 +39,7 @@
 include $(CLEAR_VARS)
 LOCAL_MODULE_TAGS := optional
 LOCAL_MODULE_PATH := $(TARGET_OUT_DATA_APPS)
-LOCAL_STATIC_JAVA_LIBRARIES := ctstestrunner legacy-android-test
+LOCAL_STATIC_JAVA_LIBRARIES := ctstestrunner
 LOCAL_SRC_FILES := $(call all-java-files-under, .)
 LOCAL_MULTILIB := both
 LOCAL_JNI_SHARED_LIBRARIES := libdynamiclinker_native_lib_a libdynamiclinker_native_lib_b
diff --git a/tests/tests/dynamic_linker/AndroidTest.xml b/tests/tests/dynamic_linker/AndroidTest.xml
index 5cc4317..483ae03 100644
--- a/tests/tests/dynamic_linker/AndroidTest.xml
+++ b/tests/tests/dynamic_linker/AndroidTest.xml
@@ -14,6 +14,7 @@
      limitations under the License.
 -->
 <configuration description="Config for CTS dynamic linker test cases">
+    <option name="test-suite-tag" value="cts" />
     <option name="config-descriptor:metadata" key="component" value="bionic" />
     <target_preparer class="com.android.tradefed.targetprep.suite.SuiteApkInstaller">
         <option name="cleanup-apks" value="true" />
diff --git a/tests/tests/effect/AndroidTest.xml b/tests/tests/effect/AndroidTest.xml
index 072028b..ae32cd5 100644
--- a/tests/tests/effect/AndroidTest.xml
+++ b/tests/tests/effect/AndroidTest.xml
@@ -14,6 +14,7 @@
      limitations under the License.
 -->
 <configuration description="Config for CTS Effect test cases">
+    <option name="test-suite-tag" value="cts" />
     <option name="config-descriptor:metadata" key="component" value="media" />
     <target_preparer class="com.android.tradefed.targetprep.suite.SuiteApkInstaller">
         <option name="cleanup-apks" value="true" />
diff --git a/tests/tests/externalservice/Android.mk b/tests/tests/externalservice/Android.mk
index 62afaad..c64c0ca 100644
--- a/tests/tests/externalservice/Android.mk
+++ b/tests/tests/externalservice/Android.mk
@@ -27,6 +27,8 @@
 
 LOCAL_STATIC_JAVA_LIBRARIES := CtsExternalServiceCommon compatibility-device-util ctstestrunner
 
+LOCAL_JAVA_LIBRARIES := android.test.base.stubs
+
 LOCAL_SRC_FILES := $(call all-java-files-under, src)
 
 # Tag this module as a cts test artifact
diff --git a/tests/tests/externalservice/service/Android.mk b/tests/tests/externalservice/service/Android.mk
index 9fc0033..8563cb1 100644
--- a/tests/tests/externalservice/service/Android.mk
+++ b/tests/tests/externalservice/service/Android.mk
@@ -21,7 +21,11 @@
 # and when built explicitly put it in the data partition
 LOCAL_MODULE_PATH := $(TARGET_OUT_DATA_APPS)
 
-LOCAL_STATIC_JAVA_LIBRARIES := CtsExternalServiceCommon ctstestrunner compatibility-device-util
+LOCAL_STATIC_JAVA_LIBRARIES := \
+    CtsExternalServiceCommon \
+    ctstestrunner \
+    compatibility-device-util \
+
 
 LOCAL_SRC_FILES := $(call all-java-files-under, src)
 
diff --git a/tests/tests/externalservice/service/AndroidManifest.xml b/tests/tests/externalservice/service/AndroidManifest.xml
index 06fa80e..74d0825 100644
--- a/tests/tests/externalservice/service/AndroidManifest.xml
+++ b/tests/tests/externalservice/service/AndroidManifest.xml
@@ -19,6 +19,8 @@
     package="android.externalservice.service">
 
     <application android:label="External Service Host">
+        <uses-library android:name="android.test.runner" />
+
         <!-- Service used to start .ExternalService from this package. -->
         <service android:name=".ServiceCreator"
                  android:isolatedProcess="false"
diff --git a/tests/tests/graphics/Android.mk b/tests/tests/graphics/Android.mk
index 7ea5eec..2d05556 100644
--- a/tests/tests/graphics/Android.mk
+++ b/tests/tests/graphics/Android.mk
@@ -20,7 +20,7 @@
 
 LOCAL_MULTILIB := both
 
-LOCAL_JAVA_LIBRARIES := android.test.runner
+LOCAL_JAVA_LIBRARIES := android.test.runner android.test.base.stubs
 
 LOCAL_STATIC_JAVA_LIBRARIES += \
     android-support-test \
@@ -29,8 +29,7 @@
     ctsdeviceutillegacy \
     ctstestrunner \
     android-support-annotations \
-    junit \
-    legacy-android-test
+    junit
 
 LOCAL_JNI_SHARED_LIBRARIES := libctsgraphics_jni
 
diff --git a/tests/tests/graphics/AndroidTest.xml b/tests/tests/graphics/AndroidTest.xml
index baa1e70..1cf31d9 100644
--- a/tests/tests/graphics/AndroidTest.xml
+++ b/tests/tests/graphics/AndroidTest.xml
@@ -14,6 +14,7 @@
      limitations under the License.
 -->
 <configuration description="Config for CTS Graphics test cases">
+    <option name="test-suite-tag" value="cts" />
     <option name="config-descriptor:metadata" key="component" value="uitoolkit" />
     <target_preparer class="com.android.tradefed.targetprep.suite.SuiteApkInstaller">
         <option name="cleanup-apks" value="true" />
diff --git a/tests/tests/graphics/assets/a3em.ttx b/tests/tests/graphics/assets/a3em.ttx
new file mode 100644
index 0000000..d3b9e16
--- /dev/null
+++ b/tests/tests/graphics/assets/a3em.ttx
@@ -0,0 +1,187 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- Copyright (C) 2017 The Android Open Source Project
+
+     Licensed under the Apache License, Version 2.0 (the "License");
+     you may not use this file except in compliance with the License.
+     You may obtain a copy of the License at
+
+          http://www.apache.org/licenses/LICENSE-2.0
+
+     Unless required by applicable law or agreed to in writing, software
+     distributed under the License is distributed on an "AS IS" BASIS
+     WITHOUT 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="Fri Mar 17 07:26:00 2017"/>
+    <macStyle value="00000000 00000000"/>
+    <lowestRecPPEM value="7"/>
+    <fontDirectionHint value="2"/>
+    <glyphDataFormat value="0"/>
+  </head>
+
+  <hhea>
+    <tableVersion value="1.0"/>
+    <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="3em" />
+      <map code="0x0062" name="1em" />
+      <map code="0x0063" name="1em" />
+      <map code="0x0064" name="1em" />
+      <map code="0x0065" name="1em" />
+    </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) 2017 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/b3em.ttx b/tests/tests/graphics/assets/b3em.ttx
new file mode 100644
index 0000000..b5a77ef
--- /dev/null
+++ b/tests/tests/graphics/assets/b3em.ttx
@@ -0,0 +1,187 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- Copyright (C) 2017 The Android Open Source Project
+
+     Licensed under the Apache License, Version 2.0 (the "License");
+     you may not use this file except in compliance with the License.
+     You may obtain a copy of the License at
+
+          http://www.apache.org/licenses/LICENSE-2.0
+
+     Unless required by applicable law or agreed to in writing, software
+     distributed under the License is distributed on an "AS IS" BASIS
+     WITHOUT 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="Fri Mar 17 07:26:00 2017"/>
+    <macStyle value="00000000 00000000"/>
+    <lowestRecPPEM value="7"/>
+    <fontDirectionHint value="2"/>
+    <glyphDataFormat value="0"/>
+  </head>
+
+  <hhea>
+    <tableVersion value="1.0"/>
+    <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" />
+      <map code="0x0062" name="3em" />
+      <map code="0x0063" name="1em" />
+      <map code="0x0064" name="1em" />
+      <map code="0x0065" name="1em" />
+    </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) 2017 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/c3em.ttx b/tests/tests/graphics/assets/c3em.ttx
new file mode 100644
index 0000000..f5ed8e5
--- /dev/null
+++ b/tests/tests/graphics/assets/c3em.ttx
@@ -0,0 +1,187 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- Copyright (C) 2017 The Android Open Source Project
+
+     Licensed under the Apache License, Version 2.0 (the "License");
+     you may not use this file except in compliance with the License.
+     You may obtain a copy of the License at
+
+          http://www.apache.org/licenses/LICENSE-2.0
+
+     Unless required by applicable law or agreed to in writing, software
+     distributed under the License is distributed on an "AS IS" BASIS
+     WITHOUT 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="Fri Mar 17 07:26:00 2017"/>
+    <macStyle value="00000000 00000000"/>
+    <lowestRecPPEM value="7"/>
+    <fontDirectionHint value="2"/>
+    <glyphDataFormat value="0"/>
+  </head>
+
+  <hhea>
+    <tableVersion value="1.0"/>
+    <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" />
+      <map code="0x0062" name="1em" />
+      <map code="0x0063" name="3em" />
+      <map code="0x0064" name="1em" />
+      <map code="0x0065" name="1em" />
+    </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) 2017 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/res/drawable-nodpi/vector_drawable_scale_golden.png b/tests/tests/graphics/res/drawable-nodpi/vector_drawable_scale_golden.png
index 38339b7..32a7516 100644
--- a/tests/tests/graphics/res/drawable-nodpi/vector_drawable_scale_golden.png
+++ b/tests/tests/graphics/res/drawable-nodpi/vector_drawable_scale_golden.png
Binary files differ
diff --git a/tests/tests/graphics/res/drawable-nodpi/vector_icon_arcto_golden.png b/tests/tests/graphics/res/drawable-nodpi/vector_icon_arcto_golden.png
index a22f678..9ba7c00 100644
--- a/tests/tests/graphics/res/drawable-nodpi/vector_icon_arcto_golden.png
+++ b/tests/tests/graphics/res/drawable-nodpi/vector_icon_arcto_golden.png
Binary files differ
diff --git a/tests/tests/graphics/res/drawable-nodpi/vector_icon_clip_path_1_golden.png b/tests/tests/graphics/res/drawable-nodpi/vector_icon_clip_path_1_golden.png
index be487d1..6cafa84 100644
--- a/tests/tests/graphics/res/drawable-nodpi/vector_icon_clip_path_1_golden.png
+++ b/tests/tests/graphics/res/drawable-nodpi/vector_icon_clip_path_1_golden.png
Binary files differ
diff --git a/tests/tests/graphics/res/drawable-nodpi/vector_icon_create_golden.png b/tests/tests/graphics/res/drawable-nodpi/vector_icon_create_golden.png
index 943fce5..b0b6eb5 100644
--- a/tests/tests/graphics/res/drawable-nodpi/vector_icon_create_golden.png
+++ b/tests/tests/graphics/res/drawable-nodpi/vector_icon_create_golden.png
Binary files differ
diff --git a/tests/tests/graphics/res/drawable-nodpi/vector_icon_delete_golden.png b/tests/tests/graphics/res/drawable-nodpi/vector_icon_delete_golden.png
index b46363e..9f60a31 100644
--- a/tests/tests/graphics/res/drawable-nodpi/vector_icon_delete_golden.png
+++ b/tests/tests/graphics/res/drawable-nodpi/vector_icon_delete_golden.png
Binary files differ
diff --git a/tests/tests/graphics/res/drawable-nodpi/vector_icon_filltype_evenodd_golden.png b/tests/tests/graphics/res/drawable-nodpi/vector_icon_filltype_evenodd_golden.png
index 01c445c..3d14630 100644
--- a/tests/tests/graphics/res/drawable-nodpi/vector_icon_filltype_evenodd_golden.png
+++ b/tests/tests/graphics/res/drawable-nodpi/vector_icon_filltype_evenodd_golden.png
Binary files differ
diff --git a/tests/tests/graphics/res/drawable-nodpi/vector_icon_filltype_nonzero_golden.png b/tests/tests/graphics/res/drawable-nodpi/vector_icon_filltype_nonzero_golden.png
index 739eea1..87ffcc1 100644
--- a/tests/tests/graphics/res/drawable-nodpi/vector_icon_filltype_nonzero_golden.png
+++ b/tests/tests/graphics/res/drawable-nodpi/vector_icon_filltype_nonzero_golden.png
Binary files differ
diff --git a/tests/tests/graphics/res/drawable-nodpi/vector_icon_gradient_1_clamp_golden.png b/tests/tests/graphics/res/drawable-nodpi/vector_icon_gradient_1_clamp_golden.png
index 4617f62..859df7e 100644
--- a/tests/tests/graphics/res/drawable-nodpi/vector_icon_gradient_1_clamp_golden.png
+++ b/tests/tests/graphics/res/drawable-nodpi/vector_icon_gradient_1_clamp_golden.png
Binary files differ
diff --git a/tests/tests/graphics/res/drawable-nodpi/vector_icon_gradient_1_golden.png b/tests/tests/graphics/res/drawable-nodpi/vector_icon_gradient_1_golden.png
index 4d6b84d..78ef42b 100644
--- a/tests/tests/graphics/res/drawable-nodpi/vector_icon_gradient_1_golden.png
+++ b/tests/tests/graphics/res/drawable-nodpi/vector_icon_gradient_1_golden.png
Binary files differ
diff --git a/tests/tests/graphics/res/drawable-nodpi/vector_icon_gradient_2_golden.png b/tests/tests/graphics/res/drawable-nodpi/vector_icon_gradient_2_golden.png
index c6540e8..29aee85 100644
--- a/tests/tests/graphics/res/drawable-nodpi/vector_icon_gradient_2_golden.png
+++ b/tests/tests/graphics/res/drawable-nodpi/vector_icon_gradient_2_golden.png
Binary files differ
diff --git a/tests/tests/graphics/res/drawable-nodpi/vector_icon_gradient_2_repeat_golden.png b/tests/tests/graphics/res/drawable-nodpi/vector_icon_gradient_2_repeat_golden.png
index 491e73e..1c13c30 100644
--- a/tests/tests/graphics/res/drawable-nodpi/vector_icon_gradient_2_repeat_golden.png
+++ b/tests/tests/graphics/res/drawable-nodpi/vector_icon_gradient_2_repeat_golden.png
Binary files differ
diff --git a/tests/tests/graphics/res/drawable-nodpi/vector_icon_gradient_3_golden.png b/tests/tests/graphics/res/drawable-nodpi/vector_icon_gradient_3_golden.png
index e335a92..f2cb106 100644
--- a/tests/tests/graphics/res/drawable-nodpi/vector_icon_gradient_3_golden.png
+++ b/tests/tests/graphics/res/drawable-nodpi/vector_icon_gradient_3_golden.png
Binary files differ
diff --git a/tests/tests/graphics/res/drawable-nodpi/vector_icon_gradient_3_mirror_golden.png b/tests/tests/graphics/res/drawable-nodpi/vector_icon_gradient_3_mirror_golden.png
index 57f2ae3..34e0571 100644
--- a/tests/tests/graphics/res/drawable-nodpi/vector_icon_gradient_3_mirror_golden.png
+++ b/tests/tests/graphics/res/drawable-nodpi/vector_icon_gradient_3_mirror_golden.png
Binary files differ
diff --git a/tests/tests/graphics/res/drawable-nodpi/vector_icon_group_clip_golden.png b/tests/tests/graphics/res/drawable-nodpi/vector_icon_group_clip_golden.png
index e74c181..c9702c9 100644
--- a/tests/tests/graphics/res/drawable-nodpi/vector_icon_group_clip_golden.png
+++ b/tests/tests/graphics/res/drawable-nodpi/vector_icon_group_clip_golden.png
Binary files differ
diff --git a/tests/tests/graphics/res/drawable-nodpi/vector_icon_heart_golden.png b/tests/tests/graphics/res/drawable-nodpi/vector_icon_heart_golden.png
index 7450751..b0af7d4 100644
--- a/tests/tests/graphics/res/drawable-nodpi/vector_icon_heart_golden.png
+++ b/tests/tests/graphics/res/drawable-nodpi/vector_icon_heart_golden.png
Binary files differ
diff --git a/tests/tests/graphics/res/drawable-nodpi/vector_icon_random_path_1_golden.png b/tests/tests/graphics/res/drawable-nodpi/vector_icon_random_path_1_golden.png
index d010d79..e779718 100644
--- a/tests/tests/graphics/res/drawable-nodpi/vector_icon_random_path_1_golden.png
+++ b/tests/tests/graphics/res/drawable-nodpi/vector_icon_random_path_1_golden.png
Binary files differ
diff --git a/tests/tests/graphics/res/drawable-nodpi/vector_icon_random_path_2_golden.png b/tests/tests/graphics/res/drawable-nodpi/vector_icon_random_path_2_golden.png
index 5ada060..7ba7191 100644
--- a/tests/tests/graphics/res/drawable-nodpi/vector_icon_random_path_2_golden.png
+++ b/tests/tests/graphics/res/drawable-nodpi/vector_icon_random_path_2_golden.png
Binary files differ
diff --git a/tests/tests/graphics/res/drawable-nodpi/vector_icon_repeated_a_1_golden.png b/tests/tests/graphics/res/drawable-nodpi/vector_icon_repeated_a_1_golden.png
index 5af7090..37a0d21 100644
--- a/tests/tests/graphics/res/drawable-nodpi/vector_icon_repeated_a_1_golden.png
+++ b/tests/tests/graphics/res/drawable-nodpi/vector_icon_repeated_a_1_golden.png
Binary files differ
diff --git a/tests/tests/graphics/res/drawable-nodpi/vector_icon_repeated_a_2_golden.png b/tests/tests/graphics/res/drawable-nodpi/vector_icon_repeated_a_2_golden.png
index 24b9662..b011284 100644
--- a/tests/tests/graphics/res/drawable-nodpi/vector_icon_repeated_a_2_golden.png
+++ b/tests/tests/graphics/res/drawable-nodpi/vector_icon_repeated_a_2_golden.png
Binary files differ
diff --git a/tests/tests/graphics/res/drawable-nodpi/vector_icon_repeated_cq_golden.png b/tests/tests/graphics/res/drawable-nodpi/vector_icon_repeated_cq_golden.png
index c9677a6..9e92c8a 100644
--- a/tests/tests/graphics/res/drawable-nodpi/vector_icon_repeated_cq_golden.png
+++ b/tests/tests/graphics/res/drawable-nodpi/vector_icon_repeated_cq_golden.png
Binary files differ
diff --git a/tests/tests/graphics/res/drawable-nodpi/vector_icon_repeated_st_golden.png b/tests/tests/graphics/res/drawable-nodpi/vector_icon_repeated_st_golden.png
index 8882a7a..0262e57 100644
--- a/tests/tests/graphics/res/drawable-nodpi/vector_icon_repeated_st_golden.png
+++ b/tests/tests/graphics/res/drawable-nodpi/vector_icon_repeated_st_golden.png
Binary files differ
diff --git a/tests/tests/graphics/res/drawable-nodpi/vector_icon_scale_1_golden.png b/tests/tests/graphics/res/drawable-nodpi/vector_icon_scale_1_golden.png
index 143ce3e..1520397 100644
--- a/tests/tests/graphics/res/drawable-nodpi/vector_icon_scale_1_golden.png
+++ b/tests/tests/graphics/res/drawable-nodpi/vector_icon_scale_1_golden.png
Binary files differ
diff --git a/tests/tests/graphics/res/drawable-nodpi/vector_icon_scale_2_golden.png b/tests/tests/graphics/res/drawable-nodpi/vector_icon_scale_2_golden.png
index 9a5efd2..727bb48 100644
--- a/tests/tests/graphics/res/drawable-nodpi/vector_icon_scale_2_golden.png
+++ b/tests/tests/graphics/res/drawable-nodpi/vector_icon_scale_2_golden.png
Binary files differ
diff --git a/tests/tests/graphics/res/drawable-nodpi/vector_icon_scale_3_golden.png b/tests/tests/graphics/res/drawable-nodpi/vector_icon_scale_3_golden.png
index 2edc3c7..d68ab90 100644
--- a/tests/tests/graphics/res/drawable-nodpi/vector_icon_scale_3_golden.png
+++ b/tests/tests/graphics/res/drawable-nodpi/vector_icon_scale_3_golden.png
Binary files differ
diff --git a/tests/tests/graphics/res/drawable-nodpi/vector_icon_schedule_golden.png b/tests/tests/graphics/res/drawable-nodpi/vector_icon_schedule_golden.png
index 9822bc2..2830840 100644
--- a/tests/tests/graphics/res/drawable-nodpi/vector_icon_schedule_golden.png
+++ b/tests/tests/graphics/res/drawable-nodpi/vector_icon_schedule_golden.png
Binary files differ
diff --git a/tests/tests/graphics/res/drawable-nodpi/vector_icon_settings_golden.png b/tests/tests/graphics/res/drawable-nodpi/vector_icon_settings_golden.png
index d12b142..b5d25be 100644
--- a/tests/tests/graphics/res/drawable-nodpi/vector_icon_settings_golden.png
+++ b/tests/tests/graphics/res/drawable-nodpi/vector_icon_settings_golden.png
Binary files differ
diff --git a/tests/tests/graphics/res/drawable-nodpi/vector_icon_state_list_2_golden.png b/tests/tests/graphics/res/drawable-nodpi/vector_icon_state_list_2_golden.png
index 3936c89..923347d 100644
--- a/tests/tests/graphics/res/drawable-nodpi/vector_icon_state_list_2_golden.png
+++ b/tests/tests/graphics/res/drawable-nodpi/vector_icon_state_list_2_golden.png
Binary files differ
diff --git a/tests/tests/graphics/res/drawable-nodpi/vector_icon_state_list_2_pressed_golden.png b/tests/tests/graphics/res/drawable-nodpi/vector_icon_state_list_2_pressed_golden.png
index c5d06f6..74c30fa 100644
--- a/tests/tests/graphics/res/drawable-nodpi/vector_icon_state_list_2_pressed_golden.png
+++ b/tests/tests/graphics/res/drawable-nodpi/vector_icon_state_list_2_pressed_golden.png
Binary files differ
diff --git a/tests/tests/graphics/res/drawable-nodpi/vector_icon_state_list_golden.png b/tests/tests/graphics/res/drawable-nodpi/vector_icon_state_list_golden.png
index 3936c89..923347d 100644
--- a/tests/tests/graphics/res/drawable-nodpi/vector_icon_state_list_golden.png
+++ b/tests/tests/graphics/res/drawable-nodpi/vector_icon_state_list_golden.png
Binary files differ
diff --git a/tests/tests/graphics/res/drawable-nodpi/vector_icon_state_list_pressed_golden.png b/tests/tests/graphics/res/drawable-nodpi/vector_icon_state_list_pressed_golden.png
index c5d06f6..74c30fa 100644
--- a/tests/tests/graphics/res/drawable-nodpi/vector_icon_state_list_pressed_golden.png
+++ b/tests/tests/graphics/res/drawable-nodpi/vector_icon_state_list_pressed_golden.png
Binary files differ
diff --git a/tests/tests/graphics/res/drawable-nodpi/vector_icon_stroke_1_golden.png b/tests/tests/graphics/res/drawable-nodpi/vector_icon_stroke_1_golden.png
index 2bf7882..c202ffc 100644
--- a/tests/tests/graphics/res/drawable-nodpi/vector_icon_stroke_1_golden.png
+++ b/tests/tests/graphics/res/drawable-nodpi/vector_icon_stroke_1_golden.png
Binary files differ
diff --git a/tests/tests/graphics/res/drawable-nodpi/vector_icon_stroke_2_golden.png b/tests/tests/graphics/res/drawable-nodpi/vector_icon_stroke_2_golden.png
index 4141d6f..ed31e47 100644
--- a/tests/tests/graphics/res/drawable-nodpi/vector_icon_stroke_2_golden.png
+++ b/tests/tests/graphics/res/drawable-nodpi/vector_icon_stroke_2_golden.png
Binary files differ
diff --git a/tests/tests/graphics/res/drawable-nodpi/vector_icon_stroke_3_golden.png b/tests/tests/graphics/res/drawable-nodpi/vector_icon_stroke_3_golden.png
index 1212fb3..a32de90 100644
--- a/tests/tests/graphics/res/drawable-nodpi/vector_icon_stroke_3_golden.png
+++ b/tests/tests/graphics/res/drawable-nodpi/vector_icon_stroke_3_golden.png
Binary files differ
diff --git a/tests/tests/graphics/res/drawable-nodpi/vector_icon_transformation_1_golden.png b/tests/tests/graphics/res/drawable-nodpi/vector_icon_transformation_1_golden.png
index 0717399..26cbe2a 100644
--- a/tests/tests/graphics/res/drawable-nodpi/vector_icon_transformation_1_golden.png
+++ b/tests/tests/graphics/res/drawable-nodpi/vector_icon_transformation_1_golden.png
Binary files differ
diff --git a/tests/tests/graphics/res/drawable-nodpi/vector_icon_transformation_2_golden.png b/tests/tests/graphics/res/drawable-nodpi/vector_icon_transformation_2_golden.png
index 505aa2e..9aeeeef 100644
--- a/tests/tests/graphics/res/drawable-nodpi/vector_icon_transformation_2_golden.png
+++ b/tests/tests/graphics/res/drawable-nodpi/vector_icon_transformation_2_golden.png
Binary files differ
diff --git a/tests/tests/graphics/res/drawable-nodpi/vector_icon_transformation_3_golden.png b/tests/tests/graphics/res/drawable-nodpi/vector_icon_transformation_3_golden.png
index 9b53e94..468a1c3 100644
--- a/tests/tests/graphics/res/drawable-nodpi/vector_icon_transformation_3_golden.png
+++ b/tests/tests/graphics/res/drawable-nodpi/vector_icon_transformation_3_golden.png
Binary files differ
diff --git a/tests/tests/graphics/res/drawable-nodpi/vector_icon_transformation_4_golden.png b/tests/tests/graphics/res/drawable-nodpi/vector_icon_transformation_4_golden.png
index a5d4d33..e4bc055 100644
--- a/tests/tests/graphics/res/drawable-nodpi/vector_icon_transformation_4_golden.png
+++ b/tests/tests/graphics/res/drawable-nodpi/vector_icon_transformation_4_golden.png
Binary files differ
diff --git a/tests/tests/graphics/res/drawable-nodpi/vector_icon_transformation_5_golden.png b/tests/tests/graphics/res/drawable-nodpi/vector_icon_transformation_5_golden.png
index 0d8ded1..bb185f2 100644
--- a/tests/tests/graphics/res/drawable-nodpi/vector_icon_transformation_5_golden.png
+++ b/tests/tests/graphics/res/drawable-nodpi/vector_icon_transformation_5_golden.png
Binary files differ
diff --git a/tests/tests/graphics/res/drawable-nodpi/vector_icon_transformation_6_golden.png b/tests/tests/graphics/res/drawable-nodpi/vector_icon_transformation_6_golden.png
index 3da7969..02901b6 100644
--- a/tests/tests/graphics/res/drawable-nodpi/vector_icon_transformation_6_golden.png
+++ b/tests/tests/graphics/res/drawable-nodpi/vector_icon_transformation_6_golden.png
Binary files differ
diff --git a/tests/tests/graphics/res/font/a3em.ttf b/tests/tests/graphics/res/font/a3em.ttf
new file mode 100644
index 0000000..a601ce2
--- /dev/null
+++ b/tests/tests/graphics/res/font/a3em.ttf
Binary files differ
diff --git a/tests/tests/graphics/res/font/b3em.ttf b/tests/tests/graphics/res/font/b3em.ttf
new file mode 100644
index 0000000..63948a2
--- /dev/null
+++ b/tests/tests/graphics/res/font/b3em.ttf
Binary files differ
diff --git a/tests/tests/graphics/res/font/c3em.ttf b/tests/tests/graphics/res/font/c3em.ttf
new file mode 100644
index 0000000..badc3e2
--- /dev/null
+++ b/tests/tests/graphics/res/font/c3em.ttf
Binary files differ
diff --git a/tests/tests/graphics/res/font/multistyle_family.xml b/tests/tests/graphics/res/font/multistyle_family.xml
new file mode 100644
index 0000000..2522e72
--- /dev/null
+++ b/tests/tests/graphics/res/font/multistyle_family.xml
@@ -0,0 +1,6 @@
+<?xml version="1.0" encoding="utf-8"?>
+<font-family xmlns:android="http://schemas.android.com/apk/res/android">
+    <font android:font="@font/a3em" android:fontWeight="400" android:fontStyle="normal" />
+    <font android:font="@font/b3em" android:fontWeight="400" android:fontStyle="italic" />
+    <font android:font="@font/c3em" android:fontWeight="700" android:fontStyle="italic" />
+</font-family>
diff --git a/tests/tests/graphics/res/font/multiweight_family.xml b/tests/tests/graphics/res/font/multiweight_family.xml
new file mode 100644
index 0000000..2ed0490
--- /dev/null
+++ b/tests/tests/graphics/res/font/multiweight_family.xml
@@ -0,0 +1,6 @@
+<?xml version="1.0" encoding="utf-8"?>
+<font-family xmlns:android="http://schemas.android.com/apk/res/android">
+    <font android:font="@font/a3em" android:fontWeight="100" android:fontStyle="normal" />
+    <font android:font="@font/b3em" android:fontWeight="400" android:fontStyle="normal" />
+    <font android:font="@font/c3em" android:fontWeight="700" android:fontStyle="normal" />
+</font-family>
diff --git a/tests/tests/graphics/src/android/graphics/cts/BlurMaskFilterTest.java b/tests/tests/graphics/src/android/graphics/cts/BlurMaskFilterTest.java
index b315bc9..0fb5978 100644
--- a/tests/tests/graphics/src/android/graphics/cts/BlurMaskFilterTest.java
+++ b/tests/tests/graphics/src/android/graphics/cts/BlurMaskFilterTest.java
@@ -35,6 +35,7 @@
 public class BlurMaskFilterTest {
     private static final int OFFSET = 10;
     private static final int RADIUS = 5;
+    private static final int CHECK_RADIUS = 8;
     private static final int BITMAP_WIDTH = 100;
     private static final int BITMAP_HEIGHT = 100;
     private static final int CENTER = BITMAP_HEIGHT / 2;
@@ -51,13 +52,13 @@
         canvas.drawRect(CENTER - OFFSET, CENTER - OFFSET, CENTER + OFFSET, CENTER + OFFSET, paint);
         for (int x = 0; x < CENTER; x++) {
             for (int y = 0; y < CENTER; y++) {
-                if (x < CENTER - OFFSET - RADIUS || y < CENTER - OFFSET - RADIUS) {
+                if (x < CENTER - OFFSET - CHECK_RADIUS || y < CENTER - OFFSET - CHECK_RADIUS) {
                     // check that color didn't bleed (much) beyond radius
                     verifyQuadrants(Color.TRANSPARENT, b, x, y, 5);
                 } else if (x > CENTER - OFFSET + RADIUS && y > CENTER - OFFSET + RADIUS) {
                     // check that color didn't wash out (much) in the center
-                    verifyQuadrants(Color.RED, b, x, y, 5);
-                } else {
+                    verifyQuadrants(Color.RED, b, x, y, 8);
+                } else if (x > CENTER - OFFSET - RADIUS && y > CENTER - OFFSET - RADIUS) {
                     // check blur zone, color should remain, alpha varies
                     verifyQuadrants(Color.RED, b, x, y, 255);
                 }
diff --git a/tests/tests/graphics/src/android/graphics/cts/CanvasTest.java b/tests/tests/graphics/src/android/graphics/cts/CanvasTest.java
index b36c23a..70c3202 100644
--- a/tests/tests/graphics/src/android/graphics/cts/CanvasTest.java
+++ b/tests/tests/graphics/src/android/graphics/cts/CanvasTest.java
@@ -1428,6 +1428,12 @@
         // normal case
         mCanvas.drawArc(new RectF(0, 0, 10, 12), 10, 11, false, mPaint);
         mCanvas.drawArc(new RectF(0, 0, 10, 12), 10, 11, true, mPaint);
+
+        // special case: sweepAngle >= abs(360)
+        mCanvas.drawArc(new RectF(0, 0, 10, 12), 10, 400, true, mPaint);
+        mCanvas.drawArc(new RectF(0, 0, 10, 12), 10, -400, true, mPaint);
+        mCanvas.drawArc(new RectF(0, 0, 10, 12), 10, Float.POSITIVE_INFINITY, true, mPaint);
+        mCanvas.drawArc(new RectF(0, 0, 10, 12), 10, Float.NEGATIVE_INFINITY, true, mPaint);
     }
 
     @Test(expected=NullPointerException.class)
diff --git a/tests/tests/graphics/src/android/graphics/cts/TypefaceTest.java b/tests/tests/graphics/src/android/graphics/cts/TypefaceTest.java
index 04d5564..32cdd38 100644
--- a/tests/tests/graphics/src/android/graphics/cts/TypefaceTest.java
+++ b/tests/tests/graphics/src/android/graphics/cts/TypefaceTest.java
@@ -52,6 +52,19 @@
     private static final String DEFAULT = (String)null;
     private static final String INVALID = "invalid-family-name";
 
+    private static final float GLYPH_1EM_WIDTH;
+    private static final float GLYPH_3EM_WIDTH;
+
+    static {
+        // 3em.ttf supports "a", "b", "c". The width of "a" is 3em, others are 1em.
+        final Context ctx = InstrumentationRegistry.getTargetContext();
+        final Typeface typeface = ctx.getResources().getFont(R.font.a3em);
+        final Paint paint = new Paint();
+        paint.setTypeface(typeface);
+        GLYPH_3EM_WIDTH = paint.measureText("a");
+        GLYPH_1EM_WIDTH = paint.measureText("b");
+    }
+
     // list of family names to try when attempting to find a typeface with a given style
     private static final String[] FAMILIES =
             { (String) null, "monospace", "serif", "sans-serif", "cursive", "arial", "times" };
@@ -531,4 +544,68 @@
 
         assertTrue(Math.abs(widthFromRegular - widthFromBlack) > 1.0f);
     }
+
+    @Test
+    public void testTypefaceCreate_withExactWeight() {
+        // multiweight_family has following fonts.
+        // - a3em.ttf with weight = 100, style=normal configuration.
+        //   This font supports "a", "b", "c". The weight "a" is 3em, others are 1em.
+        // - b3em.ttf with weight = 400, style=normal configuration.
+        //   This font supports "a", "b", "c". The weight "b" is 3em, others are 1em.
+        // - c3em.ttf with weight = 700, style=normal configuration.
+        //   This font supports "a", "b", "c". The weight "c" is 3em, others are 1em.
+        final Typeface family = mContext.getResources().getFont(R.font.multiweight_family);
+        assertNotNull(family);
+
+        final Paint paint = new Paint();
+        // By default, the font which weight is 400 is selected.
+        paint.setTypeface(family);
+        assertEquals(GLYPH_1EM_WIDTH, paint.measureText("a"), 0f);
+        assertEquals(GLYPH_3EM_WIDTH, paint.measureText("b"), 0f);
+        assertEquals(GLYPH_1EM_WIDTH, paint.measureText("c"), 0f);
+
+        // Draw with the font which weight is 100.
+        paint.setTypeface(Typeface.create(family, 100 /* weight */, false /* italic */));
+        assertEquals(GLYPH_3EM_WIDTH, paint.measureText("a"), 0f);
+        assertEquals(GLYPH_1EM_WIDTH, paint.measureText("b"), 0f);
+        assertEquals(GLYPH_1EM_WIDTH, paint.measureText("c"), 0f);
+
+        // Draw with the font which weight is 700.
+        paint.setTypeface(Typeface.create(family, 700 /* weight */, false /* italic */));
+        assertEquals(GLYPH_1EM_WIDTH, paint.measureText("a"), 0f);
+        assertEquals(GLYPH_1EM_WIDTH, paint.measureText("b"), 0f);
+        assertEquals(GLYPH_3EM_WIDTH, paint.measureText("c"), 0f);
+    }
+
+    @Test
+    public void testTypefaceCreate_withExactStyle() {
+        // multiweight_family has following fonts.
+        // - a3em.ttf with weight = 400, style=normal configuration.
+        //   This font supports "a", "b", "c". The weight "a" is 3em, others are 1em.
+        // - b3em.ttf with weight = 400, style=italic configuration.
+        //   This font supports "a", "b", "c". The weight "b" is 3em, others are 1em.
+        // - c3em.ttf with weight = 700, style=italic configuration.
+        //   This font supports "a", "b", "c". The weight "c" is 3em, others are 1em.
+        final Typeface family = mContext.getResources().getFont(R.font.multistyle_family);
+        assertNotNull(family);
+
+        final Paint paint = new Paint();
+        // By default, the normal style font which weight is 400 is selected.
+        paint.setTypeface(family);
+        assertEquals(GLYPH_3EM_WIDTH, paint.measureText("a"), 0f);
+        assertEquals(GLYPH_1EM_WIDTH, paint.measureText("b"), 0f);
+        assertEquals(GLYPH_1EM_WIDTH, paint.measureText("c"), 0f);
+
+        // Draw with the italic font.
+        paint.setTypeface(Typeface.create(family, 400 /* weight */, true /* italic */));
+        assertEquals(GLYPH_1EM_WIDTH, paint.measureText("a"), 0f);
+        assertEquals(GLYPH_3EM_WIDTH, paint.measureText("b"), 0f);
+        assertEquals(GLYPH_1EM_WIDTH, paint.measureText("c"), 0f);
+
+        // Draw with the italic font which weigth is 700.
+        paint.setTypeface(Typeface.create(family, 700 /* weight */, true /* italic */));
+        assertEquals(GLYPH_1EM_WIDTH, paint.measureText("a"), 0f);
+        assertEquals(GLYPH_1EM_WIDTH, paint.measureText("b"), 0f);
+        assertEquals(GLYPH_3EM_WIDTH, paint.measureText("c"), 0f);
+    }
 }
diff --git a/tests/tests/hardware/Android.mk b/tests/tests/hardware/Android.mk
index 3adf811..62c67fc 100644
--- a/tests/tests/hardware/Android.mk
+++ b/tests/tests/hardware/Android.mk
@@ -26,7 +26,7 @@
 
 LOCAL_MULTILIB := both
 
-LOCAL_JAVA_LIBRARIES := android.test.runner
+LOCAL_JAVA_LIBRARIES := android.test.runner android.test.base
 
 LOCAL_STATIC_JAVA_LIBRARIES := \
     android-support-test \
@@ -34,8 +34,7 @@
     ctstestrunner \
     mockito-target-minus-junit4 \
     platform-test-annotations \
-    ub-uiautomator \
-    legacy-android-test
+    ub-uiautomator
 
 LOCAL_JNI_SHARED_LIBRARIES := libctshardware_jni libnativehelper_compat_libc++
 
diff --git a/tests/tests/hardware/AndroidTest.xml b/tests/tests/hardware/AndroidTest.xml
index 6dbda37..f1fa92d 100644
--- a/tests/tests/hardware/AndroidTest.xml
+++ b/tests/tests/hardware/AndroidTest.xml
@@ -13,6 +13,7 @@
      limitations under the License.
 -->
 <configuration description="Config for CTS Hardware test cases">
+    <option name="test-suite-tag" value="cts" />
     <option name="config-descriptor:metadata" key="component" value="misc" />
     <target_preparer class="com.android.compatibility.common.tradefed.targetprep.LocationCheck" />
     <target_preparer class="com.android.tradefed.targetprep.suite.SuiteApkInstaller">
diff --git a/tests/tests/hardware/src/android/hardware/cts/GeomagneticFieldTest.java b/tests/tests/hardware/src/android/hardware/cts/GeomagneticFieldTest.java
index 75d7b7c..8968442 100644
--- a/tests/tests/hardware/src/android/hardware/cts/GeomagneticFieldTest.java
+++ b/tests/tests/hardware/src/android/hardware/cts/GeomagneticFieldTest.java
@@ -23,26 +23,79 @@
 import java.util.GregorianCalendar;
 
 public class GeomagneticFieldTest extends AndroidTestCase {
-    // Chengdu: Latitude 30d 40' 12", Longitude 104d 3' 36"
-    private static final float LATITUDE_OF_CHENGDU = 30.67f;
-    private static final float LONGITUDE_OF_CHENGDU = 104.06f;
-    private static final float ALTITUDE_OF_CHENGDU = 500f;
-    private static final long TEST_TIME = new GregorianCalendar(2010, 5, 1).getTimeInMillis();
+    private static final float DECLINATION_THRESHOLD = 0.1f;
+    private static final float INCLINATION_THRESHOLD = 0.1f;
+    private static final float FIELD_STRENGTH_THRESHOLD = 100;
 
     @Presubmit
     public void testGeomagneticField() {
-        GeomagneticField geomagneticField = new GeomagneticField(LATITUDE_OF_CHENGDU,
-                LONGITUDE_OF_CHENGDU, ALTITUDE_OF_CHENGDU, TEST_TIME);
+        // Reference values calculated from NOAA online calculator for WMM 2015
+        // https://www.ngdc.noaa.gov/geomag-web/#igrfwmm
+        TestDataPoint testPoints[] = new TestDataPoint[] {
+            // Mountain View, CA, USA on 2017/1/1
+            new TestDataPoint(37.386f, -122.083f, 32, 2017, 1, 1,
+                    13.4589f, 60.9542f, 48168.0f),
+            // Chengdu, China on 2017/8/8
+            new TestDataPoint(30.658f, 103.935f, 500f, 2017, 8, 8,
+                    -1.9784f, 47.9723f, 50717.3f),
+            // Sao Paulo, Brazil on 2018/12/25
+            new TestDataPoint(-23.682f, -46.875f, 760f, 2018, 12, 25,
+                    -21.3130f, -37.9940f, 22832.3f),
+            // Boston, MA, USA on 2019/2/10
+            new TestDataPoint(42.313f, -71.127f, 43f, 2019, 2, 10,
+                    -14.5391f, 66.9693f, 51815.1f),
+            // Cape Town, South Africa on 2019/5/1
+            new TestDataPoint(-33.913f, 18.095f, 100f, 2019, 5, 1,
+                    -25.2454f, -65.8887f, 25369.2f),
+            // Sydney, Australia on 2020/1/1
+            new TestDataPoint(-33.847f, 150.791f, 19f, 2020, 1, 1,
+                    12.4469f, -64.3443f, 57087.9f)
+        };
 
-        // Reference values calculated from NOAA online calculator for WMM 2010,
-        // and cross-checked in Matlab. The expected deltas are proportional to the
-        // magnitude of each value.
-        assertEquals(-1.867f, geomagneticField.getDeclination(), 0.1f);
-        assertEquals(47.133f, geomagneticField.getInclination(), 1.0f);
-        assertEquals(50375.6f, geomagneticField.getFieldStrength(), 100.0f);
-        assertEquals(34269.3f, geomagneticField.getHorizontalStrength(), 100.0f);
-        assertEquals(34251.2f, geomagneticField.getX(), 100.0f);
-        assertEquals(-1113.2f, geomagneticField.getY(), 10.0f);
-        assertEquals(36923.1f, geomagneticField.getZ(), 100.0f);
+        for (TestDataPoint t : testPoints) {
+            GeomagneticField field =
+                    new GeomagneticField(t.latitude, t.longitude, t.altitude, t.epochTimeMillis);
+            assertEquals(t.declinationDegree,  field.getDeclination(), DECLINATION_THRESHOLD);
+            assertEquals(t.inclinationDegree,  field.getInclination(), INCLINATION_THRESHOLD);
+            assertEquals(t.fieldStrengthNanoTesla, field.getFieldStrength(),
+                    FIELD_STRENGTH_THRESHOLD);
+
+            float horizontalFieldStrengthNanoTesla = (float)(
+                    Math.cos(Math.toRadians(t.inclinationDegree)) * t.fieldStrengthNanoTesla);
+            assertEquals(horizontalFieldStrengthNanoTesla, field.getHorizontalStrength(),
+                    FIELD_STRENGTH_THRESHOLD);
+
+            float verticalFieldStrengthNanoTesla = (float)(
+                    Math.sin(Math.toRadians(t.inclinationDegree)) * t.fieldStrengthNanoTesla);
+            assertEquals(verticalFieldStrengthNanoTesla, field.getZ(), FIELD_STRENGTH_THRESHOLD);
+
+            float declinationDegree = (float)(
+                    Math.toDegrees(Math.atan2(field.getY(), field.getX())));
+            assertEquals(t.declinationDegree, declinationDegree, DECLINATION_THRESHOLD);
+            assertEquals(horizontalFieldStrengthNanoTesla,
+                    Math.sqrt(field.getX() * field.getX() + field.getY() * field.getY()),
+                    FIELD_STRENGTH_THRESHOLD);
+        }
+    }
+
+    private class TestDataPoint {
+        public final float latitude;
+        public final float longitude;
+        public final float altitude;
+        public final long epochTimeMillis;
+        public final float declinationDegree;
+        public final float inclinationDegree;
+        public final float fieldStrengthNanoTesla;
+
+        TestDataPoint(float lat, float lng, float alt, int year, int month, int day,
+                float dec, float inc, float strength) {
+            latitude = lat;
+            longitude = lng;
+            altitude = alt;
+            epochTimeMillis = new GregorianCalendar(year, month, day).getTimeInMillis();
+            declinationDegree = dec;
+            inclinationDegree = inc;
+            fieldStrengthNanoTesla = strength;
+        }
     }
 }
diff --git a/tests/tests/hardware/src/android/hardware/cts/GlUtils.java b/tests/tests/hardware/src/android/hardware/cts/GlUtils.java
index 81628d4..bd5cecb 100644
--- a/tests/tests/hardware/src/android/hardware/cts/GlUtils.java
+++ b/tests/tests/hardware/src/android/hardware/cts/GlUtils.java
@@ -17,6 +17,7 @@
 package android.hardware.cts;
 
 import android.opengl.GLES20;
+import android.util.Pair;
 
 import java.util.Arrays;
 import java.util.regex.Matcher;
@@ -27,6 +28,10 @@
     }
 
     static int getMajorVersion() {
+        return getVersion().first;
+    }
+
+    static Pair<Integer, Integer> getVersion() {
         // Section 6.1.5 of the OpenGL ES specification indicates the GL version
         // string strictly follows this format:
         //
@@ -42,9 +47,10 @@
         Pattern pattern = Pattern.compile("OpenGL ES ([0-9]+)\\.([0-9]+)");
         Matcher matcher = pattern.matcher(version);
         if (matcher.find()) {
-            return Integer.parseInt(matcher.group(1));
+            return new Pair<>(
+                    Integer.parseInt(matcher.group(1)), Integer.parseInt(matcher.group(2)));
         }
-        return 2;
+        return new Pair<>(2, 0);
     }
 
     static String[] getExtensions() {
diff --git a/tests/tests/hardware/src/android/hardware/cts/HardwareBufferTest.java b/tests/tests/hardware/src/android/hardware/cts/HardwareBufferTest.java
index 5ea845a..6276959 100644
--- a/tests/tests/hardware/src/android/hardware/cts/HardwareBufferTest.java
+++ b/tests/tests/hardware/src/android/hardware/cts/HardwareBufferTest.java
@@ -24,6 +24,7 @@
 import android.opengl.EGLSurface;
 import android.support.test.filters.SmallTest;
 import android.support.test.runner.AndroidJUnit4;
+import android.util.Pair;
 import org.junit.BeforeClass;
 import org.junit.Test;
 import org.junit.runner.RunWith;
@@ -70,8 +71,10 @@
             }, 0);
             EGL14.eglMakeCurrent(eglDisplay, eglSurface, eglSurface, eglContext);
 
-            sHasFloatBuffers = GlUtils.getMajorVersion() >= 3 ||
-                    GlUtils.hasExtensions("GL_OES_texture_half_float",
+            Pair<Integer, Integer> version = GlUtils.getVersion();
+            sHasFloatBuffers = (version.first >= 3 && version.second >= 2) ||
+                    GlUtils.hasExtensions(
+                            "GL_OES_texture_half_float",
                             "GL_OES_texture_half_float_linear");
 
             EGL14.eglMakeCurrent(eglDisplay, EGL_NO_SURFACE, EGL_NO_SURFACE, EGL_NO_CONTEXT);
diff --git a/tests/tests/incident/Android.mk b/tests/tests/incident/Android.mk
index 1309c99..6c6ef62 100644
--- a/tests/tests/incident/Android.mk
+++ b/tests/tests/incident/Android.mk
@@ -33,7 +33,6 @@
 LOCAL_JAVA_LIBRARIES += android.test.runner
 
 LOCAL_STATIC_JAVA_LIBRARIES := \
-        ctstestrunner \
-	legacy-android-test
+        ctstestrunner
 
 include $(BUILD_CTS_PACKAGE)
diff --git a/tests/tests/jni/AndroidTest.xml b/tests/tests/jni/AndroidTest.xml
index 84cf235..e4ab2dd 100644
--- a/tests/tests/jni/AndroidTest.xml
+++ b/tests/tests/jni/AndroidTest.xml
@@ -14,6 +14,7 @@
      limitations under the License.
 -->
 <configuration description="Config for CTS JNI test cases">
+    <option name="test-suite-tag" value="cts" />
     <option name="config-descriptor:metadata" key="component" value="art" />
     <target_preparer class="com.android.tradefed.targetprep.suite.SuiteApkInstaller">
         <option name="cleanup-apks" value="true" />
diff --git a/tests/tests/jni_vendor/AndroidTest.xml b/tests/tests/jni_vendor/AndroidTest.xml
index 65afd6b..b606559 100644
--- a/tests/tests/jni_vendor/AndroidTest.xml
+++ b/tests/tests/jni_vendor/AndroidTest.xml
@@ -14,6 +14,7 @@
      limitations under the License.
 -->
 <configuration description="Config for CTS Vendor's JNI test cases">
+    <option name="test-suite-tag" value="cts" />
     <option name="config-descriptor:metadata" key="component" value="art" />
     <target_preparer class="com.android.tradefed.targetprep.suite.SuiteApkInstaller">
         <option name="cleanup-apks" value="true" />
diff --git a/tests/tests/jvmti/attaching/AndroidTest.xml b/tests/tests/jvmti/attaching/AndroidTest.xml
index 1b4ed05..5723956 100644
--- a/tests/tests/jvmti/attaching/AndroidTest.xml
+++ b/tests/tests/jvmti/attaching/AndroidTest.xml
@@ -17,6 +17,7 @@
   -->
 
 <configuration description="Config for CTS jvmti attaching test cases">
+    <option name="test-suite-tag" value="cts" />
     <option name="config-descriptor:metadata" key="component" value="art" />
     <target_preparer class="com.android.tradefed.targetprep.suite.SuiteApkInstaller">
         <option name="cleanup-apks" value="true" />
diff --git a/tests/tests/keystore/Android.mk b/tests/tests/keystore/Android.mk
index ea24954..c88f614 100644
--- a/tests/tests/keystore/Android.mk
+++ b/tests/tests/keystore/Android.mk
@@ -32,7 +32,7 @@
         ctstestrunner \
         guava \
         junit \
-        legacy-android-test
+        cts-security-test-support-library
 
 LOCAL_SRC_FILES := $(call all-java-files-under, src)
 
@@ -49,6 +49,7 @@
 # Uncomment when b/13282254 is fixed.
 # LOCAL_SDK_VERSION := current
 LOCAL_JAVA_LIBRARIES += android.test.runner
+LOCAL_JAVA_LIBRARIES += android.test.base
 
 include $(BUILD_CTS_PACKAGE)
 
diff --git a/tests/tests/keystore/AndroidTest.xml b/tests/tests/keystore/AndroidTest.xml
index 87913ed..e2b4986 100644
--- a/tests/tests/keystore/AndroidTest.xml
+++ b/tests/tests/keystore/AndroidTest.xml
@@ -14,6 +14,7 @@
      limitations under the License.
 -->
 <configuration description="Config for CTS Keystore test cases">
+    <option name="test-suite-tag" value="cts" />
     <option name="config-descriptor:metadata" key="component" value="security" />
     <target_preparer class="com.android.tradefed.targetprep.suite.SuiteApkInstaller">
         <option name="cleanup-apks" value="true" />
diff --git a/tests/tests/keystore/src/android/keystore/cts/AttestationPackageInfo.java b/tests/tests/keystore/src/android/keystore/cts/AttestationPackageInfo.java
deleted file mode 100644
index 294fda8..0000000
--- a/tests/tests/keystore/src/android/keystore/cts/AttestationPackageInfo.java
+++ /dev/null
@@ -1,83 +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.keystore.cts;
-
-import com.android.org.bouncycastle.asn1.ASN1Encodable;
-import com.android.org.bouncycastle.asn1.ASN1Sequence;
-
-import java.security.cert.CertificateParsingException;
-
-import java.io.UnsupportedEncodingException;
-
-public class AttestationPackageInfo implements java.lang.Comparable<AttestationPackageInfo> {
-    private static final int PACKAGE_NAME_INDEX = 0;
-    private static final int VERSION_INDEX = 1;
-
-    private final String packageName;
-    private final int version;
-
-    public AttestationPackageInfo(String packageName, int version) {
-        this.packageName = packageName;
-        this.version = version;
-    }
-
-    public AttestationPackageInfo(ASN1Encodable asn1Encodable) throws CertificateParsingException {
-        if (!(asn1Encodable instanceof ASN1Sequence)) {
-            throw new CertificateParsingException(
-                    "Expected sequence for AttestationPackageInfo, found "
-                            + asn1Encodable.getClass().getName());
-        }
-
-        ASN1Sequence sequence = (ASN1Sequence) asn1Encodable;
-        try {
-            packageName = Asn1Utils.getStringFromAsn1OctetStreamAssumingUTF8(
-                    sequence.getObjectAt(PACKAGE_NAME_INDEX));
-        } catch (UnsupportedEncodingException e) {
-            throw new CertificateParsingException(
-                    "Converting octet stream to String triggered an UnsupportedEncodingException",
-                    e);
-        }
-        version = Asn1Utils.getIntegerFromAsn1(sequence.getObjectAt(VERSION_INDEX));
-    }
-
-    public String getPackageName() {
-        return packageName;
-    }
-
-    public int getVersion() {
-        return version;
-    }
-
-    @Override
-    public String toString() {
-        return new StringBuilder().append("Package name: ").append(getPackageName())
-                .append("\nVersion: " + getVersion()).toString();
-    }
-
-    @Override
-    public int compareTo(AttestationPackageInfo other) {
-        int res = packageName.compareTo(other.packageName);
-        if (res != 0) return res;
-        res = Integer.compare(version, other.version);
-        if (res != 0) return res;
-        return res;
-    }
-
-    @Override
-    public boolean equals(Object o) {
-        return (o instanceof AttestationPackageInfo)
-                && (0 == compareTo((AttestationPackageInfo) o));
-    }
-}
diff --git a/tests/tests/keystore/src/android/keystore/cts/KeyAttestationTest.java b/tests/tests/keystore/src/android/keystore/cts/KeyAttestationTest.java
index d901690..0342f08 100644
--- a/tests/tests/keystore/src/android/keystore/cts/KeyAttestationTest.java
+++ b/tests/tests/keystore/src/android/keystore/cts/KeyAttestationTest.java
@@ -62,6 +62,9 @@
 import android.test.AndroidTestCase;
 import android.util.ArraySet;
 
+import com.android.org.bouncycastle.asn1.x500.X500Name;
+import com.android.org.bouncycastle.cert.jcajce.JcaX509CertificateHolder;
+
 import java.security.GeneralSecurityException;
 import java.security.InvalidAlgorithmParameterException;
 import java.security.InvalidKeyException;
@@ -70,6 +73,7 @@
 import java.security.NoSuchAlgorithmException;
 import java.security.NoSuchProviderException;
 import java.security.ProviderException;
+import java.security.PublicKey;
 import java.security.SignatureException;
 import java.security.cert.Certificate;
 import java.security.cert.CertificateException;
@@ -422,7 +426,7 @@
 
         try {
             Certificate certificates[] = keyStore.getCertificateChain(keystoreAlias);
-            verifyCertificateSignatures(certificates);
+            verifyCertificateChain(certificates);
 
             X509Certificate attestationCert = (X509Certificate) certificates[0];
             Attestation attestation = new Attestation(attestationCert);
@@ -476,7 +480,7 @@
 
         try {
             Certificate certificates[] = keyStore.getCertificateChain(keystoreAlias);
-            verifyCertificateSignatures(certificates);
+            verifyCertificateChain(certificates);
 
             X509Certificate attestationCert = (X509Certificate) certificates[0];
             Attestation attestation = new Attestation(attestationCert);
@@ -876,12 +880,36 @@
         keyPairGenerator.generateKeyPair();
     }
 
-    private void verifyCertificateSignatures(Certificate[] certChain)
+    private void verifyCertificateChain(Certificate[] certChain)
             throws GeneralSecurityException {
         assertNotNull(certChain);
         for (int i = 1; i < certChain.length; ++i) {
             try {
-                certChain[i - 1].verify(certChain[i].getPublicKey());
+                PublicKey pubKey = certChain[i].getPublicKey();
+                certChain[i - 1].verify(pubKey);
+                if (i == certChain.length - 1) {
+                    // Last cert should be self-signed.
+                    certChain[i].verify(pubKey);
+                }
+
+                // Check that issuer in the signed cert matches subject in the signing cert.
+                X509Certificate x509CurrCert = (X509Certificate) certChain[i];
+                X509Certificate x509PrevCert = (X509Certificate) certChain[i - 1];
+                X500Name signingCertSubject =
+                        new JcaX509CertificateHolder(x509CurrCert).getSubject();
+                X500Name signedCertIssuer =
+                        new JcaX509CertificateHolder(x509PrevCert).getIssuer();
+                // Use .toASN1Object().equals() rather than .equals() because .equals() is case
+                // insensitive, and we want to verify an exact match.
+                assertTrue(
+                        signedCertIssuer.toASN1Object().equals(signingCertSubject.toASN1Object()));
+
+                if (i == 1) {
+                    // First cert should have subject "CN=Android Keystore Key".
+                    X500Name signedCertSubject =
+                            new JcaX509CertificateHolder(x509PrevCert).getSubject();
+                    assertEquals(signedCertSubject, new X500Name("CN=Android Keystore Key"));
+                }
             } catch (InvalidKeyException | CertificateException | NoSuchAlgorithmException
                     | NoSuchProviderException | SignatureException e) {
                 throw new GeneralSecurityException("Failed to verify certificate "
diff --git a/tests/tests/keystore/src/android/keystore/cts/KeyStoreTest.java b/tests/tests/keystore/src/android/keystore/cts/KeyStoreTest.java
index d64e7b7..229b69c 100644
--- a/tests/tests/keystore/src/android/keystore/cts/KeyStoreTest.java
+++ b/tests/tests/keystore/src/android/keystore/cts/KeyStoreTest.java
@@ -41,6 +41,8 @@
 import java.security.UnrecoverableKeyException;
 import java.security.cert.Certificate;
 import java.security.cert.X509Certificate;
+import java.security.interfaces.RSAPrivateKey;
+import java.security.interfaces.RSAPrivateCrtKey;
 import java.util.ArrayList;
 import java.util.Arrays;
 import java.util.Collections;
@@ -1682,8 +1684,15 @@
                 assertEquals(expected.getKey(alias, null),
                              actual.getKey(alias, null));
             } else {
-                assertEquals(expected.getKey(alias, PASSWORD_KEY),
-                             actual.getKey(alias, PASSWORD_KEY));
+                Key actualKey = actual.getKey(alias, PASSWORD_KEY);
+                Key expectedKey = expected.getKey(alias, PASSWORD_KEY);
+                if (actualKey instanceof RSAPrivateKey &&
+                    expectedKey instanceof RSAPrivateKey) {
+                  assertEquals(((RSAPrivateKey)actualKey).getModulus(),
+                               ((RSAPrivateKey)expectedKey).getModulus());
+                } else {
+                  assertEquals(actualKey, expectedKey);
+                }
             }
             assertEquals(expected.getCertificate(alias), actual.getCertificate(alias));
         }
diff --git a/tests/tests/libcorefileio/Android.mk b/tests/tests/libcorefileio/Android.mk
index cfd9775..bc224f6 100644
--- a/tests/tests/libcorefileio/Android.mk
+++ b/tests/tests/libcorefileio/Android.mk
@@ -21,7 +21,9 @@
 # and when built explicitly put it in the data partition
 LOCAL_MODULE_PATH := $(TARGET_OUT_DATA_APPS)
 
-LOCAL_STATIC_JAVA_LIBRARIES := ctstestrunner junit legacy-android-test
+LOCAL_STATIC_JAVA_LIBRARIES := ctstestrunner junit
+
+LOCAL_JAVA_LIBRARIES := android.test.base
 
 LOCAL_SRC_FILES := $(call all-java-files-under, src)
 
diff --git a/tests/tests/libcorefileio/AndroidTest.xml b/tests/tests/libcorefileio/AndroidTest.xml
index c2b230e..074a833 100644
--- a/tests/tests/libcorefileio/AndroidTest.xml
+++ b/tests/tests/libcorefileio/AndroidTest.xml
@@ -14,6 +14,7 @@
      limitations under the License.
 -->
 <configuration description="Config for CTS Legacy Libcore test cases">
+    <option name="test-suite-tag" value="cts" />
     <option name="config-descriptor:metadata" key="component" value="libcore" />
     <target_preparer class="com.android.tradefed.targetprep.suite.SuiteApkInstaller">
         <option name="cleanup-apks" value="true" />
diff --git a/tests/tests/location/Android.mk b/tests/tests/location/Android.mk
index a0b8142..6e90050 100644
--- a/tests/tests/location/Android.mk
+++ b/tests/tests/location/Android.mk
@@ -26,6 +26,8 @@
 
 LOCAL_MODULE_PATH := $(TARGET_OUT_DATA_APPS)
 
+LOCAL_JAVA_LIBRARIES := telephony-common android.test.base.stubs
+
 LOCAL_STATIC_JAVA_LIBRARIES := \
     compatibility-device-util ctstestrunner apache-commons-math
 
@@ -50,6 +52,8 @@
 # Tag this module as a cts test artifact
 LOCAL_COMPATIBILITY_SUITE := cts vts general-tests
 
+LOCAL_JAVA_LIBRARIES := telephony-common android.test.base.stubs
+
 LOCAL_STATIC_JAVA_LIBRARIES := compatibility-device-util ctstestrunner  apache-commons-math
 
 LOCAL_PROTOC_OPTIMIZE_TYPE := nano
diff --git a/tests/tests/location/AndroidManifest.xml b/tests/tests/location/AndroidManifest.xml
index 1357a38..6e3bbf1 100644
--- a/tests/tests/location/AndroidManifest.xml
+++ b/tests/tests/location/AndroidManifest.xml
@@ -28,9 +28,15 @@
     <uses-permission android:name="android.permission.ACCESS_LOCATION_EXTRA_COMMANDS"/>
 
     <uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
-    <uses-permission android:name="android.permission.ACCESS_WIFI_STATE" />>
+    <uses-permission android:name="android.permission.ACCESS_WIFI_STATE" />
+    <uses-permission android:name="android.permission.CHANGE_WIFI_STATE" />
     <uses-permission android:name="android.permission.INTERNET" />
 
+    <uses-permission android:name="android.permission.READ_PHONE_STATE"/>
+    <uses-permission android:name="android.permission.READ_SMS"/>
+    <uses-permission android:name="android.permission.READ_PHONE_NUMBERS"/>
+    <uses-permission android:name="android.permission.RECEIVE_SMS" />
+    <uses-permission android:name="android.permission.SEND_SMS" />
     <instrumentation android:name="android.support.test.runner.AndroidJUnitRunner"
                      android:targetPackage="android.location.cts"
                      android:label="CTS tests of android.location">
diff --git a/tests/tests/location/AndroidTest.xml b/tests/tests/location/AndroidTest.xml
index 4f55a93..c69e224 100644
--- a/tests/tests/location/AndroidTest.xml
+++ b/tests/tests/location/AndroidTest.xml
@@ -14,6 +14,7 @@
      limitations under the License.
 -->
 <configuration description="Config for CTS Location test cases">
+    <option name="test-suite-tag" value="cts" />
     <option name="config-descriptor:metadata" key="component" value="location" />
     <target_preparer class="com.android.compatibility.common.tradefed.targetprep.LocationCheck" />
     <target_preparer class="com.android.tradefed.targetprep.suite.SuiteApkInstaller">
diff --git a/tests/tests/location/src/android/location/cts/EmergencyCallMessageTest.java b/tests/tests/location/src/android/location/cts/EmergencyCallMessageTest.java
new file mode 100644
index 0000000..d7dbc89
--- /dev/null
+++ b/tests/tests/location/src/android/location/cts/EmergencyCallMessageTest.java
@@ -0,0 +1,333 @@
+package android.location.cts;
+
+import android.app.Activity;
+import android.app.PendingIntent;
+import android.content.BroadcastReceiver;
+import android.content.ContentResolver;
+import android.content.Context;
+import android.content.Intent;
+import android.content.IntentFilter;
+import android.content.pm.PackageManager;
+import android.net.Uri;
+import android.os.SystemClock;
+import android.telephony.SmsManager;
+import android.telephony.TelephonyManager;
+import android.test.AndroidTestCase;
+import android.text.TextUtils;
+import android.util.Log;
+
+import com.google.android.mms.ContentType;
+import com.google.android.mms.InvalidHeaderValueException;
+import com.google.android.mms.pdu.CharacterSets;
+import com.google.android.mms.pdu.EncodedStringValue;
+import com.google.android.mms.pdu.GenericPdu;
+import com.google.android.mms.pdu.PduBody;
+import com.google.android.mms.pdu.PduComposer;
+import com.google.android.mms.pdu.PduHeaders;
+import com.google.android.mms.pdu.PduParser;
+import com.google.android.mms.pdu.PduPart;
+import com.google.android.mms.pdu.SendConf;
+import com.google.android.mms.pdu.SendReq;
+
+import java.io.File;
+import java.io.FileOutputStream;
+import java.io.IOException;
+import java.util.Random;
+import java.util.concurrent.CountDownLatch;
+import java.util.concurrent.TimeUnit;
+
+/**
+ * Test sending SMS and MMS using {@link android.telephony.SmsManager}.
+ */
+public class EmergencyCallMessageTest extends GnssTestCase {
+
+    private static final String TAG = "EmergencyCallMSGTest";
+
+    private static final String ACTION_MMS_SENT = "CTS_MMS_SENT_ACTION";
+    private static final long DEFAULT_EXPIRY_TIME_SECS = TimeUnit.DAYS.toSeconds(7);
+    private static final long MMS_CONFIG_DELAY_MILLIS = TimeUnit.SECONDS.toMillis(1);
+    private static final int DEFAULT_PRIORITY = PduHeaders.PRIORITY_NORMAL;
+    private static final short DEFAULT_DATA_SMS_PORT = 8091;
+    private static final String PHONE_NUMBER_KEY = "android.cts.emergencycall.phonenumber";
+    private static final String SUBJECT = "CTS Emergency Call MMS Test";
+    private static final String MMS_MESSAGE_BODY = "CTS Emergency Call MMS test message body";
+    private static final String SMS_MESSAGE_BODY = "CTS Emergency Call Sms test message body";
+    private static final String SMS_DATA_MESSAGE_BODY =
+        "CTS Emergency Call Sms data test message body";
+    private static final String TEXT_PART_FILENAME = "text_0.txt";
+    private static final String SMIL_TEXT =
+            "<smil>" +
+                    "<head>" +
+                        "<layout>" +
+                            "<root-layout/>" +
+                            "<region height=\"100%%\" id=\"Text\" left=\"0%%\" top=\"0%%\" width=\"100%%\"/>" +
+                        "</layout>" +
+                    "</head>" +
+                    "<body>" +
+                        "<par dur=\"8000ms\">" +
+                            "<text src=\"%s\" region=\"Text\"/>" +
+                        "</par>" +
+                    "</body>" +
+            "</smil>";
+
+    private static final long SENT_TIMEOUT_MILLIS = TimeUnit.MINUTES.toMillis(5); // 5 minutes
+
+    private static final String PROVIDER_AUTHORITY = "emergencycallverifier";
+
+    private Random mRandom;
+    private SentReceiver mSentReceiver;
+    private TelephonyManager mTelephonyManager;
+    private PackageManager mPackageManager;
+
+    private static class SentReceiver extends BroadcastReceiver {
+        private boolean mSuccess;
+        private boolean mDone;
+        private final CountDownLatch mLatch;
+        public SentReceiver() {
+            mLatch =  new CountDownLatch(1);
+            mSuccess = false;
+            mDone = false;
+        }
+
+        @Override
+        public void onReceive(Context context, Intent intent) {
+            Log.i(TAG, "Action " + intent.getAction());
+            if (!ACTION_MMS_SENT.equals(intent.getAction())) {
+                return;
+            }
+            final int resultCode = getResultCode();
+            if (resultCode == Activity.RESULT_OK) {
+                final byte[] response = intent.getByteArrayExtra(SmsManager.EXTRA_MMS_DATA);
+                if (response != null) {
+                    final GenericPdu pdu = new PduParser(
+                            response, shouldParseContentDisposition()).parse();
+                    if (pdu != null && pdu instanceof SendConf) {
+                        final SendConf sendConf = (SendConf) pdu;
+                        if (sendConf.getResponseStatus() == PduHeaders.RESPONSE_STATUS_OK) {
+                            mSuccess = true;
+                        } else {
+                            Log.e(TAG, "SendConf response status=" + sendConf.getResponseStatus());
+                        }
+                    } else {
+                        Log.e(TAG, "Not a SendConf: " +
+                                (pdu != null ? pdu.getClass().getCanonicalName() : "NULL"));
+                    }
+                } else {
+                    Log.e(TAG, "Empty response");
+                }
+            } else {
+                Log.e(TAG, "Failure result=" + resultCode);
+                if (resultCode == SmsManager.MMS_ERROR_HTTP_FAILURE) {
+                    final int httpError = intent.getIntExtra(SmsManager.EXTRA_MMS_HTTP_STATUS, 0);
+                    Log.e(TAG, "HTTP failure=" + httpError);
+                }
+            }
+            mDone = true;
+            mLatch.countDown();
+        }
+
+        public boolean waitForSuccess(long timeoutMs) throws Exception {
+            mLatch.await(timeoutMs, TimeUnit.MILLISECONDS);
+            Log.i(TAG, "Wait for sent: done=" + mDone + ", success=" + mSuccess);
+            return mDone && mSuccess;
+        }
+    }
+
+    @Override
+    protected void setUp() throws Exception {
+        super.setUp();
+
+        mRandom = new Random(System.currentTimeMillis());
+        mTelephonyManager =
+                (TelephonyManager) mContext.getSystemService(Context.TELEPHONY_SERVICE);
+        mPackageManager = mContext.getPackageManager();
+    }
+
+    public void testSendSmsMessage() {
+        // this test is only for cts verifier
+        if (!isCtsVerifierTest()) {
+            return;
+        }
+        SmsManager smsManager = SmsManager.getDefault();
+        final String selfNumber = getPhoneNumber(mContext);
+        smsManager.sendTextMessage(selfNumber, null, SMS_MESSAGE_BODY, null, null);
+    }
+
+    public void testSendSmsDataMessage() {
+        // this test is only for cts verifier
+        if (!isCtsVerifierTest()) {
+            return;
+        }
+        SmsManager smsManager = SmsManager.getDefault();
+        final String selfNumber = getPhoneNumber(mContext);
+        smsManager.sendDataMessage(selfNumber, null, DEFAULT_DATA_SMS_PORT,
+            SMS_DATA_MESSAGE_BODY.getBytes(), null, null);
+    }
+
+    public void testSendMmsMessage() throws Exception {
+        // this test is only for cts verifier
+        if (!isCtsVerifierTest()) {
+            return;
+        }
+        if (!mPackageManager.hasSystemFeature(PackageManager.FEATURE_TELEPHONY)
+             || !doesSupportMMS()) {
+            Log.i(TAG, "testSendMmsMessage skipped: no telephony available or MMS not supported");
+            return;
+        }
+
+        Log.i(TAG, "testSendMmsMessage");
+        // Prime the MmsService so that MMS config is loaded
+        final SmsManager smsManager = SmsManager.getDefault();
+        // MMS config is loaded asynchronously. Wait a bit so it will be loaded.
+        try {
+            Thread.sleep(MMS_CONFIG_DELAY_MILLIS);
+        } catch (InterruptedException e) {
+            // Ignore
+        }
+
+        final Context context = getContext();
+        // Register sent receiver
+        mSentReceiver = new SentReceiver();
+        context.registerReceiver(mSentReceiver, new IntentFilter(ACTION_MMS_SENT));
+        // Create local provider file for sending PDU
+        final String fileName = "send." + String.valueOf(Math.abs(mRandom.nextLong())) + ".dat";
+        final File sendFile = new File(context.getCacheDir(), fileName);
+        final String selfNumber = getPhoneNumber(context);
+        assertTrue(!TextUtils.isEmpty(selfNumber));
+        final byte[] pdu = buildPdu(context, selfNumber, SUBJECT, MMS_MESSAGE_BODY);
+        assertNotNull(pdu);
+        assertTrue(writePdu(sendFile, pdu));
+        final Uri contentUri = (new Uri.Builder())
+                .authority(PROVIDER_AUTHORITY)
+                .path(fileName)
+                .scheme(ContentResolver.SCHEME_CONTENT)
+                .build();
+        // Send
+        final PendingIntent pendingIntent = PendingIntent.getBroadcast(
+                context, 0, new Intent(ACTION_MMS_SENT), 0);
+        smsManager.sendMultimediaMessage(context,
+                contentUri, null/*locationUrl*/, null/*configOverrides*/, pendingIntent);
+        assertTrue(mSentReceiver.waitForSuccess(SENT_TIMEOUT_MILLIS));
+        sendFile.delete();
+    }
+
+    private static boolean writePdu(File file, byte[] pdu) {
+        FileOutputStream writer = null;
+        try {
+            writer = new FileOutputStream(file);
+            writer.write(pdu);
+            return true;
+        } catch (final IOException e) {
+            String stackTrace = Log.getStackTraceString(e);
+            Log.i(TAG, stackTrace);
+            return false;
+        } finally {
+            if (writer != null) {
+                try {
+                    writer.close();
+                } catch (IOException e) {
+                }
+            }
+        }
+    }
+
+    private static byte[] buildPdu(Context context, String selfNumber, String subject,
+       String text) {
+        final SendReq req = new SendReq();
+        // From, per spec
+        req.setFrom(new EncodedStringValue(selfNumber));
+        // To
+        final String[] recipients = new String[1];
+        recipients[0] = selfNumber;
+        final EncodedStringValue[] encodedNumbers = EncodedStringValue.encodeStrings(recipients);
+        if (encodedNumbers != null) {
+            req.setTo(encodedNumbers);
+        }
+        // Subject
+        if (!TextUtils.isEmpty(subject)) {
+            req.setSubject(new EncodedStringValue(subject));
+        }
+        // Date
+        req.setDate(System.currentTimeMillis() / 1000);
+        // Body
+        final PduBody body = new PduBody();
+        // Add text part. Always add a smil part for compatibility, without it there
+        // may be issues on some carriers/client apps
+        final int size = addTextPart(body, text, true/* add text smil */);
+        req.setBody(body);
+        // Message size
+        req.setMessageSize(size);
+        // Message class
+        req.setMessageClass(PduHeaders.MESSAGE_CLASS_PERSONAL_STR.getBytes());
+        // Expiry
+        req.setExpiry(DEFAULT_EXPIRY_TIME_SECS);
+        // The following set methods throw InvalidHeaderValueException
+        try {
+            // Priority
+            req.setPriority(DEFAULT_PRIORITY);
+            // Delivery report
+            req.setDeliveryReport(PduHeaders.VALUE_NO);
+            // Read report
+            req.setReadReport(PduHeaders.VALUE_NO);
+        } catch (InvalidHeaderValueException e) {
+            return null;
+        }
+
+        return new PduComposer(context, req).make();
+    }
+
+    private static int addTextPart(PduBody pb, String message, boolean addTextSmil) {
+        final PduPart part = new PduPart();
+        // Set Charset if it's a text media.
+        part.setCharset(CharacterSets.UTF_8);
+        // Set Content-Type.
+        part.setContentType(ContentType.TEXT_PLAIN.getBytes());
+        // Set Content-Location.
+        part.setContentLocation(TEXT_PART_FILENAME.getBytes());
+        int index = TEXT_PART_FILENAME.lastIndexOf(".");
+        String contentId = (index == -1) ? TEXT_PART_FILENAME
+                : TEXT_PART_FILENAME.substring(0, index);
+        part.setContentId(contentId.getBytes());
+        part.setData(message.getBytes());
+        pb.addPart(part);
+        if (addTextSmil) {
+            final String smil = String.format(SMIL_TEXT, TEXT_PART_FILENAME);
+            addSmilPart(pb, smil);
+        }
+        return part.getData().length;
+    }
+
+    private static void addSmilPart(PduBody pb, String smil) {
+        final PduPart smilPart = new PduPart();
+        smilPart.setContentId("smil".getBytes());
+        smilPart.setContentLocation("smil.xml".getBytes());
+        smilPart.setContentType(ContentType.APP_SMIL.getBytes());
+        smilPart.setData(smil.getBytes());
+        pb.addPart(0, smilPart);
+    }
+
+    private static String getPhoneNumber(Context context) {
+        final TelephonyManager telephonyManager = (TelephonyManager) context.getSystemService(
+                Context.TELEPHONY_SERVICE);
+        String phoneNumber = telephonyManager.getLine1Number();
+        if (phoneNumber.trim().isEmpty()) {
+            phoneNumber = System.getProperty(PHONE_NUMBER_KEY);
+        }
+        return phoneNumber;
+    }
+
+    private static boolean shouldParseContentDisposition() {
+        return SmsManager
+                .getDefault()
+                .getCarrierConfigValues()
+                .getBoolean(SmsManager.MMS_CONFIG_SUPPORT_MMS_CONTENT_DISPOSITION, true);
+    }
+
+    private static boolean doesSupportMMS() {
+        return SmsManager
+                .getDefault()
+                .getCarrierConfigValues()
+                .getBoolean(SmsManager.MMS_CONFIG_MMS_ENABLED, true);
+    }
+
+}
\ No newline at end of file
diff --git a/tests/tests/location/src/android/location/cts/EmergencyCallWifiTest.java b/tests/tests/location/src/android/location/cts/EmergencyCallWifiTest.java
new file mode 100644
index 0000000..7ebb04a
--- /dev/null
+++ b/tests/tests/location/src/android/location/cts/EmergencyCallWifiTest.java
@@ -0,0 +1,109 @@
+/*
+ * Copyright (C) 2017 Google Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.location.cts;
+
+import android.content.BroadcastReceiver;
+import android.content.Context;
+import android.content.Intent;
+import android.content.IntentFilter;
+import android.net.wifi.ScanResult;
+import android.net.wifi.WifiInfo;
+import android.telephony.TelephonyManager;
+import android.net.wifi.WifiManager;
+import android.test.AndroidTestCase;
+import android.util.Log;
+
+import java.io.IOException;
+import java.net.InetSocketAddress;
+import java.net.Socket;
+import java.util.List;
+import java.util.concurrent.CountDownLatch;
+import java.util.concurrent.TimeUnit;
+
+/**
+ * This class is used to test Wifi realted features.
+ */
+public class EmergencyCallWifiTest extends GnssTestCase {
+
+    private final static String TAG = EmergencyCallWifiTest.class.getCanonicalName();
+    private final static String LATCH_NAME = "EmergencyCallWifiTest";
+    private final static String GOOGLE_URL = "www.google.com";
+    private final static int WEB_PORT = 80;
+    private static final int BATCH_SCAN_BSSID_LIMIT = 25;
+    private static final int WIFI_SCAN_TIMEOUT_SEC = 30;
+    private static final long SETTINGS_PERIOD_MS = TimeUnit.SECONDS.toMillis(4);
+    private static final long INTERNET_CONNECTION_TIMEOUT = TimeUnit.SECONDS.toMillis(2);
+    private static final int MIN_SCAN_COUNT = 1;
+
+    private WifiManager mWifiManager;
+    private Context mContext;
+    private WifiScanReceiver mWifiScanReceiver;
+    private int mScanCounter = 0;
+    private CountDownLatch mLatch;
+
+    @Override
+    public void setUp() throws Exception {
+        super.setUp();
+        mContext = getContext();
+        mWifiManager = (WifiManager) mContext.getSystemService(mContext.WIFI_SERVICE);
+        mWifiScanReceiver = new WifiScanReceiver();
+    }
+
+    public void testWifiScan() throws Exception {
+        mContext.registerReceiver(mWifiScanReceiver, new IntentFilter(
+                WifiManager.SCAN_RESULTS_AVAILABLE_ACTION));
+        mLatch = new CountDownLatch(1);
+        mWifiManager.startScan();
+        Log.d(TAG, "Waiting for wifiScan to complete.");
+        mLatch.await(WIFI_SCAN_TIMEOUT_SEC, TimeUnit.SECONDS);
+        if (mScanCounter < MIN_SCAN_COUNT) {
+            fail(String.format("Expected at least %d scans but only %d scans happened",
+            MIN_SCAN_COUNT, mScanCounter));
+        }
+    }
+
+    public void testWifiConnection() {
+        boolean isReachable =
+            isReachable(GOOGLE_URL, WEB_PORT, (int)INTERNET_CONNECTION_TIMEOUT);
+        assertTrue("Can not connect to google.com."
+         + " Please make sure device has the internet connection", isReachable);
+    }
+
+    private static boolean isReachable(String addr, int openPort, int timeOutMillis) {
+        try {
+            try (Socket soc = new Socket()) {
+                soc.connect(new InetSocketAddress(addr, openPort), timeOutMillis);
+            }
+            return true;
+        } catch (IOException ex) {
+            return false;
+        }
+    }
+
+    private class WifiScanReceiver extends BroadcastReceiver {
+        @Override
+        public void onReceive(Context c, Intent intent) {
+            List <ScanResult> scanResults = mWifiManager.getScanResults();
+            Log.d(TAG, String.format("Got scan results with size %d", scanResults.size()));
+            for (ScanResult result : scanResults) {
+                Log.d(TAG, result.toString());
+            }
+            mScanCounter++;
+            mLatch.countDown();
+        }
+    }
+}
\ No newline at end of file
diff --git a/tests/tests/location/src/android/location/cts/GnssHardwareInfoTest.java b/tests/tests/location/src/android/location/cts/GnssHardwareInfoTest.java
new file mode 100644
index 0000000..160e9c0
--- /dev/null
+++ b/tests/tests/location/src/android/location/cts/GnssHardwareInfoTest.java
@@ -0,0 +1,73 @@
+/*
+ * Copyright (C) 2017 Google Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.location.cts;
+
+import android.location.LocationManager;
+import android.util.Log;
+
+/**
+ * Test the {@link LocationManager#getGnssYearOfHardware} and
+ * {@link LocationManager#getGnssHardwareModelName} values.
+ */
+public class GnssHardwareInfoTest extends GnssTestCase {
+
+  private static final String TAG = "GnssHardwareInfoTest";
+  private static final int MIN_HARDWARE_YEAR = 2015;
+
+  /**
+   * Minimum plausible descriptive hardware model name length, e.g. "ABC1" for first GNSS version
+   * ever shipped by ABC company.
+   */
+  private static final int MIN_HARDWARE_MODEL_NAME_LENGTH = 4;
+  private static final int MIN_HARDWARE_YEAR_FOR_VALID_STRING = 2018;
+
+  @Override
+  protected void setUp() throws Exception {
+    super.setUp();
+    mTestLocationManager = new TestLocationManager(getContext());
+  }
+
+  /**
+   * Verify hardware year is reported as 2015 or higher, with exception that in 2015, some
+   * devies did not implement the underlying HAL API, and thus will report 0.
+   */
+  public void testHardwareYear() throws Exception {
+    int gnssHardwareYear = mTestLocationManager.getLocationManager().getGnssYearOfHardware();
+    // Allow 0 until 2019, as older, upgrading devices may report 0.
+    assertTrue("Hardware year must be 2015 or higher",
+        gnssHardwareYear >= MIN_HARDWARE_YEAR || gnssHardwareYear == 0);
+  }
+
+  /**
+   * Verify GNSS hardware model year is reported as a valid, descriptive value.
+   * Descriptive is limited to a character count, and not the older values.
+   */
+  public void testHardwareModelName() throws Exception {
+    String gnssHardwareModelName =
+        mTestLocationManager.getLocationManager().getGnssHardwareModelName();
+    assertTrue("gnssHardwareModelName must not be null", gnssHardwareModelName != null);
+    assertTrue("gnssHardwareModelName must be descriptive - at least 4 characters long",
+        gnssHardwareModelName.length() >= MIN_HARDWARE_MODEL_NAME_LENGTH);
+
+    if (mTestLocationManager.getLocationManager().getGnssYearOfHardware() >=
+        MIN_HARDWARE_YEAR_FOR_VALID_STRING) {
+      assertFalse("gnssHardwareModelName must be descriptive - not default value",
+          gnssHardwareModelName.contentEquals(
+              LocationManager.GNSS_HARDWARE_MODEL_NAME_UNKNOWN));
+    }
+  }
+}
\ No newline at end of file
diff --git a/tests/tests/location/src/android/location/cts/GnssLocationRateChangeTest.java b/tests/tests/location/src/android/location/cts/GnssLocationRateChangeTest.java
new file mode 100644
index 0000000..cd230d5
--- /dev/null
+++ b/tests/tests/location/src/android/location/cts/GnssLocationRateChangeTest.java
@@ -0,0 +1,149 @@
+/*
+ * Copyright (C) 2017 Google Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.location.cts;
+
+/**
+ * Test the "gps" location output works through various rate changes
+ *
+ * Tests:
+ * 1. Toggle through various rates.
+ * 2. Mix toggling through various rates with start & stop.
+ *
+ * Inspired by bugs 65246279, 65425110
+ */
+
+public class GnssLocationRateChangeTest extends GnssTestCase {
+
+    private static final String TAG = "GnssLocationRateChangeTest";
+    private static final int LOCATION_TO_COLLECT_COUNT = 1;
+
+    private TestLocationListener mLocationListenerMain;
+    private TestLocationListener mLocationListenerAfterRateChanges;
+    // Various rates, where underlying GNSS hardware states may enter different modes
+    private static final int[] TBF_MSEC = {0, 4_000, 250_000, 6_000_000, 10, 1_000, 16_000, 64_000};
+    private static final int LOOPS_FOR_STRESS_TEST = 20;
+
+    @Override
+    protected void setUp() throws Exception {
+        super.setUp();
+        mTestLocationManager = new TestLocationManager(getContext());
+        // Using separate listeners, so the await trigger for the after-rate-changes listener is
+        // independent of any possible locations that flow during setup, and rate change stress
+        // testing
+        mLocationListenerMain = new TestLocationListener(LOCATION_TO_COLLECT_COUNT);
+        mLocationListenerAfterRateChanges = new TestLocationListener(LOCATION_TO_COLLECT_COUNT);
+    }
+
+    @Override
+    protected void tearDown() throws Exception {
+        // Unregister listeners
+        if (mLocationListenerMain != null) {
+            mTestLocationManager.removeLocationUpdates(mLocationListenerMain);
+        }
+        if (mLocationListenerAfterRateChanges != null) {
+            mTestLocationManager.removeLocationUpdates(mLocationListenerAfterRateChanges);
+        }
+        super.tearDown();
+    }
+
+    /**
+     * Requests (GPS) Locations at various rates that may stress the underlying GNSS software
+     * and firmware state machine layers, ensuring Location output
+     * remains responsive after all is done.
+     */
+    public void testVariedRates() throws Exception {
+        SoftAssert softAssert = new SoftAssert(TAG);
+        mTestLocationManager.requestLocationUpdates(mLocationListenerMain);
+        softAssert.assertTrue("Location should be received at test start",
+                mLocationListenerMain.await());
+
+        for (int timeBetweenLocationsMsec : TBF_MSEC) {
+            // Rapidly change rates requested, to ensure GNSS provider state changes can handle this
+            mTestLocationManager.requestLocationUpdates(mLocationListenerMain,
+                    timeBetweenLocationsMsec);
+        }
+        mTestLocationManager.removeLocationUpdates(mLocationListenerMain);
+
+        mTestLocationManager.requestLocationUpdates(mLocationListenerAfterRateChanges);
+        softAssert.assertTrue("Location should be received at test end",
+                mLocationListenerAfterRateChanges.await());
+
+        softAssert.assertAll();
+    }
+
+    /**
+     * Requests (GPS) Locations at various rates, and toggles between requests and removals,
+     * that may stress the underlying GNSS software
+     * and firmware state machine layers, ensuring Location output remains responsive
+     * after all is done.
+     */
+    public void testVariedRatesOnOff() throws Exception {
+        SoftAssert softAssert = new SoftAssert(TAG);
+        mTestLocationManager.requestLocationUpdates(mLocationListenerMain);
+        softAssert.assertTrue("Location should be received at test start",
+                mLocationListenerMain.await());
+
+        for (int timeBetweenLocationsMsec : TBF_MSEC) {
+            // Rapidly change rates requested, to ensure GNSS provider state changes can handle this
+            mTestLocationManager.requestLocationUpdates(mLocationListenerMain,
+                    timeBetweenLocationsMsec);
+            // Also flip the requests on and off quickly
+            mTestLocationManager.removeLocationUpdates(mLocationListenerMain);
+        }
+
+        mTestLocationManager.requestLocationUpdates(mLocationListenerAfterRateChanges);
+        softAssert.assertTrue("Location should be received at test end",
+                mLocationListenerAfterRateChanges.await());
+
+        softAssert.assertAll();
+    }
+
+    /**
+     * Requests (GPS) Locations at various rates, and toggles between requests and removals,
+     * in multiple loops to provide additional stress to the underlying GNSS software
+     * and firmware state machine layers, ensuring Location output remains responsive
+     * after all is done.
+     */
+    public void testVariedRatesRepetitive() throws Exception {
+        SoftAssert softAssert = new SoftAssert(TAG);
+        mTestLocationManager.requestLocationUpdates(mLocationListenerMain);
+        softAssert.assertTrue("Location should be received at test start",
+                mLocationListenerMain.await());
+
+        // Two loops, first without removes, then with removes
+        for (int i = 0; i < LOOPS_FOR_STRESS_TEST; i++) {
+            for (int timeBetweenLocationsMsec : TBF_MSEC) {
+               mTestLocationManager.requestLocationUpdates(mLocationListenerMain,
+                        timeBetweenLocationsMsec);
+            }
+        }
+        for (int i = 0; i < LOOPS_FOR_STRESS_TEST; i++) {
+            for (int timeBetweenLocationsMsec : TBF_MSEC) {
+                mTestLocationManager.requestLocationUpdates(mLocationListenerMain,
+                        timeBetweenLocationsMsec);
+                // Also flip the requests on and off quickly
+                mTestLocationManager.removeLocationUpdates(mLocationListenerMain);
+            }
+        }
+
+        mTestLocationManager.requestLocationUpdates(mLocationListenerAfterRateChanges);
+        softAssert.assertTrue("Location should be received at test end",
+                mLocationListenerAfterRateChanges.await());
+
+        softAssert.assertAll();
+    }
+}
diff --git a/tests/tests/location/src/android/location/cts/GnssLocationValuesTest.java b/tests/tests/location/src/android/location/cts/GnssLocationValuesTest.java
index 3411914..d2499a0 100644
--- a/tests/tests/location/src/android/location/cts/GnssLocationValuesTest.java
+++ b/tests/tests/location/src/android/location/cts/GnssLocationValuesTest.java
@@ -16,9 +16,7 @@
 
 package android.location.cts;
 
-import android.location.Criteria;
 import android.location.Location;
-import android.location.LocationManager;
 import android.util.Log;
 
 /**
@@ -133,14 +131,18 @@
         success);
 
     SoftAssert softAssert = new SoftAssert(TAG);
+    // don't check speed of first GNSS location - it may not be ready in some cases
+    boolean checkSpeed = false;
     for (Location location : mLocationListener.getReceivedLocationList()) {
-      checkLocationRegularFields(softAssert, location);
+      checkLocationRegularFields(softAssert, location, checkSpeed);
+      checkSpeed = true;
     }
 
     softAssert.assertAll();
   }
 
-  public static void checkLocationRegularFields(SoftAssert softAssert, Location location) {
+  public static void checkLocationRegularFields(SoftAssert softAssert, Location location,
+                                                boolean checkSpeed) {
     // For the altitude: the unit is meter
     // The lowest exposed land on Earth is at the Dead Sea shore, at -413 meters.
     // Whilst University of Tokyo Atacama Obsevatory is on 5,640m above sea level.
@@ -177,13 +179,16 @@
     softAssert.assertTrue("Latitude should be in the range of [-90.0, 90.0] degrees",
         location.getLatitude() >= -90 && location.getLatitude() <= 90);
 
-    softAssert.assertTrue("All GNSS locations generated by the LocationManager "
-        + "must have speeds.", location.hasSpeed());
+    if (checkSpeed) {
+      softAssert.assertTrue("All but the first GNSS location from LocationManager "
+              + "must have speeds.", location.hasSpeed());
+    }
 
-    // For the speed, during the cts test device shouldn't move faster than 1m/s
+    // For the speed, during the cts test device shouldn't move faster than 1m/s, but allowing up
+    // to 5m/s for possible early fix noise in moderate signal test environments
     if(location.hasSpeed()) {
-      softAssert.assertTrue("In the test enviorment, speed should be in the range of [0, 1] m/s",
-          location.getSpeed() >= 0 && location.getSpeed() <= 1);
+      softAssert.assertTrue("In the test enviorment, speed should be in the range of [0, 5] m/s",
+          location.getSpeed() >= 0 && location.getSpeed() <= 5);
     }
 
   }
diff --git a/tests/tests/location/src/android/location/cts/GnssPseudorangeVerificationTest.java b/tests/tests/location/src/android/location/cts/GnssPseudorangeVerificationTest.java
index ec78ea3..d86d612 100644
--- a/tests/tests/location/src/android/location/cts/GnssPseudorangeVerificationTest.java
+++ b/tests/tests/location/src/android/location/cts/GnssPseudorangeVerificationTest.java
@@ -39,7 +39,7 @@
   private static final int MEASUREMENT_EVENTS_TO_COLLECT_COUNT = 10;
   private static final int MIN_SATELLITES_REQUIREMENT = 4;
   private static final double SECONDS_PER_NANO = 1.0e-9;
-  private static final double POSITION_THRESHOLD_IN_DEGREES = 0.003; // degrees (~= 300 meters)
+
   // GPS/GLONASS: according to http://cdn.intechopen.com/pdfs-wm/27712.pdf, the pseudorange in time
   // is 65-83 ms, which is 18 ms range.
   // GLONASS: orbit is a bit closer than GPS, so we add 0.003ms to the range, hence deltaiSeconds
@@ -58,6 +58,12 @@
   // that are the short end of the range.
   private static final double PSEUDORANGE_THRESHOLD_BEIDOU_QZSS_IN_SEC = 0.076;
 
+  private static final float LOW_ENOUGH_POSITION_UNCERTAINTY_METERS = 100;
+  private static final float LOW_ENOUGH_VELOCITY_UNCERTAINTY_MPS = 5;
+  private static final float HORIZONTAL_OFFSET_FLOOR_METERS = 10;
+  private static final float HORIZONTAL_OFFSET_SIGMA = 3;  // 3 * the ~68%ile level
+  private static final float HORIZONTAL_OFFSET_FLOOR_MPS = 1;
+
   private TestGnssMeasurementListener mMeasurementListener;
   private TestLocationListener mLocationListener;
 
@@ -224,92 +230,173 @@
     }
   }
 
-  /*
- * Use pseudorange calculation library to calculate position then compare to location from
- * Location Manager.
- */
-  @CddTest(requirement="7.3.3")
-  public void testPseudoPosition() throws Exception {
-    // Checks if Gnss hardware feature is present, skips test (pass) if not,
-    // and hard asserts that Location/Gnss (Provider) is turned on if is Cts Verifier.
-    // From android O, CTS tests should run in the lab with GPS signal.
-    if (!TestMeasurementUtil.canTestRunOnCurrentDevice(mTestLocationManager,
-        TAG, MIN_HARDWARE_YEAR_MEASUREMENTS_REQUIRED, true)) {
-      return;
+    /*
+     * Use pseudorange calculation library to calculate position then compare to location from
+     * Location Manager.
+     */
+    @CddTest(requirement = "7.3.3")
+    public void testPseudoPosition() throws Exception {
+        // Checks if Gnss hardware feature is present, skips test (pass) if not,
+        // and hard asserts that Location/Gnss (Provider) is turned on if is Cts Verifier.
+        // From android O, CTS tests should run in the lab with GPS signal.
+        if (!TestMeasurementUtil.canTestRunOnCurrentDevice(mTestLocationManager,
+                TAG, MIN_HARDWARE_YEAR_MEASUREMENTS_REQUIRED, true)) {
+            return;
+        }
+
+        mLocationListener = new TestLocationListener(LOCATION_TO_COLLECT_COUNT);
+        mTestLocationManager.requestLocationUpdates(mLocationListener);
+
+        mMeasurementListener = new TestGnssMeasurementListener(TAG,
+                MEASUREMENT_EVENTS_TO_COLLECT_COUNT, true);
+        mTestLocationManager.registerGnssMeasurementCallback(mMeasurementListener);
+
+        boolean success = mLocationListener.await();
+
+        List<Location> receivedLocationList = mLocationListener.getReceivedLocationList();
+        assertTrue("Time elapsed without getting enough location fixes."
+                        + " Possibly, the test has been run deep indoors."
+                        + " Consider retrying test outdoors.",
+                success && receivedLocationList.size() > 0);
+        Location locationFromApi = receivedLocationList.get(0);
+
+        // Since we are checking the eventCount later, there is no need to check the return value
+        // here.
+        mMeasurementListener.await();
+
+        List<GnssMeasurementsEvent> events = mMeasurementListener.getEvents();
+        int eventCount = events.size();
+        Log.i(TAG, "Number of Gps Event received = " + eventCount);
+        int gnssYearOfHardware = mTestLocationManager.getLocationManager().getGnssYearOfHardware();
+        if (eventCount == 0 && gnssYearOfHardware < MIN_HARDWARE_YEAR_MEASUREMENTS_REQUIRED) {
+            return;
+        }
+
+        Log.i(TAG, "This is a device from 2016 or later.");
+        assertTrue("GnssMeasurementEvent count: expected > 0, received = " + eventCount,
+                eventCount > 0);
+
+        PseudorangePositionVelocityFromRealTimeEvents mPseudorangePositionFromRealTimeEvents
+                = new PseudorangePositionVelocityFromRealTimeEvents();
+        mPseudorangePositionFromRealTimeEvents.setReferencePosition(
+                (int) (locationFromApi.getLatitude() * 1E7),
+                (int) (locationFromApi.getLongitude() * 1E7),
+                (int) (locationFromApi.getAltitude() * 1E7));
+
+        Log.i(TAG, "Location from Location Manager"
+                + ", Latitude:" + locationFromApi.getLatitude()
+                + ", Longitude:" + locationFromApi.getLongitude()
+                + ", Altitude:" + locationFromApi.getAltitude());
+
+        // Ensure at least some calculated locations have a reasonably low uncertainty
+        boolean someLocationsHaveLowPosUnc = false;
+        boolean someLocationsHaveLowVelUnc = false;
+
+        int totalCalculatedLocationCnt = 0;
+        for (GnssMeasurementsEvent event : events) {
+            // In mMeasurementListener.getEvents() we already filtered out events, at this point
+            // every event will have at least 4 satellites in one constellation.
+            mPseudorangePositionFromRealTimeEvents.computePositionVelocitySolutionsFromRawMeas(
+                    event);
+            double[] calculatedLatLngAlt =
+                    mPseudorangePositionFromRealTimeEvents.getPositionSolutionLatLngDeg();
+            // it will return NaN when there is no enough measurements to calculate the position
+            if (Double.isNaN(calculatedLatLngAlt[0])) {
+                continue;
+            } else {
+                totalCalculatedLocationCnt++;
+                Log.i(TAG, "Calculated Location"
+                        + ", Latitude:" + calculatedLatLngAlt[0]
+                        + ", Longitude:" + calculatedLatLngAlt[1]
+                        + ", Altitude:" + calculatedLatLngAlt[2]);
+
+                double[] posVelUncertainties =
+                        mPseudorangePositionFromRealTimeEvents.getPositionVelocityUncertaintyEnu();
+
+                double horizontalPositionUncertaintyMeters =
+                        Math.sqrt(posVelUncertainties[0] * posVelUncertainties[0]
+                                + posVelUncertainties[1] * posVelUncertainties[1]);
+
+                // Tolerate large offsets, when the device reports a large uncertainty - while also
+                // ensuring (here) that some locations are produced before the test ends
+                // with a reasonably low set of error estimates
+                if (horizontalPositionUncertaintyMeters < LOW_ENOUGH_POSITION_UNCERTAINTY_METERS) {
+                    someLocationsHaveLowPosUnc = true;
+                }
+
+                // Root-sum-sqaure the WLS, and device generated 68%ile accuracies is a conservative
+                // 68%ile offset (given likely correlated errors) - then this is scaled by
+                // initially 3 sigma to give a high enough tolerance to make the test tolerant
+                // enough of noise to pass reliably.  Floor adds additional robustness in case of
+                // small errors and small error estimates.
+                double horizontalOffsetThresholdMeters = HORIZONTAL_OFFSET_SIGMA * Math.sqrt(
+                        horizontalPositionUncertaintyMeters * horizontalPositionUncertaintyMeters
+                                + locationFromApi.getAccuracy() * locationFromApi.getAccuracy())
+                        + HORIZONTAL_OFFSET_FLOOR_METERS;
+
+                Location calculatedLocation = new Location("gps");
+                calculatedLocation.setLatitude(calculatedLatLngAlt[0]);
+                calculatedLocation.setLongitude(calculatedLatLngAlt[1]);
+                calculatedLocation.setAltitude(calculatedLatLngAlt[2]);
+
+                double horizontalOffset = calculatedLocation.distanceTo(locationFromApi);
+
+                Log.i(TAG, "Calculated Location Offset: " + horizontalOffset
+                        + ", Threshold: " + horizontalOffsetThresholdMeters);
+                assertTrue("Latitude & Longitude calculated from pseudoranges should be close to "
+                                + "those reported from Location Manager.  Offset = "
+                                + horizontalOffset + " meters. Threshold = "
+                                + horizontalOffsetThresholdMeters + " meters ",
+                        horizontalOffset < horizontalOffsetThresholdMeters);
+
+                //TODO: Check for the altitude offset
+
+                // This 2D velocity uncertainty is conservatively larger than speed uncertainty
+                // as it also contains the effect of bearing uncertainty at a constant speed
+                double horizontalVelocityUncertaintyMps =
+                        Math.sqrt(posVelUncertainties[4] * posVelUncertainties[4]
+                                + posVelUncertainties[5] * posVelUncertainties[5]);
+                if (horizontalVelocityUncertaintyMps < LOW_ENOUGH_VELOCITY_UNCERTAINTY_MPS) {
+                    someLocationsHaveLowVelUnc = true;
+                }
+
+                // Assume 1m/s uncertainty from API, for this test, if not provided
+                float speedUncFromApiMps = locationFromApi.hasSpeedAccuracy()
+                        ? locationFromApi.getSpeedAccuracyMetersPerSecond()
+                        : HORIZONTAL_OFFSET_FLOOR_MPS;
+
+                // Similar 3-standard deviation plus floor threshold as
+                // horizontalOffsetThresholdMeters above
+                double horizontalSpeedOffsetThresholdMps = HORIZONTAL_OFFSET_SIGMA * Math.sqrt(
+                        horizontalVelocityUncertaintyMps * horizontalVelocityUncertaintyMps
+                                + speedUncFromApiMps * speedUncFromApiMps)
+                        + HORIZONTAL_OFFSET_FLOOR_MPS;
+
+                double[] calculatedVelocityEnuMps =
+                        mPseudorangePositionFromRealTimeEvents.getVelocitySolutionEnuMps();
+                double calculatedHorizontalSpeedMps =
+                        Math.sqrt(calculatedVelocityEnuMps[0] * calculatedVelocityEnuMps[0]
+                                + calculatedVelocityEnuMps[1] * calculatedVelocityEnuMps[1]);
+
+                Log.i(TAG, "Calculated Speed: " + calculatedHorizontalSpeedMps
+                        + ", Reported Speed: " + locationFromApi.getSpeed()
+                        + ", Threshold: " + horizontalSpeedOffsetThresholdMps);
+                assertTrue("Speed (" + calculatedHorizontalSpeedMps + " m/s) calculated from"
+                                + " pseudoranges should be close to the speed ("
+                                + locationFromApi.getSpeed() + " m/s) reported from"
+                                + " Location Manager.",
+                        Math.abs(calculatedHorizontalSpeedMps - locationFromApi.getSpeed())
+                                < horizontalSpeedOffsetThresholdMps);
+            }
+        }
+
+        assertTrue("Calculated Location Count should be greater than 0.",
+                totalCalculatedLocationCnt > 0);
+        assertTrue("Calculated Horizontal Location Uncertainty should at least once be less than "
+                        + LOW_ENOUGH_POSITION_UNCERTAINTY_METERS,
+                someLocationsHaveLowPosUnc);
+        assertTrue("Calculated Horizontal Velocity Uncertainty should at least once be less than "
+                        + LOW_ENOUGH_VELOCITY_UNCERTAINTY_MPS,
+                someLocationsHaveLowVelUnc);
     }
-
-    mLocationListener = new TestLocationListener(LOCATION_TO_COLLECT_COUNT);
-    mTestLocationManager.requestLocationUpdates(mLocationListener);
-
-    mMeasurementListener = new TestGnssMeasurementListener(TAG,
-                                     MEASUREMENT_EVENTS_TO_COLLECT_COUNT, true);
-    mTestLocationManager.registerGnssMeasurementCallback(mMeasurementListener);
-
-    boolean success = mLocationListener.await();
-
-    List<Location> receivedLocationList = mLocationListener.getReceivedLocationList();
-    assertTrue("Time elapsed without getting enough location fixes."
-            + " Possibly, the test has been run deep indoors."
-            + " Consider retrying test outdoors.",
-        success && receivedLocationList.size() > 0);
-    Location locationFromApi = receivedLocationList.get(0);
-
-    // Since we are checking the eventCount later, there is no need to check the return value here.
-    mMeasurementListener.await();
-
-    List<GnssMeasurementsEvent> events = mMeasurementListener.getEvents();
-    int eventCount = events.size();
-    Log.i(TAG, "Number of Gps Event received = " + eventCount);
-    int gnssYearOfHardware = mTestLocationManager.getLocationManager().getGnssYearOfHardware();
-    if (eventCount == 0 && gnssYearOfHardware < MIN_HARDWARE_YEAR_MEASUREMENTS_REQUIRED) {
-      return;
-    }
-
-    Log.i(TAG, "This is a device from 2016 or later.");
-    assertTrue("GnssMeasurementEvent count: expected > 0, received = " + eventCount,
-        eventCount > 0);
-
-    PseudorangePositionVelocityFromRealTimeEvents mPseudorangePositionFromRealTimeEvents
-        = new PseudorangePositionVelocityFromRealTimeEvents();
-    mPseudorangePositionFromRealTimeEvents.setReferencePosition(
-        (int) (locationFromApi.getLatitude() * 1E7),
-        (int) (locationFromApi.getLongitude() * 1E7),
-        (int) (locationFromApi.getAltitude()  * 1E7));
-
-    Log.i(TAG, "Location from Location Manager"
-        + ", Latitude:" + locationFromApi.getLatitude()
-        + ", Longitude:" + locationFromApi.getLongitude()
-        + ", Altitude:" + locationFromApi.getAltitude());
-
-
-    int totalCalculatedLocationCnt = 0;
-    for(GnssMeasurementsEvent event : events){
-      // In mMeasurementListener.getEvents() we already filtered out events, at this point every
-      // event will have at least 4 satellites in one constellation.
-      mPseudorangePositionFromRealTimeEvents.computePositionVelocitySolutionsFromRawMeas(event);
-      double[] calculatedLocation =
-          mPseudorangePositionFromRealTimeEvents.getPositionSolutionLatLngDeg();
-      // it will return NaN when there is no enough measurements to calculate the position
-      if (Double.isNaN(calculatedLocation[0])) {
-        continue;
-      }
-      else {
-        totalCalculatedLocationCnt ++;
-        Log.i(TAG, "Calculated Location"
-            + ", Latitude:" + calculatedLocation[0]
-            + ", Longitude:" + calculatedLocation[1]
-            + ", Altitude:" + calculatedLocation[2]);
-
-        assertTrue("Latitude should be close to " + locationFromApi.getLatitude(),
-            Math.abs(calculatedLocation[0] - locationFromApi.getLatitude())
-                < POSITION_THRESHOLD_IN_DEGREES);
-        assertTrue("Longitude should be close to" + locationFromApi.getLongitude(),
-            Math.abs(calculatedLocation[1] - locationFromApi.getLongitude())
-                < POSITION_THRESHOLD_IN_DEGREES);
-        //TODO: Check for the altitude and position uncertainty.
-      }
-    }
-    assertTrue("Calculated Location Count should be greater than 0.",
-        totalCalculatedLocationCnt > 0);
-  }
 }
diff --git a/tests/tests/location/src/android/location/cts/MmsPduProvider.java b/tests/tests/location/src/android/location/cts/MmsPduProvider.java
new file mode 100644
index 0000000..76d859e
--- /dev/null
+++ b/tests/tests/location/src/android/location/cts/MmsPduProvider.java
@@ -0,0 +1,78 @@
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.location.cts;
+
+import android.content.ContentProvider;
+import android.content.ContentValues;
+import android.database.Cursor;
+import android.net.Uri;
+import android.os.ParcelFileDescriptor;
+import android.text.TextUtils;
+
+import java.io.File;
+import java.io.FileNotFoundException;
+
+/**
+ * A simple provider to send MMS PDU to platform MMS service
+ */
+public class MmsPduProvider extends ContentProvider {
+    @Override
+    public boolean onCreate() {
+        return true;
+    }
+
+    @Override
+    public Cursor query(Uri uri, String[] projection, String selection, String[] selectionArgs,
+            String sortOrder) {
+        // Not supported
+        return null;
+    }
+
+    @Override
+    public String getType(Uri uri) {
+        // Not supported
+        return null;
+    }
+
+    @Override
+    public Uri insert(Uri uri, ContentValues values) {
+        // Not supported
+        return null;
+    }
+
+    @Override
+    public int delete(Uri uri, String selection, String[] selectionArgs) {
+        // Not supported
+        return 0;
+    }
+
+    @Override
+    public int update(Uri uri, ContentValues values, String selection, String[] selectionArgs) {
+        // Not supported
+        return 0;
+    }
+
+    @Override
+    public ParcelFileDescriptor openFile(Uri uri, String fileMode) throws FileNotFoundException {
+        File file = new File(getContext().getCacheDir(), uri.getPath());
+        int mode = (TextUtils.equals(fileMode, "r") ? ParcelFileDescriptor.MODE_READ_ONLY :
+                ParcelFileDescriptor.MODE_WRITE_ONLY
+                        |ParcelFileDescriptor.MODE_TRUNCATE
+                        |ParcelFileDescriptor.MODE_CREATE);
+        return ParcelFileDescriptor.open(file, mode);
+    }
+}
diff --git a/tests/tests/location/src/android/location/cts/TestLocationManager.java b/tests/tests/location/src/android/location/cts/TestLocationManager.java
index 390d450..370999b 100644
--- a/tests/tests/location/src/android/location/cts/TestLocationManager.java
+++ b/tests/tests/location/src/android/location/cts/TestLocationManager.java
@@ -105,11 +105,11 @@
      *
      * @param locationListener location listener for request
      */
-    public void requestLocationUpdates(LocationListener locationListener) {
+    public void requestLocationUpdates(LocationListener locationListener, int minTimeMsec) {
         if (mLocationManager.getProvider(LocationManager.GPS_PROVIDER) != null) {
             Log.i(TAG, "Request Location updates.");
             mLocationManager.requestLocationUpdates(LocationManager.GPS_PROVIDER,
-                    0 /* minTime*/,
+                    minTimeMsec,
                     0 /* minDistance */,
                     locationListener,
                     Looper.getMainLooper());
@@ -117,6 +117,15 @@
     }
 
     /**
+     * See {@code LocationManager#requestLocationUpdates}.
+     *
+     * @param locationListener location listener for request
+     */
+    public void requestLocationUpdates(LocationListener locationListener) {
+        requestLocationUpdates(locationListener, 0 /* minTimeMsec */);
+    }
+
+    /**
      * See {@code LocationManager#requestNetworkLocationUpdates}.
      *
      * @param locationListener location listener for request
diff --git a/tests/tests/location2/Android.mk b/tests/tests/location2/Android.mk
index 5b9f327..08993d5 100644
--- a/tests/tests/location2/Android.mk
+++ b/tests/tests/location2/Android.mk
@@ -24,7 +24,9 @@
 # Tag this module as a cts test artifact
 LOCAL_COMPATIBILITY_SUITE := cts vts general-tests
 
-LOCAL_STATIC_JAVA_LIBRARIES := ctstestrunner junit legacy-android-test
+LOCAL_STATIC_JAVA_LIBRARIES := ctstestrunner junit
+
+LOCAL_JAVA_LIBRARIES := android.test.base
 
 LOCAL_SRC_FILES := $(call all-java-files-under, src)
 
diff --git a/tests/tests/location2/AndroidTest.xml b/tests/tests/location2/AndroidTest.xml
index 3443098..412fd1d 100644
--- a/tests/tests/location2/AndroidTest.xml
+++ b/tests/tests/location2/AndroidTest.xml
@@ -14,6 +14,7 @@
      limitations under the License.
 -->
 <configuration description="Config for CTS Location test cases">
+    <option name="test-suite-tag" value="cts" />
     <option name="config-descriptor:metadata" key="component" value="location" />
     <target_preparer class="com.android.compatibility.common.tradefed.targetprep.LocationCheck" />
     <target_preparer class="com.android.tradefed.targetprep.suite.SuiteApkInstaller">
diff --git a/tests/tests/media/Android.mk b/tests/tests/media/Android.mk
index e03eb76..75c1f07 100644
--- a/tests/tests/media/Android.mk
+++ b/tests/tests/media/Android.mk
@@ -51,7 +51,6 @@
     ctstestrunner \
     ctstestserver \
     junit \
-    legacy-android-test \
     ndkaudio \
     testng
 
@@ -75,6 +74,7 @@
 #LOCAL_SDK_VERSION := current
 
 LOCAL_JAVA_LIBRARIES += android.test.runner org.apache.http.legacy
+LOCAL_JAVA_LIBRARIES += android.test.base
 
 # Tag this module as a cts test artifact
 LOCAL_COMPATIBILITY_SUITE := cts vts general-tests
diff --git a/tests/tests/media/AndroidManifest.xml b/tests/tests/media/AndroidManifest.xml
index 482cd6a..44af14d 100644
--- a/tests/tests/media/AndroidManifest.xml
+++ b/tests/tests/media/AndroidManifest.xml
@@ -30,7 +30,7 @@
     <uses-permission android:name="android.permission.SET_VOLUME_KEY_LONG_PRESS_LISTENER" />
     <uses-permission android:name="android.permission.SET_MEDIA_KEY_LISTENER" />
 
-    <application>
+    <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/tests/media/AndroidTest.xml b/tests/tests/media/AndroidTest.xml
index f7d06b1..e9ecdd3 100644
--- a/tests/tests/media/AndroidTest.xml
+++ b/tests/tests/media/AndroidTest.xml
@@ -14,6 +14,7 @@
      limitations under the License.
 -->
 <configuration description="Config for CTS Media test cases">
+    <option name="test-suite-tag" value="cts" />
     <option name="config-descriptor:metadata" key="component" value="media" />
     <target_preparer class="com.android.compatibility.common.tradefed.targetprep.MediaPreparer">
         <option name="images-only" value="true" />
@@ -29,6 +30,8 @@
     </target_preparer>
     <test class="com.android.tradefed.testtype.AndroidJUnitTest" >
         <option name="package" value="android.media.cts" />
+        <!-- setup can be expensive so limit the number of shards -->
+        <option name="ajur-max-shard" value="5" />
         <!-- test-timeout unit is ms, value = 30 min -->
         <option name="test-timeout" value="1800000" />
         <option name="runtime-hint" value="4h" />
diff --git a/tests/tests/media/libmediandkjni/native-mediadrm-jni.cpp b/tests/tests/media/libmediandkjni/native-mediadrm-jni.cpp
index cff5c18..d1e63ec 100644
--- a/tests/tests/media/libmediandkjni/native-mediadrm-jni.cpp
+++ b/tests/tests/media/libmediandkjni/native-mediadrm-jni.cpp
@@ -64,6 +64,8 @@
     0xac, 0xe3, 0x3c, 0x1e, 0x52, 0xe2, 0xfb, 0x4b
 };
 
+// The test content is not packaged with clearkey UUID,
+// we have to use a canned clearkey pssh for the test.
 static const uint8_t kClearkeyPssh[] = {
     // BMFF box header (4 bytes size + 'pssh')
     0x00, 0x00, 0x00, 0x34, 0x70, 0x73, 0x73, 0x68,
diff --git a/tests/tests/media/res/raw/testmp3_4.mp3 b/tests/tests/media/res/raw/testmp3_4.mp3
new file mode 100755
index 0000000..2098ebd
--- /dev/null
+++ b/tests/tests/media/res/raw/testmp3_4.mp3
Binary files differ
diff --git a/tests/tests/media/res/values/strings.xml b/tests/tests/media/res/values/strings.xml
index b01b8ec..018ab70 100644
--- a/tests/tests/media/res/values/strings.xml
+++ b/tests/tests/media/res/values/strings.xml
@@ -16,4 +16,5 @@
 <resources>
     <string name="test_user_route_name">User route\'s name for test</string>
     <string name="test_route_category_name">Route category\'s name for test</string>
+    <string name="test_localizable_title">Translatable Ringtone Title</string>
 </resources>
\ No newline at end of file
diff --git a/tests/tests/media/src/android/media/cts/AudioManagerTest.java b/tests/tests/media/src/android/media/cts/AudioManagerTest.java
index ab7e4b6..775fb35 100644
--- a/tests/tests/media/src/android/media/cts/AudioManagerTest.java
+++ b/tests/tests/media/src/android/media/cts/AudioManagerTest.java
@@ -38,8 +38,12 @@
 
 import android.app.ActivityManager;
 import android.app.NotificationManager;
+import android.content.BroadcastReceiver;
 import android.content.Context;
+import android.content.Intent;
+import android.content.IntentFilter;
 import android.content.res.Resources;
+import android.media.AudioDeviceInfo;
 import android.media.AudioManager;
 import android.content.pm.PackageManager;
 import android.media.AudioSystem;
@@ -144,6 +148,73 @@
         assertFalse(mAudioManager.isMicrophoneMute());
     }
 
+    public void testMicrophoneMuteIntent() throws Exception {
+        final MyBlockingIntentReceiver receiver = new MyBlockingIntentReceiver();
+        final boolean initialMicMute = mAudioManager.isMicrophoneMute();
+        try {
+            mContext.registerReceiver(receiver,
+                    new IntentFilter(AudioManager.ACTION_MICROPHONE_MUTE_CHANGED));
+            // change the mic mute state
+            mAudioManager.setMicrophoneMute(!initialMicMute);
+            // verify a change was reported
+            final boolean intentFired = receiver.waitForMicMuteChanged(500/*ms*/);
+            assertTrue("ACTION_MICROPHONE_MUTE_CHANGED wasn't fired", intentFired);
+            // verify the mic mute state is expected
+            final boolean newMicMute = mAudioManager.isMicrophoneMute();
+            assertTrue("new mic mute state not as expected (" + !initialMicMute + ")",
+                    newMicMute == !initialMicMute);
+        } finally {
+            mContext.unregisterReceiver(receiver);
+            mAudioManager.setMicrophoneMute(initialMicMute);
+        }
+    }
+
+    // helper class to simplify that abstracts out the handling of spurious wakeups in Object.wait()
+    private static final class SafeWaitObject {
+        private boolean mQuit = false;
+
+        public void safeNotify() {
+            synchronized (this) {
+                mQuit = true;
+                this.notify();
+            }
+        }
+
+        public void safeWait(long millis) throws InterruptedException {
+            final long timeOutTime = java.lang.System.currentTimeMillis() + millis;
+            synchronized (this) {
+                while (!mQuit) {
+                    final long timeToWait = timeOutTime - java.lang.System.currentTimeMillis();
+                    if (timeToWait < 0) { break; }
+                    this.wait(timeToWait);
+                }
+            }
+        }
+    }
+
+    private static final class MyBlockingIntentReceiver extends BroadcastReceiver {
+        private final SafeWaitObject mLock = new SafeWaitObject();
+        // state protected by mLock
+        private boolean mIntentReceived = false;
+
+        @Override
+        public void onReceive(Context context, Intent intent) {
+            synchronized (mLock) {
+                mIntentReceived = true;
+                mLock.safeNotify();
+            }
+        }
+
+        public boolean waitForMicMuteChanged(long timeOutMs) {
+            synchronized (mLock) {
+                try {
+                    mLock.safeWait(timeOutMs);
+                } catch (InterruptedException e) { }
+                return mIntentReceived;
+            }
+        }
+    }
+
     public void testSoundEffects() throws Exception {
         Settings.System.putInt(mContext.getContentResolver(), SOUND_EFFECTS_ENABLED, 1);
 
@@ -176,6 +247,29 @@
         mAudioManager.playSoundEffect(AudioManager.FX_FOCUS_NAVIGATION_RIGHT, volume);
     }
 
+    public void testCheckingZenModeBlockDoesNotRequireNotificationPolicyAccess() throws Exception {
+        try {
+            // set zen mode to priority only, so playSoundEffect will check notification policy
+            Utils.toggleNotificationPolicyAccess(mContext.getPackageName(), getInstrumentation(),
+                    true);
+            setInterruptionFilter(NotificationManager.INTERRUPTION_FILTER_PRIORITY);
+            Settings.System.putInt(mContext.getContentResolver(), SOUND_EFFECTS_ENABLED, 1);
+
+            // take away write-notification policy access from the package
+            Utils.toggleNotificationPolicyAccess(mContext.getPackageName(), getInstrumentation(),
+                    false);
+
+            // playSoundEffect should NOT throw a security exception; all apps have read-access
+            mAudioManager.playSoundEffect(SoundEffectConstants.CLICK);
+        } finally {
+            Utils.toggleNotificationPolicyAccess(mContext.getPackageName(), getInstrumentation(),
+                    true);
+            setInterruptionFilter(NotificationManager.INTERRUPTION_FILTER_ALL);
+            Utils.toggleNotificationPolicyAccess(mContext.getPackageName(), getInstrumentation(),
+                    false);
+        }
+    }
+
     public void testMusicActive() throws Exception {
         if (mAudioManager.isMusicActive()) {
             return;
@@ -967,6 +1061,93 @@
         mAudioManager.adjustVolume(37, 0);
     }
 
+    private final int[] PUBLIC_STREAM_TYPES = { AudioManager.STREAM_VOICE_CALL,
+            AudioManager.STREAM_SYSTEM, AudioManager.STREAM_RING, AudioManager.STREAM_MUSIC,
+            AudioManager.STREAM_ALARM, AudioManager.STREAM_NOTIFICATION,
+            AudioManager.STREAM_DTMF,  AudioManager.STREAM_ACCESSIBILITY };
+
+    public void testGetStreamVolumeDbWithIllegalArguments() throws Exception {
+        Exception ex = null;
+        // invalid stream type
+        try {
+            float gain = mAudioManager.getStreamVolumeDb(-100 /*streamType*/, 0,
+                    AudioDeviceInfo.TYPE_BUILTIN_SPEAKER);
+        } catch (Exception e) {
+            ex = e; // expected
+        }
+        assertNotNull("No exception was thrown for an invalid stream type", ex);
+        assertEquals("Wrong exception thrown for invalid stream type",
+                ex.getClass(), IllegalArgumentException.class);
+
+        // invalid volume index
+        ex = null;
+        try {
+            float gain = mAudioManager.getStreamVolumeDb(AudioManager.STREAM_MUSIC, -101 /*volume*/,
+                    AudioDeviceInfo.TYPE_BUILTIN_SPEAKER);
+        } catch (Exception e) {
+            ex = e; // expected
+        }
+        assertNotNull("No exception was thrown for an invalid volume index", ex);
+        assertEquals("Wrong exception thrown for invalid volume index",
+                ex.getClass(), IllegalArgumentException.class);
+
+        // invalid out of range volume index
+        ex = null;
+        try {
+            final int maxVol = mAudioManager.getStreamMaxVolume(AudioManager.STREAM_MUSIC);
+            float gain = mAudioManager.getStreamVolumeDb(AudioManager.STREAM_MUSIC, maxVol + 1,
+                    AudioDeviceInfo.TYPE_BUILTIN_SPEAKER);
+        } catch (Exception e) {
+            ex = e; // expected
+        }
+        assertNotNull("No exception was thrown for an invalid out of range volume index", ex);
+        assertEquals("Wrong exception thrown for invalid out of range volume index",
+                ex.getClass(), IllegalArgumentException.class);
+
+        // invalid device type
+        ex = null;
+        try {
+            float gain = mAudioManager.getStreamVolumeDb(AudioManager.STREAM_MUSIC, 0,
+                    -102 /*deviceType*/);
+        } catch (Exception e) {
+            ex = e; // expected
+        }
+        assertNotNull("No exception was thrown for an invalid device type", ex);
+        assertEquals("Wrong exception thrown for invalid device type",
+                ex.getClass(), IllegalArgumentException.class);
+
+        // invalid input device type
+        ex = null;
+        try {
+            float gain = mAudioManager.getStreamVolumeDb(AudioManager.STREAM_MUSIC, 0,
+                    AudioDeviceInfo.TYPE_BUILTIN_MIC);
+        } catch (Exception e) {
+            ex = e; // expected
+        }
+        assertNotNull("No exception was thrown for an invalid input device type", ex);
+        assertEquals("Wrong exception thrown for invalid input device type",
+                ex.getClass(), IllegalArgumentException.class);
+    }
+
+    public void testGetStreamVolumeDb() throws Exception {
+        for (int streamType : PUBLIC_STREAM_TYPES) {
+            // verify mininum index is strictly inferior to maximum index
+            final int minIndex = mAudioManager.getStreamMinVolume(streamType);
+            final int maxIndex = mAudioManager.getStreamMaxVolume(streamType);
+            assertTrue("Min vol index (" + minIndex + ") for stream " + streamType + " not inferior"
+                    + " to max vol index (" + maxIndex + ")", minIndex <= maxIndex);
+            float prevGain = Float.NEGATIVE_INFINITY;
+            // verify gain increases with the volume indices
+            for (int idx = minIndex ; idx <= maxIndex ; idx++) {
+                float gain = mAudioManager.getStreamVolumeDb(streamType, idx,
+                        AudioDeviceInfo.TYPE_BUILTIN_SPEAKER);
+                assertTrue("Non-monotonically increasing gain at index " + idx + " for stream"
+                        + streamType, prevGain <= gain);
+                prevGain = gain;
+            }
+        }
+    }
+
     public void testAdjustSuggestedStreamVolumeWithIllegalArguments() throws Exception {
         // Call the method with illegal direction. System should not reboot.
         mAudioManager.adjustSuggestedStreamVolume(37, AudioManager.STREAM_MUSIC, 0);
diff --git a/tests/tests/media/src/android/media/cts/AudioPlaybackConfigurationTest.java b/tests/tests/media/src/android/media/cts/AudioPlaybackConfigurationTest.java
index e5f9385..f3c17cf 100644
--- a/tests/tests/media/src/android/media/cts/AudioPlaybackConfigurationTest.java
+++ b/tests/tests/media/src/android/media/cts/AudioPlaybackConfigurationTest.java
@@ -145,11 +145,11 @@
         final Method getClientPidMethod = confClass.getDeclaredMethod("getClientPid");
         final Method getPlayerTypeMethod = confClass.getDeclaredMethod("getPlayerType");
         try {
-            Integer uid = (Integer) getClientUidMethod.invoke(config, null);
+            Integer uid = (Integer) getClientUidMethod.invoke(config, (Object[]) null);
             assertEquals("uid isn't protected", -1 /*expected*/, uid.intValue());
-            Integer pid = (Integer) getClientPidMethod.invoke(config, null);
+            Integer pid = (Integer) getClientPidMethod.invoke(config, (Object[]) null);
             assertEquals("pid isn't protected", -1 /*expected*/, pid.intValue());
-            Integer type = (Integer) getPlayerTypeMethod.invoke(config, null);
+            Integer type = (Integer) getPlayerTypeMethod.invoke(config, (Object[]) null);
             assertEquals("player type isn't protected", -1 /*expected*/, type.intValue());
         } catch (Exception e) {
             fail("Exception thrown during reflection on config privileged fields"+ e);
diff --git a/tests/tests/media/src/android/media/cts/AudioRecordTest.java b/tests/tests/media/src/android/media/cts/AudioRecordTest.java
index 8fd1b7a..98364ec 100644
--- a/tests/tests/media/src/android/media/cts/AudioRecordTest.java
+++ b/tests/tests/media/src/android/media/cts/AudioRecordTest.java
@@ -251,6 +251,13 @@
                 AudioFormat.CHANNEL_IN_STEREO, AudioFormat.ENCODING_PCM_8BIT);
     }
 
+    public void testAudioRecordLocalMono16BitShort() throws Exception {
+        doTest("local_mono_16bit_short", true /*localRecord*/, false /*customHandler*/,
+                30 /*periodsPerSecond*/, 2 /*markerPeriodsPerSecond*/,
+                false /*useByteBuffer*/, true /*blocking*/,
+                false /*auditRecording*/, false /*isChannelIndex*/, 8000 /*TEST_SR*/,
+                AudioFormat.CHANNEL_IN_MONO, AudioFormat.ENCODING_PCM_16BIT, 500 /*TEST_TIME_MS*/);
+    }
     public void testAudioRecordLocalMono16Bit() throws Exception {
         doTest("local_mono_16bit", true /*localRecord*/, false /*customHandler*/,
                 30 /*periodsPerSecond*/, 2 /*markerPeriodsPerSecond*/,
@@ -743,11 +750,21 @@
             boolean useByteBuffer, boolean blocking,
             final boolean auditRecording, final boolean isChannelIndex,
             final int TEST_SR, final int TEST_CONF, final int TEST_FORMAT) throws Exception {
+        final int TEST_TIME_MS = auditRecording ? 60000 : 2000;
+        doTest(reportName, localRecord, customHandler, periodsPerSecond, markerPeriodsPerSecond,
+                useByteBuffer, blocking, auditRecording, isChannelIndex,
+                TEST_SR, TEST_CONF, TEST_FORMAT, TEST_TIME_MS);
+    }
+    private void doTest(String reportName, boolean localRecord, boolean customHandler,
+            int periodsPerSecond, int markerPeriodsPerSecond,
+            boolean useByteBuffer, boolean blocking,
+            final boolean auditRecording, final boolean isChannelIndex,
+            final int TEST_SR, final int TEST_CONF, final int TEST_FORMAT, final int TEST_TIME_MS)
+            throws Exception {
         if (!hasMicrophone()) {
             return;
         }
         // audit recording plays back recorded audio, so use longer test timing
-        final int TEST_TIME_MS = auditRecording ? 60000 : 2000;
         final int TEST_SOURCE = MediaRecorder.AudioSource.DEFAULT;
         mIsHandleMessageCalled = false;
 
diff --git a/tests/tests/media/src/android/media/cts/AudioRecordingConfigurationTest.java b/tests/tests/media/src/android/media/cts/AudioRecordingConfigurationTest.java
index 068087d..eb7296f 100644
--- a/tests/tests/media/src/android/media/cts/AudioRecordingConfigurationTest.java
+++ b/tests/tests/media/src/android/media/cts/AudioRecordingConfigurationTest.java
@@ -319,9 +319,9 @@
         try {
             final Method getClientUidMethod = confClass.getDeclaredMethod("getClientUid");
             final Method getClientPackageName = confClass.getDeclaredMethod("getClientPackageName");
-            Integer uid = (Integer) getClientUidMethod.invoke(config, null);
+            Integer uid = (Integer) getClientUidMethod.invoke(config, (Object[]) null);
             assertEquals("client uid isn't protected", -1 /*expected*/, uid.intValue());
-            String name = (String) getClientPackageName.invoke(config, null);
+            String name = (String) getClientPackageName.invoke(config, (Object[]) null);
             assertNotNull("client package name is null", name);
             assertEquals("client package name isn't protected", 0 /*expected*/, name.length());
         } catch (Exception e) {
diff --git a/tests/tests/media/src/android/media/cts/AudioTrackTest.java b/tests/tests/media/src/android/media/cts/AudioTrackTest.java
index 3d853d4..23ec510 100644
--- a/tests/tests/media/src/android/media/cts/AudioTrackTest.java
+++ b/tests/tests/media/src/android/media/cts/AudioTrackTest.java
@@ -1461,6 +1461,30 @@
         track.release();
     }
 
+    public void testPlayStaticDataShort() throws Exception {
+        if (!hasAudioOutput()) {
+            Log.w(TAG,"AUDIO_OUTPUT feature not found. This system might not have a valid "
+                    + "audio output HAL");
+            return;
+        }
+        // constants for test
+        final String TEST_NAME = "testPlayStaticDataShort";
+        final int TEST_FORMAT = AudioFormat.ENCODING_PCM_FLOAT;
+        final int TEST_SR = 48000;
+        final int TEST_CONF = AudioFormat.CHANNEL_OUT_MONO;
+        final int TEST_MODE = AudioTrack.MODE_STATIC;
+        final int TEST_STREAM_TYPE = AudioManager.STREAM_MUSIC;
+        final double TEST_SWEEP = 100;
+        final int TEST_LOOPS = 1;
+        final double TEST_FREQUENCY = 400;
+        final long NO_WAIT = 0;
+        final double TEST_LOOP_DURATION = 0.25;
+
+        playOnceStaticData(TEST_NAME, TEST_MODE, TEST_STREAM_TYPE, TEST_SWEEP,
+                TEST_LOOPS, TEST_FORMAT, TEST_FREQUENCY, TEST_SR, TEST_CONF, NO_WAIT, TEST_LOOP_DURATION);
+
+    }
+
     public void testPlayStaticData() throws Exception {
         if (!hasAudioOutput()) {
             Log.w(TAG,"AUDIO_OUTPUT feature not found. This system might not have a valid "
@@ -1487,90 +1511,117 @@
         final int TEST_STREAM_TYPE = AudioManager.STREAM_MUSIC;
         final double TEST_SWEEP = 100;
         final int TEST_LOOPS = 1;
+        final double TEST_LOOP_DURATION=1;
 
         for (int TEST_FORMAT : TEST_FORMAT_ARRAY) {
             double frequency = 400; // frequency changes for each test
             for (int TEST_SR : TEST_SR_ARRAY) {
                 for (int TEST_CONF : TEST_CONF_ARRAY) {
-                    // -------- initialization --------------
-                    final int seconds = 1;
-                    final int channelCount = Integer.bitCount(TEST_CONF);
-                    final int bufferFrames = seconds * TEST_SR;
-                    final int bufferSamples = bufferFrames * channelCount;
-                    final int bufferSize = bufferSamples
-                            * AudioFormat.getBytesPerSample(TEST_FORMAT);
-                    final double testFrequency = frequency / channelCount;
-                    final long MILLISECONDS_PER_SECOND = 1000;
-                    AudioTrack track = new AudioTrack(TEST_STREAM_TYPE, TEST_SR,
-                            TEST_CONF, TEST_FORMAT, bufferSize, TEST_MODE);
-                    assertEquals(TEST_NAME, AudioTrack.STATE_NO_STATIC_DATA, track.getState());
+                    playOnceStaticData(TEST_NAME, TEST_MODE, TEST_STREAM_TYPE, TEST_SWEEP,
+                            TEST_LOOPS, TEST_FORMAT, frequency, TEST_SR, TEST_CONF, WAIT_MSEC, TEST_LOOP_DURATION);
 
-                    // -------- test --------------
-
-                    // test setLoopPoints and setPosition can be called here.
-                    assertEquals(TEST_NAME,
-                            android.media.AudioTrack.SUCCESS,
-                            track.setPlaybackHeadPosition(bufferFrames/2));
-                    assertEquals(TEST_NAME,
-                            android.media.AudioTrack.SUCCESS,
-                            track.setLoopPoints(
-                                    0 /*startInFrames*/, bufferFrames, 10 /*loopCount*/));
-                    // only need to write once to the static track
-                    switch (TEST_FORMAT) {
-                    case AudioFormat.ENCODING_PCM_8BIT: {
-                        byte data[] = AudioHelper.createSoundDataInByteArray(
-                                bufferSamples, TEST_SR,
-                                testFrequency, TEST_SWEEP);
-                        assertEquals(TEST_NAME,
-                                bufferSamples,
-                                track.write(data, 0 /*offsetInBytes*/, data.length));
-                        } break;
-                    case AudioFormat.ENCODING_PCM_16BIT: {
-                        short data[] = AudioHelper.createSoundDataInShortArray(
-                                bufferSamples, TEST_SR,
-                                testFrequency, TEST_SWEEP);
-                        assertEquals(TEST_NAME,
-                                bufferSamples,
-                                track.write(data, 0 /*offsetInBytes*/, data.length));
-                        } break;
-                    case AudioFormat.ENCODING_PCM_FLOAT: {
-                        float data[] = AudioHelper.createSoundDataInFloatArray(
-                                bufferSamples, TEST_SR,
-                                testFrequency, TEST_SWEEP);
-                        assertEquals(TEST_NAME,
-                                bufferSamples,
-                                track.write(data, 0 /*offsetInBytes*/, data.length,
-                                        AudioTrack.WRITE_BLOCKING));
-                        } break;
-                    }
-                    assertEquals(TEST_NAME, AudioTrack.STATE_INITIALIZED, track.getState());
-                    // test setLoopPoints and setPosition can be called here.
-                    assertEquals(TEST_NAME,
-                            android.media.AudioTrack.SUCCESS,
-                            track.setPlaybackHeadPosition(0 /*positionInFrames*/));
-                    assertEquals(TEST_NAME,
-                            android.media.AudioTrack.SUCCESS,
-                            track.setLoopPoints(0 /*startInFrames*/, bufferFrames, TEST_LOOPS));
-
-                    track.play();
-                    Thread.sleep(seconds * MILLISECONDS_PER_SECOND * (TEST_LOOPS + 1));
-                    Thread.sleep(WAIT_MSEC);
-
-                    // Check position after looping. AudioTrack.getPlaybackHeadPosition() returns
-                    // the running count of frames played, not the actual static buffer position.
-                    int position = track.getPlaybackHeadPosition();
-                    assertEquals(TEST_NAME, bufferFrames * (TEST_LOOPS + 1), position);
-
-                    track.stop();
-                    Thread.sleep(WAIT_MSEC);
-                    // -------- tear down --------------
-                    track.release();
                     frequency += 70; // increment test tone frequency
                 }
             }
         }
     }
 
+    private void playOnceStaticData(String testName, int testMode, int testStreamType,
+            double testSweep, int testLoops, int testFormat, double testFrequency, int testSr,
+            int testConf, long waitMsec, double testLoopDuration)
+            throws InterruptedException {
+        // -------- initialization --------------
+        final int channelCount = Integer.bitCount(testConf);
+        final int bufferFrames = (int)(testLoopDuration * testSr);
+        final int bufferSamples = bufferFrames * channelCount;
+        final int bufferSize = bufferSamples
+                * AudioFormat.getBytesPerSample(testFormat);
+        final double frequency = testFrequency / channelCount;
+        final long MILLISECONDS_PER_SECOND = 1000;
+        AudioTrack track = new AudioTrack(testStreamType, testSr,
+                testConf, testFormat, bufferSize, testMode);
+        assertEquals(testName, AudioTrack.STATE_NO_STATIC_DATA, track.getState());
+
+        // -------- test --------------
+
+        // test setLoopPoints and setPosition can be called here.
+        assertEquals(testName,
+                android.media.AudioTrack.SUCCESS,
+                track.setPlaybackHeadPosition(bufferFrames/2));
+        assertEquals(testName,
+                android.media.AudioTrack.SUCCESS,
+                track.setLoopPoints(
+                        0 /*startInFrames*/, bufferFrames, 10 /*loopCount*/));
+        // only need to write once to the static track
+        switch (testFormat) {
+        case AudioFormat.ENCODING_PCM_8BIT: {
+            byte data[] = AudioHelper.createSoundDataInByteArray(
+                    bufferSamples, testSr,
+                    frequency, testSweep);
+            assertEquals(testName,
+                    bufferSamples,
+                    track.write(data, 0 /*offsetInBytes*/, data.length));
+            } break;
+        case AudioFormat.ENCODING_PCM_16BIT: {
+            short data[] = AudioHelper.createSoundDataInShortArray(
+                    bufferSamples, testSr,
+                    frequency, testSweep);
+            assertEquals(testName,
+                    bufferSamples,
+                    track.write(data, 0 /*offsetInBytes*/, data.length));
+            } break;
+        case AudioFormat.ENCODING_PCM_FLOAT: {
+            float data[] = AudioHelper.createSoundDataInFloatArray(
+                    bufferSamples, testSr,
+                    frequency, testSweep);
+            assertEquals(testName,
+                    bufferSamples,
+                    track.write(data, 0 /*offsetInBytes*/, data.length,
+                            AudioTrack.WRITE_BLOCKING));
+            } break;
+        }
+        assertEquals(testName, AudioTrack.STATE_INITIALIZED, track.getState());
+        // test setLoopPoints and setPosition can be called here.
+        assertEquals(testName,
+                android.media.AudioTrack.SUCCESS,
+                track.setPlaybackHeadPosition(0 /*positionInFrames*/));
+        assertEquals(testName,
+                android.media.AudioTrack.SUCCESS,
+                track.setLoopPoints(0 /*startInFrames*/, bufferFrames, testLoops));
+
+        track.play();
+        Thread.sleep((int)(testLoopDuration * MILLISECONDS_PER_SECOND) * (testLoops + 1));
+        Thread.sleep(waitMsec);
+
+        // Check position after looping. AudioTrack.getPlaybackHeadPosition() returns
+        // the running count of frames played, not the actual static buffer position.
+        int position = track.getPlaybackHeadPosition();
+        assertEquals(testName, bufferFrames * (testLoops + 1), position);
+
+        track.stop();
+        Thread.sleep(waitMsec);
+        // -------- tear down --------------
+        track.release();
+    }
+
+    public void testPlayStreamDataShort() throws Exception {
+        // constants for test
+        final String TEST_NAME = "testPlayStreamDataShort";
+        final int TEST_FORMAT = AudioFormat.ENCODING_PCM_16BIT;
+        final int TEST_SR = 48000;
+        final int TEST_CONF = AudioFormat.CHANNEL_OUT_STEREO;
+        final int TEST_MODE = AudioTrack.MODE_STREAM;
+        final int TEST_STREAM_TYPE = AudioManager.STREAM_MUSIC;
+        final float TEST_SWEEP = 0; // sine wave only
+        final boolean TEST_IS_LOW_RAM_DEVICE = isLowRamDevice();
+        final double TEST_FREQUENCY = 1000;
+        final long NO_WAIT = 0;
+
+        playOnceStreamData(TEST_NAME, TEST_MODE, TEST_STREAM_TYPE, TEST_SWEEP,
+                TEST_IS_LOW_RAM_DEVICE, TEST_FORMAT, TEST_FREQUENCY, TEST_SR, TEST_CONF,
+                NO_WAIT);
+    }
+
     public void testPlayStreamData() throws Exception {
         // constants for test
         final String TEST_NAME = "testPlayStreamData";
@@ -1607,94 +1658,105 @@
             double frequency = 400; // frequency changes for each test
             for (int TEST_SR : TEST_SR_ARRAY) {
                 for (int TEST_CONF : TEST_CONF_ARRAY) {
-                    final int channelCount = Integer.bitCount(TEST_CONF);
-                    if (TEST_IS_LOW_RAM_DEVICE
-                            && (TEST_SR > 96000 || channelCount > 4)) {
-                        continue; // ignore. FIXME: reenable when AF memory allocation is updated.
-                    }
-                    // -------- initialization --------------
-                    final int minBufferSize = AudioTrack.getMinBufferSize(TEST_SR,
-                            TEST_CONF, TEST_FORMAT); // in bytes
-                    AudioTrack track = new AudioTrack(TEST_STREAM_TYPE, TEST_SR,
-                            TEST_CONF, TEST_FORMAT, minBufferSize, TEST_MODE);
-                    assertTrue(TEST_NAME, track.getState() == AudioTrack.STATE_INITIALIZED);
-
-                    // compute parameters for the source signal data.
-                    AudioFormat format = track.getFormat();
-                    assertEquals(TEST_NAME, TEST_SR, format.getSampleRate());
-                    assertEquals(TEST_NAME, TEST_CONF, format.getChannelMask());
-                    assertEquals(TEST_NAME, channelCount, format.getChannelCount());
-                    assertEquals(TEST_NAME, TEST_FORMAT, format.getEncoding());
-                    final int sourceSamples = channelCount
-                            * AudioHelper.frameCountFromMsec(500,
-                                    format); // duration of test tones
-                    final double testFrequency = frequency / channelCount;
-
-                    int written = 0;
-                    // For streaming tracks, it's ok to issue the play() command
-                    // before any audio is written.
-                    track.play();
-                    // -------- test --------------
-
-                    // samplesPerWrite can be any positive value.
-                    // We prefer this to be a multiple of channelCount so write()
-                    // does not return a short count.
-                    // If samplesPerWrite is very large, it is limited to the data length
-                    // and we simply write (blocking) the entire source data and not even loop.
-                    // We choose a value here which simulates double buffer writes.
-                    final int buffers = 2; // double buffering mode
-                    final int samplesPerWrite =
-                            (track.getBufferSizeInFrames() / buffers) * channelCount;
-                    switch (TEST_FORMAT) {
-                    case AudioFormat.ENCODING_PCM_8BIT: {
-                        byte data[] = AudioHelper.createSoundDataInByteArray(
-                                sourceSamples, TEST_SR,
-                                testFrequency, TEST_SWEEP);
-                        while (written < data.length) {
-                            int samples = Math.min(data.length - written, samplesPerWrite);
-                            int ret = track.write(data, written, samples);
-                            assertEquals(TEST_NAME, samples, ret);
-                            written += ret;
-                        }
-                        } break;
-                    case AudioFormat.ENCODING_PCM_16BIT: {
-                        short data[] = AudioHelper.createSoundDataInShortArray(
-                                sourceSamples, TEST_SR,
-                                testFrequency, TEST_SWEEP);
-                        while (written < data.length) {
-                            int samples = Math.min(data.length - written, samplesPerWrite);
-                            int ret = track.write(data, written, samples);
-                            assertEquals(TEST_NAME, samples, ret);
-                            written += ret;
-                        }
-                        } break;
-                    case AudioFormat.ENCODING_PCM_FLOAT: {
-                        float data[] = AudioHelper.createSoundDataInFloatArray(
-                                sourceSamples, TEST_SR,
-                                testFrequency, TEST_SWEEP);
-                        while (written < data.length) {
-                            int samples = Math.min(data.length - written, samplesPerWrite);
-                            int ret = track.write(data, written, samples,
-                                    AudioTrack.WRITE_BLOCKING);
-                            assertEquals(TEST_NAME, samples, ret);
-                            written += ret;
-                        }
-                        } break;
-                    }
-
-                    // For streaming tracks, AudioTrack.stop() doesn't immediately stop playback.
-                    // Rather, it allows the remaining data in the internal buffer to drain.
-                    track.stop();
-                    Thread.sleep(WAIT_MSEC); // wait for the data to drain.
-                    // -------- tear down --------------
-                    track.release();
-                    Thread.sleep(WAIT_MSEC); // wait for release to complete
+                    playOnceStreamData(TEST_NAME, TEST_MODE, TEST_STREAM_TYPE, TEST_SWEEP,
+                            TEST_IS_LOW_RAM_DEVICE, TEST_FORMAT, frequency, TEST_SR, TEST_CONF,
+                            WAIT_MSEC);
                     frequency += 50; // increment test tone frequency
                 }
             }
         }
     }
 
+    private void playOnceStreamData(String testName, int testMode, int testStream,
+            float testSweep, boolean isLowRamDevice, int testFormat, double testFrequency,
+            int testSr, int testConf, long waitMsec) throws InterruptedException {
+        final int channelCount = Integer.bitCount(testConf);
+        if (isLowRamDevice
+                && (testSr > 96000 || channelCount > 4)) {
+            return; // ignore. FIXME: reenable when AF memory allocation is updated.
+        }
+        // -------- initialization --------------
+        final int minBufferSize = AudioTrack.getMinBufferSize(testSr,
+                testConf, testFormat); // in bytes
+        AudioTrack track = new AudioTrack(testStream, testSr,
+                testConf, testFormat, minBufferSize, testMode);
+        assertTrue(testName, track.getState() == AudioTrack.STATE_INITIALIZED);
+
+        // compute parameters for the source signal data.
+        AudioFormat format = track.getFormat();
+        assertEquals(testName, testSr, format.getSampleRate());
+        assertEquals(testName, testConf, format.getChannelMask());
+        assertEquals(testName, channelCount, format.getChannelCount());
+        assertEquals(testName, testFormat, format.getEncoding());
+        final int sourceSamples = channelCount
+                * AudioHelper.frameCountFromMsec(500,
+                format); // duration of test tones
+        final double frequency = testFrequency / channelCount;
+
+        int written = 0;
+        // For streaming tracks, it's ok to issue the play() command
+        // before any audio is written.
+        track.play();
+        // -------- test --------------
+
+        // samplesPerWrite can be any positive value.
+        // We prefer this to be a multiple of channelCount so write()
+        // does not return a short count.
+        // If samplesPerWrite is very large, it is limited to the data length
+        // and we simply write (blocking) the entire source data and not even loop.
+        // We choose a value here which simulates double buffer writes.
+        final int buffers = 2; // double buffering mode
+        final int samplesPerWrite =
+                (track.getBufferSizeInFrames() / buffers) * channelCount;
+        switch (testFormat) {
+            case AudioFormat.ENCODING_PCM_8BIT: {
+                byte data[] = AudioHelper.createSoundDataInByteArray(
+                        sourceSamples, testSr,
+                        frequency, testSweep);
+                while (written < data.length) {
+                    int samples = Math.min(data.length - written, samplesPerWrite);
+                    int ret = track.write(data, written, samples);
+                    assertEquals(testName, samples, ret);
+                    written += ret;
+                }
+            }
+            break;
+            case AudioFormat.ENCODING_PCM_16BIT: {
+                short data[] = AudioHelper.createSoundDataInShortArray(
+                        sourceSamples, testSr,
+                        frequency, testSweep);
+                while (written < data.length) {
+                    int samples = Math.min(data.length - written, samplesPerWrite);
+                    int ret = track.write(data, written, samples);
+                    assertEquals(testName, samples, ret);
+                    written += ret;
+                }
+            }
+            break;
+            case AudioFormat.ENCODING_PCM_FLOAT: {
+                float data[] = AudioHelper.createSoundDataInFloatArray(
+                        sourceSamples, testSr,
+                        frequency, testSweep);
+                while (written < data.length) {
+                    int samples = Math.min(data.length - written, samplesPerWrite);
+                    int ret = track.write(data, written, samples,
+                            AudioTrack.WRITE_BLOCKING);
+                    assertEquals(testName, samples, ret);
+                    written += ret;
+                }
+            }
+            break;
+        }
+
+        // For streaming tracks, AudioTrack.stop() doesn't immediately stop playback.
+        // Rather, it allows the remaining data in the internal buffer to drain.
+        track.stop();
+        Thread.sleep(waitMsec); // wait for the data to drain.
+        // -------- tear down --------------
+        track.release();
+        Thread.sleep(waitMsec); // wait for release to complete
+    }
+
     public void testPlayStreamByteBuffer() throws Exception {
         // constants for test
         final String TEST_NAME = "testPlayStreamByteBuffer";
@@ -2358,6 +2420,33 @@
         assertTrue(TEST_NAME + ": buffer frame count", observedBufferSize2 > 0);
     }
 
+    // Test AudioTrack to see if there are any problems with large frame counts.
+    public void testAudioTrackLargeFrameCount() throws Exception {
+        // constants for test
+        final String TEST_NAME = "testAudioTrackLargeFrameCount";
+        final int[] BUFFER_SIZES = { 4294968, 42949680, 429496800, Integer.MAX_VALUE };
+        final int[] MODES = { AudioTrack.MODE_STATIC, AudioTrack.MODE_STREAM };
+
+        for (int mode : MODES) {
+            for (int bufferSizeInBytes : BUFFER_SIZES) {
+                try {
+                    final AudioTrack track = new AudioTrack.Builder()
+                        .setAudioFormat(new AudioFormat.Builder()
+                            .setEncoding(AudioFormat.ENCODING_PCM_8BIT)
+                            .setSampleRate(44100)
+                            .setChannelMask(AudioFormat.CHANNEL_OUT_MONO)
+                            .build())
+                        .setTransferMode(mode)
+                        .setBufferSizeInBytes(bufferSizeInBytes) // 1 byte == 1 frame
+                        .build();
+                    track.release(); // OK to successfully complete
+                } catch (UnsupportedOperationException e) {
+                    ; // OK to throw unsupported exception
+                }
+            }
+        }
+    }
+
 /* Do not run in JB-MR1. will be re-opened in the next platform release.
     public void testResourceLeakage() throws Exception {
         final int BUFFER_SIZE = 600 * 1024;
diff --git a/tests/tests/media/src/android/media/cts/ClearKeySystemTest.java b/tests/tests/media/src/android/media/cts/ClearKeySystemTest.java
index d43dce1..5afb71d 100644
--- a/tests/tests/media/src/android/media/cts/ClearKeySystemTest.java
+++ b/tests/tests/media/src/android/media/cts/ClearKeySystemTest.java
@@ -41,6 +41,7 @@
 import java.nio.ByteBuffer;
 import java.nio.CharBuffer;
 import java.nio.charset.Charset;
+import java.util.Arrays;
 import java.util.ArrayList;
 import java.util.concurrent.TimeUnit;
 import java.util.HashMap;
@@ -79,6 +80,18 @@
     private static final String MIME_VIDEO_AVC = MediaFormat.MIMETYPE_VIDEO_AVC;
     private static final String MIME_VIDEO_VP8 = MediaFormat.MIMETYPE_VIDEO_VP8;
 
+    // Property Keys
+    private static final String ALGORITHMS_PROPERTY_KEY = MediaDrm.PROPERTY_ALGORITHMS;
+    private static final String DESCRIPTION_PROPERTY_KEY = MediaDrm.PROPERTY_DESCRIPTION;
+    private static final String DEVICEID_PROPERTY_KEY = "deviceId";
+    private static final String INVALID_PROPERTY_KEY = "invalid property key";
+    private static final String LISTENER_TEST_SUPPORT_PROPERTY_KEY = "listenerTestSupport";
+    private static final String VENDOR_PROPERTY_KEY = MediaDrm.PROPERTY_VENDOR;
+    private static final String VERSION_PROPERTY_KEY = MediaDrm.PROPERTY_VERSION;
+
+    // Error message
+    private static final String ERR_MSG_CRYPTO_SCHEME_NOT_SUPPORTED = "Crypto scheme is not supported";
+
     private static final Uri CENC_AUDIO_URL = Uri.parse(
             "https://storage.googleapis.com/wvmedia/clear/h264/llama/llama_aac_audio.mp4");
     private static final Uri CENC_VIDEO_URL = Uri.parse(
@@ -100,7 +113,7 @@
     private Looper mLooper;
     private MediaCodecClearKeyPlayer mMediaCodecPlayer;
     private MediaDrm mDrm;
-    private Object mLock = new Object();
+    private final Object mLock = new Object();
     private SurfaceHolder mSurfaceHolder;
 
     @Override
@@ -348,7 +361,7 @@
             drm = startDrm(clearKeys, initDataType, drmSchemeUuid);
             if (!drm.isCryptoSchemeSupported(drmSchemeUuid)) {
                 stopDrm(drm);
-                throw new Error("Crypto scheme is not supported.");
+                throw new Error(ERR_MSG_CRYPTO_SCHEME_NOT_SUPPORTED);
             }
             mSessionId = openSession(drm);
         }
@@ -435,7 +448,7 @@
         MediaDrm drm = startDrm(new byte[][] { CLEAR_KEY_CENC }, "cenc", COMMON_PSSH_SCHEME_UUID);
         if (!drm.isCryptoSchemeSupported(COMMON_PSSH_SCHEME_UUID)) {
             stopDrm(drm);
-            throw new Error("Crypto scheme is not supported.");
+            throw new Error(ERR_MSG_CRYPTO_SCHEME_NOT_SUPPORTED);
         }
 
         mSessionId = openSession(drm);
@@ -524,4 +537,156 @@
             MPEG2TS_CLEAR_URL, false,
             VIDEO_WIDTH_MPEG2TS, VIDEO_HEIGHT_MPEG2TS, false);
     }
+
+    private String getStringProperty(final MediaDrm drm,  final String key) {
+        String value = "";
+        try {
+            value = drm.getPropertyString(key);
+        } catch (IllegalArgumentException e) {
+            // Expected exception for invalid key
+            Log.d(TAG, "Expected result: " + e.getMessage());
+        } catch (Exception e) {
+            throw new Error(e.getMessage() + "-" + key);
+        }
+        return value;
+    }
+
+    private byte[] getByteArrayProperty(final MediaDrm drm,  final String key) {
+        byte[] bytes = new byte[0];
+        try {
+            bytes = drm.getPropertyByteArray(key);
+        } catch (IllegalArgumentException e) {
+            // Expected exception for invalid key
+            Log.d(TAG, "Expected: " + e.getMessage() + " - " + key);
+        } catch (Exception e) {
+            throw new Error(e.getMessage() + "-" + key);
+        }
+        return bytes;
+    }
+
+    private void setStringProperty(final MediaDrm drm, final String key, final String value) {
+        try {
+            drm.setPropertyString(key, value);
+        } catch (IllegalArgumentException e) {
+            // Expected exception for invalid key
+            Log.d(TAG, "Expected: " + e.getMessage() + " - " + key);
+        } catch (Exception e) {
+            throw new Error(e.getMessage() + "-" + key);
+        }
+    }
+
+    private void setByteArrayProperty(final MediaDrm drm, final String key, final byte[] bytes) {
+        try {
+            drm.setPropertyByteArray(key, bytes);
+        } catch (IllegalArgumentException e) {
+            // Expected exception for invalid key
+            Log.d(TAG, "Expected: " + e.getMessage() + " - " + key);
+        } catch (Exception e) {
+            throw new Error(e.getMessage() + "-" + key);
+        }
+    }
+
+    public void testGetProperties() throws Exception {
+        MediaDrm drm = startDrm(new byte[][] { CLEAR_KEY_CENC },
+                "cenc", COMMON_PSSH_SCHEME_UUID);
+
+        try {
+            // The following tests will not verify the value we are getting
+            // back since it could change in the future.
+            final String[] sKeys = {
+                    DESCRIPTION_PROPERTY_KEY, LISTENER_TEST_SUPPORT_PROPERTY_KEY,
+                    VENDOR_PROPERTY_KEY, VERSION_PROPERTY_KEY};
+            String value;
+            for (String key : sKeys) {
+                value = getStringProperty(drm, key);
+                Log.d(TAG, "getPropertyString returns: " + key + ", " + value);
+                if (value.isEmpty()) {
+                    throw new Error("Failed to get property for: " + key);
+                }
+            }
+
+            byte[] bytes = getByteArrayProperty(drm, DEVICEID_PROPERTY_KEY);
+            if (0 == bytes.length) {
+                throw new Error("Failed to get property for: " + DEVICEID_PROPERTY_KEY);
+            }
+
+            // Test with an invalid property key.
+            value = getStringProperty(drm, INVALID_PROPERTY_KEY);
+            bytes = getByteArrayProperty(drm, INVALID_PROPERTY_KEY);
+            if (!value.isEmpty() || 0 != bytes.length) {
+                throw new Error("get property failed using an invalid property key");
+            }
+        } finally {
+            stopDrm(drm);
+        }
+    }
+
+    public void testSetProperties() throws Exception {
+        MediaDrm drm = startDrm(new byte[][]{CLEAR_KEY_CENC},
+                "cenc", COMMON_PSSH_SCHEME_UUID);
+
+        try {
+            // Test setting predefined string property
+            // - Save the value to be restored later
+            // - Set the property value
+            // - Check the value that was set
+            // - Restore previous value
+            String listenerTestSupport = getStringProperty(drm, LISTENER_TEST_SUPPORT_PROPERTY_KEY);
+
+            setStringProperty(drm, LISTENER_TEST_SUPPORT_PROPERTY_KEY, "testing");
+
+            String value = getStringProperty(drm, LISTENER_TEST_SUPPORT_PROPERTY_KEY);
+            if (!value.equals("testing")) {
+                throw new Error("Failed to set property: " + LISTENER_TEST_SUPPORT_PROPERTY_KEY);
+            }
+
+            setStringProperty(drm, LISTENER_TEST_SUPPORT_PROPERTY_KEY, listenerTestSupport);
+
+            // Test setting immutable properties
+            HashMap<String, String> defaultImmutableProperties = new HashMap<String, String>();
+            defaultImmutableProperties.put(ALGORITHMS_PROPERTY_KEY,
+                    getStringProperty(drm, ALGORITHMS_PROPERTY_KEY));
+            defaultImmutableProperties.put(DESCRIPTION_PROPERTY_KEY,
+                    getStringProperty(drm, DESCRIPTION_PROPERTY_KEY));
+            defaultImmutableProperties.put(VENDOR_PROPERTY_KEY,
+                    getStringProperty(drm, VENDOR_PROPERTY_KEY));
+            defaultImmutableProperties.put(VERSION_PROPERTY_KEY,
+                    getStringProperty(drm, VERSION_PROPERTY_KEY));
+
+            HashMap<String, String> immutableProperties = new HashMap<String, String>();
+            immutableProperties.put(ALGORITHMS_PROPERTY_KEY, "brute force");
+            immutableProperties.put(DESCRIPTION_PROPERTY_KEY, "testing only");
+            immutableProperties.put(VENDOR_PROPERTY_KEY, "my Google");
+            immutableProperties.put(VERSION_PROPERTY_KEY, "undefined");
+
+            for (String key : immutableProperties.keySet()) {
+                setStringProperty(drm, key, immutableProperties.get(key));
+            }
+
+            // Verify the immutable properties have not been set
+            for (String key : immutableProperties.keySet()) {
+                value = getStringProperty(drm, key);
+                if (!defaultImmutableProperties.get(key).equals(getStringProperty(drm, key))) {
+                    throw new Error("Immutable property has changed, key=" + key);
+                }
+            }
+
+            // Test setPropertyByteArray for immutable property
+            final byte[] bytes = new byte[] {
+                    0xf, 0xe, 0xd, 0xc, 0xb, 0xa, 0x9, 0x8,
+                    0x7, 0x6, 0x5, 0x4, 0x3, 0x2, 0x1, 0x0};
+
+            final byte[] deviceId = getByteArrayProperty(drm, DEVICEID_PROPERTY_KEY);
+
+            setByteArrayProperty(drm, DEVICEID_PROPERTY_KEY, bytes);
+
+            // Verify deviceId has not changed
+            if (!Arrays.equals(deviceId, getByteArrayProperty(drm, DEVICEID_PROPERTY_KEY))) {
+                throw new Error("Failed to set byte array for key=" + DEVICEID_PROPERTY_KEY);
+            }
+        } finally {
+            stopDrm(drm);
+        }
+    }
+
 }
diff --git a/tests/tests/media/src/android/media/cts/MediaBrowserTest.java b/tests/tests/media/src/android/media/cts/MediaBrowserTest.java
index fd8c3b1..c401c75 100644
--- a/tests/tests/media/src/android/media/cts/MediaBrowserTest.java
+++ b/tests/tests/media/src/android/media/cts/MediaBrowserTest.java
@@ -300,6 +300,24 @@
                 mSubscriptionCallback.mLastOptions.getInt(MediaBrowser.EXTRA_PAGE_SIZE));
     }
 
+    public void testSubscriptionCallbackNotCalledAfterDisconnect() {
+        createMediaBrowser(TEST_BROWSER_SERVICE);
+        connectMediaBrowserService();
+        mMediaBrowser.subscribe(StubMediaBrowserService.MEDIA_ID_ROOT, mSubscriptionCallback);
+        mMediaBrowser.disconnect();
+        resetCallbacks();
+        StubMediaBrowserService.sInstance.notifyChildrenChanged(
+                StubMediaBrowserService.MEDIA_ID_ROOT);
+        try {
+            Thread.sleep(SLEEP_MS);
+        } catch (InterruptedException e) {
+            fail("Unexpected InterruptedException occurred.");
+        }
+        assertEquals(0, mSubscriptionCallback.mChildrenLoadedCount);
+        assertEquals(0, mSubscriptionCallback.mChildrenLoadedWithOptionCount);
+        assertNull(mSubscriptionCallback.mLastParentId);
+    }
+
     public void testUnsubscribeForMultipleSubscriptions() {
         createMediaBrowser(TEST_BROWSER_SERVICE);
         connectMediaBrowserService();
@@ -436,6 +454,21 @@
         assertEquals(StubMediaBrowserService.MEDIA_ID_INVALID, mItemCallback.mLastErrorId);
     }
 
+    public void testItemCallbackNotCalledAfterDisconnect() {
+        createMediaBrowser(TEST_BROWSER_SERVICE);
+        connectMediaBrowserService();
+        mMediaBrowser.getItem(StubMediaBrowserService.MEDIA_ID_CHILDREN[0], mItemCallback);
+        mMediaBrowser.disconnect();
+        resetCallbacks();
+        try {
+            Thread.sleep(SLEEP_MS);
+        } catch (InterruptedException e) {
+            fail("Unexpected InterruptedException occurred.");
+        }
+        assertNull(mItemCallback.mLastMediaItem);
+        assertNull(mItemCallback.mLastErrorId);
+    }
+
     private void createMediaBrowser(final ComponentName component) {
         getInstrumentation().runOnMainSync(new Runnable() {
             @Override
diff --git a/tests/tests/media/src/android/media/cts/MediaPlayerTest.java b/tests/tests/media/src/android/media/cts/MediaPlayerTest.java
index 363c350..7dd08c4 100644
--- a/tests/tests/media/src/android/media/cts/MediaPlayerTest.java
+++ b/tests/tests/media/src/android/media/cts/MediaPlayerTest.java
@@ -1846,6 +1846,14 @@
     }
 
     public void testChangeTimedTextTrack() throws Throwable {
+        testChangeTimedTextTrackWithSpeed(1.0f);
+    }
+
+    public void testChangeTimedTextTrackFast() throws Throwable {
+        testChangeTimedTextTrackWithSpeed(2.0f);
+    }
+
+    private void testChangeTimedTextTrackWithSpeed(float speed) throws Throwable {
         testTimedText(R.raw.testvideo_with_2_timedtext_tracks, 2,
                 new int[] {R.raw.test_subtitle1_srt, R.raw.test_subtitle2_srt},
                 new VerifyAndSignalTimedText(),
@@ -1856,6 +1864,10 @@
                         mOnTimedTextCalled.reset();
 
                         mMediaPlayer.start();
+                        if (speed != 1.0f) {
+                            mMediaPlayer.setPlaybackParams(new PlaybackParams().setSpeed(speed));
+                        }
+
                         assertTrue(mMediaPlayer.isPlaying());
 
                         // Waits until at least two subtitles are fired. Timeout is 2.5 sec.
diff --git a/tests/tests/media/src/android/media/cts/MediaScannerTest.java b/tests/tests/media/src/android/media/cts/MediaScannerTest.java
index 137b7cf..401e2d5 100644
--- a/tests/tests/media/src/android/media/cts/MediaScannerTest.java
+++ b/tests/tests/media/src/android/media/cts/MediaScannerTest.java
@@ -110,6 +110,76 @@
                 "_data like ?", new String[] { mFileDir + "%"});
     }
 
+    public void testLocalizeRingtoneTitles() throws Exception {
+        mMediaScannerConnectionClient = new MockMediaScannerConnectionClient();
+        mMediaScannerConnection = new MockMediaScannerConnection(getContext(),
+            mMediaScannerConnectionClient);
+
+        assertFalse(mMediaScannerConnection.isConnected());
+
+        // start connection and wait until connected
+        mMediaScannerConnection.connect();
+        checkConnectionState(true);
+
+        // Write unlocalizable audio file and scan to insert into database
+        final String unlocalizablePath = mFileDir + "/unlocalizable.mp3";
+        writeFile(R.raw.testmp3, unlocalizablePath);
+        mMediaScannerConnection.scanFile(unlocalizablePath, null);
+        checkMediaScannerConnection();
+        final Uri media1Uri = mMediaScannerConnectionClient.mediaUri;
+
+        // Ensure unlocalizable titles come back correctly
+        final ContentResolver res = mContext.getContentResolver();
+        final String unlocalizedTitle = "Chimey Phone";
+        Cursor c = res.query(media1Uri, new String[] { "title" }, null, null, null);
+        assertEquals(1, c.getCount());
+        c.moveToFirst();
+        assertEquals(unlocalizedTitle, c.getString(0));
+
+        mMediaScannerConnectionClient.reset();
+
+        // Write localizable audio file and scan to insert into database
+        final String localizablePath = mFileDir + "/localizable.mp3";
+        writeFile(R.raw.testmp3_4, localizablePath);
+        mMediaScannerConnection.scanFile(localizablePath, null);
+        checkMediaScannerConnection();
+        final Uri media2Uri = mMediaScannerConnectionClient.mediaUri;
+
+        // Ensure localized title comes back localized
+        final String localizedTitle = mContext.getString(R.string.test_localizable_title);
+        c = res.query(media2Uri, new String[] { "title" }, null, null, null);
+        assertEquals(1, c.getCount());
+        c.moveToFirst();
+        assertEquals(localizedTitle, c.getString(0));
+
+        // Update localizable audio file to have unlocalizable title
+        final ContentValues values = new ContentValues();
+        final String newTitle = "New Title";
+        values.put("title", newTitle);
+        res.update(media2Uri, values, null, null);
+
+        // Ensure title comes back correctly
+        c = res.query(media2Uri, new String[] { "title" }, null, null, null);
+        assertEquals(1, c.getCount());
+        c.moveToFirst();
+        assertEquals(newTitle, c.getString(0));
+
+        // Update audio file to have localizable title once again
+        final String newLocalizableTitle =
+            "android.resource://android.media.cts/string/test_localizable_title";
+        values.put("title", newLocalizableTitle);
+        res.update(media2Uri, values, null, null);
+
+        // Ensure title comes back localized
+        c = res.query(media2Uri, new String[] { "title" }, null, null, null);
+        assertEquals(1, c.getCount());
+        c.moveToFirst();
+        assertEquals(localizedTitle, c.getString(0));
+
+        mMediaScannerConnection.disconnect();
+        c.close();
+    }
+
     public void testMediaScanner() throws InterruptedException, IOException {
         mMediaScannerConnectionClient = new MockMediaScannerConnectionClient();
         mMediaScannerConnection = new MockMediaScannerConnection(getContext(),
diff --git a/tests/tests/media/src/android/media/cts/MediaSessionTest.java b/tests/tests/media/src/android/media/cts/MediaSessionTest.java
index cc6f186..58be212 100644
--- a/tests/tests/media/src/android/media/cts/MediaSessionTest.java
+++ b/tests/tests/media/src/android/media/cts/MediaSessionTest.java
@@ -15,6 +15,8 @@
  */
 package android.media.cts;
 
+import static android.support.test.InstrumentationRegistry.getInstrumentation;
+
 import android.app.PendingIntent;
 import android.content.ComponentName;
 import android.content.Context;
@@ -33,6 +35,7 @@
 import android.os.Handler;
 import android.os.Looper;
 import android.os.Parcel;
+import android.support.test.filters.LargeTest;
 import android.test.AndroidTestCase;
 import android.view.KeyEvent;
 
@@ -417,6 +420,27 @@
         }
     }
 
+    /**
+     * Tests {@link MediaSession#setCallback} with {@code null}. No callbacks will be called
+     * once {@code setCallback(null)} is done.
+     */
+    public void testSetCallbackWithNull() throws Exception {
+        MediaSessionCallback sessionCallback = new MediaSessionCallback();
+        mSession.setCallback(sessionCallback, mHandler);
+        mSession.setFlags(MediaSession.FLAG_HANDLES_TRANSPORT_CONTROLS);
+        mSession.setActive(true);
+
+        MediaController controller = mSession.getController();
+        setPlaybackState(PlaybackState.STATE_PLAYING);
+
+        sessionCallback.reset(1);
+        mSession.setCallback(null, mHandler);
+
+        controller.getTransportControls().pause();
+        assertFalse(sessionCallback.await(WAIT_MS));
+        assertFalse("Callback shouldn't be called.", sessionCallback.mOnPauseCalled);
+    }
+
     private void setPlaybackState(int state) {
         final long allActions = PlaybackState.ACTION_PLAY | PlaybackState.ACTION_PAUSE
                 | PlaybackState.ACTION_PLAY_PAUSE | PlaybackState.ACTION_STOP
diff --git a/tests/tests/media/src/android/media/cts/NativeClearKeySystemTest.java b/tests/tests/media/src/android/media/cts/NativeClearKeySystemTest.java
index 45b2b8b..d29cfed 100644
--- a/tests/tests/media/src/android/media/cts/NativeClearKeySystemTest.java
+++ b/tests/tests/media/src/android/media/cts/NativeClearKeySystemTest.java
@@ -157,6 +157,7 @@
         try {
             testGetPropertyStringNative(uuidByteArray(CLEARKEY_SCHEME_UUID),
                     "unknown-property", value);
+            fail("Should have thrown an exception");
         } catch (RuntimeException e) {
             Log.e(TAG, "testUnknownPropertyString error = '" + e.getMessage() + "'");
             assertThat(e.getMessage(), containsString("get property string returns"));
diff --git a/tests/tests/media/src/android/media/cts/ParamsTest.java b/tests/tests/media/src/android/media/cts/ParamsTest.java
index 6cf9be6..fab616a 100644
--- a/tests/tests/media/src/android/media/cts/ParamsTest.java
+++ b/tests/tests/media/src/android/media/cts/ParamsTest.java
@@ -373,59 +373,18 @@
     }
 
     public void testBufferingParamsBuilderAndGet() {
-        final int initialMode = BufferingParams.BUFFERING_MODE_TIME_THEN_SIZE;
         final int initialMarkMs = 2;
-        final int initialMarkKB = 20;
-        final int rebufferingMode = BufferingParams.BUFFERING_MODE_TIME_THEN_SIZE;
-        final int rebufferingMarkLowMs = 1;
-        final int rebufferingMarkHighMs = 3;
-        final int rebufferingMarkLowKB = 10;
-        final int rebufferingMarkHighKB = 30;
+        final int resumePlaybackMarkMs = 3;
 
         BufferingParams p1 = new BufferingParams.Builder()
-                .setInitialBufferingMode(initialMode)
-                .setInitialBufferingWatermarkMs(initialMarkMs)
-                .setInitialBufferingWatermarkKB(initialMarkKB)
-                .setRebufferingMode(rebufferingMode)
-                .setRebufferingWatermarkLowMs(rebufferingMarkLowMs)
-                .setRebufferingWatermarkHighMs(rebufferingMarkHighMs)
-                .setRebufferingWatermarkLowKB(rebufferingMarkLowKB)
-                .setRebufferingWatermarkHighKB(rebufferingMarkHighKB)
+                .setInitialMarkMs(initialMarkMs)
+                .setResumePlaybackMarkMs(resumePlaybackMarkMs)
                 .build();
 
-        assertEquals("initial buffering mode should match",
-                p1.getInitialBufferingMode(), initialMode);
-        assertEquals("rebuffering mode should match",
-                p1.getRebufferingMode(), rebufferingMode);
         assertEquals("intial markMs should match",
-                p1.getInitialBufferingWatermarkMs(), initialMarkMs);
-        assertEquals("intial markKB should match",
-                p1.getInitialBufferingWatermarkKB(), initialMarkKB);
-        assertEquals("rebuffering low markMs should match",
-                p1.getRebufferingWatermarkLowMs(), rebufferingMarkLowMs);
-        assertEquals("rebuffering low markKB should match",
-                p1.getRebufferingWatermarkLowKB(), rebufferingMarkLowKB);
-        assertEquals("rebuffering high markMs should match",
-                p1.getRebufferingWatermarkHighMs(), rebufferingMarkHighMs);
-        assertEquals("rebuffering high markKB should match",
-                p1.getRebufferingWatermarkHighKB(), rebufferingMarkHighKB);
-
-        final int rebufferingMarkLowMsPair = 4;
-        final int rebufferingMarkHighMsPair = 5;
-        final int rebufferingMarkLowKBPair = 40;
-        final int rebufferingMarkHighKBPair = 50;
-        BufferingParams p2 = new BufferingParams.Builder(p1)
-                .setRebufferingWatermarksMs(rebufferingMarkLowMsPair, rebufferingMarkHighMsPair)
-                .setRebufferingWatermarksKB(rebufferingMarkLowKBPair, rebufferingMarkHighKBPair)
-                .build();
-        assertEquals("paired low markMs should match",
-                p2.getRebufferingWatermarkLowMs(), rebufferingMarkLowMsPair);
-        assertEquals("paired low markKB should match",
-                p2.getRebufferingWatermarkLowKB(), rebufferingMarkLowKBPair);
-        assertEquals("paired high markMs should match",
-                p2.getRebufferingWatermarkHighMs(), rebufferingMarkHighMsPair);
-        assertEquals("paired high markKB should match",
-                p2.getRebufferingWatermarkHighKB(), rebufferingMarkHighKBPair);
+                p1.getInitialMarkMs(), initialMarkMs);
+        assertEquals("resume playback markMs should match",
+                p1.getResumePlaybackMarkMs(), resumePlaybackMarkMs);
     }
 
     public void testBufferingParamsDescribeContents() {
@@ -434,24 +393,12 @@
     }
 
     public void testBufferingParamsWriteToParcel() {
-        final int initialMode = BufferingParams.BUFFERING_MODE_TIME_THEN_SIZE;
         final int initialMarkMs = 2;
-        final int initialMarkKB = 20;
-        final int rebufferingMode = BufferingParams.BUFFERING_MODE_TIME_THEN_SIZE;
-        final int rebufferingMarkLowMs = 1;
-        final int rebufferingMarkHighMs = 3;
-        final int rebufferingMarkLowKB = 10;
-        final int rebufferingMarkHighKB = 30;
+        final int resumePlaybackMarkMs = 3;
 
         BufferingParams p = new BufferingParams.Builder()
-                .setInitialBufferingMode(initialMode)
-                .setInitialBufferingWatermarkMs(initialMarkMs)
-                .setInitialBufferingWatermarkKB(initialMarkKB)
-                .setRebufferingMode(rebufferingMode)
-                .setRebufferingWatermarkLowMs(rebufferingMarkLowMs)
-                .setRebufferingWatermarkHighMs(rebufferingMarkHighMs)
-                .setRebufferingWatermarkLowKB(rebufferingMarkLowKB)
-                .setRebufferingWatermarkHighKB(rebufferingMarkHighKB)
+                .setInitialMarkMs(initialMarkMs)
+                .setResumePlaybackMarkMs(resumePlaybackMarkMs)
                 .build();
 
         Parcel parcel = Parcel.obtain();
@@ -459,22 +406,10 @@
         parcel.setDataPosition(0);
         BufferingParams q = BufferingParams.CREATOR.createFromParcel(parcel);
 
-        assertEquals("initial buffering mode should match",
-                p.getInitialBufferingMode(), q.getInitialBufferingMode());
-        assertEquals("rebuffering mode should match",
-                p.getRebufferingMode(), q.getRebufferingMode());
-        assertEquals("initial buffering markMs should match",
-                p.getInitialBufferingWatermarkMs(), q.getInitialBufferingWatermarkMs());
-        assertEquals("initial buffering markKB should match",
-                p.getInitialBufferingWatermarkKB(), q.getInitialBufferingWatermarkKB());
-        assertEquals("rebuffering low markMs should match",
-                p.getRebufferingWatermarkLowMs(), q.getRebufferingWatermarkLowMs());
-        assertEquals("rebuffering low markKB should match",
-                p.getRebufferingWatermarkLowKB(), q.getRebufferingWatermarkLowKB());
-        assertEquals("rebuffering high markMs should match",
-                p.getRebufferingWatermarkHighMs(), q.getRebufferingWatermarkHighMs());
-        assertEquals("rebuffering high markKB should match",
-                p.getRebufferingWatermarkHighKB(), q.getRebufferingWatermarkHighKB());
+        assertEquals("initial markMs should match",
+                p.getInitialMarkMs(), q.getInitialMarkMs());
+        assertEquals("resume playback markMs should match",
+                p.getResumePlaybackMarkMs(), q.getResumePlaybackMarkMs());
 
         parcel.recycle();
     }
diff --git a/tests/tests/media/src/android/media/cts/RoutingTest.java b/tests/tests/media/src/android/media/cts/RoutingTest.java
index 7614568..ddb534a 100644
--- a/tests/tests/media/src/android/media/cts/RoutingTest.java
+++ b/tests/tests/media/src/android/media/cts/RoutingTest.java
@@ -19,25 +19,39 @@
 import android.content.Context;
 import android.content.pm.PackageManager;
 
+import android.media.AudioAttributes;
 import android.media.AudioDeviceInfo;
 import android.media.AudioFormat;
 import android.media.AudioManager;
 import android.media.AudioRecord;
 import android.media.AudioRouting;
 import android.media.AudioTrack;
+import android.media.MediaPlayer;
+import android.media.MediaFormat;
 import android.media.MediaRecorder;
 
+import android.os.Environment;
 import android.os.Handler;
 import android.os.Looper;
+import android.os.SystemClock;
 
 import android.test.AndroidTestCase;
 
 import android.util.Log;
 
+import com.android.compatibility.common.util.MediaUtils;
+
+import java.io.File;
 import java.lang.Runnable;
+import java.util.Arrays;
+import java.util.HashSet;
+import java.util.Set;
+import java.util.concurrent.CountDownLatch;
+import java.util.concurrent.TimeUnit;
 
 /**
- * AudioTrack / AudioRecord preferred device and routing listener tests.
+ * AudioTrack / AudioRecord / MediaPlayer / MediaRecorder preferred device
+ * and routing listener tests.
  * The routing tests are mostly here to exercise the routing code, as an actual test would require
  * adding / removing an audio device for the listeners to be called.
  * The routing listener code is designed to run for two versions of the routing code:
@@ -46,8 +60,18 @@
  */
 public class RoutingTest extends AndroidTestCase {
     private static final String TAG = "RoutingTest";
+    private static final int MAX_WAITING_ROUTING_CHANGED_COUNT = 3;
+    private static final long WAIT_ROUTING_CHANGE_TIME_MS = 1000;
+    private static final int AUDIO_BIT_RATE_IN_BPS = 12200;
+    private static final int AUDIO_SAMPLE_RATE_HZ = 8000;
+    private static final long MAX_FILE_SIZE_BYTE = 5000;
+    private static final int RECORD_TIME_MS = 3000;
+    private static final Set<Integer> AVAILABLE_INPUT_DEVICES_TYPE = new HashSet<>(
+        Arrays.asList(AudioDeviceInfo.TYPE_BUILTIN_MIC));
 
     private AudioManager mAudioManager;
+    private CountDownLatch mRoutingChangedLatch;
+    private File mOutFile;
 
     @Override
     protected void setUp() throws Exception {
@@ -58,6 +82,14 @@
         assertNotNull(mAudioManager);
     }
 
+    @Override
+    protected void tearDown() throws Exception {
+        if (mOutFile != null && mOutFile.exists()) {
+            mOutFile.delete();
+        }
+        super.tearDown();
+    }
+
     private AudioTrack allocAudioTrack() {
         int bufferSize =
                 AudioTrack.getMinBufferSize(
@@ -455,4 +487,260 @@
         audioRecord.stop();
         audioRecord.release();
     }
+
+    private class AudioRoutingListener implements AudioRouting.OnRoutingChangedListener
+    {
+        public void onRoutingChanged(AudioRouting audioRouting) {
+            if (mRoutingChangedLatch != null) {
+                mRoutingChangedLatch.countDown();
+            }
+        }
+    }
+
+    private MediaPlayer allocMediaPlayer() {
+        final int resid = R.raw.testmp3_2;
+        MediaPlayer mediaPlayer = MediaPlayer.create(mContext, resid);
+        mediaPlayer.setAudioAttributes(
+            new AudioAttributes.Builder().setUsage(AudioAttributes.USAGE_MEDIA).build());
+        mediaPlayer.start();
+        return mediaPlayer;
+    }
+
+    public void test_mediaPlayer_preferredDevice() {
+        if (!mContext.getPackageManager().hasSystemFeature(PackageManager.FEATURE_AUDIO_OUTPUT)) {
+            // Can't do it so skip this test
+            return;
+        }
+
+        MediaPlayer mediaPlayer = allocMediaPlayer();
+        assertTrue(mediaPlayer.isPlaying());
+
+        // None selected (new MediaPlayer), so check for default
+        assertNull(mediaPlayer.getPreferredDevice());
+
+        // resets to default
+        assertTrue(mediaPlayer.setPreferredDevice(null));
+
+        // test each device
+        AudioDeviceInfo[] deviceList = mAudioManager.getDevices(AudioManager.GET_DEVICES_OUTPUTS);
+        for (int index = 0; index < deviceList.length; index++) {
+            assertTrue(mediaPlayer.setPreferredDevice(deviceList[index]));
+            assertTrue(mediaPlayer.getPreferredDevice() == deviceList[index]);
+        }
+
+        // Check defaults again
+        assertTrue(mediaPlayer.setPreferredDevice(null));
+        assertNull(mediaPlayer.getPreferredDevice());
+
+        mediaPlayer.stop();
+        mediaPlayer.release();
+    }
+
+    public void test_mediaPlayer_getRoutedDevice() {
+        if (!mContext.getPackageManager().hasSystemFeature(PackageManager.FEATURE_AUDIO_OUTPUT)) {
+            // Can't do it so skip this test
+            return;
+        }
+
+        MediaPlayer mediaPlayer = allocMediaPlayer();
+        assertTrue(mediaPlayer.isPlaying());
+
+        // Sleep for 1s to ensure the output device open
+        SystemClock.sleep(1000);
+
+        // No explicit route
+        AudioDeviceInfo routedDevice = mediaPlayer.getRoutedDevice();
+        assertNotNull(routedDevice);
+
+        mediaPlayer.stop();
+        mediaPlayer.release();
+    }
+
+    public void test_MediaPlayer_RoutingListener() {
+        if (!mContext.getPackageManager().hasSystemFeature(PackageManager.FEATURE_AUDIO_OUTPUT)) {
+            // Can't do it so skip this test
+            return;
+        }
+
+        MediaPlayer mediaPlayer = allocMediaPlayer();
+
+        // null listener
+        mediaPlayer.addOnRoutingChangedListener(null, null);
+
+        AudioRoutingListener listener = new AudioRoutingListener();
+        AudioRoutingListener someOtherListener = new AudioRoutingListener();
+
+        // add a listener
+        mediaPlayer.addOnRoutingChangedListener(listener, null);
+
+        // remove listeners
+        // remove a listener we didn't add
+        mediaPlayer.removeOnRoutingChangedListener(someOtherListener);
+        // remove a valid listener
+        mediaPlayer.removeOnRoutingChangedListener(listener);
+
+        Looper myLooper = prepareIfNeededLooper();
+
+        mediaPlayer.addOnRoutingChangedListener(listener, new Handler());
+        mediaPlayer.removeOnRoutingChangedListener(listener);
+
+        mediaPlayer.stop();
+        mediaPlayer.release();
+        if (myLooper != null) {
+            myLooper.quit();
+        }
+    }
+
+    public void test_MediaPlayer_RoutingChangedCallback() {
+        if (!mContext.getPackageManager().hasSystemFeature(PackageManager.FEATURE_AUDIO_OUTPUT)) {
+            // Can't do it so skip this test
+            return;
+        }
+
+        MediaPlayer mediaPlayer = allocMediaPlayer();
+        AudioRoutingListener listener = new AudioRoutingListener();
+        mediaPlayer.addOnRoutingChangedListener(listener, null);
+
+        AudioDeviceInfo[] deviceList = mAudioManager.getDevices(AudioManager.GET_DEVICES_OUTPUTS);
+        if (deviceList.length < 2) {
+            // The available output device is less than 2, we can't switch output device.
+            return;
+        }
+        for (int index = 0; index < deviceList.length; index++) {
+            assertTrue(mediaPlayer.setPreferredDevice(deviceList[index]));
+            boolean routingChanged = false;
+            for (int i = 0; i < MAX_WAITING_ROUTING_CHANGED_COUNT; i++) {
+                // Create a new CountDownLatch in case it is triggered by previous routing change.
+                mRoutingChangedLatch = new CountDownLatch(1);
+                try {
+                    mRoutingChangedLatch.await(WAIT_ROUTING_CHANGE_TIME_MS, TimeUnit.MILLISECONDS);
+                } catch (InterruptedException e) {
+                }
+                AudioDeviceInfo routedDevice = mediaPlayer.getRoutedDevice();
+                if (routedDevice == null) {
+                    continue;
+                }
+                if (routedDevice.getId() == deviceList[index].getId()) {
+                    routingChanged = true;
+                    break;
+                }
+            }
+            assertTrue("Switching to device" + deviceList[index].getType() + " failed",
+                    routingChanged);
+        }
+
+        mediaPlayer.removeOnRoutingChangedListener(listener);
+        mediaPlayer.stop();
+        mediaPlayer.release();
+    }
+
+    private MediaRecorder allocMediaRecorder() throws Exception {
+        final String outputPath = new File(Environment.getExternalStorageDirectory(),
+            "record.out").getAbsolutePath();
+        mOutFile = new File(outputPath);
+        MediaRecorder mediaRecorder = new MediaRecorder();
+        mediaRecorder.setAudioSource(MediaRecorder.AudioSource.MIC);
+        assertEquals(0, mediaRecorder.getMaxAmplitude());
+        mediaRecorder.setOutputFormat(MediaRecorder.OutputFormat.THREE_GPP);
+        mediaRecorder.setOutputFile(outputPath);
+        mediaRecorder.setAudioEncoder(MediaRecorder.AudioEncoder.AAC);
+        mediaRecorder.setAudioChannels(AudioFormat.CHANNEL_OUT_DEFAULT);
+        mediaRecorder.setAudioSamplingRate(AUDIO_SAMPLE_RATE_HZ);
+        mediaRecorder.setAudioEncodingBitRate(AUDIO_BIT_RATE_IN_BPS);
+        mediaRecorder.setMaxFileSize(MAX_FILE_SIZE_BYTE);
+        mediaRecorder.prepare();
+        mediaRecorder.start();
+        // Sleep a while to ensure the underlying AudioRecord is initialized.
+        Thread.sleep(1000);
+        return mediaRecorder;
+    }
+
+    public void test_mediaRecorder_preferredDevice() throws Exception {
+        if (!mContext.getPackageManager().hasSystemFeature(PackageManager.FEATURE_MICROPHONE)
+                || !MediaUtils.hasEncoder(MediaFormat.MIMETYPE_AUDIO_AAC)) {
+            MediaUtils.skipTest("no audio codecs or microphone");
+            return;
+        }
+
+        MediaRecorder mediaRecorder = allocMediaRecorder();
+
+        // None selected (new MediaPlayer), so check for default
+        assertNull(mediaRecorder.getPreferredDevice());
+
+        // resets to default
+        assertTrue(mediaRecorder.setPreferredDevice(null));
+
+        // test each device
+        AudioDeviceInfo[] deviceList = mAudioManager.getDevices(AudioManager.GET_DEVICES_INPUTS);
+        for (int index = 0; index < deviceList.length; index++) {
+            if (!AVAILABLE_INPUT_DEVICES_TYPE.contains(deviceList[index].getType())) {
+                // Only try to set devices whose type is contained in predefined set as preferred
+                // device in case of permission denied when switching input device.
+                continue;
+            }
+            assertTrue(mediaRecorder.setPreferredDevice(deviceList[index]));
+            assertTrue(mediaRecorder.getPreferredDevice() == deviceList[index]);
+        }
+
+        // Check defaults again
+        assertTrue(mediaRecorder.setPreferredDevice(null));
+        assertNull(mediaRecorder.getPreferredDevice());
+        Thread.sleep(RECORD_TIME_MS);
+
+        mediaRecorder.stop();
+        mediaRecorder.release();
+    }
+
+    public void test_mediaRecorder_getRoutedDeviceId() throws Exception {
+        if (!mContext.getPackageManager().hasSystemFeature(PackageManager.FEATURE_MICROPHONE)
+            || !MediaUtils.hasEncoder(MediaFormat.MIMETYPE_AUDIO_AAC)) {
+            MediaUtils.skipTest("no audio codecs or microphone");
+            return;
+        }
+
+        MediaRecorder mediaRecorder = allocMediaRecorder();
+
+        AudioDeviceInfo routedDevice = mediaRecorder.getRoutedDevice();
+        assertNotNull(routedDevice); // we probably can't say anything more than this
+        Thread.sleep(RECORD_TIME_MS);
+
+        mediaRecorder.stop();
+        mediaRecorder.release();
+    }
+
+    public void test_mediaRecorder_RoutingListener() throws Exception {
+        if (!mContext.getPackageManager().hasSystemFeature(PackageManager.FEATURE_MICROPHONE)
+            || !MediaUtils.hasEncoder(MediaFormat.MIMETYPE_AUDIO_AAC)) {
+            MediaUtils.skipTest("no audio codecs or microphone");
+            return;
+        }
+
+        MediaRecorder mediaRecorder = allocMediaRecorder();
+
+        // null listener
+        mediaRecorder.addOnRoutingChangedListener(null, null);
+
+        AudioRoutingListener listener = new AudioRoutingListener();
+        AudioRoutingListener someOtherListener = new AudioRoutingListener();
+
+        // add a listener
+        mediaRecorder.addOnRoutingChangedListener(listener, null);
+
+        // remove listeners we didn't add
+        mediaRecorder.removeOnRoutingChangedListener(someOtherListener);
+        // remove a valid listener
+        mediaRecorder.removeOnRoutingChangedListener(listener);
+
+        Looper myLooper = prepareIfNeededLooper();
+        mediaRecorder.addOnRoutingChangedListener(listener, new Handler());
+        mediaRecorder.removeOnRoutingChangedListener(listener);
+
+        Thread.sleep(RECORD_TIME_MS);
+
+        mediaRecorder.stop();
+        mediaRecorder.release();
+        if (myLooper != null) {
+            myLooper.quit();
+        }
+    }
 }
diff --git a/tests/tests/media/src/android/media/cts/StreamingMediaPlayerTest.java b/tests/tests/media/src/android/media/cts/StreamingMediaPlayerTest.java
index ab54e75..3a0d512 100644
--- a/tests/tests/media/src/android/media/cts/StreamingMediaPlayerTest.java
+++ b/tests/tests/media/src/android/media/cts/StreamingMediaPlayerTest.java
@@ -391,10 +391,22 @@
                 return; // skip
             }
 
-            // getDefaultBufferingParams should be called after setDataSource.
+            // getBufferingParams should be called after setDataSource.
             try {
-                BufferingParams params = mMediaPlayer.getDefaultBufferingParams();
-                fail("MediaPlayer failed to check state for getDefaultBufferingParams");
+                BufferingParams params = mMediaPlayer.getBufferingParams();
+                fail("MediaPlayer failed to check state for getBufferingParams");
+            } catch (IllegalStateException e) {
+                // expected
+            }
+
+            // setBufferingParams should be called after setDataSource.
+            try {
+                BufferingParams params = new BufferingParams.Builder()
+                        .setInitialMarkMs(2)
+                        .setResumePlaybackMarkMs(3)
+                        .build();
+                mMediaPlayer.setBufferingParams(params);
+                fail("MediaPlayer failed to check state for setBufferingParams");
             } catch (IllegalStateException e) {
                 // expected
             }
@@ -421,33 +433,17 @@
 
             assertFalse(mOnBufferingUpdateCalled.isSignalled());
 
-            BufferingParams params = mMediaPlayer.getDefaultBufferingParams();
+            BufferingParams params = mMediaPlayer.getBufferingParams();
 
-            int newMark = -1;
-            BufferingParams newParams = null;
-            int initialBufferingMode = params.getInitialBufferingMode();
-            if (initialBufferingMode == BufferingParams.BUFFERING_MODE_SIZE_ONLY
-                    || initialBufferingMode == BufferingParams.BUFFERING_MODE_TIME_THEN_SIZE) {
-                newMark = params.getInitialBufferingWatermarkKB() + 1;
-                newParams = new BufferingParams.Builder(params).setInitialBufferingWatermarkKB(
-                        newMark).build();
-            } else if (initialBufferingMode == BufferingParams.BUFFERING_MODE_TIME_ONLY) {
-                newMark = params.getInitialBufferingWatermarkMs() + 1;
-                newParams = new BufferingParams.Builder(params).setInitialBufferingWatermarkMs(
-                        newMark).build();
-            } else {
-                newParams = params;
-            }
+            int newMark = params.getInitialMarkMs() + 2;
+            BufferingParams newParams =
+                    new BufferingParams.Builder(params).setInitialMarkMs(newMark).build();
+
             mMediaPlayer.setBufferingParams(newParams);
 
             int checkMark = -1;
             BufferingParams checkParams = mMediaPlayer.getBufferingParams();
-            if (initialBufferingMode == BufferingParams.BUFFERING_MODE_SIZE_ONLY
-                    || initialBufferingMode == BufferingParams.BUFFERING_MODE_TIME_THEN_SIZE) {
-                checkMark = checkParams.getInitialBufferingWatermarkKB();
-            } else if (initialBufferingMode == BufferingParams.BUFFERING_MODE_TIME_ONLY) {
-                checkMark = checkParams.getInitialBufferingWatermarkMs();
-            }
+            checkMark = checkParams.getInitialMarkMs();
             assertEquals("marks do not match", newMark, checkMark);
 
             // TODO: add more dynamic checking, e.g., buffering shall not exceed pre-set mark.
diff --git a/tests/tests/mediastress/Android.mk b/tests/tests/mediastress/Android.mk
index 0213c00..633708f 100644
--- a/tests/tests/mediastress/Android.mk
+++ b/tests/tests/mediastress/Android.mk
@@ -28,6 +28,8 @@
 
 LOCAL_STATIC_JAVA_LIBRARIES := ctstestrunner compatibility-device-util
 
+LOCAL_JAVA_LIBRARIES := android.test.runner.stubs android.test.base.stubs
+
 LOCAL_HOST_SHARED_LIBRARIES := compatibility-device-media-preconditions
 
 LOCAL_JNI_SHARED_LIBRARIES := libctsmediastress_jni libnativehelper_compat_libc++
diff --git a/tests/tests/mediastress/AndroidTest.xml b/tests/tests/mediastress/AndroidTest.xml
index 3572ab8..5d186b8 100644
--- a/tests/tests/mediastress/AndroidTest.xml
+++ b/tests/tests/mediastress/AndroidTest.xml
@@ -14,6 +14,7 @@
      limitations under the License.
 -->
 <configuration description="Config for CTS Media Stress test cases">
+    <option name="test-suite-tag" value="cts" />
     <option name="config-descriptor:metadata" key="component" value="media" />
     <target_preparer class="com.android.compatibility.common.tradefed.targetprep.MediaPreparer" />
     <target_preparer class="com.android.tradefed.targetprep.suite.SuiteApkInstaller">
diff --git a/tests/tests/midi/Android.mk b/tests/tests/midi/Android.mk
index cefe3cc..212c8ac 100755
--- a/tests/tests/midi/Android.mk
+++ b/tests/tests/midi/Android.mk
@@ -27,6 +27,8 @@
 
 LOCAL_STATIC_JAVA_LIBRARIES := compatibility-device-util ctstestrunner
 
+LOCAL_JAVA_LIBRARIES := android.test.base.stubs
+
 LOCAL_SRC_FILES := $(call all-java-files-under, src)
 
 # Must match the package name in CtsTestCaseList.mk
diff --git a/tests/tests/midi/AndroidTest.xml b/tests/tests/midi/AndroidTest.xml
index f0b3cce..d8d12e2 100644
--- a/tests/tests/midi/AndroidTest.xml
+++ b/tests/tests/midi/AndroidTest.xml
@@ -14,6 +14,7 @@
      limitations under the License.
 -->
 <configuration description="Config for CTS MIDI test cases">
+    <option name="test-suite-tag" value="cts" />
     <option name="config-descriptor:metadata" key="component" value="media" />
     <target_preparer class="com.android.tradefed.targetprep.suite.SuiteApkInstaller">
         <option name="cleanup-apks" value="true" />
diff --git a/tests/tests/multiuser/Android.mk b/tests/tests/multiuser/Android.mk
index 992b5fe..7c9ca7e 100644
--- a/tests/tests/multiuser/Android.mk
+++ b/tests/tests/multiuser/Android.mk
@@ -29,6 +29,8 @@
 
 LOCAL_STATIC_JAVA_LIBRARIES := ctstestrunner compatibility-device-util
 
+LOCAL_JAVA_LIBRARIES := android.test.base.stubs
+
 LOCAL_SDK_VERSION := test_current
 
 include $(BUILD_CTS_PACKAGE)
diff --git a/tests/tests/multiuser/AndroidTest.xml b/tests/tests/multiuser/AndroidTest.xml
index 823ec5f..2edb2ec 100644
--- a/tests/tests/multiuser/AndroidTest.xml
+++ b/tests/tests/multiuser/AndroidTest.xml
@@ -15,6 +15,7 @@
   ~ limitations under the License
   -->
 <configuration description="Config for CTS Multiuser test cases">
+    <option name="test-suite-tag" value="cts" />
     <option name="config-descriptor:metadata" key="component" value="framework" />
     <target_preparer class="com.android.tradefed.targetprep.suite.SuiteApkInstaller">
         <option name="cleanup-apks" value="true" />
diff --git a/tests/tests/nativehardware/Android.mk b/tests/tests/nativehardware/Android.mk
index 38688be..21f2d9a 100644
--- a/tests/tests/nativehardware/Android.mk
+++ b/tests/tests/nativehardware/Android.mk
@@ -27,7 +27,7 @@
 
 LOCAL_STATIC_JAVA_LIBRARIES := compatibility-device-util
 
-LOCAL_JAVA_LIBRARIES := platform-test-annotations
+LOCAL_JAVA_LIBRARIES := platform-test-annotations android.test.base.stubs
 
 LOCAL_SDK_VERSION := current
 
diff --git a/tests/tests/nativehardware/AndroidTest.xml b/tests/tests/nativehardware/AndroidTest.xml
index 074a962..43457b3 100644
--- a/tests/tests/nativehardware/AndroidTest.xml
+++ b/tests/tests/nativehardware/AndroidTest.xml
@@ -14,6 +14,7 @@
      limitations under the License.
 -->
 <configuration description="Config for CTS Native Hardware test cases">
+    <option name="test-suite-tag" value="cts" />
     <option name="config-descriptor:metadata" key="component" value="vr" />
     <target_preparer class="com.android.tradefed.targetprep.suite.SuiteApkInstaller">
         <option name="cleanup-apks" value="true" />
diff --git a/tests/tests/nativemedia/aaudio/Android.mk b/tests/tests/nativemedia/aaudio/Android.mk
index 2f64131..2017eba 100644
--- a/tests/tests/nativemedia/aaudio/Android.mk
+++ b/tests/tests/nativemedia/aaudio/Android.mk
@@ -1,4 +1,4 @@
-# Copyright 2017 The Android Open Source Project
+# 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.
@@ -12,40 +12,30 @@
 # See the License for the specific language governing permissions and
 # limitations under the License.
 
-# Build the unit tests.
-
-LOCAL_PATH:= $(call my-dir)
+LOCAL_PATH := $(call my-dir)
 
 include $(CLEAR_VARS)
-LOCAL_MODULE := CtsNativeMediaAAudioTestCases
-LOCAL_MODULE_PATH := $(TARGET_OUT_DATA)/nativetest
+
+LOCAL_PACKAGE_NAME := CtsNativeMediaAAudioTestCases
+
+# Include both the 32 and 64 bit versions
 LOCAL_MULTILIB := both
-LOCAL_MODULE_STEM_32 := $(LOCAL_MODULE)32
-LOCAL_MODULE_STEM_64 := $(LOCAL_MODULE)64
 
-LOCAL_SRC_FILES := \
-    src/test_aaudio.cpp \
-    src/test_aaudio_callback.cpp \
-    src/test_aaudio_misc.cpp \
-    src/test_aaudio_mmap.cpp \
-    src/test_aaudio_stream_builder.cpp \
-    src/utils.cpp \
-
-LOCAL_SHARED_LIBRARIES := \
-    libaaudio \
-    liblog \
-
-LOCAL_STATIC_LIBRARIES := \
-    libgtest_ndk_c++ \
-
-LOCAL_CTS_TEST_PACKAGE := android.nativemedia.aaudio
+# 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_CFLAGS := -Werror -Wall
+LOCAL_STATIC_JAVA_LIBRARIES := ctstestrunner nativetesthelper
+
+LOCAL_JNI_SHARED_LIBRARIES := libnativeaaudiotest
+
+LOCAL_SRC_FILES := $(call all-java-files-under, src)
 
 LOCAL_SDK_VERSION := current
-LOCAL_NDK_STL_VARIANT := c++_static
 
-include $(BUILD_CTS_EXECUTABLE)
+include $(BUILD_CTS_PACKAGE)
+
+# Include the associated library's makefile.
+include $(LOCAL_PATH)/jni/Android.mk
diff --git a/tests/tests/nativemedia/aaudio/AndroidManifest.xml b/tests/tests/nativemedia/aaudio/AndroidManifest.xml
new file mode 100644
index 0000000..97bf5f9
--- /dev/null
+++ b/tests/tests/nativemedia/aaudio/AndroidManifest.xml
@@ -0,0 +1,33 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2017 The Android Open Source Project
+
+     Licensed under the Apache License, Version 2.0 (the "License");
+     you may not use this file except in compliance with the License.
+     You may obtain a copy of the License at
+
+          http://www.apache.org/licenses/LICENSE-2.0
+
+     Unless required by applicable law or agreed to in writing, software
+     distributed under the License is distributed on an "AS IS" BASIS,
+     WITHOUT 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.nativemedia.aaudio">
+
+    <uses-permission android:name="android.permission.DISABLE_KEYGUARD" />
+    <uses-permission android:name="android.permission.RECORD_AUDIO" />
+    <application>
+        <uses-library android:name="android.test.runner" />
+    </application>
+
+    <!-- This is a self-instrumenting test package. -->
+    <instrumentation android:name="android.support.test.runner.AndroidJUnitRunner"
+                     android:targetPackage="android.nativemedia.aaudio"
+                     android:label="CTS tests of native AAudio API">
+    </instrumentation>
+
+</manifest>
+
diff --git a/tests/tests/nativemedia/aaudio/AndroidTest.xml b/tests/tests/nativemedia/aaudio/AndroidTest.xml
index 7030d0c..b796ea6 100644
--- a/tests/tests/nativemedia/aaudio/AndroidTest.xml
+++ b/tests/tests/nativemedia/aaudio/AndroidTest.xml
@@ -15,16 +15,14 @@
 -->
 <configuration description="Config for CTS Native Media AAudio test cases">
     <option name="config-descriptor:metadata" key="component" value="media" />
-    <target_preparer class="com.android.compatibility.common.tradefed.targetprep.FilePusher">
-        <option name="cleanup" value="true" />
-        <option name="push" value="CtsNativeMediaAAudioTestCases->/data/local/tmp/CtsNativeMediaAAudioTestCases" />
-        <option name="append-bitness" value="true" />
+    <option name="test-suite-tag" value="cts" />
+    <option name="not-shardable" value="true" />
+    <target_preparer class="com.android.tradefed.targetprep.suite.SuiteApkInstaller">
+        <option name="cleanup-apks" value="true" />
+        <option name="test-file-name" value="CtsNativeMediaAAudioTestCases.apk" />
     </target_preparer>
-    <test class="com.android.tradefed.testtype.GTest" >
-        <option name="native-test-device-path" value="/data/local/tmp" />
-        <option name="module-name" value="CtsNativeMediaAAudioTestCases" />
-        <option name="runtime-hint" value="2m" />
-        <!-- test-timeout unit is ms, value = 2 min -->
-        <option name="native-test-timeout" value="120000" />
+    <test class="com.android.tradefed.testtype.AndroidJUnitTest" >
+        <option name="package" value="android.nativemedia.aaudio" />
+        <option name="runtime-hint" value="2m0s" />
     </test>
 </configuration>
diff --git a/tests/tests/nativemedia/aaudio/jni/Android.mk b/tests/tests/nativemedia/aaudio/jni/Android.mk
new file mode 100644
index 0000000..c6f0902
--- /dev/null
+++ b/tests/tests/nativemedia/aaudio/jni/Android.mk
@@ -0,0 +1,42 @@
+# Copyright 2017 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#      http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+# Build the unit tests.
+
+LOCAL_PATH:= $(call my-dir)
+
+include $(CLEAR_VARS)
+LOCAL_MODULE := libnativeaaudiotest
+LOCAL_MULTILIB := both
+
+LOCAL_SRC_FILES := \
+    test_aaudio.cpp \
+    test_aaudio_callback.cpp \
+    test_aaudio_mmap.cpp \
+    test_aaudio_misc.cpp \
+    test_aaudio_stream_builder.cpp \
+    utils.cpp \
+
+LOCAL_SHARED_LIBRARIES := \
+    libaaudio \
+    liblog
+
+LOCAL_WHOLE_STATIC_LIBRARIES := libnativetesthelper_jni
+
+LOCAL_CFLAGS := -Werror -Wall
+
+LOCAL_SDK_VERSION := current
+LOCAL_NDK_STL_VARIANT := c++_static
+
+include $(BUILD_SHARED_LIBRARY)
diff --git a/tests/tests/nativemedia/aaudio/src/test_aaudio.cpp b/tests/tests/nativemedia/aaudio/jni/test_aaudio.cpp
similarity index 100%
rename from tests/tests/nativemedia/aaudio/src/test_aaudio.cpp
rename to tests/tests/nativemedia/aaudio/jni/test_aaudio.cpp
diff --git a/tests/tests/nativemedia/aaudio/src/test_aaudio.h b/tests/tests/nativemedia/aaudio/jni/test_aaudio.h
similarity index 100%
rename from tests/tests/nativemedia/aaudio/src/test_aaudio.h
rename to tests/tests/nativemedia/aaudio/jni/test_aaudio.h
diff --git a/tests/tests/nativemedia/aaudio/src/test_aaudio_callback.cpp b/tests/tests/nativemedia/aaudio/jni/test_aaudio_callback.cpp
similarity index 100%
rename from tests/tests/nativemedia/aaudio/src/test_aaudio_callback.cpp
rename to tests/tests/nativemedia/aaudio/jni/test_aaudio_callback.cpp
diff --git a/tests/tests/nativemedia/aaudio/src/test_aaudio_misc.cpp b/tests/tests/nativemedia/aaudio/jni/test_aaudio_misc.cpp
similarity index 100%
rename from tests/tests/nativemedia/aaudio/src/test_aaudio_misc.cpp
rename to tests/tests/nativemedia/aaudio/jni/test_aaudio_misc.cpp
diff --git a/tests/tests/nativemedia/aaudio/src/test_aaudio_mmap.cpp b/tests/tests/nativemedia/aaudio/jni/test_aaudio_mmap.cpp
similarity index 100%
rename from tests/tests/nativemedia/aaudio/src/test_aaudio_mmap.cpp
rename to tests/tests/nativemedia/aaudio/jni/test_aaudio_mmap.cpp
diff --git a/tests/tests/nativemedia/aaudio/src/test_aaudio_stream_builder.cpp b/tests/tests/nativemedia/aaudio/jni/test_aaudio_stream_builder.cpp
similarity index 100%
rename from tests/tests/nativemedia/aaudio/src/test_aaudio_stream_builder.cpp
rename to tests/tests/nativemedia/aaudio/jni/test_aaudio_stream_builder.cpp
diff --git a/tests/tests/nativemedia/aaudio/jni/utils.cpp b/tests/tests/nativemedia/aaudio/jni/utils.cpp
new file mode 100644
index 0000000..55d1e13
--- /dev/null
+++ b/tests/tests/nativemedia/aaudio/jni/utils.cpp
@@ -0,0 +1,192 @@
+/*
+ * Copyright 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.
+ */
+
+#define LOG_TAG "AAudioTest"
+
+#include <sys/types.h>
+#include <unistd.h>
+
+#include <android/log.h>
+#include <gtest/gtest.h>
+
+#include "test_aaudio.h"
+#include "utils.h"
+
+int64_t getNanoseconds(clockid_t clockId) {
+    struct timespec time;
+    int result = clock_gettime(clockId, &time);
+    if (result < 0) {
+        return -errno;
+    }
+    return (time.tv_sec * NANOS_PER_SECOND) + time.tv_nsec;
+}
+
+const char* performanceModeToString(aaudio_performance_mode_t mode) {
+    switch (mode) {
+        case AAUDIO_PERFORMANCE_MODE_NONE: return "DEFAULT";
+        case AAUDIO_PERFORMANCE_MODE_POWER_SAVING: return "POWER_SAVING";
+        case AAUDIO_PERFORMANCE_MODE_LOW_LATENCY: return "LOW_LATENCY";
+    }
+    return "UNKNOWN";
+}
+
+const char* sharingModeToString(aaudio_sharing_mode_t mode) {
+    switch (mode) {
+        case AAUDIO_SHARING_MODE_SHARED: return "SHARED";
+        case AAUDIO_SHARING_MODE_EXCLUSIVE: return "EXCLUSIVE";
+    }
+    return "UNKNOWN";
+}
+
+
+// These periods are quite generous. They are not designed to put
+// any restrictions on the implementation, but only to ensure sanity.
+// Use int64_t because 96000 * 30000 is close to int32_t limits.
+const std::map<aaudio_performance_mode_t, int64_t> StreamBuilderHelper::sMaxFramesPerBurstMs =
+{ { AAUDIO_PERFORMANCE_MODE_NONE, 128 },
+  { AAUDIO_PERFORMANCE_MODE_POWER_SAVING, 30 * 1000 },
+  { AAUDIO_PERFORMANCE_MODE_LOW_LATENCY, 40 } };
+
+StreamBuilderHelper::StreamBuilderHelper(
+        aaudio_direction_t direction, int32_t sampleRate,
+        int32_t channelCount, aaudio_format_t dataFormat,
+        aaudio_sharing_mode_t sharingMode, aaudio_performance_mode_t perfMode)
+        : mDirection{direction},
+          mRequested{sampleRate, channelCount, dataFormat, sharingMode, perfMode},
+          mActual{0, 0, AAUDIO_FORMAT_INVALID, -1, -1}, mFramesPerBurst{-1},
+          mBuilder{nullptr}, mStream{nullptr} {}
+
+StreamBuilderHelper::~StreamBuilderHelper() {
+    close();
+}
+
+void StreamBuilderHelper::initBuilder() {
+    ASSERT_EQ(1U, sMaxFramesPerBurstMs.count(mRequested.perfMode));
+
+    // Use an AAudioStreamBuilder to define the stream.
+    aaudio_result_t result = AAudio_createStreamBuilder(&mBuilder);
+    ASSERT_EQ(AAUDIO_OK, result);
+    ASSERT_TRUE(mBuilder != nullptr);
+
+    // Request stream properties.
+    AAudioStreamBuilder_setDeviceId(mBuilder, AAUDIO_UNSPECIFIED);
+    AAudioStreamBuilder_setDirection(mBuilder, mDirection);
+    AAudioStreamBuilder_setSampleRate(mBuilder, mRequested.sampleRate);
+    AAudioStreamBuilder_setChannelCount(mBuilder, mRequested.channelCount);
+    AAudioStreamBuilder_setFormat(mBuilder, mRequested.dataFormat);
+    AAudioStreamBuilder_setSharingMode(mBuilder, mRequested.sharingMode);
+    AAudioStreamBuilder_setPerformanceMode(mBuilder, mRequested.perfMode);
+}
+
+// Needs to be a 'void' function due to ASSERT requirements.
+void StreamBuilderHelper::createAndVerifyStream(bool *success) {
+    *success = false;
+
+    aaudio_result_t result = AAudioStreamBuilder_openStream(mBuilder, &mStream);
+    if (mRequested.sharingMode == AAUDIO_SHARING_MODE_EXCLUSIVE && result != AAUDIO_OK) {
+        __android_log_write(ANDROID_LOG_WARN, LOG_TAG, "Could not open a stream in EXCLUSIVE mode");
+        return;
+    }
+    ASSERT_EQ(AAUDIO_OK, result);
+    ASSERT_TRUE(mStream != nullptr);
+    ASSERT_EQ(AAUDIO_STREAM_STATE_OPEN, AAudioStream_getState(mStream));
+    ASSERT_EQ(mDirection, AAudioStream_getDirection(mStream));
+
+    mActual.sharingMode = AAudioStream_getSharingMode(mStream);
+    if (mActual.sharingMode != mRequested.sharingMode) {
+        // Since we are covering all possible values, the "actual" mode
+        // will also be tested, so no need to run the same test twice.
+        __android_log_print(ANDROID_LOG_WARN, LOG_TAG, "Sharing mode %s is not available",
+                sharingModeToString(mRequested.sharingMode));
+        return;
+    }
+
+    // Check to see what kind of stream we actually got.
+    mActual.sampleRate = AAudioStream_getSampleRate(mStream);
+    ASSERT_GE(mActual.sampleRate, 44100);
+    ASSERT_LE(mActual.sampleRate, 96000); // TODO what is min/max?
+
+    mActual.channelCount = AAudioStream_getChannelCount(mStream);
+    ASSERT_GE(mActual.channelCount, 1);
+    ASSERT_LE(mActual.channelCount, 16); // TODO what is min/max?
+
+    mActual.dataFormat = AAudioStream_getFormat(mStream);
+    ASSERT_EQ(AAUDIO_FORMAT_PCM_I16, mActual.dataFormat);
+
+    mActual.perfMode = AAudioStream_getPerformanceMode(mStream);
+    if (mRequested.perfMode != AAUDIO_PERFORMANCE_MODE_NONE
+            && mRequested.perfMode != mActual.perfMode) {
+        // Since we are covering all possible values, the "actual" mode
+        // will also be tested, so no need to run the same test twice.
+        __android_log_print(ANDROID_LOG_WARN, LOG_TAG, "Performance mode %s is not available",
+                performanceModeToString(mRequested.sharingMode));
+        return;
+    }
+
+    mFramesPerBurst = AAudioStream_getFramesPerBurst(mStream);
+    ASSERT_GE(mFramesPerBurst, 16);
+    const int32_t maxFramesPerBurst =
+            mActual.sampleRate * sMaxFramesPerBurstMs.at(mActual.perfMode) / MILLIS_PER_SECOND;
+    ASSERT_LE(mFramesPerBurst, maxFramesPerBurst);
+
+    int32_t actualBufferSize = AAudioStream_getBufferSizeInFrames(mStream);
+    ASSERT_GT(actualBufferSize, 0);
+    ASSERT_GT(AAudioStream_setBufferSizeInFrames(mStream, actualBufferSize), 0);
+
+    *success = true;
+}
+
+void StreamBuilderHelper::close() {
+    if (mBuilder != nullptr) {
+        ASSERT_EQ(AAUDIO_OK, AAudioStreamBuilder_delete(mBuilder));
+    }
+    if (mStream != nullptr) {
+        ASSERT_EQ(AAUDIO_OK, AAudioStream_close(mStream));
+    }
+}
+
+void StreamBuilderHelper::streamCommand(
+        StreamCommand cmd, aaudio_stream_state_t fromState, aaudio_stream_state_t toState) {
+    ASSERT_EQ(AAUDIO_OK, cmd(mStream));
+    aaudio_stream_state_t state = AAUDIO_STREAM_STATE_UNINITIALIZED;
+    ASSERT_EQ(AAUDIO_OK,
+            AAudioStream_waitForStateChange(mStream, fromState, &state, DEFAULT_STATE_TIMEOUT));
+    ASSERT_EQ(toState, state);
+}
+
+
+InputStreamBuilderHelper::InputStreamBuilderHelper(
+        aaudio_sharing_mode_t requestedSharingMode, aaudio_performance_mode_t requestedPerfMode)
+        : StreamBuilderHelper{AAUDIO_DIRECTION_INPUT,
+            48000, 1, AAUDIO_FORMAT_PCM_I16, requestedSharingMode, requestedPerfMode} {}
+
+
+OutputStreamBuilderHelper::OutputStreamBuilderHelper(
+        aaudio_sharing_mode_t requestedSharingMode, aaudio_performance_mode_t requestedPerfMode)
+        : StreamBuilderHelper{AAUDIO_DIRECTION_OUTPUT,
+            48000, 2, AAUDIO_FORMAT_PCM_I16, requestedSharingMode, requestedPerfMode} {}
+
+void OutputStreamBuilderHelper::initBuilder() {
+    StreamBuilderHelper::initBuilder();
+    AAudioStreamBuilder_setBufferCapacityInFrames(mBuilder, kBufferCapacityFrames);
+}
+
+void OutputStreamBuilderHelper::createAndVerifyStream(bool *success) {
+    StreamBuilderHelper::createAndVerifyStream(success);
+    if (*success) {
+        ASSERT_GE(AAudioStream_getBufferCapacityInFrames(mStream), kBufferCapacityFrames);
+    }
+}
diff --git a/tests/tests/nativemedia/aaudio/jni/utils.h b/tests/tests/nativemedia/aaudio/jni/utils.h
new file mode 100644
index 0000000..7f38fef
--- /dev/null
+++ b/tests/tests/nativemedia/aaudio/jni/utils.h
@@ -0,0 +1,101 @@
+/*
+ * Copyright 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.
+ */
+#ifndef CTS_MEDIA_TEST_AAUDIO_UTILS_H
+#define CTS_MEDIA_TEST_AAUDIO_UTILS_H
+
+#include <map>
+
+#include <aaudio/AAudio.h>
+
+int64_t getNanoseconds(clockid_t clockId = CLOCK_MONOTONIC);
+const char* performanceModeToString(aaudio_performance_mode_t mode);
+const char* sharingModeToString(aaudio_sharing_mode_t mode);
+
+class StreamBuilderHelper {
+  public:
+    struct Parameters {
+        int32_t sampleRate;
+        int32_t channelCount;
+        aaudio_format_t dataFormat;
+        aaudio_sharing_mode_t sharingMode;
+        aaudio_performance_mode_t perfMode;
+    };
+
+    void initBuilder();
+    void createAndVerifyStream(bool *success);
+    void close();
+
+    void startStream() {
+        streamCommand(&AAudioStream_requestStart,
+                AAUDIO_STREAM_STATE_STARTING, AAUDIO_STREAM_STATE_STARTED);
+    }
+    void pauseStream() {
+        streamCommand(&AAudioStream_requestPause,
+                AAUDIO_STREAM_STATE_PAUSING, AAUDIO_STREAM_STATE_PAUSED);
+    }
+    void stopStream() {
+        streamCommand(&AAudioStream_requestStop,
+                AAUDIO_STREAM_STATE_STOPPING, AAUDIO_STREAM_STATE_STOPPED);
+    }
+    void flushStream() {
+        streamCommand(&AAudioStream_requestFlush,
+                AAUDIO_STREAM_STATE_FLUSHING, AAUDIO_STREAM_STATE_FLUSHED);
+    }
+
+    AAudioStreamBuilder* builder() const { return mBuilder; }
+    AAudioStream* stream() const { return mStream; }
+    const Parameters& actual() const { return mActual; }
+    int32_t framesPerBurst() const { return mFramesPerBurst; }
+
+  protected:
+    StreamBuilderHelper(aaudio_direction_t direction, int32_t sampleRate,
+            int32_t channelCount, aaudio_format_t dataFormat,
+            aaudio_sharing_mode_t sharingMode, aaudio_performance_mode_t perfMode);
+    ~StreamBuilderHelper();
+
+    typedef aaudio_result_t (StreamCommand)(AAudioStream*);
+    void streamCommand(
+            StreamCommand cmd, aaudio_stream_state_t fromState, aaudio_stream_state_t toState);
+
+    static const std::map<aaudio_performance_mode_t, int64_t> sMaxFramesPerBurstMs;
+    const aaudio_direction_t mDirection;
+    const Parameters mRequested;
+    Parameters mActual;
+    int32_t mFramesPerBurst;
+    AAudioStreamBuilder *mBuilder;
+    AAudioStream *mStream;
+};
+
+class InputStreamBuilderHelper : public StreamBuilderHelper {
+  public:
+    InputStreamBuilderHelper(
+            aaudio_sharing_mode_t requestedSharingMode,
+            aaudio_performance_mode_t requestedPerfMode);
+};
+
+class OutputStreamBuilderHelper : public StreamBuilderHelper {
+  public:
+    OutputStreamBuilderHelper(
+            aaudio_sharing_mode_t requestedSharingMode,
+            aaudio_performance_mode_t requestedPerfMode);
+    void initBuilder();
+    void createAndVerifyStream(bool *success);
+
+  private:
+    const int32_t kBufferCapacityFrames = 2000;
+};
+
+#endif  // CTS_MEDIA_TEST_AAUDIO_UTILS_H
diff --git a/tests/tests/nativemedia/aaudio/src/android/nativemedia/aaudio/AAudioTests.java b/tests/tests/nativemedia/aaudio/src/android/nativemedia/aaudio/AAudioTests.java
new file mode 100644
index 0000000..afb2db4
--- /dev/null
+++ b/tests/tests/nativemedia/aaudio/src/android/nativemedia/aaudio/AAudioTests.java
@@ -0,0 +1,25 @@
+/*
+ * 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.nativemedia.aaudio;
+
+import org.junit.runner.RunWith;
+import com.android.gtestrunner.GtestRunner;
+import com.android.gtestrunner.TargetLibrary;
+
+@RunWith(GtestRunner.class)
+@TargetLibrary("nativeaaudiotest")
+public class AAudioTests {}
diff --git a/tests/tests/nativemedia/aaudio/src/utils.cpp b/tests/tests/nativemedia/aaudio/src/utils.cpp
deleted file mode 100644
index b039f4e..0000000
--- a/tests/tests/nativemedia/aaudio/src/utils.cpp
+++ /dev/null
@@ -1,208 +0,0 @@
-/*
- * Copyright 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.
- */
-
-#define LOG_TAG "AAudioTest"
-
-#include <sys/types.h>
-#include <unistd.h>
-
-#include <android/log.h>
-#include <gtest/gtest.h>
-
-#include "test_aaudio.h"
-#include "utils.h"
-
-int64_t getNanoseconds(clockid_t clockId) {
-    struct timespec time;
-    int result = clock_gettime(clockId, &time);
-    if (result < 0) {
-        return -errno;
-    }
-    return (time.tv_sec * NANOS_PER_SECOND) + time.tv_nsec;
-}
-
-const char* performanceModeToString(aaudio_performance_mode_t mode) {
-    switch (mode) {
-        case AAUDIO_PERFORMANCE_MODE_NONE: return "DEFAULT";
-        case AAUDIO_PERFORMANCE_MODE_POWER_SAVING: return "POWER_SAVING";
-        case AAUDIO_PERFORMANCE_MODE_LOW_LATENCY: return "LOW_LATENCY";
-    }
-    return "UNKNOWN";
-}
-
-const char* sharingModeToString(aaudio_sharing_mode_t mode) {
-    switch (mode) {
-        case AAUDIO_SHARING_MODE_SHARED: return "SHARED";
-        case AAUDIO_SHARING_MODE_EXCLUSIVE: return "EXCLUSIVE";
-    }
-    return "UNKNOWN";
-}
-
-
-// These periods are quite generous. They are not designed to put
-// any restrictions on the implementation, but only to ensure sanity.
-// Use int64_t because 96000 * 30000 is close to int32_t limits.
-const std::map<aaudio_performance_mode_t, int64_t> StreamBuilderHelper::sMaxFramesPerBurstMs =
-{ { AAUDIO_PERFORMANCE_MODE_NONE, 128 },
-  { AAUDIO_PERFORMANCE_MODE_POWER_SAVING, 30 * 1000 },
-  { AAUDIO_PERFORMANCE_MODE_LOW_LATENCY, 40 } };
-
-StreamBuilderHelper::StreamBuilderHelper(
-        aaudio_direction_t direction, int32_t sampleRate,
-        int32_t channelCount, aaudio_format_t dataFormat,
-        aaudio_sharing_mode_t sharingMode, aaudio_performance_mode_t perfMode)
-        : mDirection{direction},
-          mRequested{sampleRate, channelCount, dataFormat, sharingMode, perfMode},
-          mActual{0, 0, AAUDIO_FORMAT_INVALID, -1, -1}, mFramesPerBurst{-1},
-          mBuilder{nullptr}, mStream{nullptr} {}
-
-StreamBuilderHelper::~StreamBuilderHelper() {
-    close();
-}
-
-void StreamBuilderHelper::initBuilder() {
-    ASSERT_EQ(1U, sMaxFramesPerBurstMs.count(mRequested.perfMode));
-
-    // Use an AAudioStreamBuilder to define the stream.
-    aaudio_result_t result = AAudio_createStreamBuilder(&mBuilder);
-    ASSERT_EQ(AAUDIO_OK, result);
-    ASSERT_TRUE(mBuilder != nullptr);
-
-    // Request stream properties.
-    AAudioStreamBuilder_setDeviceId(mBuilder, AAUDIO_UNSPECIFIED);
-    AAudioStreamBuilder_setDirection(mBuilder, mDirection);
-    AAudioStreamBuilder_setSampleRate(mBuilder, mRequested.sampleRate);
-    AAudioStreamBuilder_setChannelCount(mBuilder, mRequested.channelCount);
-    AAudioStreamBuilder_setFormat(mBuilder, mRequested.dataFormat);
-    AAudioStreamBuilder_setSharingMode(mBuilder, mRequested.sharingMode);
-    AAudioStreamBuilder_setPerformanceMode(mBuilder, mRequested.perfMode);
-}
-
-// Needs to be a 'void' function due to ASSERT requirements.
-void StreamBuilderHelper::createAndVerifyStream(bool *success) {
-    *success = false;
-
-    aaudio_result_t result = AAudioStreamBuilder_openStream(mBuilder, &mStream);
-    if (mRequested.sharingMode == AAUDIO_SHARING_MODE_EXCLUSIVE && result != AAUDIO_OK) {
-        __android_log_write(ANDROID_LOG_WARN, LOG_TAG, "Could not open a stream in EXCLUSIVE mode");
-        return;
-    }
-    ASSERT_EQ(AAUDIO_OK, result);
-    ASSERT_TRUE(mStream != nullptr);
-    ASSERT_EQ(AAUDIO_STREAM_STATE_OPEN, AAudioStream_getState(mStream));
-    ASSERT_EQ(mDirection, AAudioStream_getDirection(mStream));
-
-    mActual.sharingMode = AAudioStream_getSharingMode(mStream);
-    if (mActual.sharingMode != mRequested.sharingMode) {
-        // Since we are covering all possible values, the "actual" mode
-        // will also be tested, so no need to run the same test twice.
-        __android_log_print(ANDROID_LOG_WARN, LOG_TAG, "Sharing mode %s is not available",
-                sharingModeToString(mRequested.sharingMode));
-        return;
-    }
-
-    // Check to see what kind of stream we actually got.
-    mActual.sampleRate = AAudioStream_getSampleRate(mStream);
-    ASSERT_GE(mActual.sampleRate, 44100);
-    ASSERT_LE(mActual.sampleRate, 96000); // TODO what is min/max?
-
-    mActual.channelCount = AAudioStream_getChannelCount(mStream);
-    ASSERT_GE(mActual.channelCount, 1);
-    ASSERT_LE(mActual.channelCount, 16); // TODO what is min/max?
-
-    mActual.dataFormat = AAudioStream_getFormat(mStream);
-    ASSERT_EQ(AAUDIO_FORMAT_PCM_I16, mActual.dataFormat);
-
-    mActual.perfMode = AAudioStream_getPerformanceMode(mStream);
-    if (mRequested.perfMode != AAUDIO_PERFORMANCE_MODE_NONE
-            && mRequested.perfMode != mActual.perfMode) {
-        // Since we are covering all possible values, the "actual" mode
-        // will also be tested, so no need to run the same test twice.
-        __android_log_print(ANDROID_LOG_WARN, LOG_TAG, "Performance mode %s is not available",
-                performanceModeToString(mRequested.sharingMode));
-        return;
-    }
-
-    mFramesPerBurst = AAudioStream_getFramesPerBurst(mStream);
-    ASSERT_GE(mFramesPerBurst, 16);
-    const int32_t maxFramesPerBurst =
-            mActual.sampleRate * sMaxFramesPerBurstMs.at(mActual.perfMode) / MILLIS_PER_SECOND;
-    ASSERT_LE(mFramesPerBurst, maxFramesPerBurst);
-
-    int32_t actualBufferSize = AAudioStream_getBufferSizeInFrames(mStream);
-    ASSERT_GT(actualBufferSize, 0);
-    ASSERT_GT(AAudioStream_setBufferSizeInFrames(mStream, actualBufferSize), 0);
-
-    *success = true;
-}
-
-void StreamBuilderHelper::close() {
-    if (mBuilder != nullptr) {
-        ASSERT_EQ(AAUDIO_OK, AAudioStreamBuilder_delete(mBuilder));
-    }
-    if (mStream != nullptr) {
-        ASSERT_EQ(AAUDIO_OK, AAudioStream_close(mStream));
-    }
-}
-
-void StreamBuilderHelper::streamCommand(
-        StreamCommand cmd, aaudio_stream_state_t fromState, aaudio_stream_state_t toState) {
-    ASSERT_EQ(AAUDIO_OK, cmd(mStream));
-    aaudio_stream_state_t state = AAUDIO_STREAM_STATE_UNINITIALIZED;
-    ASSERT_EQ(AAUDIO_OK,
-            AAudioStream_waitForStateChange(mStream, fromState, &state, DEFAULT_STATE_TIMEOUT));
-    ASSERT_EQ(toState, state);
-}
-
-
-InputStreamBuilderHelper::InputStreamBuilderHelper(
-        aaudio_sharing_mode_t requestedSharingMode, aaudio_performance_mode_t requestedPerfMode)
-        : StreamBuilderHelper{AAUDIO_DIRECTION_INPUT,
-            48000, 1, AAUDIO_FORMAT_PCM_I16, requestedSharingMode, requestedPerfMode} {}
-
-// Native apps don't have permissions, thus recording can
-// only be tested when running as root.
-static bool canTestRecording() {
-    static const bool runningAsRoot = getuid() == 0;
-    return runningAsRoot;
-}
-
-void InputStreamBuilderHelper::createAndVerifyStream(bool *success) {
-    if (!canTestRecording()) {
-        __android_log_write(ANDROID_LOG_WARN, LOG_TAG, "No permissions to run recording tests");
-        *success = false;
-    } else {
-        StreamBuilderHelper::createAndVerifyStream(success);
-    }
-}
-
-
-OutputStreamBuilderHelper::OutputStreamBuilderHelper(
-        aaudio_sharing_mode_t requestedSharingMode, aaudio_performance_mode_t requestedPerfMode)
-        : StreamBuilderHelper{AAUDIO_DIRECTION_OUTPUT,
-            48000, 2, AAUDIO_FORMAT_PCM_I16, requestedSharingMode, requestedPerfMode} {}
-
-void OutputStreamBuilderHelper::initBuilder() {
-    StreamBuilderHelper::initBuilder();
-    AAudioStreamBuilder_setBufferCapacityInFrames(mBuilder, kBufferCapacityFrames);
-}
-
-void OutputStreamBuilderHelper::createAndVerifyStream(bool *success) {
-    StreamBuilderHelper::createAndVerifyStream(success);
-    if (*success) {
-        ASSERT_GE(AAudioStream_getBufferCapacityInFrames(mStream), kBufferCapacityFrames);
-    }
-}
diff --git a/tests/tests/nativemedia/aaudio/src/utils.h b/tests/tests/nativemedia/aaudio/src/utils.h
deleted file mode 100644
index 44c3c69..0000000
--- a/tests/tests/nativemedia/aaudio/src/utils.h
+++ /dev/null
@@ -1,102 +0,0 @@
-/*
- * Copyright 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.
- */
-#ifndef CTS_MEDIA_TEST_AAUDIO_UTILS_H
-#define CTS_MEDIA_TEST_AAUDIO_UTILS_H
-
-#include <map>
-
-#include <aaudio/AAudio.h>
-
-int64_t getNanoseconds(clockid_t clockId = CLOCK_MONOTONIC);
-const char* performanceModeToString(aaudio_performance_mode_t mode);
-const char* sharingModeToString(aaudio_sharing_mode_t mode);
-
-class StreamBuilderHelper {
-  public:
-    struct Parameters {
-        int32_t sampleRate;
-        int32_t channelCount;
-        aaudio_format_t dataFormat;
-        aaudio_sharing_mode_t sharingMode;
-        aaudio_performance_mode_t perfMode;
-    };
-
-    void initBuilder();
-    void createAndVerifyStream(bool *success);
-    void close();
-
-    void startStream() {
-        streamCommand(&AAudioStream_requestStart,
-                AAUDIO_STREAM_STATE_STARTING, AAUDIO_STREAM_STATE_STARTED);
-    }
-    void pauseStream() {
-        streamCommand(&AAudioStream_requestPause,
-                AAUDIO_STREAM_STATE_PAUSING, AAUDIO_STREAM_STATE_PAUSED);
-    }
-    void stopStream() {
-        streamCommand(&AAudioStream_requestStop,
-                AAUDIO_STREAM_STATE_STOPPING, AAUDIO_STREAM_STATE_STOPPED);
-    }
-    void flushStream() {
-        streamCommand(&AAudioStream_requestFlush,
-                AAUDIO_STREAM_STATE_FLUSHING, AAUDIO_STREAM_STATE_FLUSHED);
-    }
-
-    AAudioStreamBuilder* builder() const { return mBuilder; }
-    AAudioStream* stream() const { return mStream; }
-    const Parameters& actual() const { return mActual; }
-    int32_t framesPerBurst() const { return mFramesPerBurst; }
-
-  protected:
-    StreamBuilderHelper(aaudio_direction_t direction, int32_t sampleRate,
-            int32_t channelCount, aaudio_format_t dataFormat,
-            aaudio_sharing_mode_t sharingMode, aaudio_performance_mode_t perfMode);
-    ~StreamBuilderHelper();
-
-    typedef aaudio_result_t (StreamCommand)(AAudioStream*);
-    void streamCommand(
-            StreamCommand cmd, aaudio_stream_state_t fromState, aaudio_stream_state_t toState);
-
-    static const std::map<aaudio_performance_mode_t, int64_t> sMaxFramesPerBurstMs;
-    const aaudio_direction_t mDirection;
-    const Parameters mRequested;
-    Parameters mActual;
-    int32_t mFramesPerBurst;
-    AAudioStreamBuilder *mBuilder;
-    AAudioStream *mStream;
-};
-
-class InputStreamBuilderHelper : public StreamBuilderHelper {
-  public:
-    InputStreamBuilderHelper(
-            aaudio_sharing_mode_t requestedSharingMode,
-            aaudio_performance_mode_t requestedPerfMode);
-    void createAndVerifyStream(bool *success);
-};
-
-class OutputStreamBuilderHelper : public StreamBuilderHelper {
-  public:
-    OutputStreamBuilderHelper(
-            aaudio_sharing_mode_t requestedSharingMode,
-            aaudio_performance_mode_t requestedPerfMode);
-    void initBuilder();
-    void createAndVerifyStream(bool *success);
-
-  private:
-    const int32_t kBufferCapacityFrames = 2000;
-};
-
-#endif  // CTS_MEDIA_TEST_AAUDIO_UTILS_H
diff --git a/tests/tests/nativemedia/sl/AndroidTest.xml b/tests/tests/nativemedia/sl/AndroidTest.xml
index 0677a62..cfb0cae 100644
--- a/tests/tests/nativemedia/sl/AndroidTest.xml
+++ b/tests/tests/nativemedia/sl/AndroidTest.xml
@@ -14,6 +14,7 @@
      limitations under the License.
 -->
 <configuration description="Config for CTS Native Media Open SL ES test cases">
+    <option name="test-suite-tag" value="cts" />
     <option name="config-descriptor:metadata" key="component" value="media" />
     <target_preparer class="com.android.compatibility.common.tradefed.targetprep.FilePusher">
         <option name="cleanup" value="true" />
diff --git a/tests/tests/nativemedia/xa/AndroidTest.xml b/tests/tests/nativemedia/xa/AndroidTest.xml
index c63c1ab..ae07686 100644
--- a/tests/tests/nativemedia/xa/AndroidTest.xml
+++ b/tests/tests/nativemedia/xa/AndroidTest.xml
@@ -14,6 +14,7 @@
      limitations under the License.
 -->
 <configuration description="Config for CTS Native Media OpenMax AL test cases">
+    <option name="test-suite-tag" value="cts" />
     <option name="config-descriptor:metadata" key="component" value="media" />
     <target_preparer class="com.android.compatibility.common.tradefed.targetprep.FilePusher">
         <option name="cleanup" value="true" />
diff --git a/tests/tests/ndef/AndroidTest.xml b/tests/tests/ndef/AndroidTest.xml
index ca949bd..aef105f 100644
--- a/tests/tests/ndef/AndroidTest.xml
+++ b/tests/tests/ndef/AndroidTest.xml
@@ -14,6 +14,7 @@
      limitations under the License.
 -->
 <configuration description="Config for CTS NDEF test cases">
+    <option name="test-suite-tag" value="cts" />
     <option name="config-descriptor:metadata" key="component" value="systems" />
     <target_preparer class="com.android.tradefed.targetprep.suite.SuiteApkInstaller">
         <option name="cleanup-apks" value="true" />
diff --git a/tests/tests/net/Android.mk b/tests/tests/net/Android.mk
index 4aeab38..1d4d54b 100644
--- a/tests/tests/net/Android.mk
+++ b/tests/tests/net/Android.mk
@@ -24,7 +24,12 @@
 # Include both the 32 and 64 bit versions
 LOCAL_MULTILIB := both
 
-LOCAL_JAVA_LIBRARIES := voip-common conscrypt org.apache.http.legacy
+LOCAL_JAVA_LIBRARIES := \
+    voip-common \
+    conscrypt \
+    org.apache.http.legacy \
+    android.test.base \
+
 
 LOCAL_JNI_SHARED_LIBRARIES := libcts_jni libnativedns_jni \
                               libnativemultinetwork_jni libnativehelper_compat_libc++
@@ -40,8 +45,7 @@
     ctstestrunner \
     ctstestserver \
     mockwebserver \
-    junit \
-    legacy-android-test
+    junit
 
 # uncomment when b/13249961 is fixed
 #LOCAL_SDK_VERSION := current
diff --git a/tests/tests/net/AndroidManifest.xml b/tests/tests/net/AndroidManifest.xml
index dd310a1..37bf323 100644
--- a/tests/tests/net/AndroidManifest.xml
+++ b/tests/tests/net/AndroidManifest.xml
@@ -30,7 +30,7 @@
     <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
     <uses-permission android:name="android.permission.WRITE_SECURE_SETTINGS" />
 
-    <application>
+    <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/tests/net/AndroidTest.xml b/tests/tests/net/AndroidTest.xml
index 4a578ea..4190a77 100644
--- a/tests/tests/net/AndroidTest.xml
+++ b/tests/tests/net/AndroidTest.xml
@@ -13,6 +13,7 @@
      limitations under the License.
 -->
 <configuration description="Config for CTS Net test cases">
+    <option name="test-suite-tag" value="cts" />
     <option name="config-descriptor:metadata" key="component" value="networking" />
     <target_preparer class="com.android.tradefed.targetprep.suite.SuiteApkInstaller">
         <option name="cleanup-apks" value="true" />
diff --git a/tests/tests/net/native/qtaguid/AndroidTest.xml b/tests/tests/net/native/qtaguid/AndroidTest.xml
index 2eea82e..7591c87 100644
--- a/tests/tests/net/native/qtaguid/AndroidTest.xml
+++ b/tests/tests/net/native/qtaguid/AndroidTest.xml
@@ -14,6 +14,7 @@
      limitations under the License.
 -->
 <configuration description="Config for CTS Native Network xt_qtaguid test cases">
+    <option name="test-suite-tag" value="cts" />
     <option name="config-descriptor:metadata" key="component" value="networking" />
     <target_preparer class="com.android.compatibility.common.tradefed.targetprep.FilePusher">
         <option name="cleanup" value="true" />
diff --git a/tests/tests/net/src/android/net/cts/ConnectivityManagerTest.java b/tests/tests/net/src/android/net/cts/ConnectivityManagerTest.java
index e98dfcc..27dd666 100644
--- a/tests/tests/net/src/android/net/cts/ConnectivityManagerTest.java
+++ b/tests/tests/net/src/android/net/cts/ConnectivityManagerTest.java
@@ -16,8 +16,11 @@
 
 package android.net.cts;
 
+import static android.content.pm.PackageManager.FEATURE_TELEPHONY;
+import static android.content.pm.PackageManager.FEATURE_WIFI;
 import static android.net.NetworkCapabilities.NET_CAPABILITY_IMS;
 import static android.net.NetworkCapabilities.NET_CAPABILITY_INTERNET;
+import static android.net.NetworkCapabilities.TRANSPORT_CELLULAR;
 import static android.net.NetworkCapabilities.TRANSPORT_WIFI;
 
 import android.app.PendingIntent;
@@ -29,6 +32,7 @@
 import android.content.pm.PackageManager;
 import android.net.ConnectivityManager;
 import android.net.ConnectivityManager.NetworkCallback;
+import android.net.LinkProperties;
 import android.net.Network;
 import android.net.NetworkCapabilities;
 import android.net.NetworkConfig;
@@ -50,14 +54,23 @@
 import java.io.FileNotFoundException;
 import java.io.InputStream;
 import java.io.IOException;
+import java.io.InputStreamReader;
 import java.io.OutputStream;
+import java.net.HttpURLConnection;
+import java.net.Inet6Address;
+import java.net.InetAddress;
 import java.net.Socket;
 import java.net.InetSocketAddress;
+import java.net.URL;
+import java.net.UnknownHostException;
+import java.nio.charset.StandardCharsets;
+import java.util.Collection;
 import java.util.HashMap;
 import java.util.Scanner;
 import java.util.concurrent.CountDownLatch;
 import java.util.concurrent.LinkedBlockingQueue;
 import java.util.concurrent.TimeUnit;
+import libcore.io.Streams;
 
 public class ConnectivityManagerTest extends AndroidTestCase {
 
@@ -107,6 +120,8 @@
     private final HashMap<Integer, NetworkConfig> mNetworks =
             new HashMap<Integer, NetworkConfig>();
     boolean mWifiConnectAttempted;
+    private TestNetworkCallback mCellNetworkCallback;
+
 
     @Override
     protected void setUp() throws Exception {
@@ -140,6 +155,10 @@
         if (mWifiConnectAttempted) {
             disconnectFromWifi(null);
         }
+        if (cellConnectAttempted()) {
+            disconnectFromCell();
+        }
+        super.tearDown();
     }
 
     /**
@@ -246,6 +265,95 @@
         }
     }
 
+    /**
+     * Tests that connections can be opened on WiFi and cellphone networks,
+     * and that they are made from different IP addresses.
+     */
+    public void testOpenConnection() throws Exception {
+        boolean canRunTest = mPackageManager.hasSystemFeature(FEATURE_WIFI)
+                && mPackageManager.hasSystemFeature(FEATURE_TELEPHONY);
+        if (!canRunTest) {
+            Log.i(TAG,"testOpenConnection cannot execute unless device supports both WiFi "
+                    + "and a cellular connection");
+            return;
+        }
+
+        Network wifiNetwork = connectToWifi();
+        Network cellNetwork = connectToCell();
+        // This server returns the requestor's IP address as the response body.
+        URL url = new URL("http://google-ipv6test.appspot.com/ip.js?fmt=text");
+        String wifiAddressString = httpGet(wifiNetwork, url);
+        String cellAddressString = httpGet(cellNetwork, url);
+
+        assertFalse(String.format("Same address '%s' on two different networks (%s, %s)",
+                wifiAddressString, wifiNetwork, cellNetwork),
+                wifiAddressString.equals(cellAddressString));
+
+        // Sanity check that the IP addresses that the requests appeared to come from
+        // are actually on the respective networks.
+        assertOnNetwork(wifiAddressString, wifiNetwork);
+        assertOnNetwork(cellAddressString, cellNetwork);
+
+        assertFalse("Unexpectedly equal: " + wifiNetwork, wifiNetwork.equals(cellNetwork));
+    }
+
+    private Network connectToCell() throws InterruptedException {
+        if (cellConnectAttempted()) {
+            throw new IllegalStateException("Already connected");
+        }
+        NetworkRequest cellRequest = new NetworkRequest.Builder()
+                .addTransportType(TRANSPORT_CELLULAR)
+                .addCapability(NET_CAPABILITY_INTERNET)
+                .build();
+        mCellNetworkCallback = new TestNetworkCallback();
+        mCm.requestNetwork(cellRequest, mCellNetworkCallback);
+        final Network cellNetwork = mCellNetworkCallback.waitForAvailable();
+        assertNotNull("Cell network not available within timeout", cellNetwork);
+        return cellNetwork;
+    }
+
+    private boolean cellConnectAttempted() {
+        return mCellNetworkCallback != null;
+    }
+
+    private void disconnectFromCell() {
+        if (!cellConnectAttempted()) {
+            throw new IllegalStateException("Cell connection not attempted");
+        }
+        mCm.unregisterNetworkCallback(mCellNetworkCallback);
+        mCellNetworkCallback = null;
+    }
+
+    /**
+     * Performs a HTTP GET to the specified URL on the specified Network, and returns
+     * the response body decoded as UTF-8.
+     */
+    private static String httpGet(Network network, URL httpUrl) throws IOException {
+        HttpURLConnection connection = (HttpURLConnection) network.openConnection(httpUrl);
+        try {
+            InputStream inputStream = connection.getInputStream();
+            return Streams.readFully(new InputStreamReader(inputStream, StandardCharsets.UTF_8));
+        } finally {
+            connection.disconnect();
+        }
+    }
+
+    private void assertOnNetwork(String adressString, Network network) throws UnknownHostException {
+        InetAddress address = InetAddress.getByName(adressString);
+        LinkProperties linkProperties = mCm.getLinkProperties(network);
+        // To make sure that the request went out on the right network, check that
+        // the IP address seen by the server is assigned to the expected network.
+        // We can only do this for IPv6 addresses, because in IPv4 we will likely
+        // have a private IPv4 address, and that won't match what the server sees.
+        if (address instanceof Inet6Address) {
+            assertContains(linkProperties.getAddresses(), address);
+        }
+    }
+
+    private static<T> void assertContains(Collection<T> collection, T element) {
+        assertTrue(element + " not found in " + collection, collection.contains(element));
+    }
+
     private void assertStartUsingNetworkFeatureUnsupported(int networkType, String feature) {
         try {
             mCm.startUsingNetworkFeature(networkType, feature);
@@ -324,7 +432,7 @@
      * that it would increase test coverage by much (how many devices have 3G radio but not Wifi?).
      */
     public void testRegisterNetworkCallback() {
-        if (!mPackageManager.hasSystemFeature(PackageManager.FEATURE_WIFI)) {
+        if (!mPackageManager.hasSystemFeature(FEATURE_WIFI)) {
             Log.i(TAG, "testRegisterNetworkCallback cannot execute unless device supports WiFi");
             return;
         }
@@ -364,7 +472,7 @@
      * of a {@code NetworkCallback}.
      */
     public void testRegisterNetworkCallback_withPendingIntent() {
-        if (!mPackageManager.hasSystemFeature(PackageManager.FEATURE_WIFI)) {
+        if (!mPackageManager.hasSystemFeature(FEATURE_WIFI)) {
             Log.i(TAG, "testRegisterNetworkCallback cannot execute unless device supports WiFi");
             return;
         }
@@ -458,7 +566,7 @@
      * Tests reporting of connectivity changed.
      */
     public void testConnectivityChanged_manifestRequestOnly_shouldNotReceiveIntent() {
-        if (!mPackageManager.hasSystemFeature(PackageManager.FEATURE_WIFI)) {
+        if (!mPackageManager.hasSystemFeature(FEATURE_WIFI)) {
             Log.i(TAG, "testConnectivityChanged_manifestRequestOnly_shouldNotReceiveIntent cannot execute unless device supports WiFi");
             return;
         }
@@ -475,7 +583,7 @@
     }
 
     public void testConnectivityChanged_whenRegistered_shouldReceiveIntent() {
-        if (!mPackageManager.hasSystemFeature(PackageManager.FEATURE_WIFI)) {
+        if (!mPackageManager.hasSystemFeature(FEATURE_WIFI)) {
             Log.i(TAG, "testConnectivityChanged_whenRegistered_shouldReceiveIntent cannot execute unless device supports WiFi");
             return;
         }
@@ -495,7 +603,7 @@
 
     public void testConnectivityChanged_manifestRequestOnlyPreN_shouldReceiveIntent()
             throws InterruptedException {
-        if (!mPackageManager.hasSystemFeature(PackageManager.FEATURE_WIFI)) {
+        if (!mPackageManager.hasSystemFeature(FEATURE_WIFI)) {
             Log.i(TAG, "testConnectivityChanged_manifestRequestOnlyPreN_shouldReceiveIntent cannot execute unless device supports WiFi");
             return;
         }
diff --git a/tests/tests/netsecpolicy/usescleartexttraffic-false/Android.mk b/tests/tests/netsecpolicy/usescleartexttraffic-false/Android.mk
index 4c68423..55fa4d1 100644
--- a/tests/tests/netsecpolicy/usescleartexttraffic-false/Android.mk
+++ b/tests/tests/netsecpolicy/usescleartexttraffic-false/Android.mk
@@ -22,9 +22,9 @@
 
 LOCAL_STATIC_JAVA_LIBRARIES := \
     ctstestrunner \
-    ctstestserver \
-    org.apache.http.legacy \
-    legacy-android-test
+    ctstestserver
+
+LOCAL_JAVA_LIBRARIES := org.apache.http.legacy android.test.base.stubs
 
 LOCAL_SRC_FILES := $(call all-java-files-under, src common)
 
diff --git a/tests/tests/netsecpolicy/usescleartexttraffic-false/AndroidManifest.xml b/tests/tests/netsecpolicy/usescleartexttraffic-false/AndroidManifest.xml
index 49385f8..d4ce39a 100644
--- a/tests/tests/netsecpolicy/usescleartexttraffic-false/AndroidManifest.xml
+++ b/tests/tests/netsecpolicy/usescleartexttraffic-false/AndroidManifest.xml
@@ -22,6 +22,7 @@
   <uses-permission android:name="android.permission.DISABLE_KEYGUARD" />
   <application>
       <uses-library android:name="android.test.runner"/>
+      <uses-library android:name="org.apache.http.legacy" />
   </application>
 
   <instrumentation android:name="android.support.test.runner.AndroidJUnitRunner"
diff --git a/tests/tests/netsecpolicy/usescleartexttraffic-true/Android.mk b/tests/tests/netsecpolicy/usescleartexttraffic-true/Android.mk
index 584afd2..033d7ea 100644
--- a/tests/tests/netsecpolicy/usescleartexttraffic-true/Android.mk
+++ b/tests/tests/netsecpolicy/usescleartexttraffic-true/Android.mk
@@ -22,9 +22,9 @@
 
 LOCAL_STATIC_JAVA_LIBRARIES := \
     ctstestrunner \
-    ctstestserver \
-    org.apache.http.legacy \
-    legacy-android-test
+    ctstestserver
+
+LOCAL_JAVA_LIBRARIES := org.apache.http.legacy android.test.base.stubs
 
 LOCAL_SRC_FILES := $(call all-java-files-under, src common)
 
diff --git a/tests/tests/netsecpolicy/usescleartexttraffic-true/AndroidManifest.xml b/tests/tests/netsecpolicy/usescleartexttraffic-true/AndroidManifest.xml
index be698f2..fe31e80 100644
--- a/tests/tests/netsecpolicy/usescleartexttraffic-true/AndroidManifest.xml
+++ b/tests/tests/netsecpolicy/usescleartexttraffic-true/AndroidManifest.xml
@@ -22,6 +22,7 @@
   <uses-permission android:name="android.permission.DISABLE_KEYGUARD" />
   <application>
       <uses-library android:name="android.test.runner"/>
+      <uses-library android:name="org.apache.http.legacy" />
   </application>
 
   <instrumentation android:name="android.support.test.runner.AndroidJUnitRunner"
diff --git a/tests/tests/netsecpolicy/usescleartexttraffic-unspecified/Android.mk b/tests/tests/netsecpolicy/usescleartexttraffic-unspecified/Android.mk
index 9a613e7..c10a19a 100644
--- a/tests/tests/netsecpolicy/usescleartexttraffic-unspecified/Android.mk
+++ b/tests/tests/netsecpolicy/usescleartexttraffic-unspecified/Android.mk
@@ -22,9 +22,9 @@
 
 LOCAL_STATIC_JAVA_LIBRARIES := \
     ctstestrunner \
-    ctstestserver \
-    org.apache.http.legacy \
-    legacy-android-test
+    ctstestserver
+
+LOCAL_JAVA_LIBRARIES := org.apache.http.legacy android.test.base.stubs
 
 LOCAL_SRC_FILES := $(call all-java-files-under, src common)
 
diff --git a/tests/tests/netsecpolicy/usescleartexttraffic-unspecified/AndroidManifest.xml b/tests/tests/netsecpolicy/usescleartexttraffic-unspecified/AndroidManifest.xml
index 7bd8742..c6b65c0 100644
--- a/tests/tests/netsecpolicy/usescleartexttraffic-unspecified/AndroidManifest.xml
+++ b/tests/tests/netsecpolicy/usescleartexttraffic-unspecified/AndroidManifest.xml
@@ -22,6 +22,7 @@
   <uses-permission android:name="android.permission.DISABLE_KEYGUARD" />
   <application>
       <uses-library android:name="android.test.runner"/>
+      <uses-library android:name="org.apache.http.legacy" />
   </application>
 
   <instrumentation android:name="android.support.test.runner.AndroidJUnitRunner"
diff --git a/tests/tests/networksecurityconfig/networksecurityconfig-attributes/Android.mk b/tests/tests/networksecurityconfig/networksecurityconfig-attributes/Android.mk
index 10018ce..4af2de7 100644
--- a/tests/tests/networksecurityconfig/networksecurityconfig-attributes/Android.mk
+++ b/tests/tests/networksecurityconfig/networksecurityconfig-attributes/Android.mk
@@ -20,7 +20,9 @@
 LOCAL_MODULE_TAGS := tests
 LOCAL_MODULE_PATH := $(TARGET_OUT_DATA_APPS)
 
-LOCAL_STATIC_JAVA_LIBRARIES := ctstestrunner org.apache.http.legacy android-support-test
+LOCAL_STATIC_JAVA_LIBRARIES := ctstestrunner android-support-test
+
+LOCAL_JAVA_LIBRARIES := org.apache.http.legacy
 
 LOCAL_SRC_FILES := $(call all-java-files-under, src)
 LOCAL_SRC_FILES += $(call all-java-files-under, ../src)
@@ -31,4 +33,4 @@
 LOCAL_COMPATIBILITY_SUITE := cts vts general-tests
 
 LOCAL_SDK_VERSION := current
-include $(BUILD_CTS_PACKAGE)
\ No newline at end of file
+include $(BUILD_CTS_PACKAGE)
diff --git a/tests/tests/networksecurityconfig/networksecurityconfig-attributes/AndroidManifest.xml b/tests/tests/networksecurityconfig/networksecurityconfig-attributes/AndroidManifest.xml
index 9356074..972052e 100644
--- a/tests/tests/networksecurityconfig/networksecurityconfig-attributes/AndroidManifest.xml
+++ b/tests/tests/networksecurityconfig/networksecurityconfig-attributes/AndroidManifest.xml
@@ -20,6 +20,7 @@
         package="android.security.net.config.cts.CtsNetSecConfigAttributeTestCases">
   <application android:networkSecurityConfig="@xml/network_security_config">
       <uses-library android:name="android.test.runner"/>
+      <uses-library android:name="org.apache.http.legacy" />
   </application>
 
   <uses-permission android:name="android.permission.INTERNET" />
diff --git a/tests/tests/networksecurityconfig/networksecurityconfig-attributes/AndroidTest.xml b/tests/tests/networksecurityconfig/networksecurityconfig-attributes/AndroidTest.xml
index e6ee7c6..cfad3e4 100644
--- a/tests/tests/networksecurityconfig/networksecurityconfig-attributes/AndroidTest.xml
+++ b/tests/tests/networksecurityconfig/networksecurityconfig-attributes/AndroidTest.xml
@@ -14,6 +14,7 @@
      limitations under the License.
 -->
 <configuration description="Config for CTS CtsNetSecConfigAttributeTestCases test cases">
+    <option name="test-suite-tag" value="cts" />
     <option name="config-descriptor:metadata" key="component" value="security" />
     <option name="not-shardable" value="true" />
     <target_preparer class="com.android.tradefed.targetprep.suite.SuiteApkInstaller">
diff --git a/tests/tests/networksecurityconfig/networksecurityconfig-basic-domain/Android.mk b/tests/tests/networksecurityconfig/networksecurityconfig-basic-domain/Android.mk
index 161dbd3..95e14ef 100644
--- a/tests/tests/networksecurityconfig/networksecurityconfig-basic-domain/Android.mk
+++ b/tests/tests/networksecurityconfig/networksecurityconfig-basic-domain/Android.mk
@@ -20,7 +20,9 @@
 LOCAL_MODULE_TAGS := tests
 LOCAL_MODULE_PATH := $(TARGET_OUT_DATA_APPS)
 
-LOCAL_STATIC_JAVA_LIBRARIES := ctstestrunner org.apache.http.legacy android-support-test
+LOCAL_STATIC_JAVA_LIBRARIES := ctstestrunner android-support-test
+
+LOCAL_JAVA_LIBRARIES := org.apache.http.legacy
 
 LOCAL_SRC_FILES := $(call all-java-files-under, src)
 LOCAL_SRC_FILES += $(call all-java-files-under, ../src)
@@ -31,4 +33,4 @@
 LOCAL_COMPATIBILITY_SUITE := cts vts general-tests
 
 LOCAL_SDK_VERSION := current
-include $(BUILD_CTS_PACKAGE)
\ No newline at end of file
+include $(BUILD_CTS_PACKAGE)
diff --git a/tests/tests/networksecurityconfig/networksecurityconfig-basic-domain/AndroidManifest.xml b/tests/tests/networksecurityconfig/networksecurityconfig-basic-domain/AndroidManifest.xml
index 565f23a..3e5fe25 100644
--- a/tests/tests/networksecurityconfig/networksecurityconfig-basic-domain/AndroidManifest.xml
+++ b/tests/tests/networksecurityconfig/networksecurityconfig-basic-domain/AndroidManifest.xml
@@ -20,6 +20,7 @@
         package="android.security.net.config.cts.CtsNetSecConfigBasicDomainConfigTestCases">
   <application android:networkSecurityConfig="@xml/network_security_config">
       <uses-library android:name="android.test.runner"/>
+      <uses-library android:name="org.apache.http.legacy" />
   </application>
 
   <uses-permission android:name="android.permission.INTERNET" />
diff --git a/tests/tests/networksecurityconfig/networksecurityconfig-basic-domain/AndroidTest.xml b/tests/tests/networksecurityconfig/networksecurityconfig-basic-domain/AndroidTest.xml
index 36eb72a..bd43bac 100644
--- a/tests/tests/networksecurityconfig/networksecurityconfig-basic-domain/AndroidTest.xml
+++ b/tests/tests/networksecurityconfig/networksecurityconfig-basic-domain/AndroidTest.xml
@@ -14,6 +14,7 @@
      limitations under the License.
 -->
 <configuration description="Config for CTS CtsNetSecConfigBasicDomainConfigTestCases test cases">
+    <option name="test-suite-tag" value="cts" />
     <option name="config-descriptor:metadata" key="component" value="security" />
     <option name="not-shardable" value="true" />
     <target_preparer class="com.android.tradefed.targetprep.suite.SuiteApkInstaller">
diff --git a/tests/tests/networksecurityconfig/networksecurityconfig-cleartext-pre-P/Android.mk b/tests/tests/networksecurityconfig/networksecurityconfig-cleartext-pre-P/Android.mk
new file mode 100644
index 0000000..6dc6c5d
--- /dev/null
+++ b/tests/tests/networksecurityconfig/networksecurityconfig-cleartext-pre-P/Android.mk
@@ -0,0 +1,36 @@
+# 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_PACKAGE_NAME := CtsNetSecConfigPrePCleartextTrafficTestCases
+
+LOCAL_MODULE_TAGS := tests
+LOCAL_MODULE_PATH := $(TARGET_OUT_DATA_APPS)
+
+LOCAL_STATIC_JAVA_LIBRARIES := ctstestrunner android-support-test
+
+LOCAL_JAVA_LIBRARIES := org.apache.http.legacy
+
+LOCAL_SRC_FILES := $(call all-java-files-under, src)
+LOCAL_SRC_FILES += $(call all-java-files-under, ../src)
+
+LOCAL_RESOURCE_DIR := $(LOCAL_PATH)/res/
+
+# Tag this module as a cts test artifact
+LOCAL_COMPATIBILITY_SUITE := cts vts general-tests
+
+LOCAL_SDK_VERSION := 26
+include $(BUILD_CTS_PACKAGE)
diff --git a/tests/tests/networksecurityconfig/networksecurityconfig-cleartext-pre-P/AndroidManifest.xml b/tests/tests/networksecurityconfig/networksecurityconfig-cleartext-pre-P/AndroidManifest.xml
new file mode 100644
index 0000000..bec926e
--- /dev/null
+++ b/tests/tests/networksecurityconfig/networksecurityconfig-cleartext-pre-P/AndroidManifest.xml
@@ -0,0 +1,33 @@
+<?xml version="1.0" encoding="utf-8"?>
+
+<!--
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ -->
+
+<manifest xmlns:android="http://schemas.android.com/apk/res/android"
+        package="android.security.net.config.cts.CtsNetSecConfigPrePCleartextTrafficTestCases">
+  <application android:networkSecurityConfig="@xml/network_security_config">
+      <uses-library android:name="android.test.runner"/>
+      <uses-library android:name="org.apache.http.legacy" />
+  </application>
+
+  <uses-permission android:name="android.permission.INTERNET" />
+  <instrumentation android:name="android.support.test.runner.AndroidJUnitRunner"
+                   android:targetPackage="android.security.net.config.cts.CtsNetSecConfigPrePCleartextTrafficTestCases"
+                   android:label="">
+        <meta-data android:name="listener"
+            android:value="com.android.cts.runner.CtsTestRunListener" />
+    </instrumentation>
+</manifest>
diff --git a/tests/tests/networksecurityconfig/networksecurityconfig-cleartext-pre-P/AndroidTest.xml b/tests/tests/networksecurityconfig/networksecurityconfig-cleartext-pre-P/AndroidTest.xml
new file mode 100644
index 0000000..cde3314
--- /dev/null
+++ b/tests/tests/networksecurityconfig/networksecurityconfig-cleartext-pre-P/AndroidTest.xml
@@ -0,0 +1,28 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2017 The Android Open Source Project
+
+     Licensed under the Apache License, Version 2.0 (the "License");
+     you may not use this file except in compliance with the License.
+     You may obtain a copy of the License at
+
+          http://www.apache.org/licenses/LICENSE-2.0
+
+     Unless required by applicable law or agreed to in writing, software
+     distributed under the License is distributed on an "AS IS" BASIS,
+     WITHOUT 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 CtsNetSecConfigPrePCleartextTraffic test cases">
+    <option name="test-suite-tag" value="cts" />
+    <option name="config-descriptor:metadata" key="component" value="security" />
+    <option name="not-shardable" value="true" />
+    <target_preparer class="com.android.tradefed.targetprep.suite.SuiteApkInstaller">
+        <option name="cleanup-apks" value="true" />
+        <option name="test-file-name" value="CtsNetSecConfigPrePCleartextTrafficTestCases.apk" />
+    </target_preparer>
+    <test class="com.android.tradefed.testtype.AndroidJUnitTest" >
+        <option name="package" value="android.security.net.config.cts.CtsNetSecConfigPrePCleartextTrafficTestCases" />
+        <option name="runtime-hint" value="8m10s" />
+    </test>
+</configuration>
diff --git a/tests/tests/networksecurityconfig/networksecurityconfig-cleartext-pre-P/res/xml/network_security_config.xml b/tests/tests/networksecurityconfig/networksecurityconfig-cleartext-pre-P/res/xml/network_security_config.xml
new file mode 100644
index 0000000..987b178
--- /dev/null
+++ b/tests/tests/networksecurityconfig/networksecurityconfig-cleartext-pre-P/res/xml/network_security_config.xml
@@ -0,0 +1,9 @@
+<?xml version="1.0" encoding="utf-8"?>
+<network-security-config>
+  <domain-config cleartextTrafficPermitted="false">
+    <domain includeSubdomains="true">android.com</domain>
+    <domain-config cleartextTrafficPermitted="true">
+      <domain>developer.android.com</domain>
+    </domain-config>
+  </domain-config>
+</network-security-config>
diff --git a/tests/tests/networksecurityconfig/networksecurityconfig-cleartext-pre-P/src/android/security/net/config/cts/CleartextPermittedTest.java b/tests/tests/networksecurityconfig/networksecurityconfig-cleartext-pre-P/src/android/security/net/config/cts/CleartextPermittedTest.java
new file mode 100644
index 0000000..b4b400c
--- /dev/null
+++ b/tests/tests/networksecurityconfig/networksecurityconfig-cleartext-pre-P/src/android/security/net/config/cts/CleartextPermittedTest.java
@@ -0,0 +1,43 @@
+/*
+ * 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.security.net.config.cts;
+
+import java.security.cert.CertificateException;
+import java.security.cert.X509Certificate;
+import javax.net.ssl.X509TrustManager;
+import junit.framework.TestCase;
+
+public class CleartextPermittedTest extends TestCase {
+    public void testDefaultAllowed() throws Exception {
+        TestUtils.assertCleartextConnectionSucceeds("example.com", 80);
+        TestUtils.assertTlsConnectionSucceeds("example.com", 443);
+    }
+
+    public void testCleartextBlocked() throws Exception {
+        TestUtils.assertCleartextConnectionFails("android.com", 80);
+        TestUtils.assertTlsConnectionSucceeds("android.com", 443);
+        // subdomains of android.com are also disallowed.
+        TestUtils.assertCleartextConnectionFails("www.android.com", 80);
+        TestUtils.assertTlsConnectionSucceeds("www.android.com", 443);
+    }
+
+    public void testNestedCleartextPermitted() throws Exception {
+        // developer.android.com is explicitly permitted.
+        TestUtils.assertCleartextConnectionSucceeds("developer.android.com", 80);
+        TestUtils.assertTlsConnectionSucceeds("developer.android.com", 443);
+    }
+}
diff --git a/tests/tests/networksecurityconfig/networksecurityconfig-cleartext/Android.mk b/tests/tests/networksecurityconfig/networksecurityconfig-cleartext/Android.mk
index 927374c..278d634 100644
--- a/tests/tests/networksecurityconfig/networksecurityconfig-cleartext/Android.mk
+++ b/tests/tests/networksecurityconfig/networksecurityconfig-cleartext/Android.mk
@@ -20,7 +20,9 @@
 LOCAL_MODULE_TAGS := tests
 LOCAL_MODULE_PATH := $(TARGET_OUT_DATA_APPS)
 
-LOCAL_STATIC_JAVA_LIBRARIES := ctstestrunner org.apache.http.legacy android-support-test
+LOCAL_STATIC_JAVA_LIBRARIES := ctstestrunner android-support-test
+
+LOCAL_JAVA_LIBRARIES := org.apache.http.legacy
 
 LOCAL_SRC_FILES := $(call all-java-files-under, src)
 LOCAL_SRC_FILES += $(call all-java-files-under, ../src)
diff --git a/tests/tests/networksecurityconfig/networksecurityconfig-cleartext/AndroidManifest.xml b/tests/tests/networksecurityconfig/networksecurityconfig-cleartext/AndroidManifest.xml
index b387ffa..8ee5482 100644
--- a/tests/tests/networksecurityconfig/networksecurityconfig-cleartext/AndroidManifest.xml
+++ b/tests/tests/networksecurityconfig/networksecurityconfig-cleartext/AndroidManifest.xml
@@ -20,6 +20,7 @@
         package="android.security.net.config.cts.CtsNetSecConfigCleartextTrafficTestCases">
   <application android:networkSecurityConfig="@xml/network_security_config">
       <uses-library android:name="android.test.runner"/>
+      <uses-library android:name="org.apache.http.legacy" />
   </application>
 
   <uses-permission android:name="android.permission.INTERNET" />
diff --git a/tests/tests/networksecurityconfig/networksecurityconfig-cleartext/AndroidTest.xml b/tests/tests/networksecurityconfig/networksecurityconfig-cleartext/AndroidTest.xml
index f2e3f35..d5c33be 100644
--- a/tests/tests/networksecurityconfig/networksecurityconfig-cleartext/AndroidTest.xml
+++ b/tests/tests/networksecurityconfig/networksecurityconfig-cleartext/AndroidTest.xml
@@ -14,6 +14,7 @@
      limitations under the License.
 -->
 <configuration description="Config for CTS CtsNetSecConfigCleartextTraffic test cases">
+    <option name="test-suite-tag" value="cts" />
     <option name="config-descriptor:metadata" key="component" value="security" />
     <option name="not-shardable" value="true" />
     <target_preparer class="com.android.tradefed.targetprep.suite.SuiteApkInstaller">
diff --git a/tests/tests/networksecurityconfig/networksecurityconfig-cleartext/res/xml/network_security_config.xml b/tests/tests/networksecurityconfig/networksecurityconfig-cleartext/res/xml/network_security_config.xml
index 987b178..6d41bba 100644
--- a/tests/tests/networksecurityconfig/networksecurityconfig-cleartext/res/xml/network_security_config.xml
+++ b/tests/tests/networksecurityconfig/networksecurityconfig-cleartext/res/xml/network_security_config.xml
@@ -1,8 +1,8 @@
 <?xml version="1.0" encoding="utf-8"?>
 <network-security-config>
-  <domain-config cleartextTrafficPermitted="false">
+  <domain-config cleartextTrafficPermitted="true">
     <domain includeSubdomains="true">android.com</domain>
-    <domain-config cleartextTrafficPermitted="true">
+    <domain-config cleartextTrafficPermitted="false">
       <domain>developer.android.com</domain>
     </domain-config>
   </domain-config>
diff --git a/tests/tests/networksecurityconfig/networksecurityconfig-cleartext/src/android/security/net/config/cts/CleartextPermittedTest.java b/tests/tests/networksecurityconfig/networksecurityconfig-cleartext/src/android/security/net/config/cts/CleartextPermittedTest.java
index db8e40f..9592178 100644
--- a/tests/tests/networksecurityconfig/networksecurityconfig-cleartext/src/android/security/net/config/cts/CleartextPermittedTest.java
+++ b/tests/tests/networksecurityconfig/networksecurityconfig-cleartext/src/android/security/net/config/cts/CleartextPermittedTest.java
@@ -22,22 +22,22 @@
 import junit.framework.TestCase;
 
 public class CleartextPermittedTest extends TestCase {
-    public void testDefaultAllowed() throws Exception {
-        TestUtils.assertCleartextConnectionSucceeds("example.com", 80);
+    public void testDefaultDenied() throws Exception {
+        TestUtils.assertCleartextConnectionFails("example.com", 80);
         TestUtils.assertTlsConnectionSucceeds("example.com", 443);
     }
 
-    public void testCleartextBlocked() throws Exception {
-        TestUtils.assertCleartextConnectionFails("android.com", 80);
+    public void testCleartextAllowed() throws Exception {
+        TestUtils.assertCleartextConnectionSucceeds("android.com", 80);
         TestUtils.assertTlsConnectionSucceeds("android.com", 443);
         // subdomains of android.com are also disallowed.
-        TestUtils.assertCleartextConnectionFails("www.android.com", 80);
+        TestUtils.assertCleartextConnectionSucceeds("www.android.com", 80);
         TestUtils.assertTlsConnectionSucceeds("www.android.com", 443);
     }
 
-    public void testNestedCleartextPermitted() throws Exception {
-        // developer.android.com is explicitly permitted.
-        TestUtils.assertCleartextConnectionSucceeds("developer.android.com", 80);
+    public void testNestedCleartextDenied() throws Exception {
+        // developer.android.com is explicitly denied.
+        TestUtils.assertCleartextConnectionFails("developer.android.com", 80);
         TestUtils.assertTlsConnectionSucceeds("developer.android.com", 443);
     }
 }
diff --git a/tests/tests/networksecurityconfig/networksecurityconfig-debug-basic-disabled/Android.mk b/tests/tests/networksecurityconfig/networksecurityconfig-debug-basic-disabled/Android.mk
index aa0eefd..fd5f419 100644
--- a/tests/tests/networksecurityconfig/networksecurityconfig-debug-basic-disabled/Android.mk
+++ b/tests/tests/networksecurityconfig/networksecurityconfig-debug-basic-disabled/Android.mk
@@ -20,7 +20,9 @@
 LOCAL_MODULE_TAGS := tests
 LOCAL_MODULE_PATH := $(TARGET_OUT_DATA_APPS)
 
-LOCAL_STATIC_JAVA_LIBRARIES := ctstestrunner org.apache.http.legacy android-support-test
+LOCAL_STATIC_JAVA_LIBRARIES := ctstestrunner android-support-test
+
+LOCAL_JAVA_LIBRARIES := org.apache.http.legacy
 
 LOCAL_SRC_FILES := $(call all-java-files-under, src)
 LOCAL_SRC_FILES += $(call all-java-files-under, ../src)
@@ -31,4 +33,4 @@
 LOCAL_COMPATIBILITY_SUITE := cts vts general-tests
 
 LOCAL_SDK_VERSION := current
-include $(BUILD_CTS_PACKAGE)
\ No newline at end of file
+include $(BUILD_CTS_PACKAGE)
diff --git a/tests/tests/networksecurityconfig/networksecurityconfig-debug-basic-disabled/AndroidManifest.xml b/tests/tests/networksecurityconfig/networksecurityconfig-debug-basic-disabled/AndroidManifest.xml
index 3f15f81..28bba5b 100644
--- a/tests/tests/networksecurityconfig/networksecurityconfig-debug-basic-disabled/AndroidManifest.xml
+++ b/tests/tests/networksecurityconfig/networksecurityconfig-debug-basic-disabled/AndroidManifest.xml
@@ -21,6 +21,7 @@
   <application android:debuggable="false"
                android:networkSecurityConfig="@xml/network_security_config">
       <uses-library android:name="android.test.runner"/>
+      <uses-library android:name="org.apache.http.legacy" />
   </application>
 
   <uses-permission android:name="android.permission.INTERNET" />
diff --git a/tests/tests/networksecurityconfig/networksecurityconfig-debug-basic-disabled/AndroidTest.xml b/tests/tests/networksecurityconfig/networksecurityconfig-debug-basic-disabled/AndroidTest.xml
index 14e4eb4..666ed68 100644
--- a/tests/tests/networksecurityconfig/networksecurityconfig-debug-basic-disabled/AndroidTest.xml
+++ b/tests/tests/networksecurityconfig/networksecurityconfig-debug-basic-disabled/AndroidTest.xml
@@ -14,6 +14,7 @@
      limitations under the License.
 -->
 <configuration description="Config for CTS CtsNetSecConfigBasicDebugDisabledTestCases test cases">
+    <option name="test-suite-tag" value="cts" />
     <option name="config-descriptor:metadata" key="component" value="security" />
     <option name="not-shardable" value="true" />
     <target_preparer class="com.android.tradefed.targetprep.suite.SuiteApkInstaller">
diff --git a/tests/tests/networksecurityconfig/networksecurityconfig-debug-basic-enabled/Android.mk b/tests/tests/networksecurityconfig/networksecurityconfig-debug-basic-enabled/Android.mk
index be9174e..d808928 100644
--- a/tests/tests/networksecurityconfig/networksecurityconfig-debug-basic-enabled/Android.mk
+++ b/tests/tests/networksecurityconfig/networksecurityconfig-debug-basic-enabled/Android.mk
@@ -20,7 +20,9 @@
 LOCAL_MODULE_TAGS := tests
 LOCAL_MODULE_PATH := $(TARGET_OUT_DATA_APPS)
 
-LOCAL_STATIC_JAVA_LIBRARIES := ctstestrunner org.apache.http.legacy android-support-test
+LOCAL_STATIC_JAVA_LIBRARIES := ctstestrunner android-support-test
+
+LOCAL_JAVA_LIBRARIES := org.apache.http.legacy
 
 LOCAL_SRC_FILES := $(call all-java-files-under, src)
 LOCAL_SRC_FILES += $(call all-java-files-under, ../src)
diff --git a/tests/tests/networksecurityconfig/networksecurityconfig-debug-basic-enabled/AndroidManifest.xml b/tests/tests/networksecurityconfig/networksecurityconfig-debug-basic-enabled/AndroidManifest.xml
index 6d98702..b667271 100644
--- a/tests/tests/networksecurityconfig/networksecurityconfig-debug-basic-enabled/AndroidManifest.xml
+++ b/tests/tests/networksecurityconfig/networksecurityconfig-debug-basic-enabled/AndroidManifest.xml
@@ -21,6 +21,7 @@
   <application android:debuggable="true"
                android:networkSecurityConfig="@xml/network_security_config">
       <uses-library android:name="android.test.runner"/>
+      <uses-library android:name="org.apache.http.legacy" />
   </application>
 
   <uses-permission android:name="android.permission.INTERNET" />
diff --git a/tests/tests/networksecurityconfig/networksecurityconfig-debug-basic-enabled/AndroidTest.xml b/tests/tests/networksecurityconfig/networksecurityconfig-debug-basic-enabled/AndroidTest.xml
index 198f30e..a92726e 100644
--- a/tests/tests/networksecurityconfig/networksecurityconfig-debug-basic-enabled/AndroidTest.xml
+++ b/tests/tests/networksecurityconfig/networksecurityconfig-debug-basic-enabled/AndroidTest.xml
@@ -14,6 +14,7 @@
      limitations under the License.
 -->
 <configuration description="Config for CTS CtsNetSecConfigBasicDebugEnabledTestCases test cases">
+    <option name="test-suite-tag" value="cts" />
     <option name="config-descriptor:metadata" key="component" value="security" />
     <option name="not-shardable" value="true" />
     <target_preparer class="com.android.tradefed.targetprep.suite.SuiteApkInstaller">
diff --git a/tests/tests/networksecurityconfig/networksecurityconfig-downloadmanager/Android.mk b/tests/tests/networksecurityconfig/networksecurityconfig-downloadmanager/Android.mk
index 84e72b0..4a40d2a 100644
--- a/tests/tests/networksecurityconfig/networksecurityconfig-downloadmanager/Android.mk
+++ b/tests/tests/networksecurityconfig/networksecurityconfig-downloadmanager/Android.mk
@@ -22,9 +22,9 @@
 
 LOCAL_STATIC_JAVA_LIBRARIES := \
     ctstestrunner \
-    org.apache.http.legacy \
-    android-support-test \
-    legacy-android-test
+    android-support-test
+
+LOCAL_JAVA_LIBRARIES := org.apache.http.legacy android.test.base.stubs
 
 LOCAL_SRC_FILES := $(call all-java-files-under, src)
 LOCAL_SRC_FILES += $(call all-java-files-under, ../src)
diff --git a/tests/tests/networksecurityconfig/networksecurityconfig-downloadmanager/AndroidManifest.xml b/tests/tests/networksecurityconfig/networksecurityconfig-downloadmanager/AndroidManifest.xml
index e18ff4d..cc67cca 100644
--- a/tests/tests/networksecurityconfig/networksecurityconfig-downloadmanager/AndroidManifest.xml
+++ b/tests/tests/networksecurityconfig/networksecurityconfig-downloadmanager/AndroidManifest.xml
@@ -20,6 +20,7 @@
         package="android.security.net.config.cts.CtsNetSecConfigDownloadManagerTestCases">
   <application android:networkSecurityConfig="@xml/network_security_config">
       <uses-library android:name="android.test.runner"/>
+      <uses-library android:name="org.apache.http.legacy" />
   </application>
 
   <uses-permission android:name="android.permission.INTERNET" />
diff --git a/tests/tests/networksecurityconfig/networksecurityconfig-downloadmanager/AndroidTest.xml b/tests/tests/networksecurityconfig/networksecurityconfig-downloadmanager/AndroidTest.xml
index fb26391..0d1b2a2 100644
--- a/tests/tests/networksecurityconfig/networksecurityconfig-downloadmanager/AndroidTest.xml
+++ b/tests/tests/networksecurityconfig/networksecurityconfig-downloadmanager/AndroidTest.xml
@@ -14,6 +14,7 @@
      limitations under the License.
 -->
 <configuration description="Config for CTS CtsNetSecConfigDownloadManagerTestCases test cases">
+    <option name="test-suite-tag" value="cts" />
     <option name="config-descriptor:metadata" key="component" value="security" />
     <option name="not-shardable" value="true" />
     <target_preparer class="com.android.tradefed.targetprep.suite.SuiteApkInstaller">
diff --git a/tests/tests/networksecurityconfig/networksecurityconfig-invalid-pin/Android.mk b/tests/tests/networksecurityconfig/networksecurityconfig-invalid-pin/Android.mk
index 4764cab..3d66c53 100644
--- a/tests/tests/networksecurityconfig/networksecurityconfig-invalid-pin/Android.mk
+++ b/tests/tests/networksecurityconfig/networksecurityconfig-invalid-pin/Android.mk
@@ -20,7 +20,9 @@
 LOCAL_MODULE_TAGS := tests
 LOCAL_MODULE_PATH := $(TARGET_OUT_DATA_APPS)
 
-LOCAL_STATIC_JAVA_LIBRARIES := ctstestrunner org.apache.http.legacy android-support-test
+LOCAL_STATIC_JAVA_LIBRARIES := ctstestrunner android-support-test
+
+LOCAL_JAVA_LIBRARIES := org.apache.http.legacy
 
 LOCAL_SRC_FILES := $(call all-java-files-under, src)
 LOCAL_SRC_FILES += $(call all-java-files-under, ../src)
diff --git a/tests/tests/networksecurityconfig/networksecurityconfig-invalid-pin/AndroidManifest.xml b/tests/tests/networksecurityconfig/networksecurityconfig-invalid-pin/AndroidManifest.xml
index b3b32b5..bf6e369 100644
--- a/tests/tests/networksecurityconfig/networksecurityconfig-invalid-pin/AndroidManifest.xml
+++ b/tests/tests/networksecurityconfig/networksecurityconfig-invalid-pin/AndroidManifest.xml
@@ -20,6 +20,7 @@
         package="android.security.net.config.cts.CtsNetSecConfigInvalidPinTestCases">
   <application android:networkSecurityConfig="@xml/network_security_config">
       <uses-library android:name="android.test.runner"/>
+      <uses-library android:name="org.apache.http.legacy" />
   </application>
 
   <uses-permission android:name="android.permission.INTERNET" />
diff --git a/tests/tests/networksecurityconfig/networksecurityconfig-invalid-pin/AndroidTest.xml b/tests/tests/networksecurityconfig/networksecurityconfig-invalid-pin/AndroidTest.xml
index 7f3c7fd..c2c54f3 100644
--- a/tests/tests/networksecurityconfig/networksecurityconfig-invalid-pin/AndroidTest.xml
+++ b/tests/tests/networksecurityconfig/networksecurityconfig-invalid-pin/AndroidTest.xml
@@ -14,6 +14,7 @@
      limitations under the License.
 -->
 <configuration description="Config for CTS CtsNetSecConfigInvalidPinTestCases test cases">
+    <option name="test-suite-tag" value="cts" />
     <option name="config-descriptor:metadata" key="component" value="security" />
     <option name="not-shardable" value="true" />
     <target_preparer class="com.android.tradefed.targetprep.suite.SuiteApkInstaller">
diff --git a/tests/tests/networksecurityconfig/networksecurityconfig-nested-domains/Android.mk b/tests/tests/networksecurityconfig/networksecurityconfig-nested-domains/Android.mk
index 5448f34..e96ae6a 100644
--- a/tests/tests/networksecurityconfig/networksecurityconfig-nested-domains/Android.mk
+++ b/tests/tests/networksecurityconfig/networksecurityconfig-nested-domains/Android.mk
@@ -20,7 +20,9 @@
 LOCAL_MODULE_TAGS := tests
 LOCAL_MODULE_PATH := $(TARGET_OUT_DATA_APPS)
 
-LOCAL_STATIC_JAVA_LIBRARIES := ctstestrunner org.apache.http.legacy android-support-test
+LOCAL_STATIC_JAVA_LIBRARIES := ctstestrunner android-support-test
+
+LOCAL_JAVA_LIBRARIES := org.apache.http.legacy
 
 LOCAL_SRC_FILES := $(call all-java-files-under, src)
 LOCAL_SRC_FILES += $(call all-java-files-under, ../src)
diff --git a/tests/tests/networksecurityconfig/networksecurityconfig-nested-domains/AndroidManifest.xml b/tests/tests/networksecurityconfig/networksecurityconfig-nested-domains/AndroidManifest.xml
index 1790150..75247d3 100644
--- a/tests/tests/networksecurityconfig/networksecurityconfig-nested-domains/AndroidManifest.xml
+++ b/tests/tests/networksecurityconfig/networksecurityconfig-nested-domains/AndroidManifest.xml
@@ -20,6 +20,7 @@
         package="android.security.net.config.cts.CtsNetSecConfigNestedDomainConfigTestCases">
   <application android:networkSecurityConfig="@xml/network_security_config">
       <uses-library android:name="android.test.runner"/>
+      <uses-library android:name="org.apache.http.legacy" />
   </application>
 
   <uses-permission android:name="android.permission.INTERNET" />
diff --git a/tests/tests/networksecurityconfig/networksecurityconfig-nested-domains/AndroidTest.xml b/tests/tests/networksecurityconfig/networksecurityconfig-nested-domains/AndroidTest.xml
index 0209025..ca37091 100644
--- a/tests/tests/networksecurityconfig/networksecurityconfig-nested-domains/AndroidTest.xml
+++ b/tests/tests/networksecurityconfig/networksecurityconfig-nested-domains/AndroidTest.xml
@@ -14,6 +14,7 @@
      limitations under the License.
 -->
 <configuration description="Config for CTS CtsNetSecConfigNestedDomainConfigTestCases test cases">
+    <option name="test-suite-tag" value="cts" />
     <option name="config-descriptor:metadata" key="component" value="security" />
     <option name="not-shardable" value="true" />
     <target_preparer class="com.android.tradefed.targetprep.suite.SuiteApkInstaller">
diff --git a/tests/tests/networksecurityconfig/networksecurityconfig-resourcesrc/Android.mk b/tests/tests/networksecurityconfig/networksecurityconfig-resourcesrc/Android.mk
index 924f393..48bbfaf 100644
--- a/tests/tests/networksecurityconfig/networksecurityconfig-resourcesrc/Android.mk
+++ b/tests/tests/networksecurityconfig/networksecurityconfig-resourcesrc/Android.mk
@@ -22,9 +22,13 @@
 
 LOCAL_STATIC_JAVA_LIBRARIES := \
     ctstestrunner \
+    android-support-test
+
+LOCAL_JAVA_LIBRARIES := \
     org.apache.http.legacy \
-    android-support-test \
-    legacy-android-test
+    android.test.runner.stubs \
+    android.test.base.stubs \
+
 
 LOCAL_SRC_FILES := $(call all-java-files-under, src)
 LOCAL_SRC_FILES += $(call all-java-files-under, ../src)
diff --git a/tests/tests/networksecurityconfig/networksecurityconfig-resourcesrc/AndroidManifest.xml b/tests/tests/networksecurityconfig/networksecurityconfig-resourcesrc/AndroidManifest.xml
index 46787160..4884458 100644
--- a/tests/tests/networksecurityconfig/networksecurityconfig-resourcesrc/AndroidManifest.xml
+++ b/tests/tests/networksecurityconfig/networksecurityconfig-resourcesrc/AndroidManifest.xml
@@ -20,6 +20,7 @@
         package="android.security.net.config.cts.CtsNetSecConfigResourcesSrcTestCases">
   <application android:networkSecurityConfig="@xml/network_security_config">
       <uses-library android:name="android.test.runner"/>
+      <uses-library android:name="org.apache.http.legacy" />
   </application>
 
   <uses-permission android:name="android.permission.INTERNET" />
diff --git a/tests/tests/networksecurityconfig/networksecurityconfig-resourcesrc/AndroidTest.xml b/tests/tests/networksecurityconfig/networksecurityconfig-resourcesrc/AndroidTest.xml
index 671633e..1dd8db7 100644
--- a/tests/tests/networksecurityconfig/networksecurityconfig-resourcesrc/AndroidTest.xml
+++ b/tests/tests/networksecurityconfig/networksecurityconfig-resourcesrc/AndroidTest.xml
@@ -14,6 +14,7 @@
      limitations under the License.
 -->
 <configuration description="Config for CTS CtsNetSecConfigInvalidPinTestCases test cases">
+    <option name="test-suite-tag" value="cts" />
     <option name="config-descriptor:metadata" key="component" value="security" />
     <target_preparer class="com.android.tradefed.targetprep.suite.SuiteApkInstaller">
         <option name="cleanup-apks" value="true" />
diff --git a/tests/tests/neuralnetworks/AndroidTest.xml b/tests/tests/neuralnetworks/AndroidTest.xml
index cd1a0f0..131db6a 100644
--- a/tests/tests/neuralnetworks/AndroidTest.xml
+++ b/tests/tests/neuralnetworks/AndroidTest.xml
@@ -14,6 +14,7 @@
      limitations under the License.
 -->
 <configuration description="Configuration for Native NNAPI Tests">
+    <option name="test-suite-tag" value="cts" />
     <option name="config-descriptor:metadata" key="component" value="neuralnetworks" />
     <target_preparer class="com.android.compatibility.common.tradefed.targetprep.FilePusher">
         <option name="cleanup" value="true" />
diff --git a/tests/tests/opengl/Android.mk b/tests/tests/opengl/Android.mk
index d623932..f9a36eb 100644
--- a/tests/tests/opengl/Android.mk
+++ b/tests/tests/opengl/Android.mk
@@ -29,7 +29,9 @@
 
 LOCAL_JNI_SHARED_LIBRARIES := libopengltest_jni
 
-LOCAL_STATIC_JAVA_LIBRARIES := ctstestrunner legacy-android-test
+LOCAL_STATIC_JAVA_LIBRARIES := ctstestrunner
+
+LOCAL_JAVA_LIBRARIES := android.test.runner.stubs android.test.base.stubs
 
 LOCAL_SRC_FILES := $(call all-java-files-under, src)
 
diff --git a/tests/tests/opengl/AndroidTest.xml b/tests/tests/opengl/AndroidTest.xml
index fc24223..4414182 100644
--- a/tests/tests/opengl/AndroidTest.xml
+++ b/tests/tests/opengl/AndroidTest.xml
@@ -14,6 +14,7 @@
      limitations under the License.
 -->
 <configuration description="Config for CTS OpenGL test cases">
+    <option name="test-suite-tag" value="cts" />
     <option name="config-descriptor:metadata" key="component" value="graphics" />
     <target_preparer class="com.android.tradefed.targetprep.suite.SuiteApkInstaller">
         <option name="cleanup-apks" value="true" />
diff --git a/tests/tests/opengl/src/android/opengl/cts/OpenGlEsVersionTest.java b/tests/tests/opengl/src/android/opengl/cts/OpenGlEsVersionTest.java
index 4225de0..24b0f6a 100644
--- a/tests/tests/opengl/src/android/opengl/cts/OpenGlEsVersionTest.java
+++ b/tests/tests/opengl/src/android/opengl/cts/OpenGlEsVersionTest.java
@@ -191,7 +191,7 @@
             "EGL_KHR_wait_sync",
         };
 
-        for (int i = 0; i < requiredList.length; ++i) {
+        for (int i = 0; i < requiredEglList.length; ++i) {
             assertTrue("Required EGL extension for VR high-performance is missing: " +
                 requiredEglList[i], hasExtension(extensions, requiredEglList[i]));
         }
diff --git a/tests/tests/openglperf/Android.mk b/tests/tests/openglperf/Android.mk
index 7669204..78a25fc 100644
--- a/tests/tests/openglperf/Android.mk
+++ b/tests/tests/openglperf/Android.mk
@@ -26,6 +26,8 @@
 
 LOCAL_STATIC_JAVA_LIBRARIES := ctstestrunner compatibility-device-util
 
+LOCAL_JAVA_LIBRARIES := android.test.runner.stubs android.test.base.stubs
+
 LOCAL_JNI_SHARED_LIBRARIES := libctsopenglperf_jni libnativehelper_compat_libc++
 
 LOCAL_SRC_FILES := $(call all-java-files-under, src)
diff --git a/tests/tests/openglperf/AndroidTest.xml b/tests/tests/openglperf/AndroidTest.xml
index dc6b2a8..48cceec 100644
--- a/tests/tests/openglperf/AndroidTest.xml
+++ b/tests/tests/openglperf/AndroidTest.xml
@@ -13,6 +13,7 @@
      limitations under the License.
 -->
 <configuration description="Config for CTS OpenGL Performance test cases">
+    <option name="test-suite-tag" value="cts" />
     <option name="config-descriptor:metadata" key="component" value="graphics" />
     <option name="not-shardable" value="true" />
     <target_preparer class="com.android.tradefed.targetprep.suite.SuiteApkInstaller">
diff --git a/tests/tests/os/Android.mk b/tests/tests/os/Android.mk
index 1aa46cc..7984227 100644
--- a/tests/tests/os/Android.mk
+++ b/tests/tests/os/Android.mk
@@ -28,9 +28,9 @@
     android-support-test \
     compatibility-device-util \
     ctstestrunner \
+    truth-prebuilt \
     guava \
-    junit \
-    legacy-android-test
+    junit
 
 LOCAL_JNI_SHARED_LIBRARIES := libcts_jni libctsos_jni libnativehelper_compat_libc++
 
@@ -50,6 +50,7 @@
 # uncomment when b/13282254 is fixed
 #LOCAL_SDK_VERSION := current
 LOCAL_JAVA_LIBRARIES += android.test.runner
+LOCAL_JAVA_LIBRARIES += android.test.base
 
 # Do not compress minijail policy files.
 LOCAL_AAPT_FLAGS := -0 .policy
diff --git a/tests/tests/os/AndroidTest.xml b/tests/tests/os/AndroidTest.xml
index 27f235a..b66c292 100644
--- a/tests/tests/os/AndroidTest.xml
+++ b/tests/tests/os/AndroidTest.xml
@@ -14,6 +14,7 @@
      limitations under the License.
 -->
 <configuration description="Configuration for OS Tests">
+    <option name="test-suite-tag" value="cts" />
     <option name="config-descriptor:metadata" key="component" value="framework" />
     <target_preparer class="com.android.tradefed.targetprep.suite.SuiteApkInstaller">
         <option name="cleanup-apks" value="true" />
diff --git a/tests/tests/os/src/android/os/cts/BuildTest.java b/tests/tests/os/src/android/os/cts/BuildTest.java
index f62b470..7336fc0 100644
--- a/tests/tests/os/src/android/os/cts/BuildTest.java
+++ b/tests/tests/os/src/android/os/cts/BuildTest.java
@@ -16,13 +16,16 @@
 
 package android.os.cts;
 
+import static android.os.Build.VERSION.CODENAME;
+import static android.os.Build.VERSION_CODES.CUR_DEVELOPMENT;
 
 import android.os.Build;
-import android.os.SystemProperties;
 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;
@@ -31,11 +34,6 @@
 import java.util.Scanner;
 import java.util.regex.Pattern;
 
-import junit.framework.TestCase;
-
-import static android.os.Build.VERSION.CODENAME;
-import static android.os.Build.VERSION_CODES.CUR_DEVELOPMENT;
-
 public class BuildTest extends TestCase {
 
     private static final String RO_PRODUCT_CPU_ABILIST = "ro.product.cpu.abilist";
@@ -254,6 +252,25 @@
         }
     }
 
+    /**
+     * Verify that SDK versions are bounded by both high and low expected
+     * values.
+     */
+    public void testSdkInt() {
+        assertTrue(
+                "Current SDK version " + Build.VERSION.SDK_INT
+                        + " is invalid; must be at least VERSION_CODES.BASE",
+                Build.VERSION.SDK_INT >= Build.VERSION_CODES.BASE);
+        assertTrue(
+                "First SDK version " + Build.VERSION.FIRST_SDK_INT
+                        + " is invalid; must be at least VERSION_CODES.BASE",
+                Build.VERSION.FIRST_SDK_INT >= Build.VERSION_CODES.BASE);
+        assertTrue(
+                "Current SDK version " + Build.VERSION.SDK_INT
+                        + " must be at least first SDK version " + Build.VERSION.FIRST_SDK_INT,
+                Build.VERSION.SDK_INT >= Build.VERSION.FIRST_SDK_INT);
+    }
+
     static final String RO_DEBUGGABLE = "ro.debuggable";
     private static final String RO_SECURE = "ro.secure";
 
diff --git a/tests/tests/os/src/android/os/cts/CtsRemoteService.java b/tests/tests/os/src/android/os/cts/CtsRemoteService.java
index daae49e..a0d5974 100644
--- a/tests/tests/os/src/android/os/cts/CtsRemoteService.java
+++ b/tests/tests/os/src/android/os/cts/CtsRemoteService.java
@@ -20,8 +20,10 @@
 import android.content.Intent;
 import android.os.IBinder;
 import android.os.Process;
+import java.io.File;
+import java.io.IOException;
 
-public class CtsRemoteService extends Service{
+public class CtsRemoteService extends Service {
 
     @Override
     public void onCreate() {
@@ -41,6 +43,15 @@
         public String getTimeZoneID() {
             return java.util.TimeZone.getDefault().getID();
         }
+
+        public boolean performDiskWrite() {
+            try {
+                File tempFile = File.createTempFile("foo", "bar");
+                return tempFile.delete();
+            } catch (IOException exception) {
+                return false;
+            }
+        }
     };
 
     @Override
@@ -50,5 +61,4 @@
         }
         return null;
     }
-
 }
diff --git a/tests/tests/os/src/android/os/cts/ISecondary.aidl b/tests/tests/os/src/android/os/cts/ISecondary.aidl
index 2c60149..183e2d5 100644
--- a/tests/tests/os/src/android/os/cts/ISecondary.aidl
+++ b/tests/tests/os/src/android/os/cts/ISecondary.aidl
@@ -23,4 +23,6 @@
     long getElapsedCpuTime();
 
     String getTimeZoneID();
+
+    boolean performDiskWrite();
 }
diff --git a/tests/tests/os/src/android/os/cts/ParcelFileDescriptorTest.java b/tests/tests/os/src/android/os/cts/ParcelFileDescriptorTest.java
index 4679a99..38651d7 100644
--- a/tests/tests/os/src/android/os/cts/ParcelFileDescriptorTest.java
+++ b/tests/tests/os/src/android/os/cts/ParcelFileDescriptorTest.java
@@ -16,6 +16,13 @@
 
 package android.os.cts;
 
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertNull;
+import static org.junit.Assert.assertSame;
+import static org.junit.Assert.assertTrue;
+import static org.junit.Assert.fail;
+
 import android.content.Context;
 import android.os.Handler;
 import android.os.Looper;
@@ -24,13 +31,20 @@
 import android.os.ParcelFileDescriptor.AutoCloseInputStream;
 import android.os.Parcelable;
 import android.os.cts.ParcelFileDescriptorPeer.FutureCloseListener;
-import android.test.AndroidTestCase;
+import android.support.test.InstrumentationRegistry;
+import android.support.test.runner.AndroidJUnit4;
+import android.system.ErrnoException;
+import android.system.Os;
+import android.system.OsConstants;
 import android.test.MoreAsserts;
 
 import com.google.common.util.concurrent.AbstractFuture;
 
 import junit.framework.ComparisonFailure;
 
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
 import java.io.File;
 import java.io.FileDescriptor;
 import java.io.FileInputStream;
@@ -43,9 +57,15 @@
 import java.net.Socket;
 import java.util.concurrent.TimeUnit;
 
-public class ParcelFileDescriptorTest extends AndroidTestCase {
+@RunWith(AndroidJUnit4.class)
+public class ParcelFileDescriptorTest {
     private static final long DURATION = 100l;
 
+    private Context getContext() {
+        return InstrumentationRegistry.getContext();
+    }
+
+    @Test
     public void testConstructorAndOpen() throws Exception {
         ParcelFileDescriptor tempFile = makeParcelFileDescriptor(getContext());
 
@@ -73,6 +93,7 @@
         }
     }
 
+    @Test
     public void testFromSocket() throws Throwable {
         final int PORT = 12222;
         final int DATA = 1;
@@ -111,6 +132,7 @@
         done.get(5, TimeUnit.SECONDS);
     }
 
+    @Test
     public void testFromData() throws IOException {
         assertNull(ParcelFileDescriptor.fromData(null, null));
         byte[] data = new byte[] { 0 };
@@ -143,6 +165,7 @@
         }
     }
 
+    @Test
     public void testFromDataSkip() throws IOException {
         byte[] data = new byte[] { 40, 41, 42, 43, 44, 45, 46 };
         ParcelFileDescriptor pfd = ParcelFileDescriptor.fromData(data, null);
@@ -164,11 +187,13 @@
         }
     }
 
+    @Test
     public void testToString() {
         ParcelFileDescriptor pfd = ParcelFileDescriptor.fromSocket(new Socket());
         assertNotNull(pfd.toString());
     }
 
+    @Test
     public void testWriteToParcel() throws Exception {
         ParcelFileDescriptor pf = makeParcelFileDescriptor(getContext());
 
@@ -188,6 +213,7 @@
         }
     }
 
+    @Test
     public void testClose() throws Exception {
         ParcelFileDescriptor pf = makeParcelFileDescriptor(getContext());
         AutoCloseInputStream in1 = new AutoCloseInputStream(pf);
@@ -210,11 +236,13 @@
         }
     }
 
+    @Test
     public void testGetStatSize() throws Exception {
         ParcelFileDescriptor pf = makeParcelFileDescriptor(getContext());
         assertTrue(pf.getStatSize() >= 0);
     }
 
+    @Test
     public void testGetFileDescriptor() {
         ParcelFileDescriptor pfd = ParcelFileDescriptor.fromSocket(new Socket());
         assertNotNull(pfd.getFileDescriptor());
@@ -223,6 +251,7 @@
         assertSame(pfd.getFileDescriptor(), p.getFileDescriptor());
     }
 
+    @Test
     public void testDescribeContents() {
         ParcelFileDescriptor pfd = ParcelFileDescriptor.fromSocket(new Socket());
         assertTrue((Parcelable.CONTENTS_FILE_DESCRIPTOR & pfd.describeContents()) != 0);
@@ -247,6 +276,7 @@
         return new FileInputStream(pfd.getFileDescriptor()).read();
     }
 
+    @Test
     public void testPipeNormal() throws Exception {
         final ParcelFileDescriptor[] pipe = ParcelFileDescriptor.createReliablePipe();
         final ParcelFileDescriptor red = pipe[0];
@@ -262,6 +292,7 @@
 
     // Reading should be done via AutoCloseInputStream if possible, rather than
     // recreating a FileInputStream from a raw FD, what's done in read(PFD).
+    @Test
     public void testPipeError_Discouraged() throws Exception {
         final ParcelFileDescriptor[] pipe = ParcelFileDescriptor.createReliablePipe();
         final ParcelFileDescriptor red = pipe[0];
@@ -281,6 +312,7 @@
         }
     }
 
+    @Test
     public void testPipeError() throws Exception {
         final ParcelFileDescriptor[] pipe = ParcelFileDescriptor.createReliablePipe();
         final ParcelFileDescriptor red = pipe[0];
@@ -298,6 +330,7 @@
         }
     }
 
+    @Test
     public void testFileNormal() throws Exception {
         final Handler handler = new Handler(Looper.getMainLooper());
         final FutureCloseListener listener = new FutureCloseListener();
@@ -312,6 +345,7 @@
         assertEquals(null, listener.get());
     }
 
+    @Test
     public void testFileError() throws Exception {
         final Handler handler = new Handler(Looper.getMainLooper());
         final FutureCloseListener listener = new FutureCloseListener();
@@ -326,6 +360,7 @@
         assertContains("OMG BANANAS", listener.get().getMessage());
     }
 
+    @Test
     public void testFileDetach() throws Exception {
         final Handler handler = new Handler(Looper.getMainLooper());
         final FutureCloseListener listener = new FutureCloseListener();
@@ -339,6 +374,7 @@
         assertContains("DETACHED", listener.get().getMessage());
     }
 
+    @Test
     public void testSocketErrorAfterClose() throws Exception {
         final ParcelFileDescriptor[] pair = ParcelFileDescriptor.createReliableSocketPair();
         final ParcelFileDescriptor red = pair[0];
@@ -361,6 +397,7 @@
         blue.checkError();
     }
 
+    @Test
     public void testSocketMultipleCheck() throws Exception {
         final ParcelFileDescriptor[] pair = ParcelFileDescriptor.createReliableSocketPair();
         final ParcelFileDescriptor red = pair[0];
@@ -382,6 +419,7 @@
     }
 
     // http://b/21578056
+    @Test
     public void testFileNamesWithNonBmpChars() throws Exception {
         final File file = File.createTempFile("treble_clef_\ud834\udd1e", ".tmp");
         final ParcelFileDescriptor pfd = ParcelFileDescriptor.open(file,
@@ -390,6 +428,57 @@
         pfd.close();
     }
 
+    @Test
+    public void testCheckFinalizerBehavior() throws Exception {
+        final Runtime runtime = Runtime.getRuntime();
+        ParcelFileDescriptor pfd = makeParcelFileDescriptor(getContext());
+        assertTrue(checkIsValid(pfd.getFileDescriptor()));
+
+        ParcelFileDescriptor wrappedPfd = new ParcelFileDescriptor(pfd);
+        assertTrue(checkIsValid(wrappedPfd.getFileDescriptor()));
+
+        FileDescriptor fd = pfd.getFileDescriptor();
+        int rawFd = pfd.getFd();
+        pfd = null;
+        assertNull(pfd); // To keep tools happy - yes we are using the write to null
+        runtime.gc(); runtime.runFinalization();
+        assertTrue("Wrapped PFD failed to hold reference",
+                checkIsValid(wrappedPfd.getFileDescriptor()));
+        assertTrue("FileDescriptor failed to hold reference", checkIsValid(fd));
+
+        wrappedPfd = null;
+        assertNull(wrappedPfd); // To keep tools happy - yes we are using the write to null
+        runtime.gc(); runtime.runFinalization();
+        // TODO: Enable this once b/65027998 is fixed
+        //assertTrue("FileDescriptor failed to hold reference", checkIsValid(fd));
+
+        fd = null;
+        assertNull(fd); // To keep tools happy - yes we are using the write to null
+        runtime.gc(); runtime.runFinalization();
+
+        try {
+            ParcelFileDescriptor.fromFd(rawFd);
+            fail("FD leaked");
+        } catch (IOException ex) {
+            // Success
+        }
+    }
+
+    boolean checkIsValid(FileDescriptor fd) {
+        try {
+            Os.fstat(fd);
+            return true;
+        } catch (ErrnoException e) {
+            if (e.errno == OsConstants.EBADF) {
+                return false;
+            } else {
+                fail(e.getMessage());
+                // not reached
+                return false;
+            }
+        }
+    }
+
     static ParcelFileDescriptor makeParcelFileDescriptor(Context con) throws Exception {
         final String fileName = "testParcelFileDescriptor";
 
diff --git a/tests/tests/os/src/android/os/cts/StrictModeTest.java b/tests/tests/os/src/android/os/cts/StrictModeTest.java
index 21730c6..6cf3ded 100644
--- a/tests/tests/os/src/android/os/cts/StrictModeTest.java
+++ b/tests/tests/os/src/android/os/cts/StrictModeTest.java
@@ -16,340 +16,473 @@
 
 package android.os.cts;
 
+import static com.google.common.truth.Truth.assertThat;
+import static com.google.common.truth.Truth.assertWithMessage;
+
+import android.content.ComponentName;
 import android.content.Context;
 import android.content.Intent;
+import android.content.ServiceConnection;
 import android.content.pm.PackageManager;
 import android.net.TrafficStats;
 import android.net.Uri;
+import android.os.IBinder;
+import android.os.RemoteException;
 import android.os.StrictMode;
-import android.os.StrictMode.ViolationListener;
+import android.os.StrictMode.ThreadPolicy.Builder;
+import android.os.StrictMode.ViolationInfo;
+import android.os.strictmode.UntaggedSocketViolation;
+import android.support.test.InstrumentationRegistry;
+import android.support.test.runner.AndroidJUnit4;
 import android.system.Os;
 import android.system.OsConstants;
-import android.test.InstrumentationTestCase;
 import android.util.Log;
-
 import java.io.File;
 import java.io.FileDescriptor;
 import java.io.FileInputStream;
+import java.io.FileNotFoundException;
 import java.io.FileOutputStream;
+import java.io.IOException;
 import java.net.HttpURLConnection;
 import java.net.Socket;
 import java.net.URL;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.concurrent.ArrayBlockingQueue;
+import java.util.concurrent.BlockingQueue;
+import java.util.concurrent.CountDownLatch;
+import java.util.concurrent.ExecutionException;
 import java.util.concurrent.LinkedBlockingQueue;
 import java.util.concurrent.TimeUnit;
+import java.util.function.Consumer;
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
 
-/**
- * Tests for {@link StrictMode}
- */
-public class StrictModeTest extends InstrumentationTestCase {
+/** Tests for {@link StrictMode} */
+@RunWith(AndroidJUnit4.class)
+public class StrictModeTest {
     private static final String TAG = "StrictModeTest";
+    private static final String REMOTE_SERVICE_ACTION = "android.app.REMOTESERVICE";
 
     private StrictMode.ThreadPolicy mThreadPolicy;
     private StrictMode.VmPolicy mVmPolicy;
 
     private Context getContext() {
-        return getInstrumentation().getContext();
+        return InstrumentationRegistry.getContext();
     }
 
-    @Override
-    protected void setUp() {
+    @Before
+    public void setUp() {
         mThreadPolicy = StrictMode.getThreadPolicy();
         mVmPolicy = StrictMode.getVmPolicy();
     }
 
-    @Override
-    protected void tearDown() {
+    @After
+    public void tearDown() {
         StrictMode.setThreadPolicy(mThreadPolicy);
         StrictMode.setVmPolicy(mVmPolicy);
     }
 
     public interface ThrowingRunnable {
-        public void run() throws Exception;
+        void run() throws Exception;
     }
 
-    /**
-     * Insecure connection should be detected
-     */
+    @Test
+    public void testUnclosedCloseable() throws Exception {
+        StrictMode.setVmPolicy(
+                new StrictMode.VmPolicy.Builder().detectLeakedClosableObjects().build());
+
+        inspectViolation(
+                () -> leakCloseable("leaked.txt"),
+                info -> {
+                    assertThat(info.getViolationDetails())
+                            .isEqualTo(
+                                    "A resource was acquired at attached stack trace but never released. See java.io.Closeable for information on avoiding resource leaks.");
+                    assertThat(info.getStackTrace())
+                            .contains("Explicit termination method 'close' not called");
+                    assertThat(info.getStackTrace()).contains("leakCloseable");
+                    assertPolicy(info, StrictMode.DETECT_VM_CLOSABLE_LEAKS);
+                });
+    }
+
+    private void leakCloseable(String fileName) throws InterruptedException {
+        final CountDownLatch finalizedSignal = new CountDownLatch(1);
+        try {
+            new FileOutputStream(new File(getContext().getFilesDir(), fileName)) {
+                @Override
+                protected void finalize() throws IOException {
+                    super.finalize();
+                    finalizedSignal.countDown();
+                }
+            };
+        } catch (FileNotFoundException e) {
+            throw new RuntimeException(e);
+        }
+        Runtime.getRuntime().gc();
+        Runtime.getRuntime().runFinalization();
+        // Sometimes it needs extra prodding.
+        if (!finalizedSignal.await(5, TimeUnit.SECONDS)) {
+            Runtime.getRuntime().gc();
+            Runtime.getRuntime().runFinalization();
+        }
+    }
+
+    @Test
+    public void testClassInstanceLimit() throws Exception {
+        StrictMode.setVmPolicy(
+                new StrictMode.VmPolicy.Builder()
+                        .setClassInstanceLimit(LimitedClass.class, 1)
+                        .build());
+        List<LimitedClass> references = new ArrayList<>();
+        assertNoViolation(() -> references.add(new LimitedClass()));
+        references.add(new LimitedClass());
+        inspectViolation(
+                StrictMode::conditionallyCheckInstanceCounts,
+                info -> assertPolicy(info, StrictMode.DETECT_VM_INSTANCE_LEAKS));
+    }
+
+    private static final class LimitedClass {}
+
+    /** Insecure connection should be detected */
+    @Test
     public void testCleartextNetwork() throws Exception {
         if (!hasInternetConnection()) {
             Log.i(TAG, "testCleartextNetwork() ignored on device without Internet");
             return;
         }
 
-        StrictMode.setVmPolicy(new StrictMode.VmPolicy.Builder()
-                .detectCleartextNetwork()
-                .penaltyLog()
-                .build());
+        StrictMode.setVmPolicy(
+                new StrictMode.VmPolicy.Builder().detectCleartextNetwork().penaltyLog().build());
 
-        assertViolation("Detected cleartext network traffic from UID", () -> {
-            ((HttpURLConnection) new URL("http://example.com/").openConnection())
-                    .getResponseCode();
-        });
+        inspectViolation(
+                () ->
+                        ((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);
+                });
     }
 
-    /**
-     * Secure connection should be ignored
-     */
+    /** Secure connection should be ignored */
+    @Test
     public void testEncryptedNetwork() throws Exception {
         if (!hasInternetConnection()) {
             Log.i(TAG, "testEncryptedNetwork() ignored on device without Internet");
             return;
         }
 
-        StrictMode.setVmPolicy(new StrictMode.VmPolicy.Builder()
-                .detectCleartextNetwork()
-                .penaltyLog()
-                .build());
+        StrictMode.setVmPolicy(
+                new StrictMode.VmPolicy.Builder().detectCleartextNetwork().penaltyLog().build());
 
-        assertNoViolation(() -> {
-            ((HttpURLConnection) new URL("https://example.com/").openConnection())
-                    .getResponseCode();
-        });
+        assertNoViolation(
+                () ->
+                        ((HttpURLConnection) new URL("https://example.com/").openConnection())
+                                .getResponseCode());
     }
 
+    @Test
     public void testFileUriExposure() throws Exception {
-        StrictMode.setVmPolicy(new StrictMode.VmPolicy.Builder()
-                .detectFileUriExposure()
-                .penaltyLog()
-                .build());
+        StrictMode.setVmPolicy(
+                new StrictMode.VmPolicy.Builder().detectFileUriExposure().penaltyLog().build());
 
         final Uri badUri = Uri.fromFile(new File("/sdcard/meow.jpg"));
-        assertViolation(badUri + " exposed beyond app", () -> {
-            Intent intent = new Intent(Intent.ACTION_VIEW);
-            intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
-            intent.setDataAndType(badUri, "image/jpeg");
-            getContext().startActivity(intent);
-        });
+        inspectViolation(
+                () -> {
+                    Intent intent = new Intent(Intent.ACTION_VIEW);
+                    intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
+                    intent.setDataAndType(badUri, "image/jpeg");
+                    getContext().startActivity(intent);
+                },
+                violation -> {
+                    assertThat(violation.getStackTrace()).contains(badUri + " exposed beyond app");
+                });
 
         final Uri goodUri = Uri.parse("content://com.example/foobar");
-        assertNoViolation(() -> {
-            Intent intent = new Intent(Intent.ACTION_VIEW);
-            intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
-            intent.setDataAndType(goodUri, "image/jpeg");
-            getContext().startActivity(intent);
-        });
+        assertNoViolation(
+                () -> {
+                    Intent intent = new Intent(Intent.ACTION_VIEW);
+                    intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
+                    intent.setDataAndType(goodUri, "image/jpeg");
+                    getContext().startActivity(intent);
+                });
     }
 
+    @Test
     public void testContentUriWithoutPermission() throws Exception {
-        StrictMode.setVmPolicy(new StrictMode.VmPolicy.Builder()
-                .detectContentUriWithoutPermission()
-                .penaltyLog()
-                .build());
+        StrictMode.setVmPolicy(
+                new StrictMode.VmPolicy.Builder()
+                        .detectContentUriWithoutPermission()
+                        .penaltyLog()
+                        .build());
 
         final Uri uri = Uri.parse("content://com.example/foobar");
-        assertViolation(uri + " exposed beyond app", () -> {
-            Intent intent = new Intent(Intent.ACTION_VIEW);
-            intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
-            intent.setDataAndType(uri, "image/jpeg");
-            getContext().startActivity(intent);
-        });
+        inspectViolation(
+                () -> {
+                    Intent intent = new Intent(Intent.ACTION_VIEW);
+                    intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
+                    intent.setDataAndType(uri, "image/jpeg");
+                    getContext().startActivity(intent);
+                },
+                violation ->
+                        assertThat(violation.getStackTrace())
+                                .contains(uri + " exposed beyond app"));
 
-        assertNoViolation(() -> {
-            Intent intent = new Intent(Intent.ACTION_VIEW);
-            intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
-            intent.setDataAndType(uri, "image/jpeg");
-            intent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION);
-            getContext().startActivity(intent);
-        });
+        assertNoViolation(
+                () -> {
+                    Intent intent = new Intent(Intent.ACTION_VIEW);
+                    intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
+                    intent.setDataAndType(uri, "image/jpeg");
+                    intent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION);
+                    getContext().startActivity(intent);
+                });
     }
 
+    @Test
     public void testUntaggedSocketsHttp() throws Exception {
         if (!hasInternetConnection()) {
             Log.i(TAG, "testUntaggedSockets() ignored on device without Internet");
             return;
         }
 
-        StrictMode.setVmPolicy(new StrictMode.VmPolicy.Builder()
-                .detectUntaggedSockets()
-                .penaltyLog()
-                .build());
+        StrictMode.setVmPolicy(
+                new StrictMode.VmPolicy.Builder().detectUntaggedSockets().penaltyLog().build());
 
-        assertViolation("Untagged socket detected", () -> {
-            ((HttpURLConnection) new URL("http://example.com/").openConnection())
-                    .getResponseCode();
-        });
+        inspectViolation(
+                () ->
+                        ((HttpURLConnection) new URL("http://example.com/").openConnection())
+                                .getResponseCode(),
+                violation ->
+                        assertThat(violation.getStackTrace())
+                                .contains(UntaggedSocketViolation.MESSAGE));
 
-        assertNoViolation(() -> {
-            TrafficStats.setThreadStatsTag(0xDECAFBAD);
-            try {
-                ((HttpURLConnection) new URL("http://example.com/").openConnection())
-                        .getResponseCode();
-            } finally {
-                TrafficStats.clearThreadStatsTag();
-            }
-        });
+        assertNoViolation(
+                () -> {
+                    TrafficStats.setThreadStatsTag(0xDECAFBAD);
+                    try {
+                        ((HttpURLConnection) new URL("http://example.com/").openConnection())
+                                .getResponseCode();
+                    } finally {
+                        TrafficStats.clearThreadStatsTag();
+                    }
+                });
     }
 
+    @Test
     public void testUntaggedSocketsRaw() throws Exception {
         if (!hasInternetConnection()) {
             Log.i(TAG, "testUntaggedSockets() ignored on device without Internet");
             return;
         }
 
-        StrictMode.setVmPolicy(new StrictMode.VmPolicy.Builder()
-                .detectUntaggedSockets()
-                .penaltyLog()
-                .build());
+        StrictMode.setVmPolicy(
+                new StrictMode.VmPolicy.Builder().detectUntaggedSockets().penaltyLog().build());
 
-        assertNoViolation(() -> {
-            TrafficStats.setThreadStatsTag(0xDECAFBAD);
-            try (Socket socket = new Socket("example.com", 80)) {
-                socket.getOutputStream().close();
-            } finally {
-                TrafficStats.clearThreadStatsTag();
-            }
-        });
+        assertNoViolation(
+                () -> {
+                    TrafficStats.setThreadStatsTag(0xDECAFBAD);
+                    try (Socket socket = new Socket("example.com", 80)) {
+                        socket.getOutputStream().close();
+                    } finally {
+                        TrafficStats.clearThreadStatsTag();
+                    }
+                });
 
-        assertViolation("Untagged socket detected", () -> {
-            try (Socket socket = new Socket("example.com", 80)) {
-                socket.getOutputStream().close();
-            }
-        });
+        inspectViolation(
+                () -> {
+                    try (Socket socket = new Socket("example.com", 80)) {
+                        socket.getOutputStream().close();
+                    }
+                },
+                violation ->
+                        assertThat(violation.getStackTrace())
+                                .contains(UntaggedSocketViolation.MESSAGE));
     }
 
+    private static final int PERMISSION_USER_ONLY = 0600;
+
+    @Test
     public void testRead() throws Exception {
         final File test = File.createTempFile("foo", "bar");
         final File dir = test.getParentFile();
 
         final FileInputStream is = new FileInputStream(test);
-        final FileDescriptor fd = Os.open(test.getAbsolutePath(), OsConstants.O_RDONLY, 0600);
+        final FileDescriptor fd =
+                Os.open(test.getAbsolutePath(), OsConstants.O_RDONLY, PERMISSION_USER_ONLY);
 
-        StrictMode.setThreadPolicy(new StrictMode.ThreadPolicy.Builder()
-                .detectDiskReads()
-                .penaltyLog()
-                .build());
+        StrictMode.setThreadPolicy(
+                new StrictMode.ThreadPolicy.Builder().detectDiskReads().penaltyLog().build());
+        inspectViolation(
+                test::exists,
+                violation -> {
+                    assertThat(violation.getViolationDetails()).isNull();
+                    assertThat(violation.getStackTrace()).contains("DiskReadViolation");
+                });
 
-        assertViolation("StrictModeDiskReadViolation", () -> {
-            test.exists();
-        });
-        assertViolation("StrictModeDiskReadViolation", () -> {
-            test.length();
-        });
-        assertViolation("StrictModeDiskReadViolation", () -> {
-            dir.list();
-        });
-        assertViolation("StrictModeDiskReadViolation", () -> {
-            new FileInputStream(test);
-        });
-        assertViolation("StrictModeDiskReadViolation", () -> {
-            is.read();
-        });
-        assertViolation("StrictModeDiskReadViolation", () -> {
-            Os.open(test.getAbsolutePath(), OsConstants.O_RDONLY, 0600);
-        });
-        assertViolation("StrictModeDiskReadViolation", () -> {
-            Os.read(fd, new byte[10], 0, 1);
-        });
+        Consumer<ViolationInfo> assertDiskReadPolicy =
+                violation -> assertPolicy(violation, StrictMode.DETECT_DISK_READ);
+        inspectViolation(test::exists, assertDiskReadPolicy);
+        inspectViolation(test::length, assertDiskReadPolicy);
+        inspectViolation(dir::list, assertDiskReadPolicy);
+        inspectViolation(is::read, assertDiskReadPolicy);
+
+        inspectViolation(() -> new FileInputStream(test), assertDiskReadPolicy);
+        inspectViolation(
+                () -> Os.open(test.getAbsolutePath(), OsConstants.O_RDONLY, PERMISSION_USER_ONLY),
+                assertDiskReadPolicy);
+        inspectViolation(() -> Os.read(fd, new byte[10], 0, 1), assertDiskReadPolicy);
     }
 
+    @Test
     public void testWrite() throws Exception {
         File file = File.createTempFile("foo", "bar");
 
         final FileOutputStream os = new FileOutputStream(file);
-        final FileDescriptor fd = Os.open(file.getAbsolutePath(), OsConstants.O_RDWR, 0600);
+        final FileDescriptor fd =
+                Os.open(file.getAbsolutePath(), OsConstants.O_RDWR, PERMISSION_USER_ONLY);
 
-        StrictMode.setThreadPolicy(new StrictMode.ThreadPolicy.Builder()
-                .detectDiskWrites()
-                .penaltyLog()
-                .build());
+        StrictMode.setThreadPolicy(
+                new StrictMode.ThreadPolicy.Builder().detectDiskWrites().penaltyLog().build());
 
-        assertViolation("StrictModeDiskWriteViolation", () -> {
-            File.createTempFile("foo", "bar");
-        });
-        assertViolation("StrictModeDiskWriteViolation", () -> {
-            file.delete();
-        });
-        assertViolation("StrictModeDiskWriteViolation", () -> {
-            file.createNewFile();
-        });
-        assertViolation("StrictModeDiskWriteViolation", () -> {
-            new FileOutputStream(file);
-        });
-        assertViolation("StrictModeDiskWriteViolation", () -> {
-            os.write(32);
-        });
-        assertViolation("StrictModeDiskWriteViolation", () -> {
-            Os.open(file.getAbsolutePath(), OsConstants.O_RDWR, 0600);
-        });
-        assertViolation("StrictModeDiskWriteViolation", () -> {
-            Os.write(fd, new byte[10], 0, 1);
-        });
-        assertViolation("StrictModeDiskWriteViolation", () -> {
-            Os.fsync(fd);
-        });
-        assertViolation("StrictModeDiskWriteViolation", () -> {
-            file.renameTo(new File(file.getParent(), "foobar"));
-        });
+        inspectViolation(
+                file::createNewFile,
+                violation -> {
+                    assertThat(violation.getViolationDetails()).isNull();
+                    assertThat(violation.getStackTrace()).contains("DiskWriteViolation");
+                });
+
+        Consumer<ViolationInfo> assertDiskWritePolicy =
+                violation -> assertPolicy(violation, StrictMode.DETECT_DISK_WRITE);
+
+        inspectViolation(() -> File.createTempFile("foo", "bar"), assertDiskWritePolicy);
+        inspectViolation(() -> new FileOutputStream(file), assertDiskWritePolicy);
+        inspectViolation(file::delete, assertDiskWritePolicy);
+        inspectViolation(file::createNewFile, assertDiskWritePolicy);
+        inspectViolation(() -> os.write(32), assertDiskWritePolicy);
+
+        inspectViolation(
+                () -> Os.open(file.getAbsolutePath(), OsConstants.O_RDWR, PERMISSION_USER_ONLY),
+                assertDiskWritePolicy);
+        inspectViolation(() -> Os.write(fd, new byte[10], 0, 1), assertDiskWritePolicy);
+        inspectViolation(() -> Os.fsync(fd), assertDiskWritePolicy);
+        inspectViolation(
+                () -> file.renameTo(new File(file.getParent(), "foobar")), assertDiskWritePolicy);
     }
 
+    @Test
     public void testNetwork() throws Exception {
         if (!hasInternetConnection()) {
             Log.i(TAG, "testUntaggedSockets() ignored on device without Internet");
             return;
         }
 
-        StrictMode.setThreadPolicy(new StrictMode.ThreadPolicy.Builder()
-                .detectNetwork()
-                .penaltyLog()
-                .build());
+        StrictMode.setThreadPolicy(
+                new StrictMode.ThreadPolicy.Builder().detectNetwork().penaltyLog().build());
 
-        assertViolation("StrictModeNetworkViolation", () -> {
-            try (Socket socket = new Socket("example.com", 80)) {
-                socket.getOutputStream().close();
-            }
-        });
+        inspectViolation(
+                () -> {
+                    try (Socket socket = new Socket("example.com", 80)) {
+                        socket.getOutputStream().close();
+                    }
+                },
+                violation -> assertPolicy(violation, StrictMode.DETECT_NETWORK));
+        inspectViolation(
+                () ->
+                        ((HttpURLConnection) new URL("http://example.com/").openConnection())
+                                .getResponseCode(),
+                violation -> assertPolicy(violation, StrictMode.DETECT_NETWORK));
+    }
 
-        assertViolation("StrictModeNetworkViolation", () -> {
-            ((HttpURLConnection) new URL("http://example.com/").openConnection())
-                    .getResponseCode();
-        });
+    @Test
+    public void testViolationAcrossBinder() throws Exception {
+        runWithRemoteServiceBound(
+                getContext(),
+                service -> {
+                    StrictMode.setThreadPolicy(
+                            new Builder().detectDiskWrites().penaltyLog().build());
+
+                    try {
+                        inspectViolation(
+                                () -> service.performDiskWrite(),
+                                (violation) -> {
+                                    assertPolicy(violation, StrictMode.DETECT_DISK_WRITE);
+                                    assertThat(violation.getViolationDetails())
+                                            .isNull(); // Disk write has no message.
+                                    assertThat(violation.getStackTrace())
+                                            .contains("DiskWriteViolation");
+                                    assertThat(violation.getStackTrace())
+                                            .contains(
+                                                    "at android.os.StrictMode$AndroidBlockGuardPolicy.onWriteToDisk");
+                                    assertThat(violation.getStackTrace())
+                                            .contains("# via Binder call with stack:");
+                                    assertThat(violation.getStackTrace())
+                                            .contains(
+                                                    "at android.os.cts.ISecondary$Stub$Proxy.performDiskWrite");
+                                });
+                        assertNoViolation(() -> service.getPid());
+                    } catch (Exception e) {
+                        throw new RuntimeException(e);
+                    }
+                });
+    }
+
+    private static void runWithRemoteServiceBound(Context context, Consumer<ISecondary> consumer)
+            throws ExecutionException, InterruptedException, RemoteException {
+        BlockingQueue<IBinder> binderHolder = new ArrayBlockingQueue<>(1);
+        ServiceConnection secondaryConnection =
+                new ServiceConnection() {
+                    public void onServiceConnected(ComponentName className, IBinder service) {
+                        binderHolder.add(service);
+                    }
+
+                    public void onServiceDisconnected(ComponentName className) {
+                        binderHolder.drainTo(new ArrayList<>());
+                    }
+                };
+        Intent intent = new Intent(REMOTE_SERVICE_ACTION);
+        intent.setPackage(context.getPackageName());
+
+        Intent secondaryIntent = new Intent(ISecondary.class.getName());
+        secondaryIntent.setPackage(context.getPackageName());
+        assertThat(
+                        context.bindService(
+                                secondaryIntent, secondaryConnection, Context.BIND_AUTO_CREATE))
+                .isTrue();
+        IBinder binder = binderHolder.take();
+        assertThat(binder.queryLocalInterface(binder.getInterfaceDescriptor())).isNull();
+        consumer.accept(ISecondary.Stub.asInterface(binder));
+        context.unbindService(secondaryConnection);
+        context.stopService(intent);
     }
 
     private static void assertViolation(String expected, ThrowingRunnable r) throws Exception {
-        final LinkedBlockingQueue<String> violations = new LinkedBlockingQueue<>();
-        StrictMode.setViolationListener(new ViolationListener() {
-            @Override
-            public void onViolation(String message) {
-                violations.add(message);
-            }
-        });
-
-        try {
-            r.run();
-            while (true) {
-                final String violation = violations.poll(5, TimeUnit.SECONDS);
-                if (violation == null) {
-                    fail("Expected violation not found: " + expected);
-                } else if (violation.contains(expected)) {
-                    return;
-                }
-            }
-        } finally {
-            StrictMode.setViolationListener(null);
-        }
+        inspectViolation(r, violation -> assertThat(violation.getStackTrace()).contains(expected));
     }
 
     private static void assertNoViolation(ThrowingRunnable r) throws Exception {
-        final LinkedBlockingQueue<String> violations = new LinkedBlockingQueue<>();
-        StrictMode.setViolationListener(new ViolationListener() {
-            @Override
-            public void onViolation(String message) {
-                violations.add(message);
-            }
-        });
+        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);
+    }
+
+    private static void inspectViolation(
+            ThrowingRunnable violating, Consumer<ViolationInfo> consume) throws Exception {
+        final LinkedBlockingQueue<ViolationInfo> violations = new LinkedBlockingQueue<>();
+        StrictMode.setViolationLogger(violations::add);
 
         try {
-            r.run();
-            while (true) {
-                final String violation = violations.poll(5, TimeUnit.SECONDS);
-                if (violation == null) {
-                    return;
-                } else {
-                    fail("Unexpected violation found: " + violation);
-                }
-            }
+            violating.run();
+            consume.accept(violations.poll(5, TimeUnit.SECONDS));
         } finally {
-            StrictMode.setViolationListener(null);
+            StrictMode.setViolationLogger(null);
         }
     }
 
diff --git a/tests/tests/os/src/android/os/cts/WorkSourceTest.java b/tests/tests/os/src/android/os/cts/WorkSourceTest.java
index ff9d693..e7df7be 100644
--- a/tests/tests/os/src/android/os/cts/WorkSourceTest.java
+++ b/tests/tests/os/src/android/os/cts/WorkSourceTest.java
@@ -35,8 +35,6 @@
     private Object[] mAddReturningNewbsArgs = new Object[1];
     private Method mSetReturningDiffs;
     private Object[] mSetReturningDiffsArgs = new Object[1];
-    private Method mStripNames;
-    private Object[] mStripNamesArgs = new Object[0];
 
     @Override
     protected void setUp() throws Exception {
@@ -46,7 +44,6 @@
         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 });
-        mStripNames = WorkSource.class.getMethod("stripNames", new Class[] {  });
     }
 
     private WorkSource wsNew(int uid) throws IllegalArgumentException,
@@ -100,11 +97,6 @@
         return (WorkSource[])mSetReturningDiffs.invoke(ws, mSetReturningDiffsArgs);
     }
 
-    private WorkSource wsStripNames(WorkSource ws) throws IllegalArgumentException,
-            InstantiationException, IllegalAccessException, InvocationTargetException {
-        return (WorkSource)mStripNames.invoke(ws);
-    }
-
     private void printArrays(StringBuilder sb, int[] uids, String[] names) {
         sb.append("{ ");
         for (int i=0; i<uids.length; i++) {
@@ -529,31 +521,4 @@
                 new int[] { },
                 true);
     }
-
-    private void doTestStripNames(int[] uids, String[] names, int[] expected) throws Exception {
-        WorkSource ws1 = wsNew(uids, names);
-        WorkSource res = wsStripNames(ws1);
-        checkWorkSource("StripNames", res, expected);
-    }
-
-    public void testStripNamesSimple() throws Exception {
-        doTestStripNames(
-                new int[]    { 10,  20,  30,  40 },
-                new String[] { "A", "A", "A", "A" },
-                new int[]    { 10, 20, 30, 40 });
-    }
-
-    public void testStripNamesFull() throws Exception {
-        doTestStripNames(
-                new int[]    { 10,  10,  10,  10 },
-                new String[] { "A", "B", "C", "D" },
-                new int[]    { 10 });
-    }
-
-    public void testStripNamesComplex() throws Exception {
-        doTestStripNames(
-                new int[]    { 10,  20,  20,  30,  40,  40 },
-                new String[] { "A", "A", "B", "A", "A", "B" },
-                new int[]    { 10, 20, 30, 40 });
-    }
 }
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 a820ac0..676465e 100644
--- a/tests/tests/os/src/android/os/storage/cts/StorageManagerTest.java
+++ b/tests/tests/os/src/android/os/storage/cts/StorageManagerTest.java
@@ -617,6 +617,38 @@
         looperThread.join();
     }
 
+    public void testOpenProxyFileDescriptor_largeFile() throws Exception {
+        final ProxyFileDescriptorCallback callback = new ProxyFileDescriptorCallback() {
+            @Override
+            public int onRead(long offset, int size, byte[] data) throws ErrnoException {
+                for (int i = 0; i < size; i++) {
+                    data[i] = 'L';
+                }
+                return size;
+            }
+
+            @Override
+            public long onGetSize() throws ErrnoException {
+                return 8L * 1024L * 1024L * 1024L;  // 8GB
+            }
+
+            @Override
+            public void onRelease() {}
+        };
+        final byte[] bytes = new byte[128];
+        try (final ParcelFileDescriptor fd = mStorageManager.openProxyFileDescriptor(
+                ParcelFileDescriptor.MODE_READ_ONLY, callback)) {
+            assertEquals(8L * 1024L * 1024L * 1024L, fd.getStatSize());
+
+            final int readBytes = Os.pread(
+                    fd.getFileDescriptor(), bytes, 0, bytes.length, fd.getStatSize() - 64L);
+            assertEquals(64, readBytes);
+            for (int i = 0; i < 64; i++) {
+                assertEquals('L', bytes[i]);
+            }
+        }
+    }
+
     private void assertStorageVolumesEquals(StorageVolume volume, StorageVolume clone)
             throws Exception {
         // Asserts equals() method.
diff --git a/tests/tests/packageinstaller/adminpackageinstaller/Android.mk b/tests/tests/packageinstaller/adminpackageinstaller/Android.mk
index 0e24292..fd0c1da 100755
--- a/tests/tests/packageinstaller/adminpackageinstaller/Android.mk
+++ b/tests/tests/packageinstaller/adminpackageinstaller/Android.mk
@@ -29,8 +29,9 @@
 LOCAL_STATIC_JAVA_LIBRARIES := \
 	ub-uiautomator \
 	android-support-test \
-	android-support-v4 \
-	legacy-android-test
+	android-support-v4
+
+LOCAL_JAVA_LIBRARIES := android.test.base.stubs
 
 LOCAL_SDK_VERSION := test_current
 
diff --git a/tests/tests/packageinstaller/adminpackageinstaller/AndroidTest.xml b/tests/tests/packageinstaller/adminpackageinstaller/AndroidTest.xml
index 3f09b25..2e4a4fe 100644
--- a/tests/tests/packageinstaller/adminpackageinstaller/AndroidTest.xml
+++ b/tests/tests/packageinstaller/adminpackageinstaller/AndroidTest.xml
@@ -15,6 +15,7 @@
 -->
 
 <configuration description="Config for CTS Admin Package Installer test cases">
+    <option name="test-suite-tag" value="cts" />
     <option name="config-descriptor:metadata" key="component" value="framework" />
 
     <target_preparer class="com.android.tradefed.targetprep.RunCommandTargetPreparer">
diff --git a/tests/tests/packageinstaller/externalsources/AndroidTest.xml b/tests/tests/packageinstaller/externalsources/AndroidTest.xml
index 2d782d8..a39baab 100644
--- a/tests/tests/packageinstaller/externalsources/AndroidTest.xml
+++ b/tests/tests/packageinstaller/externalsources/AndroidTest.xml
@@ -15,6 +15,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" />
 
     <target_preparer class="com.android.tradefed.targetprep.suite.SuiteApkInstaller">
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
index 921f5f1..06e858e 100644
--- a/tests/tests/packageinstaller/externalsources/src/android/packageinstaller/externalsources/cts/ExternalSourcesTest.java
+++ b/tests/tests/packageinstaller/externalsources/src/android/packageinstaller/externalsources/cts/ExternalSourcesTest.java
@@ -41,7 +41,6 @@
     private PackageManager mPm;
     private String mPackageName;
     private UiDevice mUiDevice;
-    private boolean mHasFeature;
 
     @Before
     public void setUp() throws Exception {
@@ -49,7 +48,6 @@
         mPm = mContext.getPackageManager();
         mPackageName = mContext.getPackageName();
         mUiDevice = UiDevice.getInstance(InstrumentationRegistry.getInstrumentation());
-        mHasFeature = !mContext.getPackageManager().hasSystemFeature(PackageManager.FEATURE_WATCH);
     }
 
     private void setAppOpsMode(String mode) throws IOException {
@@ -86,9 +84,6 @@
 
     @Test
     public void testManageUnknownSourcesExists() {
-        if (!mHasFeature) {
-            return;
-        }
         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);
diff --git a/tests/tests/permission/Android.mk b/tests/tests/permission/Android.mk
index b24774f..2fc171a 100644
--- a/tests/tests/permission/Android.mk
+++ b/tests/tests/permission/Android.mk
@@ -33,7 +33,7 @@
     ctstestrunner \
     guava \
     android-ex-camera2 \
-    legacy-android-test
+    compatibility-device-util
 
 LOCAL_JNI_SHARED_LIBRARIES := libctspermission_jni libnativehelper_compat_libc++
 
@@ -44,6 +44,7 @@
 # uncomment when b/13249777 is fixed
 #LOCAL_SDK_VERSION := current
 LOCAL_JAVA_LIBRARIES += android.test.runner
+LOCAL_JAVA_LIBRARIES += android.test.base
 
 include $(BUILD_CTS_PACKAGE)
 
diff --git a/tests/tests/permission/AndroidTest.xml b/tests/tests/permission/AndroidTest.xml
index b99cbc2..4fbcc10 100644
--- a/tests/tests/permission/AndroidTest.xml
+++ b/tests/tests/permission/AndroidTest.xml
@@ -14,6 +14,7 @@
      limitations under the License.
 -->
 <configuration description="Config for CTS Permission test cases">
+    <option name="test-suite-tag" value="cts" />
     <option name="config-descriptor:metadata" key="component" value="framework" />
     <target_preparer class="com.android.tradefed.targetprep.suite.SuiteApkInstaller">
         <option name="cleanup-apks" value="true" />
diff --git a/tests/tests/permission/src/android/permission/cts/AppOpsTest.java b/tests/tests/permission/src/android/permission/cts/AppOpsTest.java
index c29d5f5..e17d0de 100644
--- a/tests/tests/permission/src/android/permission/cts/AppOpsTest.java
+++ b/tests/tests/permission/src/android/permission/cts/AppOpsTest.java
@@ -16,59 +16,217 @@
 
 package android.permission.cts;
 
+import static android.app.AppOpsManager.MODE_ALLOWED;
+import static android.app.AppOpsManager.MODE_DEFAULT;
+import static android.app.AppOpsManager.MODE_ERRORED;
+import static android.app.AppOpsManager.MODE_IGNORED;
+import static android.app.AppOpsManager.OPSTR_READ_SMS;
+
+import android.Manifest.permission;
 import android.app.AppOpsManager;
 import android.content.Context;
-import android.test.AndroidTestCase;
+import android.os.Process;
+import android.test.InstrumentationTestCase;
 import android.test.suitebuilder.annotation.SmallTest;
-import android.util.AttributeSet;
-import junit.framework.AssertionFailedError;
 
-import java.lang.reflect.InvocationTargetException;
-import java.lang.reflect.Method;
+import com.android.compatibility.common.util.SystemUtil;
 
-public class AppOpsTest extends AndroidTestCase {
-    static final Class<?>[] sSetModeSignature = new Class[] {
-            Context.class, AttributeSet.class};
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.Map;
+import java.util.Set;
 
+public class AppOpsTest extends InstrumentationTestCase {
     private AppOpsManager mAppOps;
+    private Context mContext;
+    private String mOpPackageName;
+    private int mMyUid;
+
+    // These permissions and opStrs must map to the same op codes.
+    private static Map<String, String> permissionToOpStr = new HashMap<>();
+
+    static {
+        permissionToOpStr.put(permission.ACCESS_COARSE_LOCATION,
+                AppOpsManager.OPSTR_COARSE_LOCATION);
+        permissionToOpStr.put(permission.ACCESS_FINE_LOCATION, AppOpsManager.OPSTR_FINE_LOCATION);
+        permissionToOpStr.put(permission.READ_CONTACTS, AppOpsManager.OPSTR_READ_CONTACTS);
+        permissionToOpStr.put(permission.WRITE_CONTACTS, AppOpsManager.OPSTR_WRITE_CONTACTS);
+        permissionToOpStr.put(permission.READ_CALL_LOG, AppOpsManager.OPSTR_READ_CALL_LOG);
+        permissionToOpStr.put(permission.WRITE_CALL_LOG, AppOpsManager.OPSTR_WRITE_CALL_LOG);
+        permissionToOpStr.put(permission.READ_CALENDAR, AppOpsManager.OPSTR_READ_CALENDAR);
+        permissionToOpStr.put(permission.WRITE_CALENDAR, AppOpsManager.OPSTR_WRITE_CALENDAR);
+        permissionToOpStr.put(permission.CALL_PHONE, AppOpsManager.OPSTR_CALL_PHONE);
+        permissionToOpStr.put(permission.READ_SMS, AppOpsManager.OPSTR_READ_SMS);
+        permissionToOpStr.put(permission.RECEIVE_SMS, AppOpsManager.OPSTR_RECEIVE_SMS);
+        permissionToOpStr.put(permission.RECEIVE_MMS, AppOpsManager.OPSTR_RECEIVE_MMS);
+        permissionToOpStr.put(permission.RECEIVE_WAP_PUSH, AppOpsManager.OPSTR_RECEIVE_WAP_PUSH);
+        permissionToOpStr.put(permission.SEND_SMS, AppOpsManager.OPSTR_SEND_SMS);
+        permissionToOpStr.put(permission.READ_SMS, AppOpsManager.OPSTR_READ_SMS);
+        permissionToOpStr.put(permission.WRITE_SETTINGS, AppOpsManager.OPSTR_WRITE_SETTINGS);
+        permissionToOpStr.put(permission.SYSTEM_ALERT_WINDOW,
+                AppOpsManager.OPSTR_SYSTEM_ALERT_WINDOW);
+        permissionToOpStr.put(permission.ACCESS_NOTIFICATIONS,
+                AppOpsManager.OPSTR_ACCESS_NOTIFICATIONS);
+        permissionToOpStr.put(permission.CAMERA, AppOpsManager.OPSTR_CAMERA);
+        permissionToOpStr.put(permission.RECORD_AUDIO, AppOpsManager.OPSTR_RECORD_AUDIO);
+        permissionToOpStr.put(permission.READ_PHONE_STATE, AppOpsManager.OPSTR_READ_PHONE_STATE);
+        permissionToOpStr.put(permission.ADD_VOICEMAIL, AppOpsManager.OPSTR_ADD_VOICEMAIL);
+        permissionToOpStr.put(permission.USE_SIP, AppOpsManager.OPSTR_USE_SIP);
+        permissionToOpStr.put(permission.PROCESS_OUTGOING_CALLS,
+                AppOpsManager.OPSTR_PROCESS_OUTGOING_CALLS);
+        permissionToOpStr.put(permission.BODY_SENSORS, AppOpsManager.OPSTR_BODY_SENSORS);
+        permissionToOpStr.put(permission.READ_CELL_BROADCASTS,
+                AppOpsManager.OPSTR_READ_CELL_BROADCASTS);
+        permissionToOpStr.put(permission.READ_EXTERNAL_STORAGE,
+                AppOpsManager.OPSTR_READ_EXTERNAL_STORAGE);
+        permissionToOpStr.put(permission.WRITE_EXTERNAL_STORAGE,
+                AppOpsManager.OPSTR_WRITE_EXTERNAL_STORAGE);
+    }
 
     @Override
     protected void setUp() throws Exception {
         super.setUp();
-        mAppOps = (AppOpsManager)getContext().getSystemService(Context.APP_OPS_SERVICE);
+        mContext = getInstrumentation().getContext();
+        mAppOps = (AppOpsManager) mContext.getSystemService(Context.APP_OPS_SERVICE);
+        mOpPackageName = mContext.getOpPackageName();
+        mMyUid = Process.myUid();
         assertNotNull(mAppOps);
     }
 
+    public void testNoteOpAndCheckOp() throws Exception {
+        setAppOpMode(OPSTR_READ_SMS, MODE_ALLOWED);
+        assertEquals(MODE_ALLOWED, mAppOps.noteOp(OPSTR_READ_SMS, mMyUid, mOpPackageName));
+        assertEquals(MODE_ALLOWED, mAppOps.noteOpNoThrow(OPSTR_READ_SMS, mMyUid, mOpPackageName));
+        assertEquals(MODE_ALLOWED, mAppOps.checkOp(OPSTR_READ_SMS, mMyUid, mOpPackageName));
+        assertEquals(MODE_ALLOWED, mAppOps.checkOpNoThrow(OPSTR_READ_SMS, mMyUid, mOpPackageName));
+
+        setAppOpMode(OPSTR_READ_SMS, MODE_IGNORED);
+        assertEquals(MODE_IGNORED, mAppOps.noteOp(OPSTR_READ_SMS, mMyUid, mOpPackageName));
+        assertEquals(MODE_IGNORED, mAppOps.noteOpNoThrow(OPSTR_READ_SMS, mMyUid, mOpPackageName));
+        assertEquals(MODE_IGNORED, mAppOps.checkOp(OPSTR_READ_SMS, mMyUid, mOpPackageName));
+        assertEquals(MODE_IGNORED, mAppOps.checkOpNoThrow(OPSTR_READ_SMS, mMyUid, mOpPackageName));
+
+        setAppOpMode(OPSTR_READ_SMS, MODE_DEFAULT);
+        assertEquals(MODE_DEFAULT, mAppOps.noteOp(OPSTR_READ_SMS, mMyUid, mOpPackageName));
+        assertEquals(MODE_DEFAULT, mAppOps.noteOpNoThrow(OPSTR_READ_SMS, mMyUid, mOpPackageName));
+        assertEquals(MODE_DEFAULT, mAppOps.checkOp(OPSTR_READ_SMS, mMyUid, mOpPackageName));
+        assertEquals(MODE_DEFAULT, mAppOps.checkOpNoThrow(OPSTR_READ_SMS, mMyUid, mOpPackageName));
+
+        setAppOpMode(OPSTR_READ_SMS, MODE_ERRORED);
+        assertEquals(MODE_ERRORED, mAppOps.noteOpNoThrow(OPSTR_READ_SMS, mMyUid, mOpPackageName));
+        assertEquals(MODE_ERRORED, mAppOps.checkOpNoThrow(OPSTR_READ_SMS, mMyUid, mOpPackageName));
+        try {
+            mAppOps.noteOp(OPSTR_READ_SMS, mMyUid, mOpPackageName);
+            fail("SecurityException expected");
+        } catch (SecurityException expected) {
+        }
+        try {
+            mAppOps.checkOp(OPSTR_READ_SMS, mMyUid, mOpPackageName);
+            fail("SecurityException expected");
+        } catch (SecurityException expected) {
+        }
+    }
+
+    public void testCheckPackagePassesTest() throws Exception {
+        mAppOps.checkPackage(mMyUid, mOpPackageName);
+        mAppOps.checkPackage(Process.SYSTEM_UID, "android");
+    }
+
+    public void testCheckPackageDoesntPassTest() throws Exception {
+        try {
+            // Package name doesn't match UID.
+            mAppOps.checkPackage(Process.SYSTEM_UID, mOpPackageName);
+            fail("SecurityException expected");
+        } catch (SecurityException expected) {
+        }
+
+        try {
+            // Package name doesn't match UID.
+            mAppOps.checkPackage(mMyUid, "android");
+            fail("SecurityException expected");
+        } catch (SecurityException expected) {
+        }
+
+        try {
+            // Package name missing
+            mAppOps.checkPackage(mMyUid, "");
+            fail("SecurityException expected");
+        } catch (SecurityException expected) {
+        }
+    }
+
+    @SmallTest
+    public void testAllOpsHaveOpString() {
+        Set<String> opStrs = new HashSet<>();
+        for (String opStr : AppOpsManager.getOpStrs()) {
+            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());
+    }
+
+    @SmallTest
+    public void testOpCodesUnique() {
+        String[] opStrs = AppOpsManager.getOpStrs();
+        Set<Integer> opCodes = new HashSet<>();
+        for (String opStr : opStrs) {
+            opCodes.add(AppOpsManager.strOpToOp(opStr));
+        }
+        assertEquals("Not all app op codes are unique", opStrs.length, opCodes.size());
+    }
+
+    @SmallTest
+    public void testPermissionMapping() {
+        for (String permission : permissionToOpStr.keySet()) {
+            testPermissionMapping(permission, permissionToOpStr.get(permission));
+        }
+    }
+
+    private void testPermissionMapping(String permission, String opStr) {
+        // Do the public value => internal op code lookups.
+        String mappedOpStr = AppOpsManager.permissionToOp(permission);
+        assertEquals(mappedOpStr, opStr);
+        int mappedOpCode = AppOpsManager.permissionToOpCode(permission);
+        int mappedOpCode2 = AppOpsManager.strOpToOp(opStr);
+        assertEquals(mappedOpCode, mappedOpCode2);
+
+        // Do the internal op code => public value lookup (reverse lookup).
+        String permissionMappedBack = AppOpsManager.opToPermission(mappedOpCode);
+        assertEquals(permission, permissionMappedBack);
+    }
+
     /**
      * Test that the app can not change the app op mode for itself.
      */
     @SmallTest
-    public void testSetMode() {
-        boolean gotToTest = false;
+    public void testCantSetModeForSelf() {
         try {
-            Method setMode = mAppOps.getClass().getMethod("setMode", int.class, int.class,
-                    String.class, int.class);
-            int writeSmsOp = mAppOps.getClass().getField("OP_WRITE_SMS").getInt(mAppOps);
-            gotToTest = true;
-            setMode.invoke(mAppOps, writeSmsOp, android.os.Process.myUid(),
-                    getContext().getPackageName(), AppOpsManager.MODE_ALLOWED);
+            int writeSmsOp = AppOpsManager.permissionToOpCode("android.permission.WRITE_SMS");
+            mAppOps.setMode(writeSmsOp, mMyUid, mOpPackageName, AppOpsManager.MODE_ALLOWED);
             fail("Was able to set mode for self");
-        } catch (NoSuchFieldException e) {
-            throw new AssertionError("Unable to find OP_WRITE_SMS", e);
-        } catch (NoSuchMethodException e) {
-            throw new AssertionError("Unable to find setMode method", e);
-        } catch (InvocationTargetException e) {
-            if (!gotToTest) {
-                throw new AssertionError("Whoops", e);
-            }
-            // If we got to the test, we want it to have thrown a security exception.
-            // We need to look inside of the wrapper exception to see.
-            Throwable t = e.getCause();
-            if (!(t instanceof SecurityException)) {
-                throw new AssertionError("Did not throw SecurityException", e);
-            }
-        } catch (IllegalAccessException e) {
-            throw new AssertionError("Whoops", e);
+        } catch (SecurityException expected) {
         }
     }
+
+    private void setAppOpMode(String opStr, int mode) throws Exception {
+        String modeStr;
+        switch (mode) {
+            case MODE_ALLOWED:
+                modeStr = "allow";
+                break;
+            case MODE_ERRORED:
+                modeStr = "deny";
+                break;
+            case MODE_IGNORED:
+                modeStr = "ignore";
+                break;
+            case MODE_DEFAULT:
+                modeStr = "default";
+                break;
+            default:
+                throw new IllegalArgumentException("Unexpected app op type");
+        }
+        String command = "appops set " + mOpPackageName + " " + opStr + " " + modeStr;
+        SystemUtil.runShellCommand(getInstrumentation(), command);
+    }
 }
diff --git a/tests/tests/permission2/Android.mk b/tests/tests/permission2/Android.mk
index 062aeb7..d1190a3 100755
--- a/tests/tests/permission2/Android.mk
+++ b/tests/tests/permission2/Android.mk
@@ -24,12 +24,11 @@
 # Tag this module as a cts test artifact
 LOCAL_COMPATIBILITY_SUITE := cts vts general-tests
 
-LOCAL_JAVA_LIBRARIES := telephony-common
+LOCAL_JAVA_LIBRARIES := telephony-common android.test.base.stubs
 
 LOCAL_STATIC_JAVA_LIBRARIES := \
 	compatibility-device-util \
-	ctstestrunner \
-	legacy-android-test
+	ctstestrunner
 
 LOCAL_SRC_FILES := $(call all-java-files-under, src)
 
diff --git a/tests/tests/permission2/AndroidTest.xml b/tests/tests/permission2/AndroidTest.xml
index 2021b88..d4fb85f 100644
--- a/tests/tests/permission2/AndroidTest.xml
+++ b/tests/tests/permission2/AndroidTest.xml
@@ -14,6 +14,7 @@
      limitations under the License.
 -->
 <configuration description="Config for CTS Permission test cases">
+    <option name="test-suite-tag" value="cts" />
     <option name="config-descriptor:metadata" key="component" value="framework" />
     <target_preparer class="com.android.tradefed.targetprep.suite.SuiteApkInstaller">
         <option name="cleanup-apks" value="true" />
diff --git a/tests/tests/permission2/res/raw/android_manifest.xml b/tests/tests/permission2/res/raw/android_manifest.xml
index a4dad83..c72c8c1 100644
--- a/tests/tests/permission2/res/raw/android_manifest.xml
+++ b/tests/tests/permission2/res/raw/android_manifest.xml
@@ -183,15 +183,23 @@
     <protected-broadcast
         android:name="android.bluetooth.a2dp-sink.profile.action.AUDIO_CONFIG_CHANGED" />
     <protected-broadcast
+        android:name="android.bluetooth.avrcp-controller.profile.action.BROWSE_CONNECTION_STATE_CHANGED" />
+    <protected-broadcast
         android:name="android.bluetooth.avrcp-controller.profile.action.CONNECTION_STATE_CHANGED" />
     <protected-broadcast
+        android:name="android.bluetooth.avrcp-controller.profile.action.FOLDER_LIST" />
+    <protected-broadcast
+        android:name="android.bluetooth.avrcp-controller.profile.action.TRACK_EVENT" />
+    <protected-broadcast
         android:name="android.bluetooth.input.profile.action.CONNECTION_STATE_CHANGED" />
     <protected-broadcast
+        android:name="android.bluetooth.input.profile.action.IDLE_TIME_CHANGED" />
+    <protected-broadcast
         android:name="android.bluetooth.input.profile.action.PROTOCOL_MODE_CHANGED" />
     <protected-broadcast
         android:name="android.bluetooth.input.profile.action.VIRTUAL_UNPLUG_STATUS" />
     <protected-broadcast
-        android:name="android.bluetooth.inputhost.profile.action.CONNECTION_STATE_CHANGED" />
+        android:name="android.bluetooth.hiddevice.profile.action.CONNECTION_STATE_CHANGED" />
     <protected-broadcast
         android:name="android.bluetooth.map.profile.action.CONNECTION_STATE_CHANGED" />
     <protected-broadcast android:name="android.bluetooth.mapmce.profile.action.CONNECTION_STATE_CHANGED" />
@@ -299,6 +307,8 @@
     <protected-broadcast android:name="android.intent.action.DREAMING_STOPPED" />
     <protected-broadcast android:name="android.intent.action.ANY_DATA_STATE" />
 
+    <protected-broadcast android:name="com.android.server.stats.action.TRIGGER_COLLECTION" />
+
     <protected-broadcast android:name="com.android.server.WifiManager.action.START_SCAN" />
     <protected-broadcast android:name="com.android.server.WifiManager.action.START_PNO" />
     <protected-broadcast android:name="com.android.server.WifiManager.action.DELAYED_DRIVER_STOP" />
@@ -309,11 +319,16 @@
     <protected-broadcast android:name="com.android.server.usb.ACTION_OPEN_IN_APPS" />
     <protected-broadcast android:name="com.android.server.am.DELETE_DUMPHEAP" />
     <protected-broadcast android:name="com.android.server.net.action.SNOOZE_WARNING" />
+    <protected-broadcast android:name="com.android.server.wifi.ConnectToNetworkNotification.USER_DISMISSED_NOTIFICATION" />
+    <protected-broadcast android:name="com.android.server.wifi.ConnectToNetworkNotification.CONNECT_TO_NETWORK" />
+    <protected-broadcast android:name="com.android.server.wifi.ConnectToNetworkNotification.PICK_WIFI_NETWORK" />
+    <protected-broadcast android:name="com.android.server.wifi.ConnectToNetworkNotification.PICK_NETWORK_AFTER_FAILURE" />
     <protected-broadcast android:name="android.net.wifi.WIFI_STATE_CHANGED" />
     <protected-broadcast android:name="android.net.wifi.WIFI_AP_STATE_CHANGED" />
     <protected-broadcast android:name="android.net.wifi.WIFI_CREDENTIAL_CHANGED" />
     <protected-broadcast android:name="android.net.wifi.WIFI_SCAN_AVAILABLE" />
     <protected-broadcast android:name="android.net.wifi.aware.action.WIFI_AWARE_STATE_CHANGED" />
+    <protected-broadcast android:name="android.net.wifi.rtt.action.WIFI_RTT_STATE_CHANGED" />
     <protected-broadcast android:name="android.net.wifi.SCAN_RESULTS" />
     <protected-broadcast android:name="android.net.wifi.RSSI_CHANGED" />
     <protected-broadcast android:name="android.net.wifi.STATE_CHANGE" />
@@ -392,6 +407,7 @@
     <protected-broadcast android:name="android.internal.policy.action.BURN_IN_PROTECTION" />
     <protected-broadcast android:name="android.app.action.SYSTEM_UPDATE_POLICY_CHANGED" />
     <protected-broadcast android:name="android.app.action.DEVICE_OWNER_CHANGED" />
+    <protected-broadcast android:name="android.app.action.MANAGED_USER_CREATED" />
 
     <!-- Added in N -->
     <protected-broadcast android:name="android.intent.action.ANR" />
@@ -470,7 +486,6 @@
     <protected-broadcast android:name="android.content.jobscheduler.JOB_DEADLINE_EXPIRED" />
     <protected-broadcast android:name="android.intent.action.ACTION_UNSOL_RESPONSE_OEM_HOOK_RAW" />
     <protected-broadcast android:name="android.net.conn.CONNECTIVITY_CHANGE_SUPL" />
-    <protected-broadcast android:name="android.os.action.ACTION_EFFECTS_SUPPRESSOR_CHANGED" />
     <protected-broadcast android:name="android.os.action.LIGHT_DEVICE_IDLE_MODE_CHANGED" />
     <protected-broadcast android:name="android.os.storage.action.VOLUME_STATE_CHANGED" />
     <protected-broadcast android:name="android.os.storage.action.DISK_SCANNED" />
@@ -489,6 +504,8 @@
     <protected-broadcast android:name="android.app.action.NOTIFICATION_POLICY_CHANGED" />
     <protected-broadcast android:name="android.app.action.NOTIFICATION_POLICY_ACCESS_GRANTED_CHANGED" />
     <protected-broadcast android:name="android.os.action.ACTION_EFFECTS_SUPPRESSOR_CHANGED" />
+    <protected-broadcast android:name="android.app.action.NOTIFICATION_CHANNEL_BLOCK_STATE_CHANGED" />
+    <protected-broadcast android:name="android.app.action.NOTIFICATION_CHANNEL_GROUP_BLOCK_STATE_CHANGED" />
 
     <protected-broadcast android:name="android.permission.GET_APP_GRANTED_URI_PERMISSIONS" />
     <protected-broadcast android:name="android.permission.CLEAR_APP_GRANTED_URI_PERMISSIONS" />
@@ -547,6 +564,9 @@
     <protected-broadcast android:name="android.media.tv.action.CHANNEL_BROWSABLE_REQUESTED" />
     <protected-broadcast android:name="com.android.server.InputMethodManagerService.SHOW_INPUT_METHOD_PICKER" />
 
+    <!-- Made protected in P (was introduced in JB-MR2) -->
+    <protected-broadcast android:name="android.intent.action.GET_RESTRICTION_ENTRIES" />
+
     <!-- ====================================================================== -->
     <!--                          RUNTIME PERMISSIONS                           -->
     <!-- ====================================================================== -->
@@ -563,6 +583,7 @@
         android:icon="@drawable/perm_group_contacts"
         android:label="@string/permgrouplab_contacts"
         android:description="@string/permgroupdesc_contacts"
+        android:request="@string/permgrouprequest_contacts"
         android:priority="100" />
 
     <!-- Allows an application to read the user's contacts data.
@@ -593,6 +614,7 @@
         android:icon="@drawable/perm_group_calendar"
         android:label="@string/permgrouplab_calendar"
         android:description="@string/permgroupdesc_calendar"
+        android:request="@string/permgrouprequest_calendar"
         android:priority="200" />
 
     <!-- Allows an application to read the user's calendar data.
@@ -623,6 +645,7 @@
         android:icon="@drawable/perm_group_sms"
         android:label="@string/permgrouplab_sms"
         android:description="@string/permgroupdesc_sms"
+        android:request="@string/permgrouprequest_sms"
         android:priority="300" />
 
     <!-- Allows an application to send SMS messages.
@@ -699,6 +722,7 @@
         android:icon="@drawable/perm_group_storage"
         android:label="@string/permgrouplab_storage"
         android:description="@string/permgroupdesc_storage"
+        android:request="@string/permgrouprequest_storage"
         android:priority="900" />
 
     <!-- Allows an application to read from external storage.
@@ -760,6 +784,7 @@
         android:icon="@drawable/perm_group_location"
         android:label="@string/permgrouplab_location"
         android:description="@string/permgroupdesc_location"
+        android:request="@string/permgrouprequest_location"
         android:priority="400" />
 
     <!-- Allows an app to access precise location.
@@ -792,6 +817,7 @@
         android:icon="@drawable/perm_group_phone_calls"
         android:label="@string/permgrouplab_phone"
         android:description="@string/permgroupdesc_phone"
+        android:request="@string/permgrouprequest_phone"
         android:priority="500" />
 
     <!-- Allows read only access to phone state, including the phone number of the device,
@@ -943,6 +969,7 @@
         android:icon="@drawable/perm_group_microphone"
         android:label="@string/permgrouplab_microphone"
         android:description="@string/permgroupdesc_microphone"
+        android:request="@string/permgrouprequest_microphone"
         android:priority="600" />
 
     <!-- Allows an application to record audio.
@@ -985,6 +1012,7 @@
         android:icon="@drawable/perm_group_camera"
         android:label="@string/permgrouplab_camera"
         android:description="@string/permgroupdesc_camera"
+        android:request="@string/permgrouprequest_camera"
         android:priority="700" />
 
     <!-- Required to be able to access the camera device.
@@ -1009,11 +1037,12 @@
     <eat-comment />
 
     <!-- Used for permissions that are associated with accessing
-         camera or capturing images/video from the device. -->
+         body or environmental sensors. -->
     <permission-group android:name="android.permission-group.SENSORS"
         android:icon="@drawable/perm_group_sensors"
         android:label="@string/permgrouplab_sensors"
         android:description="@string/permgroupdesc_sensors"
+        android:request="@string/permgrouprequest_sensors"
         android:priority="800" />
 
     <!-- Allows an application to access data from sensors that the user uses to
@@ -1591,6 +1620,11 @@
     <permission android:name="android.permission.ACCESS_PDB_STATE"
         android:protectionLevel="signature" />
 
+    <!-- Allows testing if a passwords is forbidden by the admins.
+         @hide <p>Not for use by third-party applications. -->
+    <permission android:name="android.permission.TEST_BLACKLISTED_PASSWORD"
+        android:protectionLevel="signature" />
+
     <!-- @hide Allows system update service to notify device owner about pending updates.
    <p>Not for use by third-party applications. -->
     <permission android:name="android.permission.NOTIFY_PENDING_SYSTEM_UPDATE"
@@ -1712,7 +1746,7 @@
          @hide
     -->
     <permission android:name="android.permission.BIND_IMS_SERVICE"
-        android:protectionLevel="signature|privileged" />
+        android:protectionLevel="signature|privileged|vendorPrivileged" />
 
     <!-- Allows an application to manage embedded subscriptions (those on a eUICC) through
          EuiccManager APIs.
@@ -1763,6 +1797,15 @@
     <permission android:name="android.permission.ALLOCATE_AGGRESSIVE"
         android:protectionLevel="signature|privileged" />
 
+    <!-- @SystemApi @hide
+         Allows an application to use reserved disk space.
+         <p>Not for use by third-party applications.  Should only be requested by
+         apps that provide core system functionality, to ensure system stability
+         when disk is otherwise completely full.
+    -->
+    <permission android:name="android.permission.USE_RESERVED_DISK"
+        android:protectionLevel="signature|privileged" />
+
     <!-- ================================== -->
     <!-- Permissions for screenlock         -->
     <!-- ================================== -->
@@ -1860,11 +1903,11 @@
 
     <!-- @SystemApi @hide Allows an application to create/manage/remove stacks -->
     <permission android:name="android.permission.MANAGE_ACTIVITY_STACKS"
-        android:protectionLevel="signature|privileged" />
+        android:protectionLevel="signature|privileged|development" />
 
     <!-- @SystemApi @hide Allows an application to embed other activities -->
     <permission android:name="android.permission.ACTIVITY_EMBEDDING"
-                android:protectionLevel="signature|privileged" />
+                android:protectionLevel="signature|privileged|development" />
 
     <!-- Allows an application to start any activity, regardless of permission
          protection or exported state.
@@ -2030,6 +2073,11 @@
     <eat-comment />
 
     <!-- Allows an application to install a shortcut in Launcher.
+         <p>In Android O (API level 26) and higher, the <code>INSTALL_SHORTCUT</code> broadcast no
+         longer has any effect on your app because it's a private, implicit
+         broadcast. Instead, you should create an app shortcut by using the
+         {@link android.content.pm.ShortcutManager#requestPinShortcut requestPinShortcut()}
+         method from the {@link android.content.pm.ShortcutManager} class.
          <p>Protection level: normal
     -->
     <permission android:name="com.android.launcher.permission.INSTALL_SHORTCUT"
@@ -2270,7 +2318,7 @@
          <p>An application requesting this permission is responsible for
          verifying the source and integrity of the update before passing
          it off to the installer components.
-         @hide -->
+         @SystemApi @hide -->
     <permission android:name="android.permission.UPDATE_TIME_ZONE_RULES"
         android:protectionLevel="signature|privileged" />
 
@@ -2390,7 +2438,8 @@
     <permission android:name="android.permission.UPDATE_DEVICE_STATS"
         android:protectionLevel="signature|privileged" />
 
-    <!-- @SystemApi @hide Allows an application to collect battery statistics -->
+    <!-- @SystemApi @hide Allows an application to collect application operation statistics.
+         Not for use by third party apps. -->
     <permission android:name="android.permission.GET_APP_OPS_STATS"
         android:protectionLevel="signature|privileged|development" />
 
@@ -2415,10 +2464,10 @@
         android:protectionLevel="signature" />
 
     <!-- @SystemApi Allows an application to use
-        {@link android.view.WindowManager.LayoutsParams#PRIVATE_FLAG_HIDE_NON_SYSTEM_OVERLAY_WINDOWS}
-        to hide non-system-overlay windows.
-        <p>Not for use by third-party applications.
-        @hide
+         {@link android.view.WindowManager.LayoutsParams#PRIVATE_FLAG_HIDE_NON_SYSTEM_OVERLAY_WINDOWS}
+         to hide non-system-overlay windows.
+         <p>Not for use by third-party applications.
+         @hide
     -->
     <permission android:name="android.permission.HIDE_NON_SYSTEM_OVERLAY_WINDOWS"
                 android:protectionLevel="signature|installer" />
@@ -2625,10 +2674,6 @@
     <permission android:name="android.permission.BIND_AUTOFILL_SERVICE"
         android:protectionLevel="signature" />
 
-    <!-- @hide TODO(b/37563972): remove once clients use BIND_AUTOFILL_SERVICE -->
-    <permission android:name="android.permission.BIND_AUTOFILL"
-        android:protectionLevel="signature" />
-
     <!-- Must be required by hotword enrollment application,
          to ensure that only the system can interact with it.
          @hide <p>Not for use by third-party applications.</p> -->
@@ -2754,8 +2799,9 @@
         android:protectionLevel="signature|appop" />
 
     <!-- Allows an application to request deleting packages. Apps
-         targeting APIs greater than 25 must hold this permission in
-         order to use {@link android.content.Intent#ACTION_UNINSTALL_PACKAGE}.
+         targeting APIs {@link android.os.Build.VERSION_CODES#P} or greater must hold this
+         permission in order to use {@link android.content.Intent#ACTION_UNINSTALL_PACKAGE} or
+         {@link android.content.pm.PackageInstaller#uninstall}.
          <p>Protection level: normal
     -->
     <permission android:name="android.permission.REQUEST_DELETE_PACKAGES"
@@ -2766,6 +2812,14 @@
     <!-- @SystemApi Allows an application to install packages.
     <p>Not for use by third-party applications. -->
     <permission android:name="android.permission.INSTALL_PACKAGES"
+      android:protectionLevel="signature|privileged" />
+
+    <!-- @SystemApi Allows an application to install self updates. This is a limited version
+         of {@link android.Manifest.permission#INSTALL_PACKAGES}.
+        <p>Not for use by third-party applications.
+        @hide
+    -->
+    <permission android:name="android.permission.INSTALL_SELF_UPDATES"
         android:protectionLevel="signature|privileged" />
 
     <!-- @SystemApi Allows an application to clear user data.
@@ -2877,6 +2931,18 @@
     <permission android:name="android.permission.CONFIGURE_DISPLAY_COLOR_MODE"
         android:protectionLevel="signature" />
 
+    <!-- Allows an application to collect usage infomation about brightness slider changes.
+         <p>Not for use by third-party applications.</p>
+         TODO: make a System API
+         @hide -->
+    <permission android:name="android.permission.BRIGHTNESS_SLIDER_USAGE"
+        android:protectionLevel="signature|privileged" />
+
+    <!-- Allows an application to modify the display brightness configuration
+         @hide -->
+    <permission android:name="android.permission.CONFIGURE_DISPLAY_BRIGHTNESS"
+        android:protectionLevel="signature|privileged|development" />
+
     <!-- @SystemApi Allows an application to control VPN.
          <p>Not for use by third-party applications.</p>
          @hide -->
@@ -3032,10 +3098,10 @@
         android:protectionLevel="signature|privileged|development|appop" />
     <uses-permission android:name="android.permission.PACKAGE_USAGE_STATS" />
 
-    <!-- @hide Allows an application to change the app idle state of an app.
+    <!-- @hide @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" />
+        android:protectionLevel="signature|privileged" />
 
     <!-- @hide @SystemApi Allows an application to temporarily whitelist an inactive app to
          access the network and acquire wakelocks.
@@ -3056,6 +3122,12 @@
     <permission android:name="android.permission.BATTERY_STATS"
         android:protectionLevel="signature|privileged|development" />
 
+    <!--Allows an application to manage statscompanion.
+    <p>Not for use by third-party applications.
+         @hide -->
+    <permission android:name="android.permission.STATSCOMPANION"
+        android:protectionLevel="signature" />
+
     <!-- @SystemApi Allows an application to control the backup and restore process.
     <p>Not for use by third-party applications.
          @hide pending API council -->
@@ -3082,6 +3154,13 @@
     <permission android:name="android.permission.BIND_APPWIDGET"
         android:protectionLevel="signature|privileged" />
 
+    <!-- Allows an application to bind app's slices and get their
+         content. This content will be surfaced to the
+         user and not to leave the device.
+         <p>Not for use by third-party applications. -->
+    <permission android:name="android.permission.BIND_SLICE"
+        android:protectionLevel="signature|privileged|development" />
+
     <!-- @SystemApi Private permission, to restrict who can bring up a dialog to add a new
          keyguard widget
          @hide -->
@@ -3126,6 +3205,11 @@
     <permission android:name="android.permission.READ_SEARCH_INDEXABLES"
         android:protectionLevel="signature|privileged" />
 
+    <!-- @SystemApi Internal permission to allows an application to bind to suggestion service.
+        @hide -->
+    <permission android:name="android.permission.BIND_SETTINGS_SUGGESTIONS_SERVICE"
+                android:protectionLevel="signature" />
+
     <!-- @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. -->
@@ -3430,6 +3514,7 @@
         @hide -->
     <permission android:name="android.permission.LOCAL_MAC_ADDRESS"
                 android:protectionLevel="signature|privileged" />
+    <uses-permission android:name="android.permission.LOCAL_MAC_ADDRESS"/>
 
     <!-- @SystemApi Allows access to MAC addresses of WiFi and Bluetooth peer devices.
         @hide -->
@@ -3458,11 +3543,19 @@
     @hide -->
     <permission android:name="android.permission.ACCESS_INSTANT_APPS"
             android:protectionLevel="signature|installer|verifier" />
+    <uses-permission android:name="android.permission.ACCESS_INSTANT_APPS"/>
 
     <!-- Allows the holder to view the instant applications on the device.
     @hide -->
     <permission android:name="android.permission.VIEW_INSTANT_APPS"
-            android:protectionLevel="signature|preinstalled" />
+                android:protectionLevel="signature|preinstalled" />
+
+    <!-- Allows the holder to manage whether the system can bind to services
+         provided by instant apps. This permission is intended to protect
+         test/development fucntionality and should be used only in such cases.
+    @hide -->
+    <permission android:name="android.permission.MANAGE_BIND_INSTANT_SERVICE"
+                android:protectionLevel="signature" />
 
     <!-- Allows receiving the usage of media resource e.g. video/audio codec and
          graphic memory.
@@ -3548,6 +3641,20 @@
     <permission android:name="android.permission.INSTANT_APP_FOREGROUND_SERVICE"
         android:protectionLevel="signature|development|instant|appop" />
 
+    <!-- @hide Allows system components to access all app shortcuts. -->
+    <permission android:name="android.permission.ACCESS_SHORTCUTS"
+        android:protectionLevel="signature" />
+
+    <!-- @SystemApi Allows an application to read the runtime profiles of other apps.
+         @hide <p>Not for use by third-party applications. -->
+    <permission android:name="android.permission.READ_RUNTIME_PROFILES"
+                android:protectionLevel="signature|privileged" />
+
+    <!-- @SystemApi Allows an application to turn on / off quiet mode.
+         @hide <p>Not for use by third-party applications. -->
+    <permission android:name="android.permission.MODIFY_QUIET_MODE"
+                android:protectionLevel="signature|privileged" />
+
     <application android:process="system"
                  android:persistent="true"
                  android:hasCode="false"
@@ -3605,7 +3712,7 @@
         </activity-alias>
         <activity-alias android:name="com.android.internal.app.ForwardIntentToManagedProfile"
                 android:targetActivity="com.android.internal.app.IntentForwarderActivity"
-                android:icon="@drawable/ic_corp_icon"
+                android:icon="@drawable/ic_corp_badge"
                 android:exported="true"
                 android:label="@string/managed_profile_label">
         </activity-alias>
@@ -3772,6 +3879,14 @@
             </intent-filter>
         </receiver>
 
+        <receiver android:name="com.android.server.updates.NetworkWatchlistInstallReceiver"
+                  android:permission="android.permission.UPDATE_CONFIG">
+            <intent-filter>
+                <action android:name="android.intent.action.UPDATE_NETWORK_WATCHLIST" />
+                <data android:scheme="content" android:host="*" android:mimeType="*/*" />
+            </intent-filter>
+        </receiver>
+
         <receiver android:name="com.android.server.updates.ApnDbInstallReceiver"
                 android:permission="android.permission.UPDATE_CONFIG">
             <intent-filter>
@@ -3788,14 +3903,6 @@
             </intent-filter>
         </receiver>
 
-        <receiver android:name="com.android.server.updates.TzDataInstallReceiver"
-                android:permission="android.permission.UPDATE_CONFIG">
-            <intent-filter>
-                <action android:name="android.intent.action.UPDATE_TZDATA" />
-                <data android:scheme="content" android:host="*" android:mimeType="*/*" />
-            </intent-filter>
-        </receiver>
-
         <receiver android:name="com.android.server.updates.CertificateTransparencyLogInstallReceiver"
                 android:permission="android.permission.UPDATE_CONFIG">
             <intent-filter>
@@ -3836,6 +3943,16 @@
             </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$PollingAlarmReceiver"
+                  android:permission="android.permission.STATSCOMPANION"
+                  android:exported="false">
+        </receiver>
+
         <service android:name="android.hardware.location.GeofenceHardwareService"
             android:permission="android.permission.LOCATION_HARDWARE"
             android:exported="false" />
@@ -3887,6 +4004,17 @@
                  android:permission="android.permission.BIND_JOB_SERVICE" >
         </service>
 
-    </application>
+        <service android:name="com.android.server.timezone.TimeZoneUpdateIdler"
+                 android:permission="android.permission.BIND_JOB_SERVICE" >
+        </service>
+
+        <service android:name="com.android.server.net.watchlist.ReportWatchlistJobService"
+                 android:permission="android.permission.BIND_JOB_SERVICE" >
+        </service>
+
+        <service android:name="com.android.server.display.BrightnessIdleJob"
+                 android:permission="android.permission.BIND_JOB_SERVICE" >
+        </service>
+</application>
 
 </manifest>
diff --git a/tests/tests/permission2/res/raw/automotive_android_manifest.xml b/tests/tests/permission2/res/raw/automotive_android_manifest.xml
new file mode 100644
index 0000000..4a68183
--- /dev/null
+++ b/tests/tests/permission2/res/raw/automotive_android_manifest.xml
@@ -0,0 +1,203 @@
+<?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.
+-->
+
+<manifest xmlns:android="http://schemas.android.com/apk/res/android"
+        xmlns:androidprv="http://schemas.android.com/apk/prv/res/android"
+        package="com.android.car"
+        coreApp="true"
+        android:sharedUserId="android.uid.system">
+
+    <original-package android:name="com.android.car" />
+
+    <permission-group
+        android:name="android.car.permission-group.CAR_INFORMATION"
+        android:icon="@drawable/car_ic_mode"
+        android:description="@string/car_permission_desc"
+        android:label="@string/car_permission_label" />
+    <permission
+        android:name="android.car.permission.CAR_CABIN"
+        android:protectionLevel="system|signature"
+        android:label="@string/car_permission_label_cabin"
+        android:description="@string/car_permission_desc_cabin" />
+    <permission
+        android:name="android.car.permission.CAR_CAMERA"
+        android:protectionLevel="system|signature"
+        android:label="@string/car_permission_label_camera"
+        android:description="@string/car_permission_desc_camera" />
+    <permission
+        android:name="android.car.permission.CAR_FUEL"
+        android:permissionGroup="android.car.permission-group.CAR_INFORMATION"
+        android:protectionLevel="dangerous"
+        android:label="@string/car_permission_label_fuel"
+        android:description="@string/car_permission_desc_fuel" />
+    <permission
+        android:name="android.car.permission.CAR_HVAC"
+        android:protectionLevel="system|signature"
+        android:label="@string/car_permission_label_hvac"
+        android:description="@string/car_permission_desc_hvac" />
+    <permission
+        android:name="android.car.permission.CAR_MILEAGE"
+        android:permissionGroup="android.car.permission-group.CAR_INFORMATION"
+        android:protectionLevel="dangerous"
+        android:label="@string/car_permission_label_mileage"
+        android:description="@string/car_permission_desc_mileage" />
+    <permission
+        android:name="android.car.permission.CAR_SPEED"
+        android:permissionGroup="android.permission-group.LOCATION"
+        android:protectionLevel="dangerous"
+        android:label="@string/car_permission_label_speed"
+        android:description="@string/car_permission_desc_speed" />
+    <permission
+        android:name="android.car.permission.VEHICLE_DYNAMICS_STATE"
+        android:permissionGroup="android.car.permission-group.CAR_INFORMATION"
+        android:protectionLevel="dangerous"
+        android:label="@string/car_permission_label_vehicle_dynamics_state"
+        android:description="@string/car_permission_desc_vehicle_dynamics_state" />
+    <permission
+        android:name="android.car.permission.CAR_VENDOR_EXTENSION"
+        android:protectionLevel="system|signature"
+        android:label="@string/car_permission_label_vendor_extension"
+        android:description="@string/car_permission_desc_vendor_extension" />
+    <permission
+        android:name="android.car.permission.CAR_RADIO"
+        android:protectionLevel="system|signature"
+        android:label="@string/car_permission_label_radio"
+        android:description="@string/car_permission_desc_radio" />
+    <permission
+        android:name="android.car.permission.CAR_PROJECTION"
+        android:protectionLevel="system|signature"
+        android:label="@string/car_permission_label_projection"
+        android:description="@string/car_permission_desc_projection" />
+    <permission
+        android:name="android.car.permission.CAR_MOCK_VEHICLE_HAL"
+        android:protectionLevel="system|signature"
+        android:label="@string/car_permission_label_mock_vehicle_hal"
+        android:description="@string/car_permission_desc_mock_vehicle_hal" />
+    <permission
+        android:name="android.car.permission.CAR_NAVIGATION_MANAGER"
+        android:protectionLevel="system|signature"
+        android:label="@string/car_permission_car_navigation_manager"
+        android:description="@string/car_permission_desc_car_navigation_manager" />
+    <permission
+      android:name="android.car.permission.DIAGNOSTIC_READ_ALL"
+      android:protectionLevel="system|signature"
+      android:label="@string/car_permission_label_diag_read"
+      android:description="@string/car_permission_desc_diag_read" />
+    <permission
+      android:name="android.car.permission.DIAGNOSTIC_CLEAR"
+      android:protectionLevel="system|signature"
+      android:label="@string/car_permission_label_diag_clear"
+      android:description="@string/car_permission_desc_diag_clear" />
+    <permission
+        android:name="android.car.permission.VMS_PUBLISHER"
+        android:protectionLevel="system|signature"
+        android:label="@string/car_permission_label_vms_publisher"
+        android:description="@string/car_permission_desc_vms_publisher" />
+    <permission
+        android:name="android.car.permission.VMS_SUBSCRIBER"
+        android:protectionLevel="system|signature"
+        android:label="@string/car_permission_label_vms_subscriber"
+        android:description="@string/car_permission_desc_vms_subscriber" />
+
+    <!--  may replace this with system permission if proper one is defined. -->
+    <permission
+        android:name="android.car.permission.CONTROL_APP_BLOCKING"
+        android:protectionLevel="system|signature"
+        android:label="@string/car_permission_label_control_app_blocking"
+        android:description="@string/car_permission_desc_control_app_blocking" />
+
+    <permission
+        android:name="android.car.permission.CAR_CONTROL_AUDIO_VOLUME"
+        android:protectionLevel="system|signature"
+        android:label="@string/car_permission_label_audio_volume"
+        android:description="@string/car_permission_desc_audio_volume" />
+
+    <permission
+        android:name="android.car.permission.CAR_CONTROL_AUDIO_SETTINGS"
+        android:protectionLevel="system|signature"
+        android:label="@string/car_permission_label_audio_settings"
+        android:description="@string/car_permission_desc_audio_settings" />
+
+    <permission
+            android:name="android.car.permission.BIND_INSTRUMENT_CLUSTER_RENDERER_SERVICE"
+            android:protectionLevel="signature"
+            android:label="@string/car_permission_label_bind_instrument_cluster_rendering"
+            android:description="@string/car_permission_desc_bind_instrument_cluster_rendering"/>
+
+    <permission
+            android:name="android.car.permission.BIND_CAR_INPUT_SERVICE"
+            android:protectionLevel="signature"
+            android:label="@string/car_permission_label_bind_input_service"
+            android:description="@string/car_permission_desc_bind_input_service"/>
+
+    <permission
+            android:name="android.car.permission.CAR_DISPLAY_IN_CLUSTER"
+            android:protectionLevel="system|signature"
+            android:label="@string/car_permission_car_display_in_cluster"
+            android:description="@string/car_permission_desc_car_display_in_cluster" />
+
+    <permission android:name="android.car.permission.CAR_INSTRUMENT_CLUSTER_CONTROL"
+                android:protectionLevel="system|signature"
+                android:label="@string/car_permission_car_cluster_control"
+                android:description="@string/car_permission_desc_car_cluster_control" />
+
+    <permission android:name="android.car.permission.STORAGE_MONITORING"
+        android:protectionLevel="system|signature"
+        android:label="@string/car_permission_label_storage_monitoring"
+        android:description="@string/car_permission_desc_storage_monitoring" />
+
+    <uses-permission android:name="android.permission.CALL_PHONE" />
+    <uses-permission android:name="android.permission.DEVICE_POWER" />
+    <uses-permission android:name="android.permission.GRANT_RUNTIME_PERMISSIONS" />
+    <uses-permission android:name="android.permission.INTERACT_ACROSS_USERS_FULL" />
+    <uses-permission android:name="android.permission.MANAGE_ACTIVITY_STACKS" />
+    <uses-permission android:name="android.permission.MODIFY_AUDIO_ROUTING" />
+    <uses-permission android:name="android.permission.MODIFY_DAY_NIGHT_MODE" />
+    <uses-permission android:name="android.permission.MODIFY_PHONE_STATE" />
+    <uses-permission android:name="android.permission.READ_CALL_LOG" />
+    <uses-permission android:name="android.permission.READ_PHONE_STATE" />
+    <uses-permission android:name="android.permission.REAL_GET_TASKS" />
+    <uses-permission android:name="android.permission.REBOOT" />
+    <uses-permission android:name="android.permission.RECEIVE_BOOT_COMPLETED" />
+    <uses-permission android:name="android.permission.REMOVE_TASKS" />
+    <uses-permission android:name="android.permission.WRITE_SETTINGS" />
+    <uses-permission android:name="android.permission.WRITE_SECURE_SETTINGS" />
+    <uses-permission android:name="android.permission.BLUETOOTH" />
+    <uses-permission android:name="android.permission.MANAGE_USERS" />
+
+    <application android:label="Car service"
+                 android:directBootAware="true"
+                 android:allowBackup="false"
+                 android:persistent="true">
+
+       
+        <uses-library android:name="android.test.runner" />
+ <service android:name=".CarService"
+                android:singleUser="true">
+            <intent-filter>
+                <action android:name="android.car.ICar" />
+            </intent-filter>
+        </service>
+        <service android:name=".PerUserCarService" android:exported="false" />
+        <activity android:name="com.android.car.pm.ActivityBlockingActivity"
+                  android:excludeFromRecents="true"
+                  android:exported="false">
+            <intent-filter>
+                <action android:name="android.intent.action.MAIN" />
+            </intent-filter>
+        </activity>
+    </application>
+</manifest>
diff --git a/tests/tests/permission2/src/android/permission2/cts/PermissionPolicyTest.java b/tests/tests/permission2/src/android/permission2/cts/PermissionPolicyTest.java
index 35a99da..68e29a6 100644
--- a/tests/tests/permission2/src/android/permission2/cts/PermissionPolicyTest.java
+++ b/tests/tests/permission2/src/android/permission2/cts/PermissionPolicyTest.java
@@ -16,8 +16,12 @@
 
 package android.permission2.cts;
 
+import static android.os.Build.VERSION.SECURITY_PATCH;
+
+import android.content.Context;
 import android.content.pm.PackageInfo;
 import android.content.pm.PackageManager;
+import android.content.pm.PackageManager.NameNotFoundException;
 import android.content.pm.PermissionGroupInfo;
 import android.content.pm.PermissionInfo;
 import android.test.AndroidTestCase;
@@ -26,10 +30,10 @@
 import android.util.ArraySet;
 import android.util.Log;
 import android.util.Xml;
+
 import org.xmlpull.v1.XmlPullParser;
 
 import java.io.InputStream;
-import java.lang.String;
 import java.text.ParseException;
 import java.text.SimpleDateFormat;
 import java.util.ArrayList;
@@ -38,8 +42,6 @@
 import java.util.Map;
 import java.util.Set;
 
-import static android.os.Build.VERSION.SECURITY_PATCH;
-
 /**
  * Tests for permission policy on the platform.
  */
@@ -54,6 +56,8 @@
 
     private static final String PLATFORM_ROOT_NAMESPACE = "android.";
 
+    private static final String AUTOMOTIVE_SERVICE_PACKAGE_NAME = "com.android.car";
+
     private static final String TAG_PERMISSION = "permission";
 
     private static final String ATTR_NAME = "name";
@@ -61,14 +65,10 @@
     private static final String ATTR_PROTECTION_LEVEL = "protectionLevel";
 
     public void testPlatformPermissionPolicyUnaltered() throws Exception {
-        PackageInfo platformPackage = getContext().getPackageManager()
-                .getPackageInfo(PLATFORM_PACKAGE_NAME, PackageManager.GET_PERMISSIONS);
-        Map<String, PermissionInfo> declaredPermissionsMap = new ArrayMap<>();
-        List<String> offendingList = new ArrayList<String>();
+        Map<String, PermissionInfo> declaredPermissionsMap =
+                getPermissionsForPackage(getContext(), PLATFORM_PACKAGE_NAME);
 
-        for (PermissionInfo declaredPermission : platformPackage.permissions) {
-            declaredPermissionsMap.put(declaredPermission.name, declaredPermission);
-        }
+        List<String> offendingList = new ArrayList<>();
 
         List<PermissionGroupInfo> declaredGroups = getContext().getPackageManager()
                 .getAllPermissionGroups(0);
@@ -77,9 +77,16 @@
             declaredGroupsSet.add(declaredGroup.name);
         }
 
-        Set<String> expectedPermissionGroups = new ArraySet<String>();
+        Set<String> expectedPermissionGroups = new ArraySet<>();
+        List<PermissionInfo> expectedPermissions = loadExpectedPermissions(R.raw.android_manifest);
 
-        for (PermissionInfo expectedPermission : loadExpectedPermissions()) {
+        if (getContext().getPackageManager().hasSystemFeature(PackageManager.FEATURE_AUTOMOTIVE)) {
+            expectedPermissions.addAll(loadExpectedPermissions(R.raw.automotive_android_manifest));
+            declaredPermissionsMap.putAll(
+                    getPermissionsForPackage(getContext(), AUTOMOTIVE_SERVICE_PACKAGE_NAME));
+        }
+
+        for (PermissionInfo expectedPermission : expectedPermissions) {
             String expectedPermissionName = expectedPermission.name;
             if (shouldSkipPermission(expectedPermissionName)) {
                 continue;
@@ -132,7 +139,7 @@
 
                 if (!declaredGroupsSet.contains(declaredPermission.group)) {
                     offendingList.add(
-                            "Permission group " + expectedPermission.group + "must be defined");
+                            "Permission group " + expectedPermission.group + " must be defined");
                 }
             }
         }
@@ -140,7 +147,11 @@
         // OEMs cannot define permissions in the platform namespace
         for (String permission : declaredPermissionsMap.keySet()) {
             if (permission.startsWith(PLATFORM_ROOT_NAMESPACE)) {
-                offendingList.add("Cannot define permission in android namespace:" + permission);
+                final PermissionInfo permInfo = declaredPermissionsMap.get(permission);
+                offendingList.add(
+                        "Cannot define permission " + permission
+                        + ", package " + permInfo.packageName
+                        + " in android namespace");
             }
         }
 
@@ -151,11 +162,9 @@
                     if (declaredGroup.packageName.equals(PLATFORM_PACKAGE_NAME)
                             || declaredGroup.name.startsWith(PLATFORM_ROOT_NAMESPACE)) {
                         offendingList.add(
-                                "Cannot define group "
-                                        + declaredGroup.name
-                                        + ", package "
-                                        + declaredGroup.packageName
-                                        + " in android namespace");
+                                "Cannot define group " + declaredGroup.name
+                                + ", package " + declaredGroup.packageName
+                                + " in android namespace");
                     }
                 }
             }
@@ -177,12 +186,9 @@
         assertTrue(errMsg, offendingList.isEmpty());
     }
 
-    private List<PermissionInfo> loadExpectedPermissions() throws Exception {
+    private List<PermissionInfo> loadExpectedPermissions(int resourceId) throws Exception {
         List<PermissionInfo> permissions = new ArrayList<>();
-        try (
-                InputStream in = getContext().getResources()
-                        .openRawResource(android.permission2.cts.R.raw.android_manifest)
-        ) {
+        try (InputStream in = getContext().getResources().openRawResource(resourceId)) {
             XmlPullParser parser = Xml.newPullParser();
             parser.setInput(in, null);
 
@@ -250,6 +256,9 @@
                 case "privileged": {
                     protectionLevel |= PermissionInfo.PROTECTION_FLAG_PRIVILEGED;
                 } break;
+                case "vendorPrivileged": {
+                    protectionLevel |= PermissionInfo.PROTECTION_FLAG_VENDOR_PRIVILEGED;
+                } break;
                 case "setup": {
                     protectionLevel |= PermissionInfo.PROTECTION_FLAG_SETUP;
                 } break;
@@ -264,6 +273,18 @@
         return protectionLevel;
     }
 
+    private static Map<String, PermissionInfo> getPermissionsForPackage(Context context, String pkg)
+            throws NameNotFoundException {
+        PackageInfo packageInfo = context.getPackageManager()
+                .getPackageInfo(pkg, PackageManager.GET_PERMISSIONS);
+        Map<String, PermissionInfo> declaredPermissionsMap = new ArrayMap<>();
+
+        for (PermissionInfo declaredPermission : packageInfo.permissions) {
+            declaredPermissionsMap.put(declaredPermission.name, declaredPermission);
+        }
+        return declaredPermissionsMap;
+    }
+
     private static Date parseDate(String date) {
         Date patchDate = new Date();
         try {
diff --git a/tests/tests/preference/Android.mk b/tests/tests/preference/Android.mk
index 9223ce8..dd0ebf7 100644
--- a/tests/tests/preference/Android.mk
+++ b/tests/tests/preference/Android.mk
@@ -23,7 +23,9 @@
 # Tag this module as a cts test artifact
 LOCAL_COMPATIBILITY_SUITE := cts vts general-tests
 
-LOCAL_STATIC_JAVA_LIBRARIES := ctstestrunner legacy-android-test
+LOCAL_STATIC_JAVA_LIBRARIES := ctstestrunner
+
+LOCAL_JAVA_LIBRARIES := android.test.runner.stubs android.test.base.stubs
 
 LOCAL_SRC_FILES := $(call all-java-files-under, src)
 
diff --git a/tests/tests/preference/AndroidTest.xml b/tests/tests/preference/AndroidTest.xml
index f26d282..584bbc8 100644
--- a/tests/tests/preference/AndroidTest.xml
+++ b/tests/tests/preference/AndroidTest.xml
@@ -14,6 +14,7 @@
      limitations under the License.
 -->
 <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="not-shardable" value="true" />
     <target_preparer class="com.android.tradefed.targetprep.suite.SuiteApkInstaller">
diff --git a/tests/tests/preference2/Android.mk b/tests/tests/preference2/Android.mk
index 7529f43..8f1c3a2 100644
--- a/tests/tests/preference2/Android.mk
+++ b/tests/tests/preference2/Android.mk
@@ -29,8 +29,9 @@
     ctstestrunner \
     compatibility-device-util \
     mockito-target-minus-junit4 \
-    ub-uiautomator \
-    legacy-android-test
+    ub-uiautomator
+
+LOCAL_JAVA_LIBRARIES := android.test.runner.stubs android.test.base.stubs
 
 LOCAL_SRC_FILES := $(call all-java-files-under, src)
 
diff --git a/tests/tests/preference2/AndroidTest.xml b/tests/tests/preference2/AndroidTest.xml
index 691c45b3..b13eba5 100644
--- a/tests/tests/preference2/AndroidTest.xml
+++ b/tests/tests/preference2/AndroidTest.xml
@@ -14,6 +14,7 @@
      limitations under the License.
 -->
 <configuration description="Config for CTS Preference test cases">
+    <option name="test-suite-tag" value="cts" />
     <option name="config-descriptor:metadata" key="component" value="uitoolkit" />
     <target_preparer class="com.android.tradefed.targetprep.suite.SuiteApkInstaller">
         <option name="cleanup-apks" value="true" />
diff --git a/tests/tests/print/Android.mk b/tests/tests/print/Android.mk
index c9b036a..aba1b80 100644
--- a/tests/tests/print/Android.mk
+++ b/tests/tests/print/Android.mk
@@ -16,7 +16,7 @@
 
 include $(CLEAR_VARS)
 
-LOCAL_MODULE_TAGS := optional
+LOCAL_MODULE_TAGS := tests
 
 LOCAL_MODULE_PATH := $(TARGET_OUT_DATA_APPS)
 
@@ -27,8 +27,9 @@
 
 LOCAL_PACKAGE_NAME := CtsPrintTestCases
 
-LOCAL_STATIC_JAVA_LIBRARIES := mockito-target-minus-junit4 ctstestrunner ub-uiautomator compatibility-device-util android-support-test
+LOCAL_STATIC_JAVA_LIBRARIES := print-test-util-lib
 
 LOCAL_SDK_VERSION := test_current
 
 include $(BUILD_CTS_PACKAGE)
+include $(call all-makefiles-under,$(LOCAL_PATH))
\ No newline at end of file
diff --git a/tests/tests/print/AndroidManifest.xml b/tests/tests/print/AndroidManifest.xml
index c5148c8..ef86c30 100644
--- a/tests/tests/print/AndroidManifest.xml
+++ b/tests/tests/print/AndroidManifest.xml
@@ -25,10 +25,12 @@
 
         <uses-library android:name="android.test.runner"/>
 
-        <activity android:name="android.print.cts.PrintDocumentActivity"/>
+        <activity
+            android:name="android.print.test.PrintDocumentActivity"
+            android:theme="@style/NoAnimation" />
 
         <service
-            android:name="android.print.cts.services.FirstPrintService"
+            android:name="android.print.test.services.FirstPrintService"
             android:permission="android.permission.BIND_PRINT_SERVICE">
             <intent-filter>
                 <action android:name="android.printservice.PrintService" />
@@ -40,7 +42,7 @@
         </service>
 
         <service
-            android:name="android.print.cts.services.SecondPrintService"
+            android:name="android.print.test.services.SecondPrintService"
             android:permission="android.permission.BIND_PRINT_SERVICE">
             <intent-filter>
                 <action android:name="android.printservice.PrintService" />
@@ -52,25 +54,28 @@
         </service>
 
         <activity
-            android:name="android.print.cts.services.SettingsActivity"
+            android:name="android.print.test.services.SettingsActivity"
+            android:theme="@style/NoAnimation"
             android:exported="true">
         </activity>
 
         <activity
-            android:name="android.print.cts.services.AddPrintersActivity"
+            android:name="android.print.test.services.AddPrintersActivity"
+            android:theme="@style/NoAnimation"
             android:exported="true">
         </activity>
 
         <activity
-                android:name="android.print.cts.services.InfoActivity"
-                android:exported="true">
+            android:name="android.print.test.services.InfoActivity"
+            android:theme="@style/NoAnimation"
+            android:exported="true">
         </activity>
 
         <activity
-            android:name="android.print.cts.services.CustomPrintOptionsActivity"
+            android:name="android.print.test.services.CustomPrintOptionsActivity"
             android:permission="android.permission.START_PRINT_SERVICE_CONFIG_ACTIVITY"
             android:exported="true"
-            android:theme="@style/Theme.Translucent">
+            android:theme="@style/NoAnimationTranslucent">
         </activity>
 
   </application>
diff --git a/tests/tests/print/AndroidTest.xml b/tests/tests/print/AndroidTest.xml
index 1c771d6..e8eb8cd 100644
--- a/tests/tests/print/AndroidTest.xml
+++ b/tests/tests/print/AndroidTest.xml
@@ -14,6 +14,7 @@
      limitations under the License.
 -->
 <configuration description="Config for CTS Print test cases">
+    <option name="test-suite-tag" value="cts" />
     <option name="not-shardable" value="true" />
     <option name="config-descriptor:metadata" key="component" value="framework" />
 
diff --git a/tests/tests/print/printTestUtilLib/Android.mk b/tests/tests/print/printTestUtilLib/Android.mk
new file mode 100644
index 0000000..9b6086a
--- /dev/null
+++ b/tests/tests/print/printTestUtilLib/Android.mk
@@ -0,0 +1,27 @@
+# Copyright (C) 2017s The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#      http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT 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_SRC_FILES := $(call all-java-files-under, src)
+
+LOCAL_MODULE := print-test-util-lib
+
+LOCAL_STATIC_JAVA_LIBRARIES := mockito-target-minus-junit4 ctstestrunner ub-uiautomator compatibility-device-util android-support-test
+
+include $(BUILD_STATIC_JAVA_LIBRARY)
\ No newline at end of file
diff --git a/tests/tests/print/printTestUtilLib/src/android/print/test/BasePrintTest.java b/tests/tests/print/printTestUtilLib/src/android/print/test/BasePrintTest.java
new file mode 100644
index 0000000..fbfff33
--- /dev/null
+++ b/tests/tests/print/printTestUtilLib/src/android/print/test/BasePrintTest.java
@@ -0,0 +1,1150 @@
+/*
+ * 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.print.test;
+
+import static android.content.pm.PackageManager.GET_META_DATA;
+import static android.content.pm.PackageManager.GET_SERVICES;
+import static android.print.test.Utils.getPrintManager;
+
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertTrue;
+import static org.junit.Assert.fail;
+import static org.junit.Assume.assumeTrue;
+import static org.mockito.Matchers.any;
+import static org.mockito.Matchers.eq;
+import static org.mockito.Mockito.doAnswer;
+import static org.mockito.Mockito.doCallRealMethod;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.when;
+import static org.mockito.hamcrest.MockitoHamcrest.argThat;
+
+import android.app.Activity;
+import android.app.Instrumentation;
+import android.content.ComponentName;
+import android.content.Context;
+import android.content.Intent;
+import android.content.pm.PackageManager;
+import android.content.pm.ResolveInfo;
+import android.content.res.Resources;
+import android.graphics.pdf.PdfDocument;
+import android.os.Bundle;
+import android.os.CancellationSignal;
+import android.os.ParcelFileDescriptor;
+import android.os.SystemClock;
+import android.print.PageRange;
+import android.print.PrintAttributes;
+import android.print.PrintDocumentAdapter;
+import android.print.PrintDocumentAdapter.LayoutResultCallback;
+import android.print.PrintDocumentAdapter.WriteResultCallback;
+import android.print.PrintDocumentInfo;
+import android.print.PrintManager;
+import android.print.PrinterId;
+import android.print.pdf.PrintedPdfDocument;
+import android.print.test.services.PrintServiceCallbacks;
+import android.print.test.services.PrinterDiscoverySessionCallbacks;
+import android.print.test.services.StubbablePrintService;
+import android.print.test.services.StubbablePrinterDiscoverySession;
+import android.printservice.CustomPrinterIconCallback;
+import android.printservice.PrintJob;
+import android.provider.Settings;
+import android.support.annotation.NonNull;
+import android.support.annotation.Nullable;
+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.UiSelector;
+import android.util.Log;
+import android.util.SparseArray;
+
+import com.android.compatibility.common.util.SystemUtil;
+
+import org.hamcrest.BaseMatcher;
+import org.hamcrest.Description;
+import org.junit.After;
+import org.junit.AfterClass;
+import org.junit.Before;
+import org.junit.BeforeClass;
+import org.junit.Rule;
+import org.junit.rules.TestRule;
+import org.junit.runners.model.Statement;
+import org.mockito.InOrder;
+import org.mockito.stubbing.Answer;
+
+import java.io.BufferedReader;
+import java.io.ByteArrayOutputStream;
+import java.io.FileInputStream;
+import java.io.FileOutputStream;
+import java.io.IOException;
+import java.io.InputStreamReader;
+import java.lang.annotation.Annotation;
+import java.lang.annotation.ElementType;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.lang.annotation.Target;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.concurrent.TimeoutException;
+import java.util.concurrent.atomic.AtomicInteger;
+
+/**
+ * This is the base class for print tests.
+ */
+public abstract class BasePrintTest {
+    private static final String LOG_TAG = "BasePrintTest";
+
+    protected static final long OPERATION_TIMEOUT_MILLIS = 60000;
+    protected static final String PRINT_JOB_NAME = "Test";
+    static final String TEST_ID = "BasePrintTest.EXTRA_TEST_ID";
+
+    private static final String PRINT_SPOOLER_PACKAGE_NAME = "com.android.printspooler";
+    private static final String PM_CLEAR_SUCCESS_OUTPUT = "Success";
+    private static final String COMMAND_LIST_ENABLED_IME_COMPONENTS = "ime list -s";
+    private static final String COMMAND_PREFIX_ENABLE_IME = "ime enable ";
+    private static final String COMMAND_PREFIX_DISABLE_IME = "ime disable ";
+    private static final int CURRENT_USER_ID = -2; // Mirrors UserHandle.USER_CURRENT
+    private static final String PRINTSPOOLER_PACKAGE = "com.android.printspooler";
+
+    private static final AtomicInteger sLastTestID = new AtomicInteger();
+    private int mTestId;
+    private PrintDocumentActivity mActivity;
+
+    private static String sDisabledPrintServicesBefore;
+
+    private static final SparseArray<BasePrintTest> sIdToTest = new SparseArray<>();
+
+    public final @Rule ShouldStartActivity mShouldStartActivityRule = new ShouldStartActivity();
+
+    /**
+     * Return the UI device
+     *
+     * @return the UI device
+     */
+    public UiDevice getUiDevice() {
+        return UiDevice.getInstance(getInstrumentation());
+    }
+
+    private CallCounter mCancelOperationCounter;
+    private CallCounter mLayoutCallCounter;
+    private CallCounter mWriteCallCounter;
+    private CallCounter mWriteCancelCallCounter;
+    private CallCounter mStartCallCounter;
+    private CallCounter mFinishCallCounter;
+    private CallCounter mPrintJobQueuedCallCounter;
+    private CallCounter mCreateSessionCallCounter;
+    private CallCounter mDestroySessionCallCounter;
+    private CallCounter mDestroyActivityCallCounter = new CallCounter();
+    private CallCounter mCreateActivityCallCounter = new CallCounter();
+
+    private static String[] sEnabledImes;
+
+    private static String[] getEnabledImes() throws IOException {
+        List<String> imeList = new ArrayList<>();
+
+        ParcelFileDescriptor pfd = getInstrumentation().getUiAutomation()
+                .executeShellCommand(COMMAND_LIST_ENABLED_IME_COMPONENTS);
+        try (BufferedReader reader = new BufferedReader(
+                new InputStreamReader(new FileInputStream(pfd.getFileDescriptor())))) {
+
+            String line;
+            while ((line = reader.readLine()) != null) {
+                imeList.add(line);
+            }
+        }
+
+        String[] imeArray = new String[imeList.size()];
+        imeList.toArray(imeArray);
+
+        return imeArray;
+    }
+
+    private static void disableImes() throws Exception {
+        sEnabledImes = getEnabledImes();
+        for (String ime : sEnabledImes) {
+            String disableImeCommand = COMMAND_PREFIX_DISABLE_IME + ime;
+            SystemUtil.runShellCommand(getInstrumentation(), disableImeCommand);
+        }
+    }
+
+    private static void enableImes() throws Exception {
+        for (String ime : sEnabledImes) {
+            String enableImeCommand = COMMAND_PREFIX_ENABLE_IME + ime;
+            SystemUtil.runShellCommand(getInstrumentation(), enableImeCommand);
+        }
+        sEnabledImes = null;
+    }
+
+    protected static Instrumentation getInstrumentation() {
+        return InstrumentationRegistry.getInstrumentation();
+    }
+
+    @BeforeClass
+    public static void setUpClass() throws Exception {
+        Log.d(LOG_TAG, "setUpClass()");
+
+        Instrumentation instrumentation = getInstrumentation();
+
+        // Make sure we start with a clean slate.
+        Log.d(LOG_TAG, "clearPrintSpoolerData()");
+        clearPrintSpoolerData();
+        Log.d(LOG_TAG, "disableImes()");
+        disableImes();
+        Log.d(LOG_TAG, "disablePrintServices()");
+        disablePrintServices(instrumentation.getTargetContext().getPackageName());
+
+        // Workaround for dexmaker bug: https://code.google.com/p/dexmaker/issues/detail?id=2
+        // Dexmaker is used by mockito.
+        System.setProperty("dexmaker.dexcache", instrumentation
+                .getTargetContext().getCacheDir().getPath());
+
+        Log.d(LOG_TAG, "setUpClass() done");
+    }
+
+    /**
+     * Disable all print services beside the ones we want to leave enabled.
+     *
+     * @param packageToLeaveEnabled The package of the services to leave enabled.
+     */
+    private static void disablePrintServices(@NonNull String packageToLeaveEnabled)
+            throws IOException {
+        Instrumentation instrumentation = getInstrumentation();
+
+        sDisabledPrintServicesBefore = SystemUtil.runShellCommand(instrumentation,
+                "settings get secure " + Settings.Secure.DISABLED_PRINT_SERVICES);
+
+        Intent printServiceIntent = new Intent(android.printservice.PrintService.SERVICE_INTERFACE);
+        List<ResolveInfo> installedServices = instrumentation.getContext().getPackageManager()
+                .queryIntentServices(printServiceIntent, GET_SERVICES | GET_META_DATA);
+
+        StringBuilder builder = new StringBuilder();
+        for (ResolveInfo service : installedServices) {
+            if (packageToLeaveEnabled.equals(service.serviceInfo.packageName)) {
+                continue;
+            }
+            if (builder.length() > 0) {
+                builder.append(":");
+            }
+            builder.append(new ComponentName(service.serviceInfo.packageName,
+                    service.serviceInfo.name).flattenToString());
+        }
+
+        SystemUtil.runShellCommand(instrumentation, "settings put secure "
+                + Settings.Secure.DISABLED_PRINT_SERVICES + " " + builder);
+    }
+
+    /**
+     * Revert {@link #disablePrintServices(String)}
+     */
+    private static  void enablePrintServices() throws IOException {
+        SystemUtil.runShellCommand(getInstrumentation(),
+                "settings put secure " + Settings.Secure.DISABLED_PRINT_SERVICES + " "
+                        + sDisabledPrintServicesBefore);
+    }
+
+    @Before
+    public void setUp() throws Exception {
+        Log.d(LOG_TAG, "setUp()");
+
+        assumeTrue(getInstrumentation().getContext().getPackageManager().hasSystemFeature(
+                PackageManager.FEATURE_PRINTING));
+
+        // Initialize the latches.
+        Log.d(LOG_TAG, "init counters");
+        mCancelOperationCounter = new CallCounter();
+        mLayoutCallCounter = new CallCounter();
+        mStartCallCounter = new CallCounter();
+        mFinishCallCounter = new CallCounter();
+        mWriteCallCounter = new CallCounter();
+        mWriteCancelCallCounter = new CallCounter();
+        mFinishCallCounter = new CallCounter();
+        mPrintJobQueuedCallCounter = new CallCounter();
+        mCreateSessionCallCounter = new CallCounter();
+        mDestroySessionCallCounter = new CallCounter();
+
+        mTestId = sLastTestID.incrementAndGet();
+        sIdToTest.put(mTestId, this);
+
+        // Create the activity if needed
+        if (!mShouldStartActivityRule.mNoActivity) {
+            createActivity();
+        }
+
+        Log.d(LOG_TAG, "setUp() done");
+    }
+
+    @After
+    public void tearDown() throws Exception {
+        Log.d(LOG_TAG, "tearDown()");
+
+        finishActivity();
+
+        sIdToTest.remove(mTestId);
+
+        Log.d(LOG_TAG, "tearDown() done");
+    }
+
+    @AfterClass
+    public static void tearDownClass() throws Exception {
+        Log.d(LOG_TAG, "tearDownClass()");
+
+        Instrumentation instrumentation = getInstrumentation();
+
+        Log.d(LOG_TAG, "enablePrintServices()");
+        enablePrintServices();
+
+        Log.d(LOG_TAG, "enableImes()");
+        enableImes();
+
+        // Make sure the spooler is cleaned, this also un-approves all services
+        Log.d(LOG_TAG, "clearPrintSpoolerData()");
+        clearPrintSpoolerData();
+
+        SystemUtil.runShellCommand(instrumentation, "settings put secure "
+                    + Settings.Secure.DISABLED_PRINT_SERVICES + " null");
+
+        Log.d(LOG_TAG, "tearDownClass() done");
+    }
+
+    protected android.print.PrintJob print(@NonNull final PrintDocumentAdapter adapter,
+            final PrintAttributes attributes) {
+        android.print.PrintJob[] printJob = new android.print.PrintJob[1];
+        // Initiate printing as if coming from the app.
+        getInstrumentation().runOnMainSync(() -> {
+            PrintManager printManager = (PrintManager) getActivity()
+                    .getSystemService(Context.PRINT_SERVICE);
+            printJob[0] = printManager.print("Print job", adapter, attributes);
+        });
+
+        return printJob[0];
+    }
+
+    protected void print(PrintDocumentAdapter adapter) {
+        print(adapter, (PrintAttributes) null);
+    }
+
+    protected void print(PrintDocumentAdapter adapter, String printJobName) {
+        print(adapter, printJobName, null);
+    }
+
+    /**
+     * Start printing
+     *
+     * @param adapter      Adapter supplying data to print
+     * @param printJobName The name of the print job
+     */
+    protected void print(@NonNull PrintDocumentAdapter adapter, @NonNull String printJobName,
+            @Nullable PrintAttributes attributes) {
+        // Initiate printing as if coming from the app.
+        getInstrumentation()
+                .runOnMainSync(() -> getPrintManager(getActivity()).print(printJobName, adapter,
+                        attributes));
+    }
+
+    protected void onCancelOperationCalled() {
+        mCancelOperationCounter.call();
+    }
+
+    public void onStartCalled() {
+        mStartCallCounter.call();
+    }
+
+    protected void onLayoutCalled() {
+        mLayoutCallCounter.call();
+    }
+
+    protected int getWriteCallCount() {
+        return mWriteCallCounter.getCallCount();
+    }
+
+    protected void onWriteCalled() {
+        mWriteCallCounter.call();
+    }
+
+    protected void onWriteCancelCalled() {
+        mWriteCancelCallCounter.call();
+    }
+
+    protected void onFinishCalled() {
+        mFinishCallCounter.call();
+    }
+
+    protected void onPrintJobQueuedCalled() {
+        mPrintJobQueuedCallCounter.call();
+    }
+
+    protected void onPrinterDiscoverySessionCreateCalled() {
+        mCreateSessionCallCounter.call();
+    }
+
+    protected void onPrinterDiscoverySessionDestroyCalled() {
+        mDestroySessionCallCounter.call();
+    }
+
+    protected void waitForCancelOperationCallbackCalled() {
+        waitForCallbackCallCount(mCancelOperationCounter, 1,
+                "Did not get expected call to onCancel for the current operation.");
+    }
+
+    protected void waitForPrinterDiscoverySessionCreateCallbackCalled() {
+        waitForCallbackCallCount(mCreateSessionCallCounter, 1,
+                "Did not get expected call to onCreatePrinterDiscoverySession.");
+    }
+
+    public void waitForPrinterDiscoverySessionDestroyCallbackCalled(int count) {
+        waitForCallbackCallCount(mDestroySessionCallCounter, count,
+                "Did not get expected call to onDestroyPrinterDiscoverySession.");
+    }
+
+    protected void waitForServiceOnPrintJobQueuedCallbackCalled(int count) {
+        waitForCallbackCallCount(mPrintJobQueuedCallCounter, count,
+                "Did not get expected call to onPrintJobQueued.");
+    }
+
+    protected void waitForAdapterStartCallbackCalled() {
+        waitForCallbackCallCount(mStartCallCounter, 1,
+                "Did not get expected call to start.");
+    }
+
+    protected void waitForAdapterFinishCallbackCalled() {
+        waitForCallbackCallCount(mFinishCallCounter, 1,
+                "Did not get expected call to finish.");
+    }
+
+    protected void waitForLayoutAdapterCallbackCount(int count) {
+        waitForCallbackCallCount(mLayoutCallCounter, count,
+                "Did not get expected call to layout.");
+    }
+
+    public void waitForWriteAdapterCallback(int count) {
+        waitForCallbackCallCount(mWriteCallCounter, count, "Did not get expected call to write.");
+    }
+
+    protected void waitForWriteCancelCallback(int count) {
+        waitForCallbackCallCount(mWriteCancelCallCounter, count,
+                "Did not get expected cancel of write.");
+    }
+
+    private static void waitForCallbackCallCount(CallCounter counter, int count, String message) {
+        try {
+            counter.waitForCount(count, OPERATION_TIMEOUT_MILLIS);
+        } catch (TimeoutException te) {
+            fail(message);
+        }
+    }
+
+    /**
+     * Indicate the print activity was created.
+     */
+    static void onActivityCreateCalled(int testId, PrintDocumentActivity activity) {
+        synchronized (sIdToTest) {
+            BasePrintTest test = sIdToTest.get(testId);
+            if (test != null) {
+                test.mActivity = activity;
+                test.mCreateActivityCallCounter.call();
+            }
+        }
+    }
+
+    /**
+     * Indicate the print activity was destroyed.
+     */
+    static void onActivityDestroyCalled(int testId) {
+        synchronized (sIdToTest) {
+            BasePrintTest test = sIdToTest.get(testId);
+            if (test != null) {
+                test.mDestroyActivityCallCounter.call();
+            }
+        }
+    }
+
+    private void finishActivity() {
+        Activity activity = mActivity;
+
+        if (activity != null) {
+            if (!activity.isFinishing()) {
+                activity.finish();
+            }
+
+            while (!activity.isDestroyed()) {
+                int creates = mCreateActivityCallCounter.getCallCount();
+                waitForCallbackCallCount(mDestroyActivityCallCounter, creates,
+                        "Activity was not destroyed");
+            }
+        }
+    }
+
+    /**
+     * Get the number of ties the print activity was destroyed.
+     *
+     * @return The number of onDestroy calls on the print activity.
+     */
+    protected int getActivityDestroyCallbackCallCount() {
+        return mDestroyActivityCallCounter.getCallCount();
+    }
+
+    /**
+     * Get the number of ties the print activity was created.
+     *
+     * @return The number of onCreate calls on the print activity.
+     */
+    private int getActivityCreateCallbackCallCount() {
+        return mCreateActivityCallCounter.getCallCount();
+    }
+
+    /**
+     * Wait until create was called {@code count} times.
+     *
+     * @param count The number of create calls to expect.
+     */
+    private void waitForActivityCreateCallbackCalled(int count) {
+        waitForCallbackCallCount(mCreateActivityCallCounter, count,
+                "Did not get expected call to create.");
+    }
+
+    /**
+     * Reset all counters.
+     */
+    public void resetCounters() {
+        mCancelOperationCounter.reset();
+        mLayoutCallCounter.reset();
+        mWriteCallCounter.reset();
+        mWriteCancelCallCounter.reset();
+        mStartCallCounter.reset();
+        mFinishCallCounter.reset();
+        mPrintJobQueuedCallCounter.reset();
+        mCreateSessionCallCounter.reset();
+        mDestroySessionCallCounter.reset();
+        mDestroyActivityCallCounter.reset();
+        mCreateActivityCallCounter.reset();
+    }
+
+    /**
+     * Wait until the message is shown that indicates that a printer is unavailable.
+     *
+     * @throws Exception If anything was unexpected.
+     */
+    protected void waitForPrinterUnavailable() throws Exception {
+        final String printerUnavailableMessage =
+                getPrintSpoolerString("print_error_printer_unavailable");
+
+        UiObject message = getUiDevice().findObject(new UiSelector().resourceId(
+                "com.android.printspooler:id/message"));
+        if (!message.getText().equals(printerUnavailableMessage)) {
+            throw new Exception("Wrong message: " + message.getText() + " instead of "
+                    + printerUnavailableMessage);
+        }
+    }
+
+    protected void selectPrinter(String printerName) throws UiObjectNotFoundException, IOException {
+        try {
+            long delay = 1;
+            while (true) {
+                try {
+                    UiDevice uiDevice = getUiDevice();
+                    UiObject destinationSpinner = uiDevice.findObject(new UiSelector()
+                            .resourceId("com.android.printspooler:id/destination_spinner"));
+
+                    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);
+                }
+
+                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) {
+            dumpWindowHierarchy();
+            throw e;
+        }
+    }
+
+    protected void answerPrintServicesWarning(boolean confirm) throws UiObjectNotFoundException {
+        UiObject button;
+        if (confirm) {
+            button = getUiDevice().findObject(new UiSelector().resourceId("android:id/button1"));
+        } else {
+            button = getUiDevice().findObject(new UiSelector().resourceId("android:id/button2"));
+        }
+        button.click();
+    }
+
+    protected void changeOrientation(String orientation) throws UiObjectNotFoundException,
+            IOException {
+        try {
+            UiDevice uiDevice = getUiDevice();
+            UiObject orientationSpinner = uiDevice.findObject(new UiSelector().resourceId(
+                    "com.android.printspooler:id/orientation_spinner"));
+            orientationSpinner.click();
+            UiObject orientationOption = uiDevice.findObject(new UiSelector().text(orientation));
+            orientationOption.click();
+        } catch (UiObjectNotFoundException e) {
+            dumpWindowHierarchy();
+            throw e;
+        }
+    }
+
+    public String getOrientation() throws UiObjectNotFoundException, IOException {
+        try {
+            UiObject orientationSpinner = getUiDevice().findObject(new UiSelector().resourceId(
+                    "com.android.printspooler:id/orientation_spinner"));
+            return orientationSpinner.getText();
+        } catch (UiObjectNotFoundException e) {
+            dumpWindowHierarchy();
+            throw e;
+        }
+    }
+
+    protected void changeMediaSize(String mediaSize) throws UiObjectNotFoundException, IOException {
+        try {
+            UiDevice uiDevice = getUiDevice();
+            UiObject mediaSizeSpinner = uiDevice.findObject(new UiSelector().resourceId(
+                    "com.android.printspooler:id/paper_size_spinner"));
+            mediaSizeSpinner.click();
+            UiObject mediaSizeOption = uiDevice.findObject(new UiSelector().text(mediaSize));
+            mediaSizeOption.click();
+        } catch (UiObjectNotFoundException e) {
+            dumpWindowHierarchy();
+            throw e;
+        }
+    }
+
+    protected void changeColor(String color) throws UiObjectNotFoundException, IOException {
+        try {
+            UiDevice uiDevice = getUiDevice();
+            UiObject colorSpinner = uiDevice.findObject(new UiSelector().resourceId(
+                    "com.android.printspooler:id/color_spinner"));
+            colorSpinner.click();
+            UiObject colorOption = uiDevice.findObject(new UiSelector().text(color));
+            colorOption.click();
+        } catch (UiObjectNotFoundException e) {
+            dumpWindowHierarchy();
+            throw e;
+        }
+    }
+
+    public String getColor() throws UiObjectNotFoundException, IOException {
+        try {
+            UiObject colorSpinner = getUiDevice().findObject(new UiSelector().resourceId(
+                    "com.android.printspooler:id/color_spinner"));
+            return colorSpinner.getText();
+        } catch (UiObjectNotFoundException e) {
+            dumpWindowHierarchy();
+            throw e;
+        }
+    }
+
+    protected void changeDuplex(String duplex) throws UiObjectNotFoundException, IOException {
+        try {
+            UiDevice uiDevice = getUiDevice();
+            UiObject duplexSpinner = uiDevice.findObject(new UiSelector().resourceId(
+                    "com.android.printspooler:id/duplex_spinner"));
+            duplexSpinner.click();
+            UiObject duplexOption = uiDevice.findObject(new UiSelector().text(duplex));
+            duplexOption.click();
+        } catch (UiObjectNotFoundException e) {
+            dumpWindowHierarchy();
+            throw e;
+        }
+    }
+
+    protected void changeCopies(int newCopies) throws UiObjectNotFoundException, IOException {
+        try {
+            UiObject copies = getUiDevice().findObject(new UiSelector().resourceId(
+                    "com.android.printspooler:id/copies_edittext"));
+            copies.setText(Integer.valueOf(newCopies).toString());
+        } catch (UiObjectNotFoundException e) {
+            dumpWindowHierarchy();
+            throw e;
+        }
+    }
+
+    protected String getCopies() throws UiObjectNotFoundException, IOException {
+        try {
+            UiObject copies = getUiDevice().findObject(new UiSelector().resourceId(
+                    "com.android.printspooler:id/copies_edittext"));
+            return copies.getText();
+        } catch (UiObjectNotFoundException e) {
+            dumpWindowHierarchy();
+            throw e;
+        }
+    }
+
+    protected void assertNoPrintButton() throws UiObjectNotFoundException, IOException {
+        assertFalse(getUiDevice().hasObject(By.res("com.android.printspooler:id/print_button")));
+    }
+
+    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) {
+            dumpWindowHierarchy();
+            throw e;
+        }
+    }
+
+    protected void clickRetryButton() throws UiObjectNotFoundException, IOException {
+        try {
+            UiObject retryButton = getUiDevice().findObject(new UiSelector().resourceId(
+                    "com.android.printspooler:id/action_button"));
+            retryButton.click();
+        } catch (UiObjectNotFoundException e) {
+            dumpWindowHierarchy();
+            throw e;
+        }
+    }
+
+    public void dumpWindowHierarchy() throws IOException {
+        ByteArrayOutputStream os = new ByteArrayOutputStream();
+        getUiDevice().dumpWindowHierarchy(os);
+
+        Log.w(LOG_TAG, "Window hierarchy:");
+        for (String line : os.toString("UTF-8").split("\n")) {
+            Log.w(LOG_TAG, line);
+        }
+    }
+
+    protected PrintDocumentActivity getActivity() {
+        return mActivity;
+    }
+
+    protected void createActivity() throws IOException {
+        Log.d(LOG_TAG, "createActivity()");
+
+        int createBefore = getActivityCreateCallbackCallCount();
+
+        Intent intent = new Intent(Intent.ACTION_MAIN);
+        intent.putExtra(TEST_ID, mTestId);
+
+        Instrumentation instrumentation = getInstrumentation();
+
+        // Unlock screen.
+        SystemUtil.runShellCommand(instrumentation, "input keyevent KEYCODE_WAKEUP");
+
+        intent.setClassName(instrumentation.getTargetContext().getPackageName(),
+                PrintDocumentActivity.class.getName());
+        intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
+        instrumentation.startActivitySync(intent);
+
+        waitForActivityCreateCallbackCalled(createBefore + 1);
+    }
+
+    protected void openPrintOptions() throws UiObjectNotFoundException {
+        UiObject expandHandle = getUiDevice().findObject(new UiSelector().resourceId(
+                "com.android.printspooler:id/expand_collapse_handle"));
+        expandHandle.click();
+    }
+
+    protected void openCustomPrintOptions() throws UiObjectNotFoundException {
+        UiObject expandHandle = getUiDevice().findObject(new UiSelector().resourceId(
+                "com.android.printspooler:id/more_options_button"));
+        expandHandle.click();
+    }
+
+    protected static void clearPrintSpoolerData() throws Exception {
+        if (getInstrumentation().getContext().getPackageManager().hasSystemFeature(
+                  PackageManager.FEATURE_PRINTING)) {
+            assertTrue("failed to clear print spooler data",
+                    SystemUtil.runShellCommand(getInstrumentation(), String.format(
+                            "pm clear --user %d %s", CURRENT_USER_ID, PRINT_SPOOLER_PACKAGE_NAME))
+                            .contains(PM_CLEAR_SUCCESS_OUTPUT));
+        }
+    }
+
+    protected void verifyLayoutCall(InOrder inOrder, PrintDocumentAdapter mock,
+            PrintAttributes oldAttributes, PrintAttributes newAttributes,
+            final boolean forPreview) {
+        inOrder.verify(mock).onLayout(eq(oldAttributes), eq(newAttributes),
+                any(CancellationSignal.class), any(LayoutResultCallback.class), argThat(
+                        new BaseMatcher<Bundle>() {
+                            @Override
+                            public boolean matches(Object item) {
+                                Bundle bundle = (Bundle) item;
+                                return forPreview == bundle.getBoolean(
+                                        PrintDocumentAdapter.EXTRA_PRINT_PREVIEW);
+                            }
+
+                            @Override
+                            public void describeTo(Description description) {
+                                /* do nothing */
+                            }
+                        }));
+    }
+
+    protected PrintDocumentAdapter createMockPrintDocumentAdapter(Answer<Void> layoutAnswer,
+            Answer<Void> writeAnswer, Answer<Void> finishAnswer) {
+        // Create a mock print adapter.
+        PrintDocumentAdapter adapter = mock(PrintDocumentAdapter.class);
+        if (layoutAnswer != null) {
+            doAnswer(layoutAnswer).when(adapter).onLayout(any(PrintAttributes.class),
+                    any(PrintAttributes.class), any(CancellationSignal.class),
+                    any(LayoutResultCallback.class), any(Bundle.class));
+        }
+        if (writeAnswer != null) {
+            doAnswer(writeAnswer).when(adapter).onWrite(any(PageRange[].class),
+                    any(ParcelFileDescriptor.class), any(CancellationSignal.class),
+                    any(WriteResultCallback.class));
+        }
+        if (finishAnswer != null) {
+            doAnswer(finishAnswer).when(adapter).onFinish();
+        }
+        return adapter;
+    }
+
+    /**
+     * Create a mock {@link PrintDocumentAdapter} that provides one empty page.
+     *
+     * @return The mock adapter
+     */
+    public @NonNull PrintDocumentAdapter createDefaultPrintDocumentAdapter(int numPages) {
+        final PrintAttributes[] printAttributes = new PrintAttributes[1];
+
+        return createMockPrintDocumentAdapter(
+                invocation -> {
+                    PrintAttributes oldAttributes = (PrintAttributes) invocation.getArguments()[0];
+                    printAttributes[0] = (PrintAttributes) invocation.getArguments()[1];
+                    LayoutResultCallback callback =
+                            (LayoutResultCallback) invocation
+                                    .getArguments()[3];
+
+                    callback.onLayoutFinished(new PrintDocumentInfo.Builder(PRINT_JOB_NAME)
+                            .setPageCount(numPages).build(),
+                            !oldAttributes.equals(printAttributes[0]));
+
+                    oldAttributes = printAttributes[0];
+
+                    onLayoutCalled();
+                    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, pages[0].getStart(), pages[0].getEnd());
+                    fd.close();
+                    callback.onWriteFinished(pages);
+
+                    onWriteCalled();
+                    return null;
+                }, invocation -> {
+                    onFinishCalled();
+                    return null;
+                });
+    }
+
+    @SuppressWarnings("unchecked")
+    protected static PrinterDiscoverySessionCallbacks createMockPrinterDiscoverySessionCallbacks(
+            Answer<Void> onStartPrinterDiscovery, Answer<Void> onStopPrinterDiscovery,
+            Answer<Void> onValidatePrinters, Answer<Void> onStartPrinterStateTracking,
+            Answer<Void> onRequestCustomPrinterIcon, Answer<Void> onStopPrinterStateTracking,
+            Answer<Void> onDestroy) {
+        PrinterDiscoverySessionCallbacks callbacks = mock(PrinterDiscoverySessionCallbacks.class);
+
+        doCallRealMethod().when(callbacks).setSession(any(StubbablePrinterDiscoverySession.class));
+        when(callbacks.getSession()).thenCallRealMethod();
+
+        if (onStartPrinterDiscovery != null) {
+            doAnswer(onStartPrinterDiscovery).when(callbacks).onStartPrinterDiscovery(
+                    any(List.class));
+        }
+        if (onStopPrinterDiscovery != null) {
+            doAnswer(onStopPrinterDiscovery).when(callbacks).onStopPrinterDiscovery();
+        }
+        if (onValidatePrinters != null) {
+            doAnswer(onValidatePrinters).when(callbacks).onValidatePrinters(
+                    any(List.class));
+        }
+        if (onStartPrinterStateTracking != null) {
+            doAnswer(onStartPrinterStateTracking).when(callbacks).onStartPrinterStateTracking(
+                    any(PrinterId.class));
+        }
+        if (onRequestCustomPrinterIcon != null) {
+            doAnswer(onRequestCustomPrinterIcon).when(callbacks).onRequestCustomPrinterIcon(
+                    any(PrinterId.class), any(CancellationSignal.class),
+                    any(CustomPrinterIconCallback.class));
+        }
+        if (onStopPrinterStateTracking != null) {
+            doAnswer(onStopPrinterStateTracking).when(callbacks).onStopPrinterStateTracking(
+                    any(PrinterId.class));
+        }
+        if (onDestroy != null) {
+            doAnswer(onDestroy).when(callbacks).onDestroy();
+        }
+
+        return callbacks;
+    }
+
+    protected PrintServiceCallbacks createMockPrintServiceCallbacks(
+            Answer<PrinterDiscoverySessionCallbacks> onCreatePrinterDiscoverySessionCallbacks,
+            Answer<Void> onPrintJobQueued, Answer<Void> onRequestCancelPrintJob) {
+        final PrintServiceCallbacks service = mock(PrintServiceCallbacks.class);
+
+        doCallRealMethod().when(service).setService(any(StubbablePrintService.class));
+        when(service.getService()).thenCallRealMethod();
+
+        if (onCreatePrinterDiscoverySessionCallbacks != null) {
+            doAnswer(onCreatePrinterDiscoverySessionCallbacks).when(service)
+                    .onCreatePrinterDiscoverySessionCallbacks();
+        }
+        if (onPrintJobQueued != null) {
+            doAnswer(onPrintJobQueued).when(service).onPrintJobQueued(any(PrintJob.class));
+        }
+        if (onRequestCancelPrintJob != null) {
+            doAnswer(onRequestCancelPrintJob).when(service).onRequestCancelPrintJob(
+                    any(PrintJob.class));
+        }
+
+        return service;
+    }
+
+    protected void writeBlankPages(PrintAttributes constraints, ParcelFileDescriptor output,
+            int fromIndex, int toIndex) throws IOException {
+        PrintedPdfDocument document = new PrintedPdfDocument(getActivity(), constraints);
+        final int pageCount = toIndex - fromIndex + 1;
+        for (int i = 0; i < pageCount; i++) {
+            PdfDocument.Page page = document.startPage(i);
+            document.finishPage(page);
+        }
+        FileOutputStream fos = new FileOutputStream(output.getFileDescriptor());
+        document.writeTo(fos);
+        fos.flush();
+        document.close();
+    }
+
+    protected void selectPages(String pages, int totalPages) throws Exception {
+        UiObject pagesSpinner = getUiDevice().findObject(new UiSelector().resourceId(
+                "com.android.printspooler:id/range_options_spinner"));
+        pagesSpinner.click();
+
+        UiObject rangeOption = getUiDevice().findObject(new UiSelector().textContains(
+                getPrintSpoolerStringOneParam("template_page_range", totalPages)));
+        rangeOption.click();
+
+        UiObject pagesEditText = getUiDevice().findObject(new UiSelector().resourceId(
+                "com.android.printspooler:id/page_range_edittext"));
+        pagesEditText.setText(pages);
+
+        // Hide the keyboard.
+        getUiDevice().pressBack();
+    }
+
+    private static final class CallCounter {
+        private final Object mLock = new Object();
+
+        private int mCallCount;
+
+        public void call() {
+            synchronized (mLock) {
+                mCallCount++;
+                mLock.notifyAll();
+            }
+        }
+
+        int getCallCount() {
+            synchronized (mLock) {
+                return mCallCount;
+            }
+        }
+
+        public void reset() {
+            synchronized (mLock) {
+                mCallCount = 0;
+            }
+        }
+
+        public void waitForCount(int count, long timeoutMillis) throws TimeoutException {
+            synchronized (mLock) {
+                final long startTimeMillis = SystemClock.uptimeMillis();
+                while (mCallCount < count) {
+                    try {
+                        final long elapsedTimeMillis = SystemClock.uptimeMillis() - startTimeMillis;
+                        final long remainingTimeMillis = timeoutMillis - elapsedTimeMillis;
+                        if (remainingTimeMillis <= 0) {
+                            throw new TimeoutException();
+                        }
+                        mLock.wait(timeoutMillis);
+                    } catch (InterruptedException ie) {
+                        /* ignore */
+                    }
+                }
+            }
+        }
+    }
+
+
+    /**
+     * Make {@code printerName} the default printer by adding it to the history of printers by
+     * printing once.
+     *
+     * @param adapter The {@link PrintDocumentAdapter} used
+     * @throws Exception If the printer could not be made default
+     */
+    public void makeDefaultPrinter(PrintDocumentAdapter adapter, String printerName)
+            throws Exception {
+        // Perform a full print operation on the printer
+        Log.d(LOG_TAG, "print");
+        print(adapter);
+        Log.d(LOG_TAG, "waitForWriteAdapterCallback");
+        waitForWriteAdapterCallback(1);
+        Log.d(LOG_TAG, "selectPrinter");
+        selectPrinter(printerName);
+        Log.d(LOG_TAG, "clickPrintButton");
+        clickPrintButton();
+        Log.d(LOG_TAG, "answerPrintServicesWarning");
+        answerPrintServicesWarning(true);
+        Log.d(LOG_TAG, "waitForPrinterDiscoverySessionDestroyCallbackCalled");
+        waitForPrinterDiscoverySessionDestroyCallbackCalled(1);
+
+        // Switch to new activity, which should now use the default printer
+        Log.d(LOG_TAG, "getActivity().finish()");
+        getActivity().finish();
+
+        createActivity();
+    }
+
+    /**
+     * Get a string array from the print spooler's resources.
+     *
+     * @param resourceName The name of the array resource
+     * @return The localized string array
+     *
+     * @throws Exception If anything is unexpected
+     */
+    protected String[] getPrintSpoolerStringArray(String resourceName) throws Exception {
+        PackageManager pm = getActivity().getPackageManager();
+        Resources printSpoolerRes = pm.getResourcesForApplication(PRINTSPOOLER_PACKAGE);
+        int id = printSpoolerRes.getIdentifier(resourceName, "array", PRINTSPOOLER_PACKAGE);
+        return printSpoolerRes.getStringArray(id);
+    }
+
+    /**
+     * Get a string from the print spooler's resources.
+     *
+     * @param resourceName The name of the string resource
+     * @return The localized string
+     *
+     * @throws Exception If anything is unexpected
+     */
+    private String getPrintSpoolerString(String resourceName) throws Exception {
+        PackageManager pm = getActivity().getPackageManager();
+        Resources printSpoolerRes = pm.getResourcesForApplication(PRINTSPOOLER_PACKAGE);
+        int id = printSpoolerRes.getIdentifier(resourceName, "string", PRINTSPOOLER_PACKAGE);
+        return printSpoolerRes.getString(id);
+    }
+
+    /**
+     * Get a string with one parameter from the print spooler's resources.
+     *
+     * @param resourceName The name of the string resource
+     * @return The localized string
+     *
+     * @throws Exception If anything is unexpected
+     */
+    protected String getPrintSpoolerStringOneParam(String resourceName, Object p)
+            throws Exception {
+        PackageManager pm = getActivity().getPackageManager();
+        Resources printSpoolerRes = pm.getResourcesForApplication(PRINTSPOOLER_PACKAGE);
+        int id = printSpoolerRes.getIdentifier(resourceName, "string", PRINTSPOOLER_PACKAGE);
+        return printSpoolerRes.getString(id, p);
+    }
+
+    /**
+     * Get the default media size for the current locale.
+     *
+     * @return The default media size for the current locale
+     *
+     * @throws Exception If anything is unexpected
+     */
+    protected PrintAttributes.MediaSize getDefaultMediaSize() throws Exception {
+        PackageManager pm = getActivity().getPackageManager();
+        Resources printSpoolerRes = pm.getResourcesForApplication(PRINTSPOOLER_PACKAGE);
+        int defaultMediaSizeResId = printSpoolerRes.getIdentifier("mediasize_default", "string",
+                PRINTSPOOLER_PACKAGE);
+        String defaultMediaSizeName = printSpoolerRes.getString(defaultMediaSizeResId);
+
+        switch (defaultMediaSizeName) {
+            case "NA_LETTER":
+                return PrintAttributes.MediaSize.NA_LETTER;
+            case "JIS_B5":
+                return PrintAttributes.MediaSize.JIS_B5;
+            case "ISO_A4":
+                return PrintAttributes.MediaSize.ISO_A4;
+            default:
+                throw new Exception("Unknown default media size " + defaultMediaSizeName);
+        }
+    }
+
+    /**
+     * Annotation used to signal that a test does not need an activity.
+     */
+    @Retention(RetentionPolicy.RUNTIME)
+    @Target(ElementType.METHOD)
+    protected @interface NoActivity { }
+
+    /**
+     * Rule that handles the {@link NoActivity} annotation.
+     */
+    private static class ShouldStartActivity implements TestRule {
+        boolean mNoActivity;
+
+        @Override
+        public Statement apply(Statement base, org.junit.runner.Description description) {
+            for (Annotation annotation : description.getAnnotations()) {
+                if (annotation instanceof NoActivity) {
+                    mNoActivity = true;
+                    break;
+                }
+            }
+
+            return base;
+        }
+    }
+}
diff --git a/tests/tests/print/printTestUtilLib/src/android/print/test/PrintDocumentActivity.java b/tests/tests/print/printTestUtilLib/src/android/print/test/PrintDocumentActivity.java
new file mode 100644
index 0000000..cfb644f
--- /dev/null
+++ b/tests/tests/print/printTestUtilLib/src/android/print/test/PrintDocumentActivity.java
@@ -0,0 +1,59 @@
+/*
+ * 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.print.test;
+
+import android.app.Activity;
+import android.app.KeyguardManager;
+import android.os.Bundle;
+import android.util.Log;
+import android.view.WindowManager;
+
+public class PrintDocumentActivity extends Activity {
+    private static final String LOG_TAG = "PrintDocumentActivity";
+    int mTestId;
+
+    @Override
+    protected void onCreate(Bundle savedInstanceState) {
+        super.onCreate(savedInstanceState);
+
+        mTestId = getIntent().getIntExtra(BasePrintTest.TEST_ID, -1);
+        Log.d(LOG_TAG, "onCreate() " + this + " for " + mTestId);
+
+        getWindow().addFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON
+                | WindowManager.LayoutParams.FLAG_TURN_SCREEN_ON);
+
+        KeyguardManager km = getSystemService(KeyguardManager.class);
+        if (km != null) {
+            km.requestDismissKeyguard(this, null);
+        }
+
+        BasePrintTest.onActivityCreateCalled(mTestId, this);
+
+        if (savedInstanceState != null) {
+            Log.d(LOG_TAG,
+                    "We cannot deal with lifecycle. Hence finishing " + this + " for " + mTestId);
+            finish();
+        }
+    }
+
+    @Override
+    protected void onDestroy() {
+        Log.d(LOG_TAG, "onDestroy() " + this);
+        BasePrintTest.onActivityDestroyCalled(mTestId);
+        super.onDestroy();
+    }
+}
diff --git a/tests/tests/print/printTestUtilLib/src/android/print/test/Utils.java b/tests/tests/print/printTestUtilLib/src/android/print/test/Utils.java
new file mode 100644
index 0000000..dbc6644
--- /dev/null
+++ b/tests/tests/print/printTestUtilLib/src/android/print/test/Utils.java
@@ -0,0 +1,148 @@
+/*
+ * 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.print.test;
+
+import android.content.Context;
+import android.os.Handler;
+import android.os.Looper;
+import android.print.PrintJob;
+import android.print.PrintManager;
+import android.support.annotation.NonNull;
+import android.util.Log;
+
+/**
+ * Utilities for print tests
+ */
+public class Utils {
+    private static final String LOG_TAG = "Utils";
+
+    /**
+     * A {@link Runnable} that can throw an {@link Throwable}.
+     */
+    public interface Invokable {
+        void run() throws Throwable;
+    }
+
+    /**
+     * Run a {@link Invokable} and expect and {@link Throwable} of a certain type.
+     *
+     * @param r             The {@link Invokable} to run
+     * @param expectedClass The expected {@link Throwable} type
+     */
+    public static void assertException(@NonNull Invokable r,
+            @NonNull Class<? extends Throwable> expectedClass) throws Throwable {
+        try {
+            r.run();
+        } catch (Throwable e) {
+            if (e.getClass().isAssignableFrom(expectedClass)) {
+                return;
+            } else {
+                Log.e(LOG_TAG, "Expected: " + expectedClass.getName() + ", got: "
+                        + e.getClass().getName());
+                throw e;
+            }
+        }
+
+        throw new AssertionError("No throwable thrown");
+    }
+
+    /**
+     * Run a {@link Invokable} on the main thread and forward the {@link Throwable} if one was
+     * thrown.
+     *
+     * @param r The {@link Invokable} to run
+     *
+     * @throws Throwable If the {@link Runnable} caused an issue
+     */
+    public static void runOnMainThread(@NonNull final Invokable r) throws Throwable {
+        final Object synchronizer = new Object();
+        final Throwable[] thrown = new Throwable[1];
+
+        synchronized (synchronizer) {
+            (new Handler(Looper.getMainLooper())).post(() -> {
+                synchronized (synchronizer) {
+                    try {
+                        r.run();
+                    } catch (Throwable t) {
+                        thrown[0] = t;
+                    }
+
+                    synchronizer.notify();
+                }
+            });
+
+            synchronizer.wait();
+        }
+
+        if (thrown[0] != null) {
+            throw thrown[0];
+        }
+    }
+
+    /**
+     * 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 {
+        long start = System.currentTimeMillis();
+
+        while (true) {
+            try {
+                r.run();
+                break;
+            } catch (Throwable e) {
+                if (System.currentTimeMillis() - start < BasePrintTest.OPERATION_TIMEOUT_MILLIS) {
+                    Log.e(LOG_TAG, "Ignoring exception", e);
+
+                    try {
+                        Thread.sleep(500);
+                    } catch (InterruptedException e1) {
+                        Log.e(LOG_TAG, "Interrupted", e);
+                    }
+                } else {
+                    throw e;
+                }
+            }
+        }
+    }
+
+    /**
+     * @param name Name of print job
+     *
+     * @return The print job for the name
+     *
+     * @throws Exception If print job could not be found
+     */
+    public static @NonNull PrintJob getPrintJob(@NonNull PrintManager pm, @NonNull String name)
+            throws Exception {
+        for (android.print.PrintJob job : pm.getPrintJobs()) {
+            if (job.getInfo().getLabel().equals(name)) {
+                return job;
+            }
+        }
+
+        throw new Exception("Print job " + name + " not found in " + pm.getPrintJobs());
+    }
+
+    /**
+     * @return The print manager
+     */
+    public static @NonNull PrintManager getPrintManager(@NonNull Context context) {
+        return (PrintManager) context.getSystemService(Context.PRINT_SERVICE);
+    }
+}
diff --git a/tests/tests/print/printTestUtilLib/src/android/print/test/services/AddPrintersActivity.java b/tests/tests/print/printTestUtilLib/src/android/print/test/services/AddPrintersActivity.java
new file mode 100644
index 0000000..5e01d7c
--- /dev/null
+++ b/tests/tests/print/printTestUtilLib/src/android/print/test/services/AddPrintersActivity.java
@@ -0,0 +1,52 @@
+/*
+ * 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.print.test.services;
+
+import android.app.Activity;
+import android.os.Bundle;
+import android.support.annotation.NonNull;
+
+import java.util.ArrayList;
+
+public class AddPrintersActivity extends Activity {
+    private static final ArrayList<Runnable> sObservers = new ArrayList<>();
+
+    public static void addObserver(@NonNull Runnable observer) {
+        synchronized (sObservers) {
+            sObservers.add(observer);
+        }
+    }
+
+    @Override
+    protected void onCreate(Bundle savedInstanceState) {
+        super.onCreate(savedInstanceState);
+
+        synchronized (sObservers) {
+            for (Runnable sObserver : sObservers) {
+                sObserver.run();
+            }
+        }
+
+        finish();
+    }
+
+    public static void clearObservers() {
+        synchronized (sObservers) {
+            sObservers.clear();
+        }
+    }
+}
diff --git a/tests/tests/print/printTestUtilLib/src/android/print/test/services/CustomPrintOptionsActivity.java b/tests/tests/print/printTestUtilLib/src/android/print/test/services/CustomPrintOptionsActivity.java
new file mode 100644
index 0000000..94993c7
--- /dev/null
+++ b/tests/tests/print/printTestUtilLib/src/android/print/test/services/CustomPrintOptionsActivity.java
@@ -0,0 +1,76 @@
+/*
+ * 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.print.test.services;
+
+import android.app.Activity;
+import android.content.Intent;
+import android.os.Bundle;
+import android.print.PrintJobInfo;
+import android.print.PrinterInfo;
+import android.printservice.PrintService;
+
+/**
+ * Custom print options activity for both print services
+ */
+public class CustomPrintOptionsActivity extends Activity {
+    /** Lock for {@link #sCallback} */
+    private static final Object sLock = new Object();
+
+    /** Currently registered callback for _both_ first and second print service. */
+    private static CustomPrintOptionsCallback sCallback = null;
+
+    /**
+     * Set a new callback called when the custom options activity is launched.
+     *
+     * @param callback The new callback or null, if the callback should be unregistered.
+     */
+    public static void setCallBack(CustomPrintOptionsCallback callback) {
+        synchronized (sLock) {
+            sCallback = callback;
+        }
+    }
+
+    /**
+     * Callback executed for this activity. Set via {@link #setCallBack}.
+     */
+    public interface CustomPrintOptionsCallback {
+        PrintJobInfo executeCustomPrintOptionsActivity(PrintJobInfo printJob,
+                PrinterInfo printer);
+    }
+
+    @Override
+    protected void onCreate(Bundle savedInstanceState) {
+        super.onCreate(savedInstanceState);
+
+        Intent result = new Intent();
+
+        synchronized (sLock) {
+            if (sCallback != null) {
+                PrintJobInfo printJobInfo = getIntent().getParcelableExtra(
+                        PrintService.EXTRA_PRINT_JOB_INFO);
+                PrinterInfo printerInfo = getIntent().getParcelableExtra(
+                        "android.intent.extra.print.EXTRA_PRINTER_INFO");
+
+                result.putExtra(PrintService.EXTRA_PRINT_JOB_INFO,
+                        sCallback.executeCustomPrintOptionsActivity(printJobInfo, printerInfo));
+            }
+        }
+
+        setResult(Activity.RESULT_OK, result);
+        finish();
+    }
+}
diff --git a/tests/tests/print/printTestUtilLib/src/android/print/test/services/FirstPrintService.java b/tests/tests/print/printTestUtilLib/src/android/print/test/services/FirstPrintService.java
new file mode 100644
index 0000000..2c3a4ee
--- /dev/null
+++ b/tests/tests/print/printTestUtilLib/src/android/print/test/services/FirstPrintService.java
@@ -0,0 +1,40 @@
+/*
+ * 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.print.test.services;
+
+public class FirstPrintService extends StubbablePrintService {
+
+    private static final Object sLock = new Object();
+
+    private static PrintServiceCallbacks sCallbacks;
+
+    public static void setCallbacks(PrintServiceCallbacks callbacks) {
+        synchronized (sLock) {
+            sCallbacks = callbacks;
+        }
+    }
+
+    @Override
+    protected PrintServiceCallbacks getCallbacks() {
+        synchronized (sLock) {
+            if (sCallbacks != null) {
+                sCallbacks.setService(this);
+            }
+            return sCallbacks;
+        }
+    }
+}
diff --git a/tests/tests/print/printTestUtilLib/src/android/print/test/services/InfoActivity.java b/tests/tests/print/printTestUtilLib/src/android/print/test/services/InfoActivity.java
new file mode 100644
index 0000000..627fec9
--- /dev/null
+++ b/tests/tests/print/printTestUtilLib/src/android/print/test/services/InfoActivity.java
@@ -0,0 +1,55 @@
+/*
+ * 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.print.test.services;
+
+import android.app.Activity;
+import android.os.Bundle;
+
+import java.util.ArrayList;
+
+public class InfoActivity extends Activity {
+    public interface Observer {
+        void onCreate(Activity createdActivity);
+    }
+
+    private static final ArrayList<Observer> sObservers = new ArrayList<>();
+
+    public static void addObserver(Observer observer) {
+        synchronized (sObservers) {
+            sObservers.add(observer);
+        }
+    }
+
+    public static void clearObservers() {
+        synchronized (sObservers) {
+            sObservers.clear();
+        }
+    }
+
+    @Override
+    protected void onCreate(Bundle savedInstanceState) {
+        super.onCreate(savedInstanceState);
+
+        synchronized (sObservers) {
+            ArrayList<Observer> observers = (ArrayList<Observer>) sObservers.clone();
+
+            for (Observer observer : observers) {
+                observer.onCreate(this);
+            }
+        }
+    }
+}
diff --git a/tests/tests/print/printTestUtilLib/src/android/print/test/services/PrintServiceCallbacks.java b/tests/tests/print/printTestUtilLib/src/android/print/test/services/PrintServiceCallbacks.java
new file mode 100644
index 0000000..d2d42f2
--- /dev/null
+++ b/tests/tests/print/printTestUtilLib/src/android/print/test/services/PrintServiceCallbacks.java
@@ -0,0 +1,38 @@
+/*
+ * 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.print.test.services;
+
+import android.printservice.PrintJob;
+
+public abstract class PrintServiceCallbacks {
+
+    private StubbablePrintService mService;
+
+    public StubbablePrintService getService() {
+        return mService;
+    }
+
+    public void setService(StubbablePrintService service) {
+        mService = service;
+    }
+
+    public abstract PrinterDiscoverySessionCallbacks onCreatePrinterDiscoverySessionCallbacks();
+
+    public abstract void onRequestCancelPrintJob(PrintJob printJob);
+
+    public abstract void onPrintJobQueued(PrintJob printJob);
+}
diff --git a/tests/tests/print/printTestUtilLib/src/android/print/test/services/PrinterDiscoverySessionCallbacks.java b/tests/tests/print/printTestUtilLib/src/android/print/test/services/PrinterDiscoverySessionCallbacks.java
new file mode 100644
index 0000000..7b7a1b9
--- /dev/null
+++ b/tests/tests/print/printTestUtilLib/src/android/print/test/services/PrinterDiscoverySessionCallbacks.java
@@ -0,0 +1,51 @@
+/*
+ * 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.print.test.services;
+
+import android.os.CancellationSignal;
+import android.print.PrinterId;
+import android.printservice.CustomPrinterIconCallback;
+
+import java.util.List;
+
+public abstract class PrinterDiscoverySessionCallbacks {
+
+    private StubbablePrinterDiscoverySession mSession;
+
+    public void setSession(StubbablePrinterDiscoverySession session) {
+        mSession = session;
+    }
+
+    public StubbablePrinterDiscoverySession getSession() {
+        return mSession;
+    }
+
+    public abstract void onStartPrinterDiscovery(List<PrinterId> priorityList);
+
+    public abstract void onStopPrinterDiscovery();
+
+    public abstract void onValidatePrinters(List<PrinterId> printerIds);
+
+    public abstract void onStartPrinterStateTracking(PrinterId printerId);
+
+    public abstract void onRequestCustomPrinterIcon(PrinterId printerId,
+            CancellationSignal cancellationSignal, CustomPrinterIconCallback callback);
+
+    public abstract void onStopPrinterStateTracking(PrinterId printerId);
+
+    public abstract void onDestroy();
+}
diff --git a/tests/tests/print/printTestUtilLib/src/android/print/test/services/SecondPrintService.java b/tests/tests/print/printTestUtilLib/src/android/print/test/services/SecondPrintService.java
new file mode 100644
index 0000000..3e8a6a7
--- /dev/null
+++ b/tests/tests/print/printTestUtilLib/src/android/print/test/services/SecondPrintService.java
@@ -0,0 +1,40 @@
+/*
+ * 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.print.test.services;
+
+public class SecondPrintService extends StubbablePrintService {
+
+    private static final Object sLock = new Object();
+
+    private static PrintServiceCallbacks sCallbacks;
+
+    public static void setCallbacks(PrintServiceCallbacks callbacks) {
+        synchronized (sLock) {
+            sCallbacks = callbacks;
+        }
+    }
+
+    @Override
+    protected PrintServiceCallbacks getCallbacks() {
+        synchronized (sLock) {
+            if (sCallbacks != null) {
+                sCallbacks.setService(this);
+            }
+            return sCallbacks;
+        }
+    }
+}
diff --git a/tests/tests/print/printTestUtilLib/src/android/print/test/services/SettingsActivity.java b/tests/tests/print/printTestUtilLib/src/android/print/test/services/SettingsActivity.java
new file mode 100644
index 0000000..56c63fa
--- /dev/null
+++ b/tests/tests/print/printTestUtilLib/src/android/print/test/services/SettingsActivity.java
@@ -0,0 +1,28 @@
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.print.test.services;
+
+import android.app.Activity;
+import android.os.Bundle;
+
+public class SettingsActivity extends Activity {
+
+    @Override
+    protected void onCreate(Bundle savedInstanceState) {
+        super.onCreate(savedInstanceState);
+    }
+}
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
new file mode 100644
index 0000000..09d1f78
--- /dev/null
+++ b/tests/tests/print/printTestUtilLib/src/android/print/test/services/StubbablePrintService.java
@@ -0,0 +1,64 @@
+/*
+ * 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.print.test.services;
+
+import android.content.Context;
+import android.printservice.PrintJob;
+import android.printservice.PrintService;
+import android.printservice.PrinterDiscoverySession;
+
+import java.util.List;
+
+public abstract class StubbablePrintService extends PrintService {
+
+    @Override
+    public PrinterDiscoverySession onCreatePrinterDiscoverySession() {
+        PrintServiceCallbacks callbacks = getCallbacks();
+        if (callbacks != null) {
+            return new StubbablePrinterDiscoverySession(this,
+                    getCallbacks().onCreatePrinterDiscoverySessionCallbacks());
+        }
+        return null;
+    }
+
+    @Override
+    public void onRequestCancelPrintJob(PrintJob printJob) {
+        PrintServiceCallbacks callbacks = getCallbacks();
+        if (callbacks != null) {
+            callbacks.onRequestCancelPrintJob(printJob);
+        }
+    }
+
+    @Override
+    public void onPrintJobQueued(PrintJob printJob) {
+        PrintServiceCallbacks callbacks = getCallbacks();
+        if (callbacks != null) {
+            callbacks.onPrintJobQueued(printJob);
+        }
+    }
+
+    protected abstract PrintServiceCallbacks getCallbacks();
+
+    public void callAttachBaseContext(Context base) {
+        attachBaseContext(base);
+    }
+
+    public List<PrintJob> callGetActivePrintJobs() {
+        return getActivePrintJobs();
+    }
+
+}
diff --git a/tests/tests/print/printTestUtilLib/src/android/print/test/services/StubbablePrinterDiscoverySession.java b/tests/tests/print/printTestUtilLib/src/android/print/test/services/StubbablePrinterDiscoverySession.java
new file mode 100644
index 0000000..c9fb015
--- /dev/null
+++ b/tests/tests/print/printTestUtilLib/src/android/print/test/services/StubbablePrinterDiscoverySession.java
@@ -0,0 +1,95 @@
+/*
+ * 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.print.test.services;
+
+import android.os.CancellationSignal;
+import android.print.PrinterId;
+import android.printservice.CustomPrinterIconCallback;
+import android.printservice.PrintService;
+import android.printservice.PrinterDiscoverySession;
+import android.support.annotation.NonNull;
+
+import java.util.List;
+
+public class StubbablePrinterDiscoverySession extends PrinterDiscoverySession {
+    private final PrintService mService;
+    private final PrinterDiscoverySessionCallbacks mCallbacks;
+
+    public StubbablePrinterDiscoverySession(PrintService service,
+            PrinterDiscoverySessionCallbacks callbacks) {
+        mService = service;
+        mCallbacks = callbacks;
+        if (mCallbacks != null) {
+            mCallbacks.setSession(this);
+        }
+    }
+
+    public PrintService getService() {
+        return mService;
+    }
+
+    @Override
+    public void onStartPrinterDiscovery(@NonNull List<PrinterId> priorityList) {
+        if (mCallbacks != null) {
+            mCallbacks.onStartPrinterDiscovery(priorityList);
+        }
+    }
+
+    @Override
+    public void onStopPrinterDiscovery() {
+        if (mCallbacks != null) {
+            mCallbacks.onStopPrinterDiscovery();
+        }
+    }
+
+    @Override
+    public void onValidatePrinters(@NonNull List<PrinterId> printerIds) {
+        if (mCallbacks != null) {
+            mCallbacks.onValidatePrinters(printerIds);
+        }
+    }
+
+    @Override
+    public void onStartPrinterStateTracking(@NonNull PrinterId printerId) {
+        if (mCallbacks != null) {
+            mCallbacks.onStartPrinterStateTracking(printerId);
+        }
+    }
+
+    @Override
+    public void onRequestCustomPrinterIcon(@NonNull PrinterId printerId,
+            @NonNull CancellationSignal cancellationSignal,
+            @NonNull CustomPrinterIconCallback callback) {
+        if (mCallbacks != null) {
+            mCallbacks.onRequestCustomPrinterIcon(printerId, cancellationSignal, callback);
+        }
+    }
+
+    @Override
+    public void onStopPrinterStateTracking(@NonNull PrinterId printerId) {
+        if (mCallbacks != null) {
+            mCallbacks.onStopPrinterStateTracking(printerId);
+        }
+    }
+
+    @Override
+    public void onDestroy() {
+        if (mCallbacks != null) {
+            mCallbacks.onDestroy();
+        }
+    }
+}
diff --git a/tests/tests/print/res/values/themes.xml b/tests/tests/print/res/values/themes.xml
index f5c9b977..07e228c 100644
--- a/tests/tests/print/res/values/themes.xml
+++ b/tests/tests/print/res/values/themes.xml
@@ -17,7 +17,12 @@
 -->
 
 <resources>
-    <style name="Theme.Translucent" parent="@android:style/Theme.DeviceDefault">
+    <style name="NoAnimationTranslucent" parent="@android:style/Theme.DeviceDefault">
         <item name="android:windowIsTranslucent">true</item>
+        <item name="android:windowAnimationStyle">@null</item>
+    </style>
+
+    <style name="NoAnimation" parent="@android:style/Theme.DeviceDefault">
+        <item name="android:windowAnimationStyle">@null</item>
     </style>
 </resources>
diff --git a/tests/tests/print/res/xml/printservice.xml b/tests/tests/print/res/xml/printservice.xml
index 78c7504..07f7d6c 100644
--- a/tests/tests/print/res/xml/printservice.xml
+++ b/tests/tests/print/res/xml/printservice.xml
@@ -17,6 +17,6 @@
 -->
 
 <print-service xmlns:android="http://schemas.android.com/apk/res/android"
-     android:settingsActivity="android.print.cts.services.SettingsActivity"
-     android:addPrintersActivity="android.print.cts.services.AddPrintersActivity"
-     android:advancedPrintOptionsActivity="android.print.cts.services.CustomPrintOptionsActivity"/>
+     android:settingsActivity="android.print.test.services.SettingsActivity"
+     android:addPrintersActivity="android.print.test.services.AddPrintersActivity"
+     android:advancedPrintOptionsActivity="android.print.test.services.CustomPrintOptionsActivity"/>
diff --git a/tests/tests/print/src/android/print/cts/BasePrintTest.java b/tests/tests/print/src/android/print/cts/BasePrintTest.java
deleted file mode 100644
index db1efd7..0000000
--- a/tests/tests/print/src/android/print/cts/BasePrintTest.java
+++ /dev/null
@@ -1,1105 +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.print.cts;
-
-import static android.content.pm.PackageManager.GET_META_DATA;
-import static android.content.pm.PackageManager.GET_SERVICES;
-import static android.print.cts.Utils.getPrintManager;
-
-import static org.junit.Assert.assertFalse;
-import static org.junit.Assert.assertTrue;
-import static org.junit.Assert.fail;
-import static org.junit.Assume.assumeTrue;
-import static org.mockito.Matchers.any;
-import static org.mockito.Matchers.eq;
-import static org.mockito.Mockito.doAnswer;
-import static org.mockito.Mockito.doCallRealMethod;
-import static org.mockito.Mockito.mock;
-import static org.mockito.Mockito.when;
-import static org.mockito.hamcrest.MockitoHamcrest.argThat;
-
-import android.app.Activity;
-import android.app.Instrumentation;
-import android.content.ComponentName;
-import android.content.Intent;
-import android.content.pm.PackageManager;
-import android.content.pm.ResolveInfo;
-import android.content.res.Resources;
-import android.graphics.pdf.PdfDocument;
-import android.os.Bundle;
-import android.os.CancellationSignal;
-import android.os.ParcelFileDescriptor;
-import android.os.SystemClock;
-import android.print.PageRange;
-import android.print.PrintAttributes;
-import android.print.PrintDocumentAdapter;
-import android.print.PrintDocumentAdapter.LayoutResultCallback;
-import android.print.PrintDocumentAdapter.WriteResultCallback;
-import android.print.PrintDocumentInfo;
-import android.print.PrinterId;
-import android.print.cts.services.PrintServiceCallbacks;
-import android.print.cts.services.PrinterDiscoverySessionCallbacks;
-import android.print.cts.services.StubbablePrintService;
-import android.print.cts.services.StubbablePrinterDiscoverySession;
-import android.print.pdf.PrintedPdfDocument;
-import android.printservice.CustomPrinterIconCallback;
-import android.printservice.PrintJob;
-import android.provider.Settings;
-import android.support.annotation.NonNull;
-import android.support.annotation.Nullable;
-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.UiSelector;
-import android.util.Log;
-import android.util.SparseArray;
-
-import com.android.compatibility.common.util.SystemUtil;
-
-import org.hamcrest.BaseMatcher;
-import org.hamcrest.Description;
-import org.junit.After;
-import org.junit.AfterClass;
-import org.junit.Before;
-import org.junit.BeforeClass;
-import org.junit.Rule;
-import org.junit.rules.TestRule;
-import org.junit.runners.model.Statement;
-import org.mockito.InOrder;
-import org.mockito.stubbing.Answer;
-
-import java.io.BufferedReader;
-import java.io.ByteArrayOutputStream;
-import java.io.FileInputStream;
-import java.io.FileOutputStream;
-import java.io.IOException;
-import java.io.InputStreamReader;
-import java.lang.annotation.Annotation;
-import java.lang.annotation.ElementType;
-import java.lang.annotation.Retention;
-import java.lang.annotation.RetentionPolicy;
-import java.lang.annotation.Target;
-import java.util.ArrayList;
-import java.util.List;
-import java.util.concurrent.TimeoutException;
-import java.util.concurrent.atomic.AtomicInteger;
-
-/**
- * This is the base class for print tests.
- */
-public abstract class BasePrintTest {
-    private final static String LOG_TAG = "BasePrintTest";
-
-    static final long OPERATION_TIMEOUT_MILLIS = 60000;
-    static final String PRINT_JOB_NAME = "Test";
-    static final String TEST_ID = "BasePrintTest.EXTRA_TEST_ID";
-
-    private static final String PRINT_SPOOLER_PACKAGE_NAME = "com.android.printspooler";
-    private static final String PM_CLEAR_SUCCESS_OUTPUT = "Success";
-    private static final String COMMAND_LIST_ENABLED_IME_COMPONENTS = "ime list -s";
-    private static final String COMMAND_PREFIX_ENABLE_IME = "ime enable ";
-    private static final String COMMAND_PREFIX_DISABLE_IME = "ime disable ";
-    private static final int CURRENT_USER_ID = -2; // Mirrors UserHandle.USER_CURRENT
-    private static final String PRINTSPOOLER_PACKAGE = "com.android.printspooler";
-
-    private static final AtomicInteger sLastTestID = new AtomicInteger();
-    private int mTestId;
-    private PrintDocumentActivity mActivity;
-
-    private static String sDisabledPrintServicesBefore;
-
-    private static final SparseArray<BasePrintTest> sIdToTest = new SparseArray<>();
-
-    public final @Rule ShouldStartActivity mShouldStartActivityRule = new ShouldStartActivity();
-
-    /**
-     * Return the UI device
-     *
-     * @return the UI device
-     */
-    public UiDevice getUiDevice() {
-        return UiDevice.getInstance(getInstrumentation());
-    }
-
-    private CallCounter mCancelOperationCounter;
-    private CallCounter mLayoutCallCounter;
-    private CallCounter mWriteCallCounter;
-    private CallCounter mWriteCancelCallCounter;
-    private CallCounter mFinishCallCounter;
-    private CallCounter mPrintJobQueuedCallCounter;
-    private CallCounter mCreateSessionCallCounter;
-    private CallCounter mDestroySessionCallCounter;
-    private CallCounter mDestroyActivityCallCounter = new CallCounter();
-    private CallCounter mCreateActivityCallCounter = new CallCounter();
-
-    private static String[] sEnabledImes;
-
-    private static String[] getEnabledImes() throws IOException {
-        List<String> imeList = new ArrayList<>();
-
-        ParcelFileDescriptor pfd = getInstrumentation().getUiAutomation()
-                .executeShellCommand(COMMAND_LIST_ENABLED_IME_COMPONENTS);
-        try (BufferedReader reader = new BufferedReader(
-                new InputStreamReader(new FileInputStream(pfd.getFileDescriptor())))) {
-
-            String line;
-            while ((line = reader.readLine()) != null) {
-                imeList.add(line);
-            }
-        }
-
-        String[] imeArray = new String[imeList.size()];
-        imeList.toArray(imeArray);
-
-        return imeArray;
-    }
-
-    private static void disableImes() throws Exception {
-        sEnabledImes = getEnabledImes();
-        for (String ime : sEnabledImes) {
-            String disableImeCommand = COMMAND_PREFIX_DISABLE_IME + ime;
-            SystemUtil.runShellCommand(getInstrumentation(), disableImeCommand);
-        }
-    }
-
-    private static void enableImes() throws Exception {
-        for (String ime : sEnabledImes) {
-            String enableImeCommand = COMMAND_PREFIX_ENABLE_IME + ime;
-            SystemUtil.runShellCommand(getInstrumentation(), enableImeCommand);
-        }
-        sEnabledImes = null;
-    }
-
-    protected static Instrumentation getInstrumentation() {
-        return InstrumentationRegistry.getInstrumentation();
-    }
-
-    @BeforeClass
-    public static void setUpClass() throws Exception {
-        Log.d(LOG_TAG, "setUpClass()");
-
-        Instrumentation instrumentation = getInstrumentation();
-
-        // Make sure we start with a clean slate.
-        Log.d(LOG_TAG, "clearPrintSpoolerData()");
-        clearPrintSpoolerData();
-        Log.d(LOG_TAG, "disableImes()");
-        disableImes();
-        Log.d(LOG_TAG, "disablePrintServices()");
-        disablePrintServices(instrumentation.getTargetContext().getPackageName());
-
-        // Workaround for dexmaker bug: https://code.google.com/p/dexmaker/issues/detail?id=2
-        // Dexmaker is used by mockito.
-        System.setProperty("dexmaker.dexcache", instrumentation
-                .getTargetContext().getCacheDir().getPath());
-
-        Log.d(LOG_TAG, "setUpClass() done");
-    }
-
-    /**
-     * Disable all print services beside the ones we want to leave enabled.
-     *
-     * @param packageToLeaveEnabled The package of the services to leave enabled.
-     */
-    private static void disablePrintServices(@NonNull String packageToLeaveEnabled)
-            throws IOException {
-        Instrumentation instrumentation = getInstrumentation();
-
-        sDisabledPrintServicesBefore = SystemUtil.runShellCommand(instrumentation,
-                "settings get secure " + Settings.Secure.DISABLED_PRINT_SERVICES);
-
-        Intent printServiceIntent = new Intent(android.printservice.PrintService.SERVICE_INTERFACE);
-        List<ResolveInfo> installedServices = instrumentation.getContext().getPackageManager()
-                .queryIntentServices(printServiceIntent, GET_SERVICES | GET_META_DATA);
-
-        StringBuilder builder = new StringBuilder();
-        for (ResolveInfo service : installedServices) {
-            if (packageToLeaveEnabled.equals(service.serviceInfo.packageName)) {
-                continue;
-            }
-            if (builder.length() > 0) {
-                builder.append(":");
-            }
-            builder.append(new ComponentName(service.serviceInfo.packageName,
-                    service.serviceInfo.name).flattenToString());
-        }
-
-        SystemUtil.runShellCommand(instrumentation, "settings put secure "
-                + Settings.Secure.DISABLED_PRINT_SERVICES + " " + builder);
-    }
-
-    /**
-     * Revert {@link #disablePrintServices(String)}
-     */
-    private static  void enablePrintServices() throws IOException {
-        SystemUtil.runShellCommand(getInstrumentation(),
-                "settings put secure " + Settings.Secure.DISABLED_PRINT_SERVICES + " "
-                        + sDisabledPrintServicesBefore);
-    }
-
-    @Before
-    public void setUp() throws Exception {
-        Log.d(LOG_TAG, "setUp()");
-
-        assumeTrue(getInstrumentation().getContext().getPackageManager().hasSystemFeature(
-                PackageManager.FEATURE_PRINTING));
-
-        // Initialize the latches.
-        Log.d(LOG_TAG, "init counters");
-        mCancelOperationCounter = new CallCounter();
-        mLayoutCallCounter = new CallCounter();
-        mFinishCallCounter = new CallCounter();
-        mWriteCallCounter = new CallCounter();
-        mWriteCancelCallCounter = new CallCounter();
-        mFinishCallCounter = new CallCounter();
-        mPrintJobQueuedCallCounter = new CallCounter();
-        mCreateSessionCallCounter = new CallCounter();
-        mDestroySessionCallCounter = new CallCounter();
-
-        mTestId = sLastTestID.incrementAndGet();
-        sIdToTest.put(mTestId, this);
-
-        // Create the activity if needed
-        if (!mShouldStartActivityRule.noActivity) {
-            createActivity();
-        }
-
-        Log.d(LOG_TAG, "setUp() done");
-    }
-
-    @After
-    public void tearDown() throws Exception {
-        Log.d(LOG_TAG, "tearDown()");
-
-        finishActivity();
-
-        sIdToTest.remove(mTestId);
-
-        Log.d(LOG_TAG, "tearDown() done");
-    }
-
-    @AfterClass
-    public static void tearDownClass() throws Exception {
-        Log.d(LOG_TAG, "tearDownClass()");
-
-        Instrumentation instrumentation = getInstrumentation();
-
-        Log.d(LOG_TAG, "enablePrintServices()");
-        enablePrintServices();
-
-        Log.d(LOG_TAG, "enableImes()");
-        enableImes();
-
-        // Make sure the spooler is cleaned, this also un-approves all services
-        Log.d(LOG_TAG, "clearPrintSpoolerData()");
-        clearPrintSpoolerData();
-
-        SystemUtil.runShellCommand(instrumentation, "settings put secure "
-                    + Settings.Secure.DISABLED_PRINT_SERVICES + " null");
-
-        Log.d(LOG_TAG, "tearDownClass() done");
-    }
-
-    protected void print(final PrintDocumentAdapter adapter, final PrintAttributes attributes) {
-        print(adapter, "Print job", attributes);
-    }
-
-    protected void print(PrintDocumentAdapter adapter) {
-        print(adapter, (PrintAttributes) null);
-    }
-
-    protected void print(PrintDocumentAdapter adapter, String printJobName) {
-        print(adapter, printJobName, null);
-    }
-
-    /**
-     * Start printing
-     *
-     * @param adapter      Adapter supplying data to print
-     * @param printJobName The name of the print job
-     */
-    protected void print(@NonNull PrintDocumentAdapter adapter, @NonNull String printJobName,
-            @Nullable PrintAttributes attributes) {
-        // Initiate printing as if coming from the app.
-        getInstrumentation()
-                .runOnMainSync(() -> getPrintManager(getActivity()).print(printJobName, adapter,
-                        attributes));
-    }
-
-    void onCancelOperationCalled() {
-        mCancelOperationCounter.call();
-    }
-
-    void onLayoutCalled() {
-        mLayoutCallCounter.call();
-    }
-
-    int getWriteCallCount() {
-        return mWriteCallCounter.getCallCount();
-    }
-
-    protected void onWriteCalled() {
-        mWriteCallCounter.call();
-    }
-
-    protected void onWriteCancelCalled() {
-        mWriteCancelCallCounter.call();
-    }
-
-    void onFinishCalled() {
-        mFinishCallCounter.call();
-    }
-
-    void onPrintJobQueuedCalled() {
-        mPrintJobQueuedCallCounter.call();
-    }
-
-    void onPrinterDiscoverySessionCreateCalled() {
-        mCreateSessionCallCounter.call();
-    }
-
-    protected void onPrinterDiscoverySessionDestroyCalled() {
-        mDestroySessionCallCounter.call();
-    }
-
-    void waitForCancelOperationCallbackCalled() {
-        waitForCallbackCallCount(mCancelOperationCounter, 1,
-                "Did not get expected call to onCancel for the current operation.");
-    }
-
-    void waitForPrinterDiscoverySessionCreateCallbackCalled() {
-        waitForCallbackCallCount(mCreateSessionCallCounter, 1,
-                "Did not get expected call to onCreatePrinterDiscoverySession.");
-    }
-
-    void waitForPrinterDiscoverySessionDestroyCallbackCalled(int count) {
-        waitForCallbackCallCount(mDestroySessionCallCounter, count,
-                "Did not get expected call to onDestroyPrinterDiscoverySession.");
-    }
-
-    void waitForServiceOnPrintJobQueuedCallbackCalled(int count) {
-        waitForCallbackCallCount(mPrintJobQueuedCallCounter, count,
-                "Did not get expected call to onPrintJobQueued.");
-    }
-
-    void waitForAdapterFinishCallbackCalled() {
-        waitForCallbackCallCount(mFinishCallCounter, 1,
-                "Did not get expected call to finish.");
-    }
-
-    void waitForLayoutAdapterCallbackCount(int count) {
-        waitForCallbackCallCount(mLayoutCallCounter, count,
-                "Did not get expected call to layout.");
-    }
-
-    void waitForWriteAdapterCallback(int count) {
-        waitForCallbackCallCount(mWriteCallCounter, count, "Did not get expected call to write.");
-    }
-
-    void waitForWriteCancelCallback(int count) {
-        waitForCallbackCallCount(mWriteCancelCallCounter, count,
-                "Did not get expected cancel of write.");
-    }
-
-    private static void waitForCallbackCallCount(CallCounter counter, int count, String message) {
-        try {
-            counter.waitForCount(count, OPERATION_TIMEOUT_MILLIS);
-        } catch (TimeoutException te) {
-            fail(message);
-        }
-    }
-
-    /**
-     * Indicate the print activity was created.
-     */
-    static void onActivityCreateCalled(int testId, PrintDocumentActivity activity) {
-        synchronized (sIdToTest) {
-            BasePrintTest test = sIdToTest.get(testId);
-            if (test != null) {
-                test.mActivity = activity;
-                test.mCreateActivityCallCounter.call();
-            }
-        }
-    }
-
-    /**
-     * Indicate the print activity was destroyed.
-     */
-    static void onActivityDestroyCalled(int testId) {
-        synchronized (sIdToTest) {
-            BasePrintTest test = sIdToTest.get(testId);
-            if (test != null) {
-                test.mDestroyActivityCallCounter.call();
-            }
-        }
-    }
-
-    private void finishActivity() {
-        Activity activity = mActivity;
-
-        if (activity != null) {
-            if (!activity.isFinishing()) {
-                activity.finish();
-            }
-
-            while (!activity.isDestroyed()) {
-                int creates = mCreateActivityCallCounter.getCallCount();
-                waitForCallbackCallCount(mDestroyActivityCallCounter, creates,
-                        "Activity was not destroyed");
-            }
-        }
-    }
-
-    /**
-     * Get the number of ties the print activity was destroyed.
-     *
-     * @return The number of onDestroy calls on the print activity.
-     */
-    int getActivityDestroyCallbackCallCount() {
-        return mDestroyActivityCallCounter.getCallCount();
-    }
-
-    /**
-     * Get the number of ties the print activity was created.
-     *
-     * @return The number of onCreate calls on the print activity.
-     */
-    private int getActivityCreateCallbackCallCount() {
-        return mCreateActivityCallCounter.getCallCount();
-    }
-
-    /**
-     * Wait until create was called {@code count} times.
-     *
-     * @param count The number of create calls to expect.
-     */
-    private void waitForActivityCreateCallbackCalled(int count) {
-        waitForCallbackCallCount(mCreateActivityCallCounter, count,
-                "Did not get expected call to create.");
-    }
-
-    /**
-     * Reset all counters.
-     */
-    void resetCounters() {
-        mCancelOperationCounter.reset();
-        mLayoutCallCounter.reset();
-        mWriteCallCounter.reset();
-        mWriteCancelCallCounter.reset();
-        mFinishCallCounter.reset();
-        mPrintJobQueuedCallCounter.reset();
-        mCreateSessionCallCounter.reset();
-        mDestroySessionCallCounter.reset();
-        mDestroyActivityCallCounter.reset();
-        mCreateActivityCallCounter.reset();
-    }
-
-    void selectPrinter(String printerName) throws UiObjectNotFoundException, IOException {
-        try {
-            long delay = 1;
-            while (true) {
-                try {
-                    UiDevice uiDevice = getUiDevice();
-                    UiObject destinationSpinner = uiDevice.findObject(new UiSelector()
-                            .resourceId("com.android.printspooler:id/destination_spinner"));
-
-                    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);
-                }
-
-                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) {
-            dumpWindowHierarchy();
-            throw e;
-        }
-    }
-
-    void answerPrintServicesWarning(boolean confirm) throws UiObjectNotFoundException {
-        UiObject button;
-        if (confirm) {
-            button = getUiDevice().findObject(new UiSelector().resourceId("android:id/button1"));
-        } else {
-            button = getUiDevice().findObject(new UiSelector().resourceId("android:id/button2"));
-        }
-        button.click();
-    }
-
-    void changeOrientation(String orientation) throws UiObjectNotFoundException, IOException {
-        try {
-            UiDevice uiDevice = getUiDevice();
-            UiObject orientationSpinner = uiDevice.findObject(new UiSelector().resourceId(
-                    "com.android.printspooler:id/orientation_spinner"));
-            orientationSpinner.click();
-            UiObject orientationOption = uiDevice.findObject(new UiSelector().text(orientation));
-            orientationOption.click();
-        } catch (UiObjectNotFoundException e) {
-            dumpWindowHierarchy();
-            throw e;
-        }
-    }
-
-    protected String getOrientation() throws UiObjectNotFoundException, IOException {
-        try {
-            UiObject orientationSpinner = getUiDevice().findObject(new UiSelector().resourceId(
-                    "com.android.printspooler:id/orientation_spinner"));
-            return orientationSpinner.getText();
-        } catch (UiObjectNotFoundException e) {
-            dumpWindowHierarchy();
-            throw e;
-        }
-    }
-
-    void changeMediaSize(String mediaSize) throws UiObjectNotFoundException, IOException {
-        try {
-            UiDevice uiDevice = getUiDevice();
-            UiObject mediaSizeSpinner = uiDevice.findObject(new UiSelector().resourceId(
-                    "com.android.printspooler:id/paper_size_spinner"));
-            mediaSizeSpinner.click();
-            UiObject mediaSizeOption = uiDevice.findObject(new UiSelector().text(mediaSize));
-            mediaSizeOption.click();
-        } catch (UiObjectNotFoundException e) {
-            dumpWindowHierarchy();
-            throw e;
-        }
-    }
-
-    void changeColor(String color) throws UiObjectNotFoundException, IOException {
-        try {
-            UiDevice uiDevice = getUiDevice();
-            UiObject colorSpinner = uiDevice.findObject(new UiSelector().resourceId(
-                    "com.android.printspooler:id/color_spinner"));
-            colorSpinner.click();
-            UiObject colorOption = uiDevice.findObject(new UiSelector().text(color));
-            colorOption.click();
-        } catch (UiObjectNotFoundException e) {
-            dumpWindowHierarchy();
-            throw e;
-        }
-    }
-
-    protected String getColor() throws UiObjectNotFoundException, IOException {
-        try {
-            UiObject colorSpinner = getUiDevice().findObject(new UiSelector().resourceId(
-                    "com.android.printspooler:id/color_spinner"));
-            return colorSpinner.getText();
-        } catch (UiObjectNotFoundException e) {
-            dumpWindowHierarchy();
-            throw e;
-        }
-    }
-
-    void changeDuplex(String duplex) throws UiObjectNotFoundException, IOException {
-        try {
-            UiDevice uiDevice = getUiDevice();
-            UiObject duplexSpinner = uiDevice.findObject(new UiSelector().resourceId(
-                    "com.android.printspooler:id/duplex_spinner"));
-            duplexSpinner.click();
-            UiObject duplexOption = uiDevice.findObject(new UiSelector().text(duplex));
-            duplexOption.click();
-        } catch (UiObjectNotFoundException e) {
-            dumpWindowHierarchy();
-            throw e;
-        }
-    }
-
-    void changeCopies(int newCopies) throws UiObjectNotFoundException, IOException {
-        try {
-            UiObject copies = getUiDevice().findObject(new UiSelector().resourceId(
-                    "com.android.printspooler:id/copies_edittext"));
-            copies.setText(Integer.valueOf(newCopies).toString());
-        } catch (UiObjectNotFoundException e) {
-            dumpWindowHierarchy();
-            throw e;
-        }
-    }
-
-    protected String getCopies() throws UiObjectNotFoundException, IOException {
-        try {
-            UiObject copies = getUiDevice().findObject(new UiSelector().resourceId(
-                    "com.android.printspooler:id/copies_edittext"));
-            return copies.getText();
-        } catch (UiObjectNotFoundException e) {
-            dumpWindowHierarchy();
-            throw e;
-        }
-    }
-
-    void assertNoPrintButton() throws UiObjectNotFoundException, IOException {
-        assertFalse(getUiDevice().hasObject(By.res("com.android.printspooler:id/print_button")));
-    }
-
-    void clickPrintButton() throws UiObjectNotFoundException, IOException {
-        try {
-            UiObject printButton = getUiDevice().findObject(new UiSelector().resourceId(
-                    "com.android.printspooler:id/print_button"));
-            printButton.click();
-        } catch (UiObjectNotFoundException e) {
-            dumpWindowHierarchy();
-            throw e;
-        }
-    }
-
-    void clickRetryButton() throws UiObjectNotFoundException, IOException {
-        try {
-            UiObject retryButton = getUiDevice().findObject(new UiSelector().resourceId(
-                    "com.android.printspooler:id/action_button"));
-            retryButton.click();
-        } catch (UiObjectNotFoundException e) {
-            dumpWindowHierarchy();
-            throw e;
-        }
-    }
-
-    void dumpWindowHierarchy() throws IOException {
-        ByteArrayOutputStream os = new ByteArrayOutputStream();
-        getUiDevice().dumpWindowHierarchy(os);
-
-        Log.w(LOG_TAG, "Window hierarchy:");
-        for (String line : os.toString("UTF-8").split("\n")) {
-            Log.w(LOG_TAG, line);
-        }
-    }
-
-    protected PrintDocumentActivity getActivity() {
-        return mActivity;
-    }
-
-    protected void createActivity() {
-        Log.d(LOG_TAG, "createActivity()");
-
-        int createBefore = getActivityCreateCallbackCallCount();
-
-        Intent intent = new Intent(Intent.ACTION_MAIN);
-        intent.putExtra(TEST_ID, mTestId);
-
-        Instrumentation instrumentation = getInstrumentation();
-        intent.setClassName(instrumentation.getTargetContext().getPackageName(),
-                PrintDocumentActivity.class.getName());
-        intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
-        instrumentation.startActivitySync(intent);
-
-        waitForActivityCreateCallbackCalled(createBefore + 1);
-    }
-
-    void openPrintOptions() throws UiObjectNotFoundException {
-        UiObject expandHandle = getUiDevice().findObject(new UiSelector().resourceId(
-                "com.android.printspooler:id/expand_collapse_handle"));
-        expandHandle.click();
-    }
-
-    void openCustomPrintOptions() throws UiObjectNotFoundException {
-        UiObject expandHandle = getUiDevice().findObject(new UiSelector().resourceId(
-                "com.android.printspooler:id/more_options_button"));
-        expandHandle.click();
-    }
-
-    static void clearPrintSpoolerData() throws Exception {
-        if (getInstrumentation().getContext().getPackageManager().hasSystemFeature(
-                PackageManager.FEATURE_PRINTING)) {
-            assertTrue("failed to clear print spooler data",
-                    SystemUtil.runShellCommand(getInstrumentation(), String.format(
-                            "pm clear --user %d %s", CURRENT_USER_ID, PRINT_SPOOLER_PACKAGE_NAME))
-                            .contains(PM_CLEAR_SUCCESS_OUTPUT));
-        }
-    }
-
-    void verifyLayoutCall(InOrder inOrder, PrintDocumentAdapter mock,
-            PrintAttributes oldAttributes, PrintAttributes newAttributes,
-            final boolean forPreview) {
-        inOrder.verify(mock).onLayout(eq(oldAttributes), eq(newAttributes),
-                any(CancellationSignal.class), any(LayoutResultCallback.class), argThat(
-                        new BaseMatcher<Bundle>() {
-                            @Override
-                            public boolean matches(Object item) {
-                                Bundle bundle = (Bundle) item;
-                                return forPreview == bundle.getBoolean(
-                                        PrintDocumentAdapter.EXTRA_PRINT_PREVIEW);
-                            }
-
-                            @Override
-                            public void describeTo(Description description) {
-                                /* do nothing */
-                            }
-                        }));
-    }
-
-    PrintDocumentAdapter createMockPrintDocumentAdapter(Answer<Void> layoutAnswer,
-            Answer<Void> writeAnswer, Answer<Void> finishAnswer) {
-        // Create a mock print adapter.
-        PrintDocumentAdapter adapter = mock(PrintDocumentAdapter.class);
-        if (layoutAnswer != null) {
-            doAnswer(layoutAnswer).when(adapter).onLayout(any(PrintAttributes.class),
-                    any(PrintAttributes.class), any(CancellationSignal.class),
-                    any(LayoutResultCallback.class), any(Bundle.class));
-        }
-        if (writeAnswer != null) {
-            doAnswer(writeAnswer).when(adapter).onWrite(any(PageRange[].class),
-                    any(ParcelFileDescriptor.class), any(CancellationSignal.class),
-                    any(WriteResultCallback.class));
-        }
-        if (finishAnswer != null) {
-            doAnswer(finishAnswer).when(adapter).onFinish();
-        }
-        return adapter;
-    }
-
-    /**
-     * Create a mock {@link PrintDocumentAdapter} that provides one empty page.
-     *
-     * @return The mock adapter
-     */
-    @NonNull PrintDocumentAdapter createDefaultPrintDocumentAdapter(int numPages) {
-        final PrintAttributes[] printAttributes = new PrintAttributes[1];
-
-        return createMockPrintDocumentAdapter(
-                invocation -> {
-                    PrintAttributes oldAttributes = (PrintAttributes) invocation.getArguments()[0];
-                    printAttributes[0] = (PrintAttributes) invocation.getArguments()[1];
-                    PrintDocumentAdapter.LayoutResultCallback callback =
-                            (PrintDocumentAdapter.LayoutResultCallback) invocation
-                                    .getArguments()[3];
-
-                    callback.onLayoutFinished(new PrintDocumentInfo.Builder(PRINT_JOB_NAME)
-                            .setPageCount(numPages).build(),
-                            !oldAttributes.equals(printAttributes[0]));
-
-                    oldAttributes = printAttributes[0];
-
-                    onLayoutCalled();
-                    return null;
-                }, invocation -> {
-                    Object[] args = invocation.getArguments();
-                    PageRange[] pages = (PageRange[]) args[0];
-                    ParcelFileDescriptor fd = (ParcelFileDescriptor) args[1];
-                    PrintDocumentAdapter.WriteResultCallback callback =
-                            (PrintDocumentAdapter.WriteResultCallback) args[3];
-
-                    writeBlankPages(printAttributes[0], fd, pages[0].getStart(), pages[0].getEnd());
-                    fd.close();
-                    callback.onWriteFinished(pages);
-
-                    onWriteCalled();
-                    return null;
-                }, invocation -> {
-                    onFinishCalled();
-                    return null;
-                });
-    }
-
-    @SuppressWarnings("unchecked")
-    protected static PrinterDiscoverySessionCallbacks createMockPrinterDiscoverySessionCallbacks(
-            Answer<Void> onStartPrinterDiscovery, Answer<Void> onStopPrinterDiscovery,
-            Answer<Void> onValidatePrinters, Answer<Void> onStartPrinterStateTracking,
-            Answer<Void> onRequestCustomPrinterIcon, Answer<Void> onStopPrinterStateTracking,
-            Answer<Void> onDestroy) {
-        PrinterDiscoverySessionCallbacks callbacks = mock(PrinterDiscoverySessionCallbacks.class);
-
-        doCallRealMethod().when(callbacks).setSession(any(StubbablePrinterDiscoverySession.class));
-        when(callbacks.getSession()).thenCallRealMethod();
-
-        if (onStartPrinterDiscovery != null) {
-            doAnswer(onStartPrinterDiscovery).when(callbacks).onStartPrinterDiscovery(
-                    any(List.class));
-        }
-        if (onStopPrinterDiscovery != null) {
-            doAnswer(onStopPrinterDiscovery).when(callbacks).onStopPrinterDiscovery();
-        }
-        if (onValidatePrinters != null) {
-            doAnswer(onValidatePrinters).when(callbacks).onValidatePrinters(
-                    any(List.class));
-        }
-        if (onStartPrinterStateTracking != null) {
-            doAnswer(onStartPrinterStateTracking).when(callbacks).onStartPrinterStateTracking(
-                    any(PrinterId.class));
-        }
-        if (onRequestCustomPrinterIcon != null) {
-            doAnswer(onRequestCustomPrinterIcon).when(callbacks).onRequestCustomPrinterIcon(
-                    any(PrinterId.class), any(CancellationSignal.class),
-                    any(CustomPrinterIconCallback.class));
-        }
-        if (onStopPrinterStateTracking != null) {
-            doAnswer(onStopPrinterStateTracking).when(callbacks).onStopPrinterStateTracking(
-                    any(PrinterId.class));
-        }
-        if (onDestroy != null) {
-            doAnswer(onDestroy).when(callbacks).onDestroy();
-        }
-
-        return callbacks;
-    }
-
-    protected PrintServiceCallbacks createMockPrintServiceCallbacks(
-            Answer<PrinterDiscoverySessionCallbacks> onCreatePrinterDiscoverySessionCallbacks,
-            Answer<Void> onPrintJobQueued, Answer<Void> onRequestCancelPrintJob) {
-        final PrintServiceCallbacks service = mock(PrintServiceCallbacks.class);
-
-        doCallRealMethod().when(service).setService(any(StubbablePrintService.class));
-        when(service.getService()).thenCallRealMethod();
-
-        if (onCreatePrinterDiscoverySessionCallbacks != null) {
-            doAnswer(onCreatePrinterDiscoverySessionCallbacks).when(service)
-                    .onCreatePrinterDiscoverySessionCallbacks();
-        }
-        if (onPrintJobQueued != null) {
-            doAnswer(onPrintJobQueued).when(service).onPrintJobQueued(any(PrintJob.class));
-        }
-        if (onRequestCancelPrintJob != null) {
-            doAnswer(onRequestCancelPrintJob).when(service).onRequestCancelPrintJob(
-                    any(PrintJob.class));
-        }
-
-        return service;
-    }
-
-    protected void writeBlankPages(PrintAttributes constraints, ParcelFileDescriptor output,
-            int fromIndex, int toIndex) throws IOException {
-        PrintedPdfDocument document = new PrintedPdfDocument(getActivity(), constraints);
-        final int pageCount = toIndex - fromIndex + 1;
-        for (int i = 0; i < pageCount; i++) {
-            PdfDocument.Page page = document.startPage(i);
-            document.finishPage(page);
-        }
-        FileOutputStream fos = new FileOutputStream(output.getFileDescriptor());
-        document.writeTo(fos);
-        fos.flush();
-        document.close();
-    }
-
-    protected void selectPages(String pages, int totalPages) throws Exception {
-        UiObject pagesSpinner = getUiDevice().findObject(new UiSelector().resourceId(
-                "com.android.printspooler:id/range_options_spinner"));
-        pagesSpinner.click();
-
-        UiObject rangeOption = getUiDevice().findObject(new UiSelector().textContains(
-                getPrintSpoolerStringOneParam("template_page_range", totalPages)));
-        rangeOption.click();
-
-        UiObject pagesEditText = getUiDevice().findObject(new UiSelector().resourceId(
-                "com.android.printspooler:id/page_range_edittext"));
-        pagesEditText.setText(pages);
-
-        // Hide the keyboard.
-        getUiDevice().pressBack();
-    }
-
-    private static final class CallCounter {
-        private final Object mLock = new Object();
-
-        private int mCallCount;
-
-        public void call() {
-            synchronized (mLock) {
-                mCallCount++;
-                mLock.notifyAll();
-            }
-        }
-
-        int getCallCount() {
-            synchronized (mLock) {
-                return mCallCount;
-            }
-        }
-
-        public void reset() {
-            synchronized (mLock) {
-                mCallCount = 0;
-            }
-        }
-
-        public void waitForCount(int count, long timeoutMillis) throws TimeoutException {
-            synchronized (mLock) {
-                final long startTimeMillis = SystemClock.uptimeMillis();
-                while (mCallCount < count) {
-                    try {
-                        final long elapsedTimeMillis = SystemClock.uptimeMillis() - startTimeMillis;
-                        final long remainingTimeMillis = timeoutMillis - elapsedTimeMillis;
-                        if (remainingTimeMillis <= 0) {
-                            throw new TimeoutException();
-                        }
-                        mLock.wait(timeoutMillis);
-                    } catch (InterruptedException ie) {
-                        /* ignore */
-                    }
-                }
-            }
-        }
-    }
-
-
-    /**
-     * Make {@code printerName} the default printer by adding it to the history of printers by
-     * printing once.
-     *
-     * @param adapter The {@link PrintDocumentAdapter} used
-     * @throws Exception If the printer could not be made default
-     */
-    void makeDefaultPrinter(PrintDocumentAdapter adapter, String printerName)
-            throws Exception {
-        // Perform a full print operation on the printer
-        Log.d(LOG_TAG, "print");
-        print(adapter);
-        Log.d(LOG_TAG, "waitForWriteAdapterCallback");
-        waitForWriteAdapterCallback(1);
-        Log.d(LOG_TAG, "selectPrinter");
-        selectPrinter(printerName);
-        Log.d(LOG_TAG, "clickPrintButton");
-        clickPrintButton();
-        Log.d(LOG_TAG, "answerPrintServicesWarning");
-        answerPrintServicesWarning(true);
-        Log.d(LOG_TAG, "waitForPrinterDiscoverySessionDestroyCallbackCalled");
-        waitForPrinterDiscoverySessionDestroyCallbackCalled(1);
-
-        // Switch to new activity, which should now use the default printer
-        Log.d(LOG_TAG, "getActivity().finish()");
-        getActivity().finish();
-
-        createActivity();
-    }
-
-    /**
-     * Get a string array from the print spooler's resources.
-     *
-     * @param resourceName The name of the array resource
-     * @return The localized string array
-     *
-     * @throws Exception If anything is unexpected
-     */
-    String[] getPrintSpoolerStringArray(String resourceName) throws Exception {
-        PackageManager pm = getActivity().getPackageManager();
-        Resources printSpoolerRes = pm.getResourcesForApplication(PRINTSPOOLER_PACKAGE);
-        int id = printSpoolerRes.getIdentifier(resourceName, "array", PRINTSPOOLER_PACKAGE);
-        return printSpoolerRes.getStringArray(id);
-    }
-
-    /**
-     * Get a string from the print spooler's resources.
-     *
-     * @param resourceName The name of the string resource
-     * @return The localized string
-     *
-     * @throws Exception If anything is unexpected
-     */
-    String getPrintSpoolerString(String resourceName) throws Exception {
-        PackageManager pm = getActivity().getPackageManager();
-        Resources printSpoolerRes = pm.getResourcesForApplication(PRINTSPOOLER_PACKAGE);
-        int id = printSpoolerRes.getIdentifier(resourceName, "string", PRINTSPOOLER_PACKAGE);
-        return printSpoolerRes.getString(id);
-    }
-
-    /**
-     * Get a string with one parameter from the print spooler's resources.
-     *
-     * @param resourceName The name of the string resource
-     * @return The localized string
-     *
-     * @throws Exception If anything is unexpected
-     */
-    String getPrintSpoolerStringOneParam(String resourceName, Object p)
-            throws Exception {
-        PackageManager pm = getActivity().getPackageManager();
-        Resources printSpoolerRes = pm.getResourcesForApplication(PRINTSPOOLER_PACKAGE);
-        int id = printSpoolerRes.getIdentifier(resourceName, "string", PRINTSPOOLER_PACKAGE);
-        return printSpoolerRes.getString(id, p);
-    }
-
-    /**
-     * Get the default media size for the current locale.
-     *
-     * @return The default media size for the current locale
-     *
-     * @throws Exception If anything is unexpected
-     */
-    PrintAttributes.MediaSize getDefaultMediaSize() throws Exception {
-        PackageManager pm = getActivity().getPackageManager();
-        Resources printSpoolerRes = pm.getResourcesForApplication(PRINTSPOOLER_PACKAGE);
-        int defaultMediaSizeResId = printSpoolerRes.getIdentifier("mediasize_default", "string",
-                PRINTSPOOLER_PACKAGE);
-        String defaultMediaSizeName = printSpoolerRes.getString(defaultMediaSizeResId);
-
-        switch (defaultMediaSizeName) {
-            case "NA_LETTER":
-                return PrintAttributes.MediaSize.NA_LETTER;
-            case "JIS_B5":
-                return PrintAttributes.MediaSize.JIS_B5;
-            case "ISO_A4":
-                return PrintAttributes.MediaSize.ISO_A4;
-            default:
-                throw new Exception("Unknown default media size " + defaultMediaSizeName);
-        }
-    }
-
-    /**
-     * Annotation used to signal that a test does not need an activity.
-     */
-    @Retention(RetentionPolicy.RUNTIME)
-    @Target(ElementType.METHOD)
-    @interface NoActivity { }
-
-    /**
-     * Rule that handles the {@link NoActivity} annotation.
-     */
-    private static class ShouldStartActivity implements TestRule {
-        boolean noActivity;
-
-        @Override
-        public Statement apply(Statement base, org.junit.runner.Description description) {
-            for (Annotation annotation : description.getAnnotations()) {
-                if (annotation instanceof NoActivity) {
-                    noActivity = true;
-                    break;
-                }
-            }
-
-            return base;
-        }
-    }
-}
diff --git a/tests/tests/print/src/android/print/cts/ClassParametersTest.java b/tests/tests/print/src/android/print/cts/ClassParametersTest.java
index e0c41ea..a5e5d49 100644
--- a/tests/tests/print/src/android/print/cts/ClassParametersTest.java
+++ b/tests/tests/print/src/android/print/cts/ClassParametersTest.java
@@ -16,17 +16,21 @@
 
 package android.print.cts;
 
+import static android.print.test.Utils.assertException;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertTrue;
+
 import android.print.PrintAttributes;
 import android.print.PrintAttributes.MediaSize;
 import android.print.PrintAttributes.Resolution;
 import android.print.PrintDocumentInfo;
 import android.support.test.runner.AndroidJUnit4;
+
 import org.junit.Test;
 import org.junit.runner.RunWith;
 
-import static android.print.cts.Utils.*;
-import static org.junit.Assert.*;
-
 /**
  * Test that the print attributes can be constructed correctly. This does not test that the
  * attributes have the desired effect when send to the print framework.
diff --git a/tests/tests/print/src/android/print/cts/CustomPrintOptionsTest.java b/tests/tests/print/src/android/print/cts/CustomPrintOptionsTest.java
index eb9806e..3ea14f6 100644
--- a/tests/tests/print/src/android/print/cts/CustomPrintOptionsTest.java
+++ b/tests/tests/print/src/android/print/cts/CustomPrintOptionsTest.java
@@ -16,6 +16,8 @@
 
 package android.print.cts;
 
+import static android.print.test.Utils.eventually;
+
 import android.os.ParcelFileDescriptor;
 import android.print.PageRange;
 import android.print.PrintAttributes;
@@ -30,32 +32,29 @@
 import android.print.PrinterCapabilitiesInfo;
 import android.print.PrinterId;
 import android.print.PrinterInfo;
-import android.print.cts.services.CustomPrintOptionsActivity;
-import android.print.cts.services.FirstPrintService;
-import android.print.cts.services.PrintServiceCallbacks;
-import android.print.cts.services.PrinterDiscoverySessionCallbacks;
-import android.print.cts.services.SecondPrintService;
-import android.print.cts.services.StubbablePrinterDiscoverySession;
+import android.print.test.BasePrintTest;
+import android.print.test.services.CustomPrintOptionsActivity;
+import android.print.test.services.FirstPrintService;
+import android.print.test.services.PrintServiceCallbacks;
+import android.print.test.services.PrinterDiscoverySessionCallbacks;
+import android.print.test.services.SecondPrintService;
+import android.print.test.services.StubbablePrinterDiscoverySession;
 import android.support.test.runner.AndroidJUnit4;
 import android.support.test.uiautomator.By;
 import android.support.test.uiautomator.UiObject;
-import android.support.test.uiautomator.UiSelector;
 import android.support.test.uiautomator.UiObjectNotFoundException;
+import android.support.test.uiautomator.UiSelector;
 import android.util.Log;
+
 import org.junit.Before;
-import org.junit.BeforeClass;
 import org.junit.Test;
 import org.junit.runner.RunWith;
-import org.mockito.invocation.InvocationOnMock;
-import org.mockito.stubbing.Answer;
 
 import java.util.ArrayList;
 import java.util.Arrays;
 import java.util.List;
 import java.util.concurrent.TimeoutException;
 
-import static android.print.cts.Utils.eventually;
-
 /**
  * This test verifies changes to the printer capabilities are applied correctly.
  */
diff --git a/tests/tests/print/src/android/print/cts/InterfaceForAppsTest.java b/tests/tests/print/src/android/print/cts/InterfaceForAppsTest.java
index 37eab57..da7e82d 100644
--- a/tests/tests/print/src/android/print/cts/InterfaceForAppsTest.java
+++ b/tests/tests/print/src/android/print/cts/InterfaceForAppsTest.java
@@ -16,6 +16,14 @@
 
 package android.print.cts;
 
+import static android.print.test.Utils.eventually;
+import static android.print.test.Utils.getPrintJob;
+import static android.print.test.Utils.getPrintManager;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertTrue;
+
 import android.print.PrintAttributes;
 import android.print.PrintDocumentAdapter;
 import android.print.PrintJob;
@@ -23,23 +31,22 @@
 import android.print.PrinterCapabilitiesInfo;
 import android.print.PrinterId;
 import android.print.PrinterInfo;
-import android.print.cts.services.FirstPrintService;
-import android.print.cts.services.PrintServiceCallbacks;
-import android.print.cts.services.PrinterDiscoverySessionCallbacks;
-import android.print.cts.services.SecondPrintService;
-import android.print.cts.services.StubbablePrinterDiscoverySession;
+import android.print.test.BasePrintTest;
+import android.print.test.services.FirstPrintService;
+import android.print.test.services.PrintServiceCallbacks;
+import android.print.test.services.PrinterDiscoverySessionCallbacks;
+import android.print.test.services.SecondPrintService;
+import android.print.test.services.StubbablePrinterDiscoverySession;
 import android.support.annotation.NonNull;
 import android.support.test.runner.AndroidJUnit4;
 import android.util.Log;
+
 import org.junit.Before;
 import org.junit.Test;
 import org.junit.runner.RunWith;
 
 import java.util.ArrayList;
 
-import static android.print.cts.Utils.*;
-import static org.junit.Assert.*;
-
 /**
  * Test interface from the application to the print service.
  */
diff --git a/tests/tests/print/src/android/print/cts/PageRangeAdjustAndVerify.java b/tests/tests/print/src/android/print/cts/PageRangeAdjustAndVerify.java
index f128bab..c4fd77e 100644
--- a/tests/tests/print/src/android/print/cts/PageRangeAdjustAndVerify.java
+++ b/tests/tests/print/src/android/print/cts/PageRangeAdjustAndVerify.java
@@ -36,12 +36,13 @@
 import android.print.PrinterCapabilitiesInfo;
 import android.print.PrinterId;
 import android.print.PrinterInfo;
-import android.print.cts.services.FirstPrintService;
-import android.print.cts.services.PrintServiceCallbacks;
-import android.print.cts.services.PrinterDiscoverySessionCallbacks;
-import android.print.cts.services.SecondPrintService;
-import android.print.cts.services.StubbablePrinterDiscoverySession;
 import android.print.pdf.PrintedPdfDocument;
+import android.print.test.BasePrintTest;
+import android.print.test.services.FirstPrintService;
+import android.print.test.services.PrintServiceCallbacks;
+import android.print.test.services.PrinterDiscoverySessionCallbacks;
+import android.print.test.services.SecondPrintService;
+import android.print.test.services.StubbablePrinterDiscoverySession;
 import android.printservice.PrintJob;
 import android.printservice.PrintService;
 import android.support.annotation.NonNull;
diff --git a/tests/tests/print/src/android/print/cts/PageRangeAdjustmentTest.java b/tests/tests/print/src/android/print/cts/PageRangeAdjustmentTest.java
index 0364624..c1abafc 100644
--- a/tests/tests/print/src/android/print/cts/PageRangeAdjustmentTest.java
+++ b/tests/tests/print/src/android/print/cts/PageRangeAdjustmentTest.java
@@ -16,7 +16,8 @@
 
 package android.print.cts;
 
-import static org.junit.Assert.*;
+import static org.junit.Assert.assertSame;
+import static org.junit.Assert.assertTrue;
 import static org.mockito.Matchers.any;
 import static org.mockito.Mockito.inOrder;
 import static org.mockito.Mockito.never;
@@ -35,15 +36,16 @@
 import android.print.PrinterCapabilitiesInfo;
 import android.print.PrinterId;
 import android.print.PrinterInfo;
-import android.print.cts.services.FirstPrintService;
-import android.print.cts.services.PrintServiceCallbacks;
-import android.print.cts.services.PrinterDiscoverySessionCallbacks;
-import android.print.cts.services.SecondPrintService;
-import android.print.cts.services.StubbablePrinterDiscoverySession;
+import android.print.test.BasePrintTest;
+import android.print.test.services.FirstPrintService;
+import android.print.test.services.PrintServiceCallbacks;
+import android.print.test.services.PrinterDiscoverySessionCallbacks;
+import android.print.test.services.SecondPrintService;
+import android.print.test.services.StubbablePrinterDiscoverySession;
 import android.printservice.PrintJob;
 import android.printservice.PrintService;
-
 import android.support.test.runner.AndroidJUnit4;
+
 import org.junit.Before;
 import org.junit.Test;
 import org.junit.runner.RunWith;
diff --git a/tests/tests/print/src/android/print/cts/PrintAttributesTest.java b/tests/tests/print/src/android/print/cts/PrintAttributesTest.java
index e6bc164..7661ff9 100644
--- a/tests/tests/print/src/android/print/cts/PrintAttributesTest.java
+++ b/tests/tests/print/src/android/print/cts/PrintAttributesTest.java
@@ -16,6 +16,8 @@
 
 package android.print.cts;
 
+import static org.junit.Assert.assertEquals;
+
 import android.os.ParcelFileDescriptor;
 import android.print.PageRange;
 import android.print.PrintAttributes;
@@ -29,15 +31,16 @@
 import android.print.PrinterCapabilitiesInfo;
 import android.print.PrinterId;
 import android.print.PrinterInfo;
-import android.print.cts.services.FirstPrintService;
-import android.print.cts.services.PrintServiceCallbacks;
-import android.print.cts.services.PrinterDiscoverySessionCallbacks;
-import android.print.cts.services.SecondPrintService;
-import android.print.cts.services.StubbablePrinterDiscoverySession;
+import android.print.test.BasePrintTest;
+import android.print.test.services.FirstPrintService;
+import android.print.test.services.PrintServiceCallbacks;
+import android.print.test.services.PrinterDiscoverySessionCallbacks;
+import android.print.test.services.SecondPrintService;
+import android.print.test.services.StubbablePrinterDiscoverySession;
 import android.printservice.PrintJob;
-
 import android.support.test.runner.AndroidJUnit4;
 import android.util.Log;
+
 import org.junit.Before;
 import org.junit.Test;
 import org.junit.runner.RunWith;
@@ -46,8 +49,6 @@
 import java.util.Arrays;
 import java.util.List;
 
-import static org.junit.Assert.*;
-
 /**
  * Test that the print attributes are correctly propagated through the print framework
  */
diff --git a/tests/tests/print/src/android/print/cts/PrintDocumentActivity.java b/tests/tests/print/src/android/print/cts/PrintDocumentActivity.java
deleted file mode 100644
index c3e6e96..0000000
--- a/tests/tests/print/src/android/print/cts/PrintDocumentActivity.java
+++ /dev/null
@@ -1,53 +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.print.cts;
-
-import android.app.Activity;
-import android.app.KeyguardManager;
-import android.os.Bundle;
-import android.util.Log;
-import android.view.WindowManager;
-
-public class PrintDocumentActivity extends Activity {
-    private static final String LOG_TAG = "PrintDocumentActivity";
-    int mTestId;
-
-    @Override
-    protected void onCreate(Bundle savedInstanceState) {
-        super.onCreate(savedInstanceState);
-
-        mTestId = getIntent().getIntExtra(BasePrintTest.TEST_ID, -1);
-        Log.d(LOG_TAG, "onCreate() " + this + " for " + mTestId);
-
-        getWindow().addFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON
-                | WindowManager.LayoutParams.FLAG_TURN_SCREEN_ON);
-
-        KeyguardManager km = getSystemService(KeyguardManager.class);
-        if (km != null) {
-            km.requestDismissKeyguard(this, null);
-        }
-
-        BasePrintTest.onActivityCreateCalled(mTestId, this);
-    }
-
-    @Override
-    protected void onDestroy() {
-        Log.d(LOG_TAG, "onDestroy() " + this);
-        BasePrintTest.onActivityDestroyCalled(mTestId);
-        super.onDestroy();
-    }
-}
diff --git a/tests/tests/print/src/android/print/cts/PrintDocumentAdapterContractTest.java b/tests/tests/print/src/android/print/cts/PrintDocumentAdapterContractTest.java
index 38ba4dd..4851ba9 100644
--- a/tests/tests/print/src/android/print/cts/PrintDocumentAdapterContractTest.java
+++ b/tests/tests/print/src/android/print/cts/PrintDocumentAdapterContractTest.java
@@ -36,11 +36,12 @@
 import android.print.PrinterCapabilitiesInfo;
 import android.print.PrinterId;
 import android.print.PrinterInfo;
-import android.print.cts.services.FirstPrintService;
-import android.print.cts.services.PrintServiceCallbacks;
-import android.print.cts.services.PrinterDiscoverySessionCallbacks;
-import android.print.cts.services.SecondPrintService;
-import android.print.cts.services.StubbablePrinterDiscoverySession;
+import android.print.test.BasePrintTest;
+import android.print.test.services.FirstPrintService;
+import android.print.test.services.PrintServiceCallbacks;
+import android.print.test.services.PrinterDiscoverySessionCallbacks;
+import android.print.test.services.SecondPrintService;
+import android.print.test.services.StubbablePrinterDiscoverySession;
 import android.printservice.PrintJob;
 import android.printservice.PrintService;
 import android.support.test.runner.AndroidJUnit4;
diff --git a/tests/tests/print/src/android/print/cts/PrintDocumentInfoTest.java b/tests/tests/print/src/android/print/cts/PrintDocumentInfoTest.java
index a9d03c9..63e83e3 100644
--- a/tests/tests/print/src/android/print/cts/PrintDocumentInfoTest.java
+++ b/tests/tests/print/src/android/print/cts/PrintDocumentInfoTest.java
@@ -16,6 +16,11 @@
 
 package android.print.cts;
 
+import static android.print.test.Utils.eventually;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertTrue;
+
 import android.os.ParcelFileDescriptor;
 import android.print.PageRange;
 import android.print.PrintAttributes;
@@ -29,14 +34,16 @@
 import android.print.PrinterCapabilitiesInfo;
 import android.print.PrinterId;
 import android.print.PrinterInfo;
-import android.print.cts.services.FirstPrintService;
-import android.print.cts.services.PrintServiceCallbacks;
-import android.print.cts.services.PrinterDiscoverySessionCallbacks;
-import android.print.cts.services.SecondPrintService;
-import android.print.cts.services.StubbablePrinterDiscoverySession;
+import android.print.test.BasePrintTest;
+import android.print.test.services.FirstPrintService;
+import android.print.test.services.PrintServiceCallbacks;
+import android.print.test.services.PrinterDiscoverySessionCallbacks;
+import android.print.test.services.SecondPrintService;
+import android.print.test.services.StubbablePrinterDiscoverySession;
 import android.printservice.PrintJob;
 import android.printservice.PrintService;
 import android.support.test.runner.AndroidJUnit4;
+
 import org.junit.Before;
 import org.junit.Test;
 import org.junit.runner.RunWith;
@@ -45,16 +52,12 @@
 import java.util.ArrayList;
 import java.util.List;
 
-import static android.print.cts.Utils.eventually;
-import static org.junit.Assert.assertEquals;
-import static org.junit.Assert.assertTrue;
-
 /**
  * This test verifies that the system respects the {@link PrintDocumentAdapter}
  * contract and invokes all callbacks as expected.
  */
 @RunWith(AndroidJUnit4.class)
-public class PrintDocumentInfoTest extends android.print.cts.BasePrintTest {
+public class PrintDocumentInfoTest extends BasePrintTest {
     private static boolean sIsDefaultPrinterSet;
 
     @Before
diff --git a/tests/tests/print/src/android/print/cts/PrintJobStateTransitionsTest.java b/tests/tests/print/src/android/print/cts/PrintJobStateTransitionsTest.java
index d5edcb6..75a7847 100644
--- a/tests/tests/print/src/android/print/cts/PrintJobStateTransitionsTest.java
+++ b/tests/tests/print/src/android/print/cts/PrintJobStateTransitionsTest.java
@@ -16,7 +16,7 @@
 
 package android.print.cts;
 
-import static android.print.cts.Utils.eventually;
+import static android.print.test.Utils.eventually;
 
 import static org.junit.Assert.assertEquals;
 import static org.junit.Assert.assertTrue;
@@ -30,11 +30,12 @@
 import android.print.PrinterCapabilitiesInfo;
 import android.print.PrinterId;
 import android.print.PrinterInfo;
-import android.print.cts.services.FirstPrintService;
-import android.print.cts.services.PrintServiceCallbacks;
-import android.print.cts.services.PrinterDiscoverySessionCallbacks;
-import android.print.cts.services.SecondPrintService;
-import android.print.cts.services.StubbablePrinterDiscoverySession;
+import android.print.test.BasePrintTest;
+import android.print.test.services.FirstPrintService;
+import android.print.test.services.PrintServiceCallbacks;
+import android.print.test.services.PrinterDiscoverySessionCallbacks;
+import android.print.test.services.SecondPrintService;
+import android.print.test.services.StubbablePrinterDiscoverySession;
 import android.printservice.PrintJob;
 import android.util.Log;
 
diff --git a/tests/tests/print/src/android/print/cts/PrintJobTest.java b/tests/tests/print/src/android/print/cts/PrintJobTest.java
index f3081fb..1fcfc17 100644
--- a/tests/tests/print/src/android/print/cts/PrintJobTest.java
+++ b/tests/tests/print/src/android/print/cts/PrintJobTest.java
@@ -16,6 +16,14 @@
 
 package android.print.cts;
 
+import static android.print.test.Utils.eventually;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertNull;
+import static org.junit.Assert.assertTrue;
+
 import android.print.PrintAttributes;
 import android.print.PrintAttributes.Margins;
 import android.print.PrintAttributes.MediaSize;
@@ -26,23 +34,22 @@
 import android.print.PrinterCapabilitiesInfo;
 import android.print.PrinterId;
 import android.print.PrinterInfo;
-import android.print.cts.services.CustomPrintOptionsActivity;
-import android.print.cts.services.FirstPrintService;
-import android.print.cts.services.PrintServiceCallbacks;
-import android.print.cts.services.PrinterDiscoverySessionCallbacks;
-import android.print.cts.services.SecondPrintService;
-import android.print.cts.services.StubbablePrinterDiscoverySession;
+import android.print.test.BasePrintTest;
+import android.print.test.services.CustomPrintOptionsActivity;
+import android.print.test.services.FirstPrintService;
+import android.print.test.services.PrintServiceCallbacks;
+import android.print.test.services.PrinterDiscoverySessionCallbacks;
+import android.print.test.services.SecondPrintService;
+import android.print.test.services.StubbablePrinterDiscoverySession;
 import android.printservice.PrintJob;
 import android.support.test.runner.AndroidJUnit4;
+
 import org.junit.Before;
 import org.junit.Test;
 import org.junit.runner.RunWith;
 
 import java.util.ArrayList;
 
-import static android.print.cts.Utils.eventually;
-import static org.junit.Assert.*;
-
 /**
  * Tests all possible states of print jobs.
  */
diff --git a/tests/tests/print/src/android/print/cts/PrintServicesTest.java b/tests/tests/print/src/android/print/cts/PrintServicesTest.java
index 7be5fc1..b5a23e2 100644
--- a/tests/tests/print/src/android/print/cts/PrintServicesTest.java
+++ b/tests/tests/print/src/android/print/cts/PrintServicesTest.java
@@ -16,6 +16,16 @@
 
 package android.print.cts;
 
+import static android.print.test.Utils.assertException;
+import static android.print.test.Utils.eventually;
+import static android.print.test.Utils.getPrintJob;
+import static android.print.test.Utils.runOnMainThread;
+
+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 android.app.Activity;
 import android.app.PendingIntent;
 import android.content.ComponentName;
@@ -38,13 +48,14 @@
 import android.print.PrinterCapabilitiesInfo;
 import android.print.PrinterId;
 import android.print.PrinterInfo;
-import android.print.cts.services.FirstPrintService;
-import android.print.cts.services.InfoActivity;
-import android.print.cts.services.PrintServiceCallbacks;
-import android.print.cts.services.PrinterDiscoverySessionCallbacks;
-import android.print.cts.services.SecondPrintService;
-import android.print.cts.services.StubbablePrintService;
-import android.print.cts.services.StubbablePrinterDiscoverySession;
+import android.print.test.BasePrintTest;
+import android.print.test.services.FirstPrintService;
+import android.print.test.services.InfoActivity;
+import android.print.test.services.PrintServiceCallbacks;
+import android.print.test.services.PrinterDiscoverySessionCallbacks;
+import android.print.test.services.SecondPrintService;
+import android.print.test.services.StubbablePrintService;
+import android.print.test.services.StubbablePrinterDiscoverySession;
 import android.printservice.CustomPrinterIconCallback;
 import android.printservice.PrintJob;
 import android.printservice.PrintService;
@@ -52,15 +63,13 @@
 import android.support.test.uiautomator.UiDevice;
 import android.support.test.uiautomator.UiObject;
 import android.support.test.uiautomator.UiSelector;
+
 import org.junit.Test;
 import org.junit.runner.RunWith;
 
 import java.util.ArrayList;
 import java.util.List;
 
-import static android.print.cts.Utils.*;
-import static org.junit.Assert.*;
-
 /**
  * Test the interface from a print service to the print manager
  */
diff --git a/tests/tests/print/src/android/print/cts/PrinterCapabilitiesChangeTest.java b/tests/tests/print/src/android/print/cts/PrinterCapabilitiesChangeTest.java
index 9b277e9..251b371 100644
--- a/tests/tests/print/src/android/print/cts/PrinterCapabilitiesChangeTest.java
+++ b/tests/tests/print/src/android/print/cts/PrinterCapabilitiesChangeTest.java
@@ -16,6 +16,8 @@
 
 package android.print.cts;
 
+import static android.print.test.Utils.runOnMainThread;
+
 import android.os.ParcelFileDescriptor;
 import android.print.PageRange;
 import android.print.PrintAttributes;
@@ -29,14 +31,14 @@
 import android.print.PrinterCapabilitiesInfo;
 import android.print.PrinterId;
 import android.print.PrinterInfo;
-import android.print.cts.services.FirstPrintService;
-import android.print.cts.services.PrintServiceCallbacks;
-import android.print.cts.services.PrinterDiscoverySessionCallbacks;
-import android.print.cts.services.SecondPrintService;
-import android.print.cts.services.StubbablePrinterDiscoverySession;
-import android.support.test.uiautomator.UiObject;
-import android.support.test.uiautomator.UiSelector;
+import android.print.test.BasePrintTest;
+import android.print.test.services.FirstPrintService;
+import android.print.test.services.PrintServiceCallbacks;
+import android.print.test.services.PrinterDiscoverySessionCallbacks;
+import android.print.test.services.SecondPrintService;
+import android.print.test.services.StubbablePrinterDiscoverySession;
 import android.util.Log;
+
 import org.junit.Before;
 import org.junit.Test;
 import org.junit.runner.RunWith;
@@ -47,8 +49,6 @@
 import java.util.List;
 import java.util.concurrent.TimeoutException;
 
-import static android.print.cts.Utils.runOnMainThread;
-
 /**
  * This test verifies changes to the printer capabilities are applied correctly.
  */
@@ -154,23 +154,6 @@
                         PrinterInfo.STATUS_UNAVAILABLE)));
     }
 
-    /**
-     * Wait until the message is shown that indicates that a printer is unavilable.
-     *
-     * @throws Exception If anything was unexpected.
-     */
-    private void waitForPrinterUnavailable() throws Exception {
-        final String PRINTER_UNAVAILABLE_MSG =
-                getPrintSpoolerString("print_error_printer_unavailable");
-
-        UiObject message = getUiDevice().findObject(new UiSelector().resourceId(
-                "com.android.printspooler:id/message"));
-        if (!message.getText().equals(PRINTER_UNAVAILABLE_MSG)) {
-            throw new Exception("Wrong message: " + message.getText() + " instead of " +
-                    PRINTER_UNAVAILABLE_MSG);
-        }
-    }
-
     @Before
     public void setUpPrinting() throws Exception {
         // Create the mSession[0] callbacks that we will be checking.
diff --git a/tests/tests/print/src/android/print/cts/PrinterCapabilitiesTest.java b/tests/tests/print/src/android/print/cts/PrinterCapabilitiesTest.java
index 3ea03af..3954948 100644
--- a/tests/tests/print/src/android/print/cts/PrinterCapabilitiesTest.java
+++ b/tests/tests/print/src/android/print/cts/PrinterCapabilitiesTest.java
@@ -16,6 +16,11 @@
 
 package android.print.cts;
 
+import static android.print.test.Utils.assertException;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertTrue;
+
 import android.os.ParcelFileDescriptor;
 import android.print.PageRange;
 import android.print.PrintAttributes;
@@ -29,12 +34,14 @@
 import android.print.PrinterCapabilitiesInfo;
 import android.print.PrinterId;
 import android.print.PrinterInfo;
-import android.print.cts.services.FirstPrintService;
-import android.print.cts.services.PrintServiceCallbacks;
-import android.print.cts.services.PrinterDiscoverySessionCallbacks;
-import android.print.cts.services.SecondPrintService;
-import android.print.cts.services.StubbablePrinterDiscoverySession;
+import android.print.test.BasePrintTest;
+import android.print.test.services.FirstPrintService;
+import android.print.test.services.PrintServiceCallbacks;
+import android.print.test.services.PrinterDiscoverySessionCallbacks;
+import android.print.test.services.SecondPrintService;
+import android.print.test.services.StubbablePrinterDiscoverySession;
 import android.support.test.runner.AndroidJUnit4;
+
 import org.junit.Test;
 import org.junit.runner.RunWith;
 
@@ -42,9 +49,6 @@
 import java.util.function.Consumer;
 import java.util.function.Function;
 
-import static android.print.cts.Utils.assertException;
-import static org.junit.Assert.*;
-
 /**
  * This test verifies changes to the printer capabilities are applied correctly.
  */
diff --git a/tests/tests/print/src/android/print/cts/PrinterDiscoverySessionLifecycleTest.java b/tests/tests/print/src/android/print/cts/PrinterDiscoverySessionLifecycleTest.java
index 499d21e..a21ed18 100644
--- a/tests/tests/print/src/android/print/cts/PrinterDiscoverySessionLifecycleTest.java
+++ b/tests/tests/print/src/android/print/cts/PrinterDiscoverySessionLifecycleTest.java
@@ -16,8 +16,13 @@
 
 package android.print.cts;
 
-import static android.print.cts.Utils.*;
-import static org.junit.Assert.*;
+import static android.print.test.Utils.eventually;
+import static android.print.test.Utils.runOnMainThread;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertTrue;
 import static org.mockito.Mockito.inOrder;
 
 import android.print.PrintAttributes;
@@ -28,15 +33,19 @@
 import android.print.PrinterCapabilitiesInfo;
 import android.print.PrinterId;
 import android.print.PrinterInfo;
-import android.print.cts.services.FirstPrintService;
-import android.print.cts.services.PrintServiceCallbacks;
-import android.print.cts.services.PrinterDiscoverySessionCallbacks;
-import android.print.cts.services.SecondPrintService;
-import android.print.cts.services.StubbablePrinterDiscoverySession;
+import android.print.test.BasePrintTest;
+import android.print.test.services.FirstPrintService;
+import android.print.test.services.PrintServiceCallbacks;
+import android.print.test.services.PrinterDiscoverySessionCallbacks;
+import android.print.test.services.SecondPrintService;
+import android.print.test.services.StubbablePrinterDiscoverySession;
 import android.printservice.PrintJob;
 import android.printservice.PrinterDiscoverySession;
-
+import android.support.annotation.NonNull;
 import android.support.test.runner.AndroidJUnit4;
+import android.support.test.uiautomator.UiObject;
+import android.support.test.uiautomator.UiSelector;
+
 import org.junit.Before;
 import org.junit.Test;
 import org.junit.runner.RunWith;
@@ -53,10 +62,7 @@
  */
 @RunWith(AndroidJUnit4.class)
 public class PrinterDiscoverySessionLifecycleTest extends BasePrintTest {
-    private static final String FIRST_PRINTER_NAME = "First printer";
-    private static final String SECOND_PRINTER_NAME = "Second printer";
-
-    private static final String FIRST_PRINTER_LOCAL_ID= "first_printer";
+    private static final String FIRST_PRINTER_LOCAL_ID = "first_printer";
     private static final String SECOND_PRINTER_LOCAL_ID = "second_printer";
 
     private static StubbablePrinterDiscoverySession sSession;
@@ -66,6 +72,172 @@
         clearPrintSpoolerData();
     }
 
+    /**
+     * Add a printer to {@#sSession}.
+     *
+     * @param localId The id of the printer to add
+     * @param hasCapabilities If the printer has capabilities
+     */
+    private void addPrinter(@NonNull String localId, boolean hasCapabilities) {
+        // Add the first printer.
+        PrinterId firstPrinterId = sSession.getService().generatePrinterId(
+                localId);
+
+        PrinterInfo.Builder printer = new PrinterInfo.Builder(firstPrinterId,
+                localId, PrinterInfo.STATUS_IDLE);
+
+        if (hasCapabilities) {
+            printer.setCapabilities(new PrinterCapabilitiesInfo.Builder(firstPrinterId)
+                    .setMinMargins(new Margins(200, 200, 200, 200))
+                    .addMediaSize(MediaSize.ISO_A0, true)
+                    .addResolution(new Resolution("300x300", "300x300", 300, 300), true)
+                    .setColorModes(PrintAttributes.COLOR_MODE_COLOR,
+                            PrintAttributes.COLOR_MODE_COLOR)
+                    .build());
+        }
+
+        sSession.addPrinters(Collections.singletonList(printer.build()));
+    }
+
+    /**
+     * Make {@code localPrinterId} the default printer. This requires a full print workflow.
+     *
+     * As a side-effect also approved the print service.
+     *
+     * @param localPrinterId The printer to make default
+     */
+    private void makeDefaultPrinter(String localPrinterId) throws Throwable {
+        PrintDocumentAdapter adapter = createDefaultPrintDocumentAdapter(1);
+
+        print(adapter);
+        waitForWriteAdapterCallback(1);
+
+        runOnMainThread(() -> addPrinter(localPrinterId, true));
+        selectPrinter(localPrinterId);
+        waitForWriteAdapterCallback(2);
+
+        clickPrintButton();
+        answerPrintServicesWarning(true);
+
+        waitForPrinterDiscoverySessionDestroyCallbackCalled(1);
+        resetCounters();
+    }
+
+    /**
+     * Select a printer in the all printers activity
+     *
+     * @param printerName The name of the printer to select
+     */
+    private void selectInAllPrintersActivity(@NonNull String printerName) throws Exception {
+        while (true) {
+            UiObject printerItem = getUiDevice().findObject(
+                    new UiSelector().text(printerName));
+
+            if (printerItem.isEnabled()) {
+                printerItem.click();
+                break;
+            } else {
+                Thread.sleep(100);
+            }
+        }
+    }
+
+    @Test
+    public void defaultPrinterBecomesAvailableWhileInBackground() throws Throwable {
+        // Create the session callbacks that we will be checking.
+        final PrinterDiscoverySessionCallbacks firstSessionCallbacks =
+                createMockPrinterDiscoverySessionCallbacks(invocation -> {
+                    sSession =
+                            ((PrinterDiscoverySessionCallbacks) invocation.getMock()).getSession();
+
+                    onPrinterDiscoverySessionCreateCalled();
+                    return null;
+                }, null, null, null, null, null, invocation -> {
+                    onPrinterDiscoverySessionDestroyCalled();
+                    return null;
+                });
+
+        // Create the service callbacks for the first print service.
+        PrintServiceCallbacks firstServiceCallbacks = createMockPrintServiceCallbacks(
+                invocation -> firstSessionCallbacks, null, null);
+
+        // Configure the print services.
+        FirstPrintService.setCallbacks(firstServiceCallbacks);
+        SecondPrintService.setCallbacks(createSecondMockPrintServiceCallbacks());
+
+        makeDefaultPrinter(FIRST_PRINTER_LOCAL_ID);
+
+        PrintDocumentAdapter adapter = createDefaultPrintDocumentAdapter(1);
+        print(adapter);
+        waitForPrinterDiscoverySessionCreateCallbackCalled();
+
+        waitForPrinterUnavailable();
+
+        selectPrinter("All printers…");
+        // Let all printers activity start
+        Thread.sleep(500);
+
+        // Add printer
+        runOnMainThread(() -> addPrinter(FIRST_PRINTER_LOCAL_ID, true));
+
+        // Select printer once available (this returns to main print activity)
+        selectInAllPrintersActivity(FIRST_PRINTER_LOCAL_ID);
+
+        // Wait for preview to load and finish print
+        waitForWriteAdapterCallback(1);
+        clickPrintButton();
+        waitForPrinterDiscoverySessionDestroyCallbackCalled(1);
+    }
+
+    @Test
+    public void defaultPrinterBecomesUsableWhileInBackground() throws Throwable {
+        // Create the session callbacks that we will be checking.
+        final PrinterDiscoverySessionCallbacks firstSessionCallbacks =
+                createMockPrinterDiscoverySessionCallbacks(invocation -> {
+                    sSession =
+                            ((PrinterDiscoverySessionCallbacks) invocation.getMock()).getSession();
+
+                    onPrinterDiscoverySessionCreateCalled();
+                    return null;
+                }, null, null, null, null, null, invocation -> {
+                    onPrinterDiscoverySessionDestroyCalled();
+                    return null;
+                });
+
+        // Create the service callbacks for the first print service.
+        PrintServiceCallbacks firstServiceCallbacks = createMockPrintServiceCallbacks(
+                invocation -> firstSessionCallbacks, null, null);
+
+        // Configure the print services.
+        FirstPrintService.setCallbacks(firstServiceCallbacks);
+        SecondPrintService.setCallbacks(createSecondMockPrintServiceCallbacks());
+
+        makeDefaultPrinter(FIRST_PRINTER_LOCAL_ID);
+
+        PrintDocumentAdapter adapter = createDefaultPrintDocumentAdapter(1);
+        print(adapter);
+        waitForPrinterDiscoverySessionCreateCallbackCalled();
+
+        // Add printer but do not enable it (capabilities == null)
+        runOnMainThread(() -> addPrinter(FIRST_PRINTER_LOCAL_ID, false));
+        waitForPrinterUnavailable();
+
+        selectPrinter("All printers…");
+        // Let all printers activity start
+        Thread.sleep(500);
+
+        // Enable printer
+        runOnMainThread(() -> addPrinter(FIRST_PRINTER_LOCAL_ID, true));
+
+        // Select printer once available (this returns to main print activity)
+        selectInAllPrintersActivity(FIRST_PRINTER_LOCAL_ID);
+
+        // Wait for preview to load and finish print
+        waitForWriteAdapterCallback(1);
+        clickPrintButton();
+        waitForPrinterDiscoverySessionDestroyCallbackCalled(1);
+    }
+
     @Test
     public void normalLifecycle() throws Throwable {
         // Create the session callbacks that we will be checking.
@@ -99,7 +271,7 @@
         runOnMainThread(() -> assertEquals(0, sSession.getTrackedPrinters().size()));
 
         // Select the first printer.
-        selectPrinter(FIRST_PRINTER_NAME);
+        selectPrinter(FIRST_PRINTER_LOCAL_ID);
 
         eventually(() -> runOnMainThread(() -> assertEquals(FIRST_PRINTER_LOCAL_ID,
                 sSession.getTrackedPrinters().get(0).getLocalId())));
@@ -111,7 +283,7 @@
 
         // Select the second printer (same capabilities as the other
         // one so no layout should happen).
-        selectPrinter(SECOND_PRINTER_NAME);
+        selectPrinter(SECOND_PRINTER_LOCAL_ID);
 
         eventually(() -> runOnMainThread(() -> assertEquals(SECOND_PRINTER_LOCAL_ID,
                 sSession.getTrackedPrinters().get(0).getLocalId())));
@@ -205,7 +377,7 @@
         runOnMainThread(() -> assertEquals(0, sSession.getTrackedPrinters().size()));
 
         // Select the first printer.
-        selectPrinter(FIRST_PRINTER_NAME);
+        selectPrinter(FIRST_PRINTER_LOCAL_ID);
 
         eventually(() -> runOnMainThread(() -> assertEquals(FIRST_PRINTER_LOCAL_ID,
                 sSession.getTrackedPrinters().get(0).getLocalId())));
@@ -295,7 +467,7 @@
         runOnMainThread(() -> assertEquals(0, sSession.getTrackedPrinters().size()));
 
         // Select the first printer.
-        selectPrinter(FIRST_PRINTER_NAME);
+        selectPrinter(FIRST_PRINTER_LOCAL_ID);
 
         eventually(() -> runOnMainThread(() -> assertEquals(FIRST_PRINTER_LOCAL_ID,
                 sSession.getTrackedPrinters().get(0).getLocalId())));
@@ -491,27 +663,9 @@
 
             assertTrue(sSession.isPrinterDiscoveryStarted());
 
-            if (sSession.getPrinters().isEmpty()) {
-                List<PrinterInfo> printers = new ArrayList<>();
+            addPrinter(FIRST_PRINTER_LOCAL_ID, false);
+            addPrinter(SECOND_PRINTER_LOCAL_ID, false);
 
-                // Add the first printer.
-                PrinterId firstPrinterId = sSession.getService().generatePrinterId(
-                        FIRST_PRINTER_LOCAL_ID);
-                PrinterInfo firstPrinter = new PrinterInfo.Builder(firstPrinterId,
-                        FIRST_PRINTER_NAME, PrinterInfo.STATUS_IDLE)
-                    .build();
-                printers.add(firstPrinter);
-
-                // Add the first printer.
-                PrinterId secondPrinterId = sSession.getService().generatePrinterId(
-                        SECOND_PRINTER_LOCAL_ID);
-                PrinterInfo secondPrinter = new PrinterInfo.Builder(secondPrinterId,
-                        SECOND_PRINTER_NAME, PrinterInfo.STATUS_IDLE)
-                    .build();
-                printers.add(secondPrinter);
-
-                sSession.addPrinters(printers);
-            }
             return null;
         }, invocation -> {
             assertFalse(sSession.isPrinterDiscoveryStarted());
diff --git a/tests/tests/print/src/android/print/cts/PrinterInfoTest.java b/tests/tests/print/src/android/print/cts/PrinterInfoTest.java
index 9c5c6ab..6a65e00 100644
--- a/tests/tests/print/src/android/print/cts/PrinterInfoTest.java
+++ b/tests/tests/print/src/android/print/cts/PrinterInfoTest.java
@@ -27,11 +27,12 @@
 import android.print.PrinterCapabilitiesInfo;
 import android.print.PrinterId;
 import android.print.PrinterInfo;
-import android.print.cts.services.FirstPrintService;
-import android.print.cts.services.PrintServiceCallbacks;
-import android.print.cts.services.PrinterDiscoverySessionCallbacks;
-import android.print.cts.services.SecondPrintService;
-import android.print.cts.services.StubbablePrinterDiscoverySession;
+import android.print.test.BasePrintTest;
+import android.print.test.services.FirstPrintService;
+import android.print.test.services.PrintServiceCallbacks;
+import android.print.test.services.PrinterDiscoverySessionCallbacks;
+import android.print.test.services.SecondPrintService;
+import android.print.test.services.StubbablePrinterDiscoverySession;
 import android.support.test.runner.AndroidJUnit4;
 import android.support.test.uiautomator.UiObject;
 import android.support.test.uiautomator.UiSelector;
diff --git a/tests/tests/print/src/android/print/cts/Utils.java b/tests/tests/print/src/android/print/cts/Utils.java
deleted file mode 100644
index 93f5632..0000000
--- a/tests/tests/print/src/android/print/cts/Utils.java
+++ /dev/null
@@ -1,148 +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.print.cts;
-
-import android.content.Context;
-import android.os.Handler;
-import android.os.Looper;
-import android.print.PrintJob;
-import android.print.PrintManager;
-import android.support.annotation.NonNull;
-import android.util.Log;
-
-/**
- * Utilities for print tests
- */
-public class Utils {
-    private static final String LOG_TAG = "Utils";
-
-    /**
-     * A {@link Runnable} that can throw an {@link Throwable}.
-     */
-    public interface Invokable {
-        void run() throws Throwable;
-    }
-
-    /**
-     * Run a {@link Invokable} and expect and {@link Throwable} of a certain type.
-     *
-     * @param r             The {@link Invokable} to run
-     * @param expectedClass The expected {@link Throwable} type
-     */
-    public static void assertException(@NonNull Invokable r,
-            @NonNull Class<? extends Throwable> expectedClass) throws Throwable {
-        try {
-            r.run();
-        } catch (Throwable e) {
-            if (e.getClass().isAssignableFrom(expectedClass)) {
-                return;
-            } else {
-                Log.e(LOG_TAG, "Expected: " + expectedClass.getName() + ", got: "
-                        + e.getClass().getName());
-                throw e;
-            }
-        }
-
-        throw new AssertionError("No throwable thrown");
-    }
-
-    /**
-     * Run a {@link Invokable} on the main thread and forward the {@link Throwable} if one was
-     * thrown.
-     *
-     * @param r The {@link Invokable} to run
-     *
-     * @throws Throwable If the {@link Runnable} caused an issue
-     */
-    static void runOnMainThread(@NonNull final Invokable r) throws Throwable {
-        final Object synchronizer = new Object();
-        final Throwable[] thrown = new Throwable[1];
-
-        synchronized (synchronizer) {
-            (new Handler(Looper.getMainLooper())).post(() -> {
-                synchronized (synchronizer) {
-                    try {
-                        r.run();
-                    } catch (Throwable t) {
-                        thrown[0] = t;
-                    }
-
-                    synchronizer.notify();
-                }
-            });
-
-            synchronizer.wait();
-        }
-
-        if (thrown[0] != null) {
-            throw thrown[0];
-        }
-    }
-
-    /**
-     * 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 {
-        long start = System.currentTimeMillis();
-
-        while (true) {
-            try {
-                r.run();
-                break;
-            } catch (Throwable e) {
-                if (System.currentTimeMillis() - start < BasePrintTest.OPERATION_TIMEOUT_MILLIS) {
-                    Log.e(LOG_TAG, "Ignoring exception", e);
-
-                    try {
-                        Thread.sleep(500);
-                    } catch (InterruptedException e1) {
-                        Log.e(LOG_TAG, "Interrupted", e);
-                    }
-                } else {
-                    throw e;
-                }
-            }
-        }
-    }
-
-    /**
-     * @param name Name of print job
-     *
-     * @return The print job for the name
-     *
-     * @throws Exception If print job could not be found
-     */
-    static @NonNull PrintJob getPrintJob(@NonNull PrintManager pm, @NonNull String name)
-            throws Exception {
-        for (android.print.PrintJob job : pm.getPrintJobs()) {
-            if (job.getInfo().getLabel().equals(name)) {
-                return job;
-            }
-        }
-
-        throw new Exception("Print job " + name + " not found in " + pm.getPrintJobs());
-    }
-
-    /**
-     * @return The print manager
-     */
-    static @NonNull PrintManager getPrintManager(@NonNull Context context) {
-        return (PrintManager) context.getSystemService(Context.PRINT_SERVICE);
-    }
-}
diff --git a/tests/tests/print/src/android/print/cts/services/AddPrintersActivity.java b/tests/tests/print/src/android/print/cts/services/AddPrintersActivity.java
deleted file mode 100644
index c72d6f9..0000000
--- a/tests/tests/print/src/android/print/cts/services/AddPrintersActivity.java
+++ /dev/null
@@ -1,28 +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.print.cts.services;
-
-import android.app.Activity;
-import android.os.Bundle;
-
-public class AddPrintersActivity extends Activity {
-
-    @Override
-    protected void onCreate(Bundle savedInstanceState) {
-        super.onCreate(savedInstanceState);
-    }
-}
diff --git a/tests/tests/print/src/android/print/cts/services/CustomPrintOptionsActivity.java b/tests/tests/print/src/android/print/cts/services/CustomPrintOptionsActivity.java
deleted file mode 100644
index cf52ece..0000000
--- a/tests/tests/print/src/android/print/cts/services/CustomPrintOptionsActivity.java
+++ /dev/null
@@ -1,76 +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.print.cts.services;
-
-import android.app.Activity;
-import android.content.Intent;
-import android.os.Bundle;
-import android.print.PrintJobInfo;
-import android.print.PrinterInfo;
-import android.printservice.PrintService;
-
-/**
- * Custom print options activity for both print services
- */
-public class CustomPrintOptionsActivity extends Activity {
-    /** Lock for {@link #sCallback} */
-    private static final Object sLock = new Object();
-
-    /** Currently registered callback for _both_ first and second print service. */
-    private static CustomPrintOptionsCallback sCallback = null;
-
-    /**
-     * Set a new callback called when the custom options activity is launched.
-     *
-     * @param callback The new callback or null, if the callback should be unregistered.
-     */
-    public static void setCallBack(CustomPrintOptionsCallback callback) {
-        synchronized (sLock) {
-            sCallback = callback;
-        }
-    }
-
-    /**
-     * Callback executed for this activity. Set via {@link #setCallBack}.
-     */
-    public interface CustomPrintOptionsCallback {
-        PrintJobInfo executeCustomPrintOptionsActivity(PrintJobInfo printJob,
-                PrinterInfo printer);
-    }
-
-    @Override
-    protected void onCreate(Bundle savedInstanceState) {
-        super.onCreate(savedInstanceState);
-
-        Intent result = new Intent();
-
-        synchronized (sLock) {
-            if (sCallback != null) {
-                PrintJobInfo printJobInfo = getIntent().getParcelableExtra(
-                        PrintService.EXTRA_PRINT_JOB_INFO);
-                PrinterInfo printerInfo = getIntent().getParcelableExtra(
-                        "android.intent.extra.print.EXTRA_PRINTER_INFO");
-
-                result.putExtra(PrintService.EXTRA_PRINT_JOB_INFO,
-                        sCallback.executeCustomPrintOptionsActivity(printJobInfo, printerInfo));
-            }
-        }
-
-        setResult(Activity.RESULT_OK, result);
-        finish();
-    }
-}
diff --git a/tests/tests/print/src/android/print/cts/services/FirstPrintService.java b/tests/tests/print/src/android/print/cts/services/FirstPrintService.java
deleted file mode 100644
index a234de4..0000000
--- a/tests/tests/print/src/android/print/cts/services/FirstPrintService.java
+++ /dev/null
@@ -1,40 +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.print.cts.services;
-
-public class FirstPrintService extends StubbablePrintService {
-
-    private static final Object sLock = new Object();
-
-    private static PrintServiceCallbacks sCallbacks;
-
-    public static void setCallbacks(PrintServiceCallbacks callbacks) {
-        synchronized (sLock) {
-            sCallbacks = callbacks;
-        }
-    }
-
-    @Override
-    protected PrintServiceCallbacks getCallbacks() {
-        synchronized (sLock) {
-            if (sCallbacks != null) {
-                sCallbacks.setService(this);
-            }
-            return sCallbacks;
-        }
-    }
-}
diff --git a/tests/tests/print/src/android/print/cts/services/InfoActivity.java b/tests/tests/print/src/android/print/cts/services/InfoActivity.java
deleted file mode 100644
index 22c1bb5..0000000
--- a/tests/tests/print/src/android/print/cts/services/InfoActivity.java
+++ /dev/null
@@ -1,56 +0,0 @@
-/*
- * Copyright (C) 2016 The Android Open Source Project
- *
- *  Licensed under the Apache License, Version 2.0 (the "License");
- *  you may not use this file except in compliance with the License.
- *  You may obtain a copy of the License at
- *
- *       http://www.apache.org/licenses/LICENSE-2.0
- *
- *  Unless required by applicable law or agreed to in writing, software
- *  distributed under the License is distributed on an "AS IS" BASIS,
- *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- *  See the License for the specific language governing permissions and
- *  limitations under the License.
- */
-
-package android.print.cts.services;
-
-import android.app.Activity;
-import android.os.Bundle;
-
-import java.util.ArrayList;
-import java.util.Observable;
-
-public class InfoActivity extends Activity {
-    public interface Observer {
-        void onCreate(Activity createdActivity);
-    }
-
-    private static final ArrayList<Observer> sObservers = new ArrayList<>();
-
-    public static void addObserver(Observer observer) {
-        synchronized (sObservers) {
-            sObservers.add(observer);
-        }
-    }
-
-    public static void clearObservers() {
-        synchronized (sObservers) {
-            sObservers.clear();
-        }
-    }
-
-    @Override
-    protected void onCreate(Bundle savedInstanceState) {
-        super.onCreate(savedInstanceState);
-
-        synchronized (sObservers) {
-            ArrayList<Observer> observers = (ArrayList<Observer>) sObservers.clone();
-
-            for (Observer observer : observers) {
-                observer.onCreate(this);
-            }
-        }
-    }
-}
diff --git a/tests/tests/print/src/android/print/cts/services/PrintServiceCallbacks.java b/tests/tests/print/src/android/print/cts/services/PrintServiceCallbacks.java
deleted file mode 100644
index 749e8a9..0000000
--- a/tests/tests/print/src/android/print/cts/services/PrintServiceCallbacks.java
+++ /dev/null
@@ -1,38 +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.print.cts.services;
-
-import android.printservice.PrintJob;
-
-public abstract class PrintServiceCallbacks {
-
-    private StubbablePrintService mService;
-
-    public StubbablePrintService getService() {
-        return mService;
-    }
-
-    public void setService(StubbablePrintService service) {
-        mService = service;
-    }
-
-    public abstract PrinterDiscoverySessionCallbacks onCreatePrinterDiscoverySessionCallbacks();
-
-    public abstract void onRequestCancelPrintJob(PrintJob printJob);
-
-    public abstract void onPrintJobQueued(PrintJob printJob);
-}
diff --git a/tests/tests/print/src/android/print/cts/services/PrinterDiscoverySessionCallbacks.java b/tests/tests/print/src/android/print/cts/services/PrinterDiscoverySessionCallbacks.java
deleted file mode 100644
index ebddda1..0000000
--- a/tests/tests/print/src/android/print/cts/services/PrinterDiscoverySessionCallbacks.java
+++ /dev/null
@@ -1,51 +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.print.cts.services;
-
-import android.os.CancellationSignal;
-import android.print.PrinterId;
-import android.printservice.CustomPrinterIconCallback;
-
-import java.util.List;
-
-public abstract class PrinterDiscoverySessionCallbacks {
-
-    private StubbablePrinterDiscoverySession mSession;
-
-    public void setSession(StubbablePrinterDiscoverySession session) {
-        mSession = session;
-    }
-
-    public StubbablePrinterDiscoverySession getSession() {
-        return mSession;
-    }
-
-    public abstract void onStartPrinterDiscovery(List<PrinterId> priorityList);
-
-    public abstract void onStopPrinterDiscovery();
-
-    public abstract void onValidatePrinters(List<PrinterId> printerIds);
-
-    public abstract void onStartPrinterStateTracking(PrinterId printerId);
-
-    public abstract void onRequestCustomPrinterIcon(PrinterId printerId,
-            CancellationSignal cancellationSignal, CustomPrinterIconCallback callback);
-
-    public abstract void onStopPrinterStateTracking(PrinterId printerId);
-
-    public abstract void onDestroy();
-}
diff --git a/tests/tests/print/src/android/print/cts/services/SecondPrintService.java b/tests/tests/print/src/android/print/cts/services/SecondPrintService.java
deleted file mode 100644
index 1029a8e..0000000
--- a/tests/tests/print/src/android/print/cts/services/SecondPrintService.java
+++ /dev/null
@@ -1,40 +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.print.cts.services;
-
-public class SecondPrintService extends StubbablePrintService {
-
-    private static final Object sLock = new Object();
-
-    private static PrintServiceCallbacks sCallbacks;
-
-    public static void setCallbacks(PrintServiceCallbacks callbacks) {
-        synchronized (sLock) {
-            sCallbacks = callbacks;
-        }
-    }
-
-    @Override
-    protected PrintServiceCallbacks getCallbacks() {
-        synchronized (sLock) {
-            if (sCallbacks != null) {
-                sCallbacks.setService(this);
-            }
-            return sCallbacks;
-        }
-    }
-}
diff --git a/tests/tests/print/src/android/print/cts/services/SettingsActivity.java b/tests/tests/print/src/android/print/cts/services/SettingsActivity.java
deleted file mode 100644
index eb23574..0000000
--- a/tests/tests/print/src/android/print/cts/services/SettingsActivity.java
+++ /dev/null
@@ -1,28 +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.print.cts.services;
-
-import android.app.Activity;
-import android.os.Bundle;
-
-public class SettingsActivity extends Activity {
-
-    @Override
-    protected void onCreate(Bundle savedInstanceState) {
-        super.onCreate(savedInstanceState);
-    }
-}
diff --git a/tests/tests/print/src/android/print/cts/services/StubbablePrintService.java b/tests/tests/print/src/android/print/cts/services/StubbablePrintService.java
deleted file mode 100644
index 8c3b89b..0000000
--- a/tests/tests/print/src/android/print/cts/services/StubbablePrintService.java
+++ /dev/null
@@ -1,64 +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.print.cts.services;
-
-import android.content.Context;
-import android.printservice.PrintJob;
-import android.printservice.PrintService;
-import android.printservice.PrinterDiscoverySession;
-
-import java.util.List;
-
-public abstract class StubbablePrintService extends PrintService {
-
-    @Override
-    public PrinterDiscoverySession onCreatePrinterDiscoverySession() {
-        PrintServiceCallbacks callbacks = getCallbacks();
-        if (callbacks != null) {
-            return new StubbablePrinterDiscoverySession(this,
-                    getCallbacks().onCreatePrinterDiscoverySessionCallbacks());
-        }
-        return null;
-    }
-
-    @Override
-    public void onRequestCancelPrintJob(PrintJob printJob) {
-        PrintServiceCallbacks callbacks = getCallbacks();
-        if (callbacks != null) {
-            callbacks.onRequestCancelPrintJob(printJob);
-        }
-    }
-
-    @Override
-    public void onPrintJobQueued(PrintJob printJob) {
-        PrintServiceCallbacks callbacks = getCallbacks();
-        if (callbacks != null) {
-            callbacks.onPrintJobQueued(printJob);
-        }
-    }
-
-    protected abstract PrintServiceCallbacks getCallbacks();
-
-    public void callAttachBaseContext(Context base) {
-        attachBaseContext(base);
-    }
-
-    public List<PrintJob> callGetActivePrintJobs() {
-        return getActivePrintJobs();
-    }
-
-}
diff --git a/tests/tests/print/src/android/print/cts/services/StubbablePrinterDiscoverySession.java b/tests/tests/print/src/android/print/cts/services/StubbablePrinterDiscoverySession.java
deleted file mode 100644
index e0ec1c1..0000000
--- a/tests/tests/print/src/android/print/cts/services/StubbablePrinterDiscoverySession.java
+++ /dev/null
@@ -1,95 +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.print.cts.services;
-
-import android.support.annotation.NonNull;
-import android.os.CancellationSignal;
-import android.print.PrinterId;
-import android.printservice.CustomPrinterIconCallback;
-import android.printservice.PrintService;
-import android.printservice.PrinterDiscoverySession;
-
-import java.util.List;
-
-public class StubbablePrinterDiscoverySession extends PrinterDiscoverySession {
-    private final PrintService mService;
-    private final PrinterDiscoverySessionCallbacks mCallbacks;
-
-    public StubbablePrinterDiscoverySession(PrintService service,
-            PrinterDiscoverySessionCallbacks callbacks) {
-        mService = service;
-        mCallbacks = callbacks;
-        if (mCallbacks != null) {
-            mCallbacks.setSession(this);
-        }
-    }
-
-    public PrintService getService() {
-        return mService;
-    }
-
-    @Override
-    public void onStartPrinterDiscovery(@NonNull List<PrinterId> priorityList) {
-        if (mCallbacks != null) {
-            mCallbacks.onStartPrinterDiscovery(priorityList);
-        }
-    }
-
-    @Override
-    public void onStopPrinterDiscovery() {
-        if (mCallbacks != null) {
-            mCallbacks.onStopPrinterDiscovery();
-        }
-    }
-
-    @Override
-    public void onValidatePrinters(@NonNull List<PrinterId> printerIds) {
-        if (mCallbacks != null) {
-            mCallbacks.onValidatePrinters(printerIds);
-        }
-    }
-
-    @Override
-    public void onStartPrinterStateTracking(@NonNull PrinterId printerId) {
-        if (mCallbacks != null) {
-            mCallbacks.onStartPrinterStateTracking(printerId);
-        }
-    }
-
-    @Override
-    public void onRequestCustomPrinterIcon(@NonNull PrinterId printerId,
-            @NonNull CancellationSignal cancellationSignal,
-            @NonNull CustomPrinterIconCallback callback) {
-        if (mCallbacks != null) {
-            mCallbacks.onRequestCustomPrinterIcon(printerId, cancellationSignal, callback);
-        }
-    }
-
-    @Override
-    public void onStopPrinterStateTracking(@NonNull PrinterId printerId) {
-        if (mCallbacks != null) {
-            mCallbacks.onStopPrinterStateTracking(printerId);
-        }
-    }
-
-    @Override
-    public void onDestroy() {
-        if (mCallbacks != null) {
-            mCallbacks.onDestroy();
-        }
-    }
-}
diff --git a/tests/tests/print/src/android/print/pdf/cts/PrintedPdfDocumentTest.java b/tests/tests/print/src/android/print/pdf/cts/PrintedPdfDocumentTest.java
index 344bcfc..4c2b369 100644
--- a/tests/tests/print/src/android/print/pdf/cts/PrintedPdfDocumentTest.java
+++ b/tests/tests/print/src/android/print/pdf/cts/PrintedPdfDocumentTest.java
@@ -16,6 +16,10 @@
 
 package android.print.pdf.cts;
 
+import static android.print.test.Utils.assertException;
+
+import static org.junit.Assert.assertEquals;
+
 import android.content.Context;
 import android.graphics.Rect;
 import android.graphics.pdf.PdfDocument;
@@ -23,13 +27,11 @@
 import android.print.pdf.PrintedPdfDocument;
 import android.support.test.InstrumentationRegistry;
 import android.support.test.runner.AndroidJUnit4;
+
 import org.junit.BeforeClass;
 import org.junit.Test;
 import org.junit.runner.RunWith;
 
-import static android.print.cts.Utils.assertException;
-import static org.junit.Assert.assertEquals;
-
 /**
  * Tests {@link PrintedPdfDocument}. This class is a subclass of {@link PdfDocument}, hence only the
  * overridden methods are tested.
diff --git a/tests/tests/proto/AndroidTest.xml b/tests/tests/proto/AndroidTest.xml
index 025fc26..05df543 100644
--- a/tests/tests/proto/AndroidTest.xml
+++ b/tests/tests/proto/AndroidTest.xml
@@ -14,6 +14,7 @@
      limitations under the License.
 -->
 <configuration description="Configuration for Proto Tests">
+    <option name="test-suite-tag" value="cts" />
     <option name="config-descriptor:metadata" key="component" value="metrics" />
     <target_preparer class="com.android.tradefed.targetprep.suite.SuiteApkInstaller">
         <option name="cleanup-apks" value="true" />
diff --git a/tests/tests/proto/src/android/util/proto/cts/ProtoOutputStreamObjectTest.java b/tests/tests/proto/src/android/util/proto/cts/ProtoOutputStreamObjectTest.java
index fb23607..8178b46 100644
--- a/tests/tests/proto/src/android/util/proto/cts/ProtoOutputStreamObjectTest.java
+++ b/tests/tests/proto/src/android/util/proto/cts/ProtoOutputStreamObjectTest.java
@@ -79,7 +79,7 @@
         final ProtoOutputStream po = new ProtoOutputStream(chunkSize);
 
         long token = po.startObject(ProtoOutputStream.makeFieldId(1,
-                    ProtoOutputStream.FIELD_COUNT_SINGLE | ProtoOutputStream.FIELD_TYPE_OBJECT));
+                    ProtoOutputStream.FIELD_COUNT_SINGLE | ProtoOutputStream.FIELD_TYPE_MESSAGE));
         po.writeUInt32(ProtoOutputStream.makeFieldId(2,
                     ProtoOutputStream.FIELD_COUNT_SINGLE | ProtoOutputStream.FIELD_TYPE_UINT32),
                 'b');
@@ -106,7 +106,7 @@
         final ProtoOutputStream po = new ProtoOutputStream(chunkSize);
 
         long token = po.startObject(ProtoOutputStream.makeFieldId(1,
-                    ProtoOutputStream.FIELD_COUNT_SINGLE | ProtoOutputStream.FIELD_TYPE_OBJECT));
+                    ProtoOutputStream.FIELD_COUNT_SINGLE | ProtoOutputStream.FIELD_TYPE_MESSAGE));
         po.writeUInt32(ProtoOutputStream.makeFieldId(5000,
                     ProtoOutputStream.FIELD_COUNT_SINGLE | ProtoOutputStream.FIELD_TYPE_UINT32),
                 '\u3110');
@@ -138,7 +138,7 @@
                 'a');
 
         long token = po.startObject(ProtoOutputStream.makeFieldId(2,
-                    ProtoOutputStream.FIELD_COUNT_SINGLE | ProtoOutputStream.FIELD_TYPE_OBJECT));
+                    ProtoOutputStream.FIELD_COUNT_SINGLE | ProtoOutputStream.FIELD_TYPE_MESSAGE));
         po.writeUInt32(ProtoOutputStream.makeFieldId(3,
                     ProtoOutputStream.FIELD_COUNT_SINGLE | ProtoOutputStream.FIELD_TYPE_UINT32),
                 'b');
@@ -177,7 +177,7 @@
         final ProtoOutputStream po = new ProtoOutputStream(chunkSize);
 
         long token = po.startObject(ProtoOutputStream.makeFieldId(1,
-                    ProtoOutputStream.FIELD_COUNT_SINGLE | ProtoOutputStream.FIELD_TYPE_OBJECT));
+                    ProtoOutputStream.FIELD_COUNT_SINGLE | ProtoOutputStream.FIELD_TYPE_MESSAGE));
         po.endObject(token);
 
         Assert.assertArrayEquals(new byte[0], po.getBytes());
@@ -199,11 +199,11 @@
         final ProtoOutputStream po = new ProtoOutputStream(chunkSize);
 
         long token1 = po.startObject(ProtoOutputStream.makeFieldId(1,
-                    ProtoOutputStream.FIELD_COUNT_SINGLE | ProtoOutputStream.FIELD_TYPE_OBJECT));
+                    ProtoOutputStream.FIELD_COUNT_SINGLE | ProtoOutputStream.FIELD_TYPE_MESSAGE));
         long token2 = po.startObject(ProtoOutputStream.makeFieldId(2,
-                    ProtoOutputStream.FIELD_COUNT_SINGLE | ProtoOutputStream.FIELD_TYPE_OBJECT));
+                    ProtoOutputStream.FIELD_COUNT_SINGLE | ProtoOutputStream.FIELD_TYPE_MESSAGE));
         long token3 = po.startObject(ProtoOutputStream.makeFieldId(3,
-                    ProtoOutputStream.FIELD_COUNT_SINGLE | ProtoOutputStream.FIELD_TYPE_OBJECT));
+                    ProtoOutputStream.FIELD_COUNT_SINGLE | ProtoOutputStream.FIELD_TYPE_MESSAGE));
         po.endObject(token3);
         po.endObject(token2);
         po.endObject(token1);
@@ -231,7 +231,7 @@
                 'a');
 
         long token = po.startObject(ProtoOutputStream.makeFieldId(2,
-                    ProtoOutputStream.FIELD_COUNT_SINGLE | ProtoOutputStream.FIELD_TYPE_OBJECT));
+                    ProtoOutputStream.FIELD_COUNT_SINGLE | ProtoOutputStream.FIELD_TYPE_MESSAGE));
         po.endObject(token);
 
         po.writeUInt32(ProtoOutputStream.makeFieldId(4,
@@ -263,11 +263,11 @@
         long token;
 
         token = po.startRepeatedObject(ProtoOutputStream.makeFieldId(1,
-                    ProtoOutputStream.FIELD_COUNT_REPEATED | ProtoOutputStream.FIELD_TYPE_OBJECT));
+                    ProtoOutputStream.FIELD_COUNT_REPEATED | ProtoOutputStream.FIELD_TYPE_MESSAGE));
         po.endRepeatedObject(token);
 
         token = po.startRepeatedObject(ProtoOutputStream.makeFieldId(1,
-                    ProtoOutputStream.FIELD_COUNT_REPEATED | ProtoOutputStream.FIELD_TYPE_OBJECT));
+                    ProtoOutputStream.FIELD_COUNT_REPEATED | ProtoOutputStream.FIELD_TYPE_MESSAGE));
         po.endRepeatedObject(token);
 
         Assert.assertArrayEquals(new byte[] {
@@ -299,7 +299,7 @@
                 'x');
 
         long token = po.startObject(ProtoOutputStream.makeFieldId(2,
-                    ProtoOutputStream.FIELD_COUNT_SINGLE | ProtoOutputStream.FIELD_TYPE_OBJECT));
+                    ProtoOutputStream.FIELD_COUNT_SINGLE | ProtoOutputStream.FIELD_TYPE_MESSAGE));
         po.writeUInt32(ProtoOutputStream.makeFieldId(3,
                     ProtoOutputStream.FIELD_COUNT_SINGLE | ProtoOutputStream.FIELD_TYPE_UINT32),
                 'y');
@@ -308,7 +308,7 @@
                 "abcdefghijkl");
 
         long tokenEmpty = po.startObject(ProtoOutputStream.makeFieldId(500,
-                    ProtoOutputStream.FIELD_COUNT_SINGLE | ProtoOutputStream.FIELD_TYPE_OBJECT));
+                    ProtoOutputStream.FIELD_COUNT_SINGLE | ProtoOutputStream.FIELD_TYPE_MESSAGE));
         po.endObject(tokenEmpty);
 
         po.endObject(token);
@@ -349,19 +349,19 @@
         final ProtoOutputStream po = new ProtoOutputStream(chunkSize);
 
         long token1 = po.startObject(ProtoOutputStream.makeFieldId(1,
-                    ProtoOutputStream.FIELD_COUNT_SINGLE | ProtoOutputStream.FIELD_TYPE_OBJECT));
+                    ProtoOutputStream.FIELD_COUNT_SINGLE | ProtoOutputStream.FIELD_TYPE_MESSAGE));
         po.writeUInt32(ProtoOutputStream.makeFieldId(2,
                     ProtoOutputStream.FIELD_COUNT_SINGLE | ProtoOutputStream.FIELD_TYPE_UINT32),
                 'a');
 
         long token2 = po.startObject(ProtoOutputStream.makeFieldId(3,
-                    ProtoOutputStream.FIELD_COUNT_SINGLE | ProtoOutputStream.FIELD_TYPE_OBJECT));
+                    ProtoOutputStream.FIELD_COUNT_SINGLE | ProtoOutputStream.FIELD_TYPE_MESSAGE));
         po.writeUInt32(ProtoOutputStream.makeFieldId(4,
                     ProtoOutputStream.FIELD_COUNT_SINGLE | ProtoOutputStream.FIELD_TYPE_UINT32),
                 'b');
 
         long token3 = po.startObject(ProtoOutputStream.makeFieldId(5,
-                    ProtoOutputStream.FIELD_COUNT_SINGLE | ProtoOutputStream.FIELD_TYPE_OBJECT));
+                    ProtoOutputStream.FIELD_COUNT_SINGLE | ProtoOutputStream.FIELD_TYPE_MESSAGE));
         po.writeUInt32(ProtoOutputStream.makeFieldId(6,
                     ProtoOutputStream.FIELD_COUNT_SINGLE | ProtoOutputStream.FIELD_TYPE_UINT32),
                 'c');
@@ -394,13 +394,13 @@
         final ProtoOutputStream po = new ProtoOutputStream();
 
         long token1 = po.startObject(ProtoOutputStream.makeFieldId(1,
-                    ProtoOutputStream.FIELD_COUNT_SINGLE | ProtoOutputStream.FIELD_TYPE_OBJECT));
+                    ProtoOutputStream.FIELD_COUNT_SINGLE | ProtoOutputStream.FIELD_TYPE_MESSAGE));
         po.writeUInt32(ProtoOutputStream.makeFieldId(2,
                     ProtoOutputStream.FIELD_COUNT_SINGLE | ProtoOutputStream.FIELD_TYPE_UINT32),
                 'a');
 
         long token2 = po.startObject(ProtoOutputStream.makeFieldId(3,
-                    ProtoOutputStream.FIELD_COUNT_SINGLE | ProtoOutputStream.FIELD_TYPE_OBJECT));
+                    ProtoOutputStream.FIELD_COUNT_SINGLE | ProtoOutputStream.FIELD_TYPE_MESSAGE));
         po.writeUInt32(ProtoOutputStream.makeFieldId(4,
                     ProtoOutputStream.FIELD_COUNT_SINGLE | ProtoOutputStream.FIELD_TYPE_UINT32),
                 'b');
@@ -422,10 +422,10 @@
         final ProtoOutputStream po = new ProtoOutputStream();
 
         long token1 = po.startObject(ProtoOutputStream.makeFieldId(1,
-                    ProtoOutputStream.FIELD_COUNT_SINGLE | ProtoOutputStream.FIELD_TYPE_OBJECT));
+                    ProtoOutputStream.FIELD_COUNT_SINGLE | ProtoOutputStream.FIELD_TYPE_MESSAGE));
 
         long token2 = po.startObject(ProtoOutputStream.makeFieldId(3,
-                    ProtoOutputStream.FIELD_COUNT_SINGLE | ProtoOutputStream.FIELD_TYPE_OBJECT));
+                    ProtoOutputStream.FIELD_COUNT_SINGLE | ProtoOutputStream.FIELD_TYPE_MESSAGE));
 
         po.endObject(token2);
         try {
@@ -444,13 +444,13 @@
         final ProtoOutputStream po = new ProtoOutputStream();
 
         long token1 = po.startObject(ProtoOutputStream.makeFieldId(1,
-                    ProtoOutputStream.FIELD_COUNT_SINGLE | ProtoOutputStream.FIELD_TYPE_OBJECT));
+                    ProtoOutputStream.FIELD_COUNT_SINGLE | ProtoOutputStream.FIELD_TYPE_MESSAGE));
         po.writeUInt32(ProtoOutputStream.makeFieldId(2,
                     ProtoOutputStream.FIELD_COUNT_SINGLE | ProtoOutputStream.FIELD_TYPE_UINT32),
                 'a');
 
         long token2 = po.startObject(ProtoOutputStream.makeFieldId(3,
-                    ProtoOutputStream.FIELD_COUNT_SINGLE | ProtoOutputStream.FIELD_TYPE_OBJECT));
+                    ProtoOutputStream.FIELD_COUNT_SINGLE | ProtoOutputStream.FIELD_TYPE_MESSAGE));
         po.writeUInt32(ProtoOutputStream.makeFieldId(4,
                     ProtoOutputStream.FIELD_COUNT_SINGLE | ProtoOutputStream.FIELD_TYPE_UINT32),
                 'b');
@@ -472,10 +472,10 @@
         final ProtoOutputStream po = new ProtoOutputStream();
 
         long token1 = po.startObject(ProtoOutputStream.makeFieldId(1,
-                    ProtoOutputStream.FIELD_COUNT_SINGLE | ProtoOutputStream.FIELD_TYPE_OBJECT));
+                    ProtoOutputStream.FIELD_COUNT_SINGLE | ProtoOutputStream.FIELD_TYPE_MESSAGE));
 
         long token2 = po.startObject(ProtoOutputStream.makeFieldId(3,
-                    ProtoOutputStream.FIELD_COUNT_SINGLE | ProtoOutputStream.FIELD_TYPE_OBJECT));
+                    ProtoOutputStream.FIELD_COUNT_SINGLE | ProtoOutputStream.FIELD_TYPE_MESSAGE));
 
         po.endObject(token2);
         try {
@@ -494,13 +494,13 @@
         final ProtoOutputStream po = new ProtoOutputStream();
 
         long token1 = po.startObject(ProtoOutputStream.makeFieldId(1,
-                    ProtoOutputStream.FIELD_COUNT_SINGLE | ProtoOutputStream.FIELD_TYPE_OBJECT));
+                    ProtoOutputStream.FIELD_COUNT_SINGLE | ProtoOutputStream.FIELD_TYPE_MESSAGE));
         po.writeUInt32(ProtoOutputStream.makeFieldId(2,
                     ProtoOutputStream.FIELD_COUNT_SINGLE | ProtoOutputStream.FIELD_TYPE_UINT32),
                 'a');
 
         long token2 = po.startObject(ProtoOutputStream.makeFieldId(3,
-                    ProtoOutputStream.FIELD_COUNT_SINGLE | ProtoOutputStream.FIELD_TYPE_OBJECT));
+                    ProtoOutputStream.FIELD_COUNT_SINGLE | ProtoOutputStream.FIELD_TYPE_MESSAGE));
         po.writeUInt32(ProtoOutputStream.makeFieldId(4,
                     ProtoOutputStream.FIELD_COUNT_SINGLE | ProtoOutputStream.FIELD_TYPE_UINT32),
                 'b');
@@ -521,10 +521,10 @@
         final ProtoOutputStream po = new ProtoOutputStream();
 
         long token1 = po.startObject(ProtoOutputStream.makeFieldId(1,
-                    ProtoOutputStream.FIELD_COUNT_SINGLE | ProtoOutputStream.FIELD_TYPE_OBJECT));
+                    ProtoOutputStream.FIELD_COUNT_SINGLE | ProtoOutputStream.FIELD_TYPE_MESSAGE));
 
         long token2 = po.startObject(ProtoOutputStream.makeFieldId(3,
-                    ProtoOutputStream.FIELD_COUNT_SINGLE | ProtoOutputStream.FIELD_TYPE_OBJECT));
+                    ProtoOutputStream.FIELD_COUNT_SINGLE | ProtoOutputStream.FIELD_TYPE_MESSAGE));
 
         try {
             po.endObject(token1);
@@ -542,20 +542,20 @@
         final ProtoOutputStream po = new ProtoOutputStream();
 
         long token1 = po.startObject(ProtoOutputStream.makeFieldId(1,
-                    ProtoOutputStream.FIELD_COUNT_SINGLE | ProtoOutputStream.FIELD_TYPE_OBJECT));
+                    ProtoOutputStream.FIELD_COUNT_SINGLE | ProtoOutputStream.FIELD_TYPE_MESSAGE));
         po.writeUInt32(ProtoOutputStream.makeFieldId(2,
                     ProtoOutputStream.FIELD_COUNT_SINGLE | ProtoOutputStream.FIELD_TYPE_UINT32),
                 'a');
 
         long token2 = po.startObject(ProtoOutputStream.makeFieldId(3,
-                    ProtoOutputStream.FIELD_COUNT_SINGLE | ProtoOutputStream.FIELD_TYPE_OBJECT));
+                    ProtoOutputStream.FIELD_COUNT_SINGLE | ProtoOutputStream.FIELD_TYPE_MESSAGE));
         po.writeUInt32(ProtoOutputStream.makeFieldId(4,
                     ProtoOutputStream.FIELD_COUNT_SINGLE | ProtoOutputStream.FIELD_TYPE_UINT32),
                 'b');
         po.endObject(token2);
 
         long token3 = po.startObject(ProtoOutputStream.makeFieldId(5,
-                    ProtoOutputStream.FIELD_COUNT_SINGLE | ProtoOutputStream.FIELD_TYPE_OBJECT));
+                    ProtoOutputStream.FIELD_COUNT_SINGLE | ProtoOutputStream.FIELD_TYPE_MESSAGE));
         po.writeUInt32(ProtoOutputStream.makeFieldId(4,
                     ProtoOutputStream.FIELD_COUNT_SINGLE | ProtoOutputStream.FIELD_TYPE_UINT32),
                 'b');
@@ -576,14 +576,14 @@
         final ProtoOutputStream po = new ProtoOutputStream();
 
         long token1 = po.startObject(ProtoOutputStream.makeFieldId(1,
-                    ProtoOutputStream.FIELD_COUNT_SINGLE | ProtoOutputStream.FIELD_TYPE_OBJECT));
+                    ProtoOutputStream.FIELD_COUNT_SINGLE | ProtoOutputStream.FIELD_TYPE_MESSAGE));
 
         long token2 = po.startObject(ProtoOutputStream.makeFieldId(3,
-                    ProtoOutputStream.FIELD_COUNT_SINGLE | ProtoOutputStream.FIELD_TYPE_OBJECT));
+                    ProtoOutputStream.FIELD_COUNT_SINGLE | ProtoOutputStream.FIELD_TYPE_MESSAGE));
         po.endObject(token2);
 
         long token3 = po.startObject(ProtoOutputStream.makeFieldId(5,
-                    ProtoOutputStream.FIELD_COUNT_SINGLE | ProtoOutputStream.FIELD_TYPE_OBJECT));
+                    ProtoOutputStream.FIELD_COUNT_SINGLE | ProtoOutputStream.FIELD_TYPE_MESSAGE));
 
         try {
             po.endObject(token2);
@@ -601,14 +601,14 @@
         final ProtoOutputStream po = new ProtoOutputStream();
 
         long token1 = po.startObject(ProtoOutputStream.makeFieldId(1,
-                    ProtoOutputStream.FIELD_COUNT_SINGLE | ProtoOutputStream.FIELD_TYPE_OBJECT));
+                    ProtoOutputStream.FIELD_COUNT_SINGLE | ProtoOutputStream.FIELD_TYPE_MESSAGE));
 
         long token2 = po.startObject(ProtoOutputStream.makeFieldId(3,
-                    ProtoOutputStream.FIELD_COUNT_SINGLE | ProtoOutputStream.FIELD_TYPE_OBJECT));
+                    ProtoOutputStream.FIELD_COUNT_SINGLE | ProtoOutputStream.FIELD_TYPE_MESSAGE));
         po.endObject(token2);
 
         long token3 = po.startObject(ProtoOutputStream.makeFieldId(5,
-                    ProtoOutputStream.FIELD_COUNT_SINGLE | ProtoOutputStream.FIELD_TYPE_OBJECT));
+                    ProtoOutputStream.FIELD_COUNT_SINGLE | ProtoOutputStream.FIELD_TYPE_MESSAGE));
 
         try {
             po.endObject(token2);
@@ -638,17 +638,17 @@
         all.nestedField.nested.nested.data = 3;
 
         final long token1 = po.startObject(ProtoOutputStream.makeFieldId(170,
-                    ProtoOutputStream.FIELD_COUNT_SINGLE | ProtoOutputStream.FIELD_TYPE_OBJECT));
+                    ProtoOutputStream.FIELD_COUNT_SINGLE | ProtoOutputStream.FIELD_TYPE_MESSAGE));
         po.writeInt32(ProtoOutputStream.makeFieldId(10001,
                         ProtoOutputStream.FIELD_COUNT_SINGLE | ProtoOutputStream.FIELD_TYPE_INT32),
                 1);
         final long token2 = po.startObject(ProtoOutputStream.makeFieldId(10002,
-                    ProtoOutputStream.FIELD_COUNT_SINGLE | ProtoOutputStream.FIELD_TYPE_OBJECT));
+                    ProtoOutputStream.FIELD_COUNT_SINGLE | ProtoOutputStream.FIELD_TYPE_MESSAGE));
         po.writeInt32(ProtoOutputStream.makeFieldId(10001,
                         ProtoOutputStream.FIELD_COUNT_SINGLE | ProtoOutputStream.FIELD_TYPE_INT32),
                 2);
         final long token3 = po.startObject(ProtoOutputStream.makeFieldId(10002,
-                    ProtoOutputStream.FIELD_COUNT_SINGLE | ProtoOutputStream.FIELD_TYPE_OBJECT));
+                    ProtoOutputStream.FIELD_COUNT_SINGLE | ProtoOutputStream.FIELD_TYPE_MESSAGE));
         po.writeInt32(ProtoOutputStream.makeFieldId(10001,
                         ProtoOutputStream.FIELD_COUNT_SINGLE | ProtoOutputStream.FIELD_TYPE_INT32),
                 3);
@@ -680,17 +680,17 @@
             all.nestedFieldRepeated[i].nested.nested.data = 3;
 
             final long token1 = po.startObject(ProtoOutputStream.makeFieldId(171,
-                        ProtoOutputStream.FIELD_COUNT_SINGLE | ProtoOutputStream.FIELD_TYPE_OBJECT));
+                        ProtoOutputStream.FIELD_COUNT_SINGLE | ProtoOutputStream.FIELD_TYPE_MESSAGE));
             po.writeInt32(ProtoOutputStream.makeFieldId(10001,
                         ProtoOutputStream.FIELD_COUNT_SINGLE | ProtoOutputStream.FIELD_TYPE_INT32),
                     1);
             final long token2 = po.startObject(ProtoOutputStream.makeFieldId(10002,
-                        ProtoOutputStream.FIELD_COUNT_SINGLE | ProtoOutputStream.FIELD_TYPE_OBJECT));
+                        ProtoOutputStream.FIELD_COUNT_SINGLE | ProtoOutputStream.FIELD_TYPE_MESSAGE));
             po.writeInt32(ProtoOutputStream.makeFieldId(10001,
                         ProtoOutputStream.FIELD_COUNT_SINGLE | ProtoOutputStream.FIELD_TYPE_INT32),
                     2);
             final long token3 = po.startObject(ProtoOutputStream.makeFieldId(10002,
-                        ProtoOutputStream.FIELD_COUNT_SINGLE | ProtoOutputStream.FIELD_TYPE_OBJECT));
+                        ProtoOutputStream.FIELD_COUNT_SINGLE | ProtoOutputStream.FIELD_TYPE_MESSAGE));
             po.writeInt32(ProtoOutputStream.makeFieldId(10001,
                         ProtoOutputStream.FIELD_COUNT_SINGLE | ProtoOutputStream.FIELD_TYPE_INT32),
                     3);
@@ -724,7 +724,7 @@
         try {
             final ProtoOutputStream po = new ProtoOutputStream();
             po.startObject(ProtoOutputStream.makeFieldId(1,
-                        ProtoOutputStream.FIELD_COUNT_PACKED | ProtoOutputStream.FIELD_TYPE_OBJECT));
+                        ProtoOutputStream.FIELD_COUNT_PACKED | ProtoOutputStream.FIELD_TYPE_MESSAGE));
         } catch (IllegalArgumentException ex) {
             // good
         }
@@ -744,7 +744,7 @@
         try {
             final ProtoOutputStream po = new ProtoOutputStream();
             po.startRepeatedObject(ProtoOutputStream.makeFieldId(1,
-                        ProtoOutputStream.FIELD_COUNT_PACKED | ProtoOutputStream.FIELD_TYPE_OBJECT));
+                        ProtoOutputStream.FIELD_COUNT_PACKED | ProtoOutputStream.FIELD_TYPE_MESSAGE));
         } catch (IllegalArgumentException ex) {
             // good
         }
@@ -756,7 +756,7 @@
     public void testMismatchedEndObject() {
         final ProtoOutputStream po = new ProtoOutputStream();
         final long token = po.startObject(ProtoOutputStream.makeFieldId(1,
-                    ProtoOutputStream.FIELD_COUNT_SINGLE | ProtoOutputStream.FIELD_TYPE_OBJECT));
+                    ProtoOutputStream.FIELD_COUNT_SINGLE | ProtoOutputStream.FIELD_TYPE_MESSAGE));
 
         try {
             po.endRepeatedObject(token);
@@ -771,7 +771,7 @@
     public void testMismatchedEndRepeatedObject() {
         final ProtoOutputStream po = new ProtoOutputStream();
         final long token = po.startRepeatedObject(ProtoOutputStream.makeFieldId(1,
-                    ProtoOutputStream.FIELD_COUNT_REPEATED | ProtoOutputStream.FIELD_TYPE_OBJECT));
+                    ProtoOutputStream.FIELD_COUNT_REPEATED | ProtoOutputStream.FIELD_TYPE_MESSAGE));
 
         try {
             po.endObject(token);
@@ -806,7 +806,7 @@
 
         final ProtoOutputStream po = new ProtoOutputStream();
         po.writeObject(ProtoOutputStream.makeFieldId(10,
-                    ProtoOutputStream.FIELD_COUNT_SINGLE | ProtoOutputStream.FIELD_TYPE_OBJECT),
+                    ProtoOutputStream.FIELD_COUNT_SINGLE | ProtoOutputStream.FIELD_TYPE_MESSAGE),
                 innerRaw);
 
         final byte[] result = po.getBytes();
@@ -827,7 +827,7 @@
 
         final ProtoOutputStream po = new ProtoOutputStream();
         po.writeObject(ProtoOutputStream.makeFieldId(10,
-                    ProtoOutputStream.FIELD_COUNT_SINGLE | ProtoOutputStream.FIELD_TYPE_OBJECT),
+                    ProtoOutputStream.FIELD_COUNT_SINGLE | ProtoOutputStream.FIELD_TYPE_MESSAGE),
                 innerRaw);
 
         final byte[] result = po.getBytes();
@@ -861,7 +861,7 @@
 
         final ProtoOutputStream po = new ProtoOutputStream();
         po.writeRepeatedObject(ProtoOutputStream.makeFieldId(10,
-                    ProtoOutputStream.FIELD_COUNT_REPEATED | ProtoOutputStream.FIELD_TYPE_OBJECT),
+                    ProtoOutputStream.FIELD_COUNT_REPEATED | ProtoOutputStream.FIELD_TYPE_MESSAGE),
                 innerRaw);
 
         final byte[] result = po.getBytes();
@@ -882,7 +882,7 @@
 
         final ProtoOutputStream po = new ProtoOutputStream();
         po.writeRepeatedObject(ProtoOutputStream.makeFieldId(10,
-                    ProtoOutputStream.FIELD_COUNT_REPEATED | ProtoOutputStream.FIELD_TYPE_OBJECT),
+                    ProtoOutputStream.FIELD_COUNT_REPEATED | ProtoOutputStream.FIELD_TYPE_MESSAGE),
                 innerRaw);
 
         Assert.assertArrayEquals(new byte[] {
diff --git a/tests/tests/proto/src/android/util/proto/cts/ProtoOutputStreamTagTest.java b/tests/tests/proto/src/android/util/proto/cts/ProtoOutputStreamTagTest.java
index 3b38aba..c408d92 100644
--- a/tests/tests/proto/src/android/util/proto/cts/ProtoOutputStreamTagTest.java
+++ b/tests/tests/proto/src/android/util/proto/cts/ProtoOutputStreamTagTest.java
@@ -92,6 +92,7 @@
         // Try it in the provided name
         try {
             ProtoOutputStream.checkFieldId(42 | goodCount | badType, goodCount | fieldType);
+            fail("Should have thrown an exception.");
         } catch (IllegalArgumentException ex) {
             assertEquals("writeRepeated" + string
                         + " called for field 42 which should be used"
@@ -102,6 +103,7 @@
         // Try it in the expected name
         try {
             ProtoOutputStream.checkFieldId(43 | goodCount | fieldType, goodCount | badType);
+            fail("Should have thrown an exception.");
         } catch (IllegalArgumentException ex) {
             assertEquals("writeRepeated" + badTypeString
                         + " called for field 43 which should be used"
@@ -126,6 +128,7 @@
         // Try it in the provided name
         try {
             ProtoOutputStream.checkFieldId(44 | badCount | goodType, fieldCount | goodType);
+            fail("Should have thrown an exception.");
         } catch (IllegalArgumentException ex) {
             assertEquals("write" + string
                     + "Fixed32 called for field 44 which should be used"
@@ -136,6 +139,7 @@
         // Try it in the expected name
         try {
             ProtoOutputStream.checkFieldId(45 | fieldCount | goodType, badCount | goodType);
+            fail("Should have thrown an exception.");
         } catch (IllegalArgumentException ex) {
             String extraString = "";
             if (fieldCount == ProtoOutputStream.FIELD_COUNT_PACKED) {
@@ -151,7 +155,7 @@
     /**
      * Validate one call to checkFieldId that is expected to throw.
      */
-    public void assertCheckFieldIdThrows(long fieldId, long expectedFlags) 
+    public void assertCheckFieldIdThrows(long fieldId, long expectedFlags)
             throws Exception {
         try {
             ProtoOutputStream.checkFieldId(fieldId, expectedFlags);
diff --git a/tests/tests/provider/Android.mk b/tests/tests/provider/Android.mk
index d4b3e99..6effdfa 100644
--- a/tests/tests/provider/Android.mk
+++ b/tests/tests/provider/Android.mk
@@ -28,7 +28,7 @@
 # Tag this module as a cts test artifact
 LOCAL_COMPATIBILITY_SUITE := cts vts general-tests
 
-LOCAL_JAVA_LIBRARIES := android.test.mock legacy-android-test telephony-common
+LOCAL_JAVA_LIBRARIES := android.test.mock android.test.base android.test.runner telephony-common
 
 LOCAL_STATIC_JAVA_LIBRARIES := \
     android-support-v4 \
diff --git a/tests/tests/provider/AndroidTest.xml b/tests/tests/provider/AndroidTest.xml
index 57198a8..a8fe97d 100644
--- a/tests/tests/provider/AndroidTest.xml
+++ b/tests/tests/provider/AndroidTest.xml
@@ -14,6 +14,7 @@
      limitations under the License.
 -->
 <configuration description="Config for CTS Provider test cases">
+    <option name="test-suite-tag" value="cts" />
     <option name="config-descriptor:metadata" key="component" value="framework" />
     <target_preparer class="com.android.tradefed.targetprep.suite.SuiteApkInstaller">
         <option name="cleanup-apks" value="true" />
diff --git a/tests/tests/provider/src/android/provider/cts/contacts/ContactsContract_DataTest.java b/tests/tests/provider/src/android/provider/cts/contacts/ContactsContract_DataTest.java
index 31afce9..df94bda 100644
--- a/tests/tests/provider/src/android/provider/cts/contacts/ContactsContract_DataTest.java
+++ b/tests/tests/provider/src/android/provider/cts/contacts/ContactsContract_DataTest.java
@@ -72,6 +72,8 @@
             Data.DATA14,
             Data.DATA15,
             Data.CARRIER_PRESENCE,
+            Data.PREFERRED_PHONE_ACCOUNT_COMPONENT_NAME,
+            Data.PREFERRED_PHONE_ACCOUNT_ID,
             Data.DATA_VERSION,
             Data.IS_PRIMARY,
             Data.IS_SUPER_PRIMARY,
@@ -286,6 +288,8 @@
                         Data.DATA14,
                         Data.DATA15,
                         Data.CARRIER_PRESENCE,
+                        Data.PREFERRED_PHONE_ACCOUNT_COMPONENT_NAME,
+                        Data.PREFERRED_PHONE_ACCOUNT_ID,
                         Data.DATA_VERSION,
                         Data.IS_PRIMARY,
                         Data.IS_SUPER_PRIMARY,
@@ -379,6 +383,8 @@
                         Data.DATA14,
                         Data.DATA15,
                         Data.CARRIER_PRESENCE,
+                        Data.PREFERRED_PHONE_ACCOUNT_COMPONENT_NAME,
+                        Data.PREFERRED_PHONE_ACCOUNT_ID,
                         Data.DATA_VERSION,
                         Data.IS_PRIMARY,
                         Data.IS_SUPER_PRIMARY,
diff --git a/tests/tests/provider/src/android/provider/cts/contacts/VoicemailContractTest.java b/tests/tests/provider/src/android/provider/cts/contacts/VoicemailContractTest.java
index bdc973b..c0b4253 100644
--- a/tests/tests/provider/src/android/provider/cts/contacts/VoicemailContractTest.java
+++ b/tests/tests/provider/src/android/provider/cts/contacts/VoicemailContractTest.java
@@ -88,32 +88,46 @@
 
     public void testVoicemailsTable() throws Exception {
         final String[] VOICEMAILS_PROJECTION = new String[] {
-                Voicemails._ID, Voicemails.NUMBER, Voicemails.DATE, Voicemails.DURATION,
-                Voicemails.IS_READ, Voicemails.SOURCE_PACKAGE, Voicemails.SOURCE_DATA,
-                Voicemails.HAS_CONTENT, Voicemails.MIME_TYPE, Voicemails.TRANSCRIPTION,
+                Voicemails._ID,
+                Voicemails.NUMBER,
+                Voicemails.DATE,
+                Voicemails.DURATION,
+                Voicemails.NEW,
+                Voicemails.IS_READ,
+                Voicemails.SOURCE_PACKAGE,
+                Voicemails.SOURCE_DATA,
+                Voicemails.HAS_CONTENT,
+                Voicemails.MIME_TYPE,
+                Voicemails.TRANSCRIPTION,
                 Voicemails.PHONE_ACCOUNT_COMPONENT_NAME,
-                Voicemails.PHONE_ACCOUNT_ID, Voicemails.DIRTY, Voicemails.DELETED,
-                Voicemails.LAST_MODIFIED, Voicemails.BACKED_UP, Voicemails.RESTORED,
-                Voicemails.ARCHIVED, Voicemails.IS_OMTP_VOICEMAIL};
+                Voicemails.PHONE_ACCOUNT_ID,
+                Voicemails.DIRTY,
+                Voicemails.DELETED,
+                Voicemails.LAST_MODIFIED,
+                Voicemails.BACKED_UP,
+                Voicemails.RESTORED,
+                Voicemails.ARCHIVED,
+                Voicemails.IS_OMTP_VOICEMAIL};
         final int ID_INDEX = 0;
         final int NUMBER_INDEX = 1;
         final int DATE_INDEX = 2;
         final int DURATION_INDEX = 3;
-        final int IS_READ_INDEX = 4;
-        final int SOURCE_PACKAGE_INDEX = 5;
-        final int SOURCE_DATA_INDEX = 6;
-        final int HAS_CONTENT_INDEX = 7;
-        final int MIME_TYPE_INDEX = 8;
-        final int TRANSCRIPTION_INDEX= 9;
-        final int PHONE_ACCOUNT_COMPONENT_NAME_INDEX = 10;
-        final int PHONE_ACCOUNT_ID_INDEX = 11;
-        final int DIRTY_INDEX = 12;
-        final int DELETED_INDEX = 13;
-        final int LAST_MODIFIED_INDEX = 14;
-        final int BACKED_UP_INDEX = 15;
-        final int RESTORED_INDEX = 16;
-        final int ARCHIVED_INDEX = 17;
-        final int IS_OMTP_VOICEMAIL_INDEX = 18;
+        final int NEW_INDEX = 4;
+        final int IS_READ_INDEX = 5;
+        final int SOURCE_PACKAGE_INDEX = 6;
+        final int SOURCE_DATA_INDEX = 7;
+        final int HAS_CONTENT_INDEX = 8;
+        final int MIME_TYPE_INDEX = 9;
+        final int TRANSCRIPTION_INDEX = 10;
+        final int PHONE_ACCOUNT_COMPONENT_NAME_INDEX = 11;
+        final int PHONE_ACCOUNT_ID_INDEX = 12;
+        final int DIRTY_INDEX = 13;
+        final int DELETED_INDEX = 14;
+        final int LAST_MODIFIED_INDEX = 15;
+        final int BACKED_UP_INDEX = 16;
+        final int RESTORED_INDEX = 17;
+        final int ARCHIVED_INDEX = 18;
+        final int IS_OMTP_VOICEMAIL_INDEX = 19;
 
         String insertCallsNumber = "0123456789";
         long insertCallsDuration = 120;
@@ -131,6 +145,7 @@
         value.put(Voicemails.NUMBER, insertCallsNumber);
         value.put(Voicemails.DATE, insertDate);
         value.put(Voicemails.DURATION, insertCallsDuration);
+        value.put(Voicemails.NEW, 0);
         // Source package is expected to be inserted by the provider, if not set.
         value.put(Voicemails.SOURCE_DATA, insertSourceData);
         value.put(Voicemails.MIME_TYPE, insertMimeType);
@@ -158,17 +173,18 @@
         assertEquals(mSourcePackageName, cursor.getString(SOURCE_PACKAGE_INDEX));
         assertEquals(insertSourceData, cursor.getString(SOURCE_DATA_INDEX));
         assertEquals(insertMimeType, cursor.getString(MIME_TYPE_INDEX));
+        assertEquals(0, cursor.getInt(NEW_INDEX));
         assertEquals(0, cursor.getInt(IS_READ_INDEX));
         assertEquals(1, cursor.getInt(HAS_CONTENT_INDEX));
-        assertEquals("foo",cursor.getString(TRANSCRIPTION_INDEX));
-        assertEquals("com.foo",cursor.getString(PHONE_ACCOUNT_COMPONENT_NAME_INDEX));
-        assertEquals("bar",cursor.getString(PHONE_ACCOUNT_ID_INDEX));
-        assertEquals(0,cursor.getInt(DIRTY_INDEX));
-        assertEquals(0,cursor.getInt(DELETED_INDEX));
-        assertEquals(0,cursor.getInt(BACKED_UP_INDEX));
-        assertEquals(0,cursor.getInt(RESTORED_INDEX));
-        assertEquals(0,cursor.getInt(ARCHIVED_INDEX));
-        assertEquals(0,cursor.getInt(IS_OMTP_VOICEMAIL_INDEX));
+        assertEquals("foo", cursor.getString(TRANSCRIPTION_INDEX));
+        assertEquals("com.foo", cursor.getString(PHONE_ACCOUNT_COMPONENT_NAME_INDEX));
+        assertEquals("bar", cursor.getString(PHONE_ACCOUNT_ID_INDEX));
+        assertEquals(0, cursor.getInt(DIRTY_INDEX));
+        assertEquals(0, cursor.getInt(DELETED_INDEX));
+        assertEquals(0, cursor.getInt(BACKED_UP_INDEX));
+        assertEquals(0, cursor.getInt(RESTORED_INDEX));
+        assertEquals(0, cursor.getInt(ARCHIVED_INDEX));
+        assertEquals(0, cursor.getInt(IS_OMTP_VOICEMAIL_INDEX));
         int id = cursor.getInt(ID_INDEX);
         assertEquals(id, Integer.parseInt(uri.getLastPathSegment()));
         cursor.close();
@@ -179,6 +195,7 @@
         value.put(Voicemails.DATE, updateDate);
         value.put(Voicemails.DURATION, updateCallsDuration);
         value.put(Voicemails.SOURCE_DATA, updateSourceData);
+        value.put(Voicemails.NEW, 1);
         value.put(Voicemails.DIRTY, 1);
         value.put(Voicemails.DELETED, 1);
         value.put(Voicemails.BACKED_UP, 1);
@@ -196,12 +213,13 @@
         assertEquals(updateDate, cursor.getLong(DATE_INDEX));
         assertEquals(updateCallsDuration, cursor.getLong(DURATION_INDEX));
         assertEquals(updateSourceData, cursor.getString(SOURCE_DATA_INDEX));
-        assertEquals(1,cursor.getInt(DIRTY_INDEX));
-        assertEquals(1,cursor.getInt(DELETED_INDEX));
-        assertEquals(1,cursor.getInt(BACKED_UP_INDEX));
-        assertEquals(1,cursor.getInt(RESTORED_INDEX));
-        assertEquals(1,cursor.getInt(ARCHIVED_INDEX));
-        assertEquals(1,cursor.getInt(IS_OMTP_VOICEMAIL_INDEX));
+        assertEquals(1, cursor.getInt(NEW_INDEX));
+        assertEquals(1, cursor.getInt(DIRTY_INDEX));
+        assertEquals(1, cursor.getInt(DELETED_INDEX));
+        assertEquals(1, cursor.getInt(BACKED_UP_INDEX));
+        assertEquals(1, cursor.getInt(RESTORED_INDEX));
+        assertEquals(1, cursor.getInt(ARCHIVED_INDEX));
+        assertEquals(1, cursor.getInt(IS_OMTP_VOICEMAIL_INDEX));
         cursor.close();
 
         // Test: delete
@@ -213,7 +231,7 @@
     }
 
     public void testForeignUpdate_dirty() throws Exception {
-        if(!hasTelephony(getInstrumentation().getContext())){
+        if (!hasTelephony(getInstrumentation().getContext())) {
             Log.d(TAG, "skipping test that requires telephony feature");
             return;
         }
@@ -234,8 +252,34 @@
         }
     }
 
+    public void testForeignUpdate_retainDirty_notDirty() throws Exception {
+        if (!hasTelephony(getInstrumentation().getContext())) {
+            Log.d(TAG, "skipping test that requires telephony feature");
+            return;
+        }
+        // only the default dialer has WRITE_VOICEMAIL permission, which can modify voicemails of
+        // a foreign source package.
+        setTestAsDefaultDialer();
+        ContentValues values = new ContentValues();
+        values.put(Voicemails.SOURCE_PACKAGE, FOREIGN_SOURCE);
+
+        Uri uri = mVoicemailProvider.insert(Voicemails.buildSourceUri(FOREIGN_SOURCE), values);
+
+        ContentValues newValues = new ContentValues();
+        newValues.put(Voicemails.TRANSCRIPTION, "foo");
+        newValues.put(Voicemails.DIRTY, Voicemails.DIRTY_RETAIN);
+
+        mVoicemailProvider.update(uri, newValues, null, null);
+
+        try (Cursor cursor = mVoicemailProvider
+                .query(uri, new String[] {Voicemails.DIRTY}, null, null, null)) {
+            cursor.moveToFirst();
+            assertEquals(0, cursor.getInt(0));
+        }
+    }
+
     public void testForeignUpdate_explicitNotDirty() throws Exception {
-        if(!hasTelephony(getInstrumentation().getContext())){
+        if (!hasTelephony(getInstrumentation().getContext())) {
             Log.d(TAG, "skipping test that requires telephony feature");
             return;
         }
@@ -246,7 +290,7 @@
         Uri uri = mVoicemailProvider.insert(Voicemails.buildSourceUri(FOREIGN_SOURCE), values);
 
         ContentValues updateValues = new ContentValues();
-        updateValues.put(Voicemails.DIRTY,0);
+        updateValues.put(Voicemails.DIRTY, 0);
         mVoicemailProvider.update(uri, updateValues, null, null);
 
         try (Cursor cursor = mVoicemailProvider
@@ -257,7 +301,7 @@
     }
 
     public void testForeignUpdate_null_dirty() throws Exception {
-        if(!hasTelephony(getInstrumentation().getContext())){
+        if (!hasTelephony(getInstrumentation().getContext())) {
             Log.d(TAG, "skipping test that requires telephony feature");
             return;
         }
@@ -279,7 +323,7 @@
     }
 
     public void testForeignUpdate_NotNormalized_normalized() throws Exception {
-        if(!hasTelephony(getInstrumentation().getContext())){
+        if (!hasTelephony(getInstrumentation().getContext())) {
             Log.d(TAG, "skipping test that requires telephony feature");
             return;
         }
@@ -303,7 +347,7 @@
     public void testLocalUpdate_notDirty() throws Exception {
 
         ContentValues values = new ContentValues();
-        values.put(Voicemails.DIRTY,1);
+        values.put(Voicemails.DIRTY, 1);
 
         Uri uri = mVoicemailProvider.insert(Voicemails.buildSourceUri(mSourcePackageName), values);
 
@@ -316,6 +360,25 @@
         }
     }
 
+    public void testLocalUpdate_retainDirty_dirty() throws Exception {
+
+        ContentValues values = new ContentValues();
+        values.put(Voicemails.DIRTY, 1);
+
+        Uri uri = mVoicemailProvider.insert(Voicemails.buildSourceUri(mSourcePackageName), values);
+
+        ContentValues newValues = new ContentValues();
+        newValues.put(Voicemails.TRANSCRIPTION, "foo");
+        newValues.put(Voicemails.DIRTY, Voicemails.DIRTY_RETAIN);
+
+        mVoicemailProvider.update(uri, newValues, null, null);
+
+        try (Cursor cursor = mVoicemailProvider
+                .query(uri, new String[] {Voicemails.DIRTY}, null, null, null)) {
+            cursor.moveToFirst();
+            assertEquals(cursor.getInt(0), 1);
+        }
+    }
 
     // Data column should be automatically generated during insert.
     public void testInsert_doesNotUpdateDataColumn() throws Exception {
@@ -351,7 +414,7 @@
     private void assertDataNotEquals(String newFilePath) throws RemoteException {
         // Make sure data value is not actually updated.
         final Cursor cursor = mVoicemailProvider.query(mVoicemailContentUri,
-                new String[]{Voicemails._DATA}, null, null, null);
+                new String[] {Voicemails._DATA}, null, null, null);
         cursor.moveToNext();
         final String data = cursor.getString(0);
         assertFalse(data.equals(newFilePath));
@@ -494,10 +557,10 @@
                 packageManager.hasSystemFeature(PackageManager.FEATURE_CONNECTION_SERVICE);
     }
 
-    private void setTestAsDefaultDialer() throws Exception{
+    private void setTestAsDefaultDialer() throws Exception {
         assertTrue(mPreviousDefaultDialer == null);
         mPreviousDefaultDialer = getDefaultDialer(getInstrumentation());
-        setDefaultDialer(getInstrumentation(),PACKAGE);
+        setDefaultDialer(getInstrumentation(), PACKAGE);
     }
 
     private static String setDefaultDialer(Instrumentation instrumentation, String packageName)
diff --git a/tests/tests/renderscript/Android.mk b/tests/tests/renderscript/Android.mk
index 1a8c549..5da2036 100644
--- a/tests/tests/renderscript/Android.mk
+++ b/tests/tests/renderscript/Android.mk
@@ -30,8 +30,8 @@
 
 LOCAL_STATIC_JAVA_LIBRARIES := \
     ctstestrunner \
-    xmp_toolkit \
-    legacy-android-test
+    xmp_toolkit
+LOCAL_JAVA_LIBRARIES := android.test.base.stubs
 LOCAL_JNI_SHARED_LIBRARIES := libcoremathtestcpp_jni
 
 LOCAL_SRC_FILES := $(call all-java-files-under, src) $(call all-renderscript-files-under, src)
diff --git a/tests/tests/renderscript/AndroidTest.xml b/tests/tests/renderscript/AndroidTest.xml
index 979b756..2788236 100644
--- a/tests/tests/renderscript/AndroidTest.xml
+++ b/tests/tests/renderscript/AndroidTest.xml
@@ -14,6 +14,7 @@
      limitations under the License.
 -->
 <configuration description="Configuration for Renderscript Tests">
+    <option name="test-suite-tag" value="cts" />
     <option name="config-descriptor:metadata" key="component" value="renderscript" />
     <target_preparer class="com.android.tradefed.targetprep.suite.SuiteApkInstaller">
         <option name="cleanup-apks" value="true" />
diff --git a/tests/tests/renderscript/src/android/renderscript/cts/refocus/RefocusTest.java b/tests/tests/renderscript/src/android/renderscript/cts/refocus/RefocusTest.java
index 9b355b0..ab326ff 100644
--- a/tests/tests/renderscript/src/android/renderscript/cts/refocus/RefocusTest.java
+++ b/tests/tests/renderscript/src/android/renderscript/cts/refocus/RefocusTest.java
@@ -37,14 +37,14 @@
     /**
      * Test the orignal refocus code
      */
-    public void testOriginalRefocus() {
+    public void testOriginalRefocus() throws IOException {
         refocus(RenderScriptTask.script.f32, 95);
     }
 
     /**
      * Test the new refocus code
      */
-    public void testNewRefocus() {
+    public void testNewRefocus() throws IOException {
         // The new implementation may run on a GPU using relaxed floating point
         // mathematics. Hence more relaxed precision requirement.
         refocus(RenderScriptTask.script.d1new, 45);
@@ -54,7 +54,7 @@
      * Test a refocus operator against the refocus_reference image
      * @param impl version of refocus to run
      */
-    private void refocus(RenderScriptTask.script impl, double minimumPSNR) {
+    private void refocus(RenderScriptTask.script impl, double minimumPSNR) throws IOException {
         Context ctx = getContext();
 
         RenderScript rs = RenderScript.create(ctx);
@@ -63,27 +63,26 @@
             current_rgbz = new RGBZ(getResourceRef(R.drawable.test_image),
                                     getResourceRef(R.drawable.test_depthmap),
                                     ctx.getContentResolver(), ctx);
-        } catch (IOException e) {
-            e.printStackTrace();
-            assertNull(e);
+            DepthOfFieldOptions current_depth_options = new DepthOfFieldOptions(current_rgbz);
+            RsTaskParams rsTaskParam = new RsTaskParams(rs, current_depth_options);
+
+            RenderScriptTask renderScriptTask = new RenderScriptTask(rs, impl);
+            Bitmap outputImage = renderScriptTask.applyRefocusFilter(rsTaskParam.mOptions);
+
+            Bitmap expectedImage = BitmapFactory.decodeResource(ctx.getResources(),
+                    R.drawable.expected_output);
+
+            double psnr = ImageCompare.psnr(outputImage, expectedImage);
+            android.util.Log.i("RefocusTest", "psnr = " + String.format("%.02f", psnr));
+            if (psnr < minimumPSNR) {
+                MediaStoreSaver.savePNG(outputImage, "refocus", "refocus_output" , ctx);
+                assertTrue("Required minimum psnr = " + String.format("%.02f; ", minimumPSNR) +
+                           "Actual psnr = " + String.format("%.02f", psnr),
+                           false);
+            }
+        } finally {
+            rs.destroy();
         }
-        DepthOfFieldOptions current_depth_options = new DepthOfFieldOptions(current_rgbz);
-        RsTaskParams rsTaskParam = new RsTaskParams(rs, current_depth_options);
-
-        RenderScriptTask renderScriptTask = new RenderScriptTask(rs, impl);
-        Bitmap outputImage = renderScriptTask.applyRefocusFilter(rsTaskParam.mOptions);
-
-        Bitmap expectedImage = BitmapFactory.decodeResource(ctx.getResources(), R.drawable.expected_output);
-
-        double psnr = ImageCompare.psnr(outputImage, expectedImage);
-        android.util.Log.i("RefocusTest", "psnr = " + String.format("%.02f", psnr));
-        if (psnr < minimumPSNR) {
-            MediaStoreSaver.savePNG(outputImage, "refocus", "refocus_output" , ctx);
-            assertTrue("Required minimum psnr = " + String.format("%.02f; ", minimumPSNR) +
-                       "Actual psnr = " + String.format("%.02f", psnr),
-                       false);
-        }
-        rs.destroy();
     }
 
 
diff --git a/tests/tests/renderscriptlegacy/AndroidTest.xml b/tests/tests/renderscriptlegacy/AndroidTest.xml
index 6550d5d..127ace1 100644
--- a/tests/tests/renderscriptlegacy/AndroidTest.xml
+++ b/tests/tests/renderscriptlegacy/AndroidTest.xml
@@ -14,6 +14,7 @@
      limitations under the License.
 -->
 <configuration description="Configuration for Renderscript legacy Tests">
+    <option name="test-suite-tag" value="cts" />
     <option name="config-descriptor:metadata" key="component" value="renderscript" />
     <target_preparer class="com.android.tradefed.targetprep.suite.SuiteApkInstaller">
         <option name="cleanup-apks" value="true" />
diff --git a/tests/tests/rsblas/Android.mk b/tests/tests/rsblas/Android.mk
index 0385573..637f08e 100644
--- a/tests/tests/rsblas/Android.mk
+++ b/tests/tests/rsblas/Android.mk
@@ -27,7 +27,8 @@
 # When built, explicitly put it in the data partition.
 LOCAL_MODULE_PATH := $(TARGET_OUT_DATA_APPS)
 
-LOCAL_STATIC_JAVA_LIBRARIES := ctstestrunner legacy-android-test
+LOCAL_STATIC_JAVA_LIBRARIES := ctstestrunner
+LOCAL_JAVA_LIBRARIES := android.test.base.stubs
 LOCAL_JNI_SHARED_LIBRARIES := libbnnmdata_jni
 
 LOCAL_SRC_FILES := $(call all-java-files-under, src) $(call all-renderscript-files-under, src)
diff --git a/tests/tests/rsblas/AndroidTest.xml b/tests/tests/rsblas/AndroidTest.xml
index 5f8164b..7cbfe32 100644
--- a/tests/tests/rsblas/AndroidTest.xml
+++ b/tests/tests/rsblas/AndroidTest.xml
@@ -14,6 +14,7 @@
      limitations under the License.
 -->
 <configuration description="Config for CTS RS BLAS test cases">
+    <option name="test-suite-tag" value="cts" />
     <option name="config-descriptor:metadata" key="component" value="renderscript" />
     <target_preparer class="com.android.tradefed.targetprep.suite.SuiteApkInstaller">
         <option name="cleanup-apks" value="true" />
diff --git a/tests/tests/rscpp/Android.mk b/tests/tests/rscpp/Android.mk
index 11957bf..f5c20ca 100644
--- a/tests/tests/rscpp/Android.mk
+++ b/tests/tests/rscpp/Android.mk
@@ -28,7 +28,8 @@
 # When built, explicitly put it in the data partition.
 LOCAL_MODULE_PATH := $(TARGET_OUT_DATA_APPS)
 
-LOCAL_STATIC_JAVA_LIBRARIES := ctstestrunner legacy-android-test
+LOCAL_STATIC_JAVA_LIBRARIES := ctstestrunner
+LOCAL_JAVA_LIBRARIES := android.test.base.stubs
 LOCAL_JNI_SHARED_LIBRARIES := librscpptest_jni
 
 LOCAL_SRC_FILES := $(call all-java-files-under, src) $(call all-renderscript-files-under, src)
diff --git a/tests/tests/rscpp/AndroidTest.xml b/tests/tests/rscpp/AndroidTest.xml
index 9a9c6ba..61d05a8 100644
--- a/tests/tests/rscpp/AndroidTest.xml
+++ b/tests/tests/rscpp/AndroidTest.xml
@@ -14,6 +14,7 @@
      limitations under the License.
 -->
 <configuration description="Configuration for renderscript cpp Tests">
+    <option name="test-suite-tag" value="cts" />
     <option name="config-descriptor:metadata" key="component" value="renderscript" />
     <target_preparer class="com.android.tradefed.targetprep.suite.SuiteApkInstaller">
         <option name="cleanup-apks" value="true" />
diff --git a/tests/tests/sax/Android.mk b/tests/tests/sax/Android.mk
index 0c2a2b3..d854016 100644
--- a/tests/tests/sax/Android.mk
+++ b/tests/tests/sax/Android.mk
@@ -21,7 +21,9 @@
 # and when built explicitly put it in the data partition
 LOCAL_MODULE_PATH := $(TARGET_OUT_DATA_APPS)
 
-LOCAL_STATIC_JAVA_LIBRARIES := ctstestrunner legacy-android-test
+LOCAL_STATIC_JAVA_LIBRARIES := ctstestrunner
+
+LOCAL_JAVA_LIBRARIES := android.test.base.stubs
 
 LOCAL_SRC_FILES := $(call all-java-files-under, src)
 
diff --git a/tests/tests/sax/AndroidTest.xml b/tests/tests/sax/AndroidTest.xml
index 20609bc..f81aa67 100644
--- a/tests/tests/sax/AndroidTest.xml
+++ b/tests/tests/sax/AndroidTest.xml
@@ -14,6 +14,7 @@
      limitations under the License.
 -->
 <configuration description="Config for CTS SAX test cases">
+    <option name="test-suite-tag" value="cts" />
     <option name="config-descriptor:metadata" key="component" value="framework" />
     <option name="not-shardable" value="true" />
     <target_preparer class="com.android.tradefed.targetprep.suite.SuiteApkInstaller">
diff --git a/tests/tests/security/Android.mk b/tests/tests/security/Android.mk
index b22f6c7..62952ef 100644
--- a/tests/tests/security/Android.mk
+++ b/tests/tests/security/Android.mk
@@ -28,10 +28,13 @@
     ctstestrunner \
     compatibility-device-util \
     guava \
-    platform-test-annotations \
-    legacy-android-test
+    platform-test-annotations
 
-LOCAL_JAVA_LIBRARIES := android.test.runner org.apache.http.legacy
+LOCAL_JAVA_LIBRARIES := \
+    android.test.runner \
+    org.apache.http.legacy \
+    android.test.base \
+
 
 LOCAL_JNI_SHARED_LIBRARIES := libctssecurity_jni libcts_jni libnativehelper_compat_libc++ \
                       libnativehelper \
diff --git a/tests/tests/security/AndroidTest.xml b/tests/tests/security/AndroidTest.xml
index 2c47d89..03b2f9a 100644
--- a/tests/tests/security/AndroidTest.xml
+++ b/tests/tests/security/AndroidTest.xml
@@ -14,6 +14,7 @@
      limitations under the License.
 -->
 <configuration description="Config for CTS security test cases">
+    <option name="test-suite-tag" value="cts" />
     <option name="config-descriptor:metadata" key="component" value="security" />
     <target_preparer class="com.android.tradefed.targetprep.suite.SuiteApkInstaller">
         <option name="cleanup-apks" value="true" />
diff --git a/tests/tests/security/jni/Android.mk b/tests/tests/security/jni/Android.mk
index fe4f779..4fefdab 100644
--- a/tests/tests/security/jni/Android.mk
+++ b/tests/tests/security/jni/Android.mk
@@ -28,7 +28,6 @@
 		android_security_cts_LinuxRngTest.cpp \
 		android_security_cts_NativeCodeTest.cpp \
 		android_security_cts_SELinuxTest.cpp \
-		android_security_cts_SeccompTest.cpp \
 		android_security_cts_MMapExecutableTest.cpp \
 		android_security_cts_EncryptionTest.cpp \
 
diff --git a/tests/tests/security/jni/CtsSecurityJniOnLoad.cpp b/tests/tests/security/jni/CtsSecurityJniOnLoad.cpp
index 835d1a6..2014591 100644
--- a/tests/tests/security/jni/CtsSecurityJniOnLoad.cpp
+++ b/tests/tests/security/jni/CtsSecurityJniOnLoad.cpp
@@ -49,11 +49,7 @@
         return JNI_ERR;
     }
 
-    if (register_android_security_cts_SeccompTest(env)) {
-        return JNI_ERR;
-    }
-
-     if (register_android_security_cts_KernelSettingsTest(env)) {
+    if (register_android_security_cts_KernelSettingsTest(env)) {
         return JNI_ERR;
     }
 
diff --git a/tests/tests/security/jni/android_security_cts_SeccompTest.cpp b/tests/tests/security/jni/android_security_cts_SeccompTest.cpp
deleted file mode 100644
index ee36cdd..0000000
--- a/tests/tests/security/jni/android_security_cts_SeccompTest.cpp
+++ /dev/null
@@ -1,81 +0,0 @@
-/*
- * Copyright (C) 2017 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#include <jni.h>
-
-#define LOG_TAG "SeccompTest"
-
-#include <cutils/log.h>
-#include <unistd.h>
-#include <sys/types.h>
-#include <sys/wait.h>
-
-/*
- * Function: testSyscallBlocked
- * Purpose: test that the syscall listed is blocked by seccomp
- * Parameters:
- *        nr: syscall number
- * Returns:
- *        1 if blocked, else 0
- * Exceptions: None
- */
-static jboolean testSyscallBlocked(JNIEnv *, jobject, int nr) {
-    int pid = fork();
-    if (pid == 0) {
-        ALOGI("Calling syscall %d", nr);
-        int ret = syscall(nr);
-        return false;
-    } else {
-        int status;
-        int ret = waitpid(pid, &status, 0);
-        if (ret != pid) {
-            ALOGE("Unexpected return result from waitpid");
-            return false;
-        }
-
-        if (WIFEXITED(status)) {
-            ALOGE("syscall was not blocked");
-            return false;
-        }
-
-        if (WIFSIGNALED(status)) {
-            int signal = WTERMSIG(status);
-            if (signal == 31) {
-                ALOGI("syscall caused process termination");
-                return true;
-            }
-
-            ALOGE("Unexpected signal");
-            return false;
-        }
-
-        ALOGE("Unexpected status from syscall_exists");
-        return false;
-    }
-}
-
-static JNINativeMethod gMethods[] = {
-    { "testSyscallBlocked", "(I)Z",
-            (void*) testSyscallBlocked },
-};
-
-int register_android_security_cts_SeccompTest(JNIEnv* env)
-{
-    jclass clazz = env->FindClass("android/security/cts/SeccompTest");
-
-    return env->RegisterNatives(clazz, gMethods,
-            sizeof(gMethods) / sizeof(JNINativeMethod));
-}
diff --git a/tests/tests/security/res/raw/b38116746_new.ico b/tests/tests/security/res/raw/b38116746_new.ico
new file mode 100644
index 0000000..35ee5b5
--- /dev/null
+++ b/tests/tests/security/res/raw/b38116746_new.ico
Binary files differ
diff --git a/tests/tests/security/res/raw/bug_67381469.webp b/tests/tests/security/res/raw/bug_67381469.webp
new file mode 100644
index 0000000..82ff6e4
--- /dev/null
+++ b/tests/tests/security/res/raw/bug_67381469.webp
Binary files differ
diff --git a/tests/tests/security/src/android/security/cts/BitmapFactorySecurityTests.java b/tests/tests/security/src/android/security/cts/BitmapFactorySecurityTests.java
index c8bfbb1..203db12 100644
--- a/tests/tests/security/src/android/security/cts/BitmapFactorySecurityTests.java
+++ b/tests/tests/security/src/android/security/cts/BitmapFactorySecurityTests.java
@@ -17,30 +17,56 @@
 package android.security.cts;
 
 import android.graphics.BitmapFactory;
+import android.os.ParcelFileDescriptor;
 import android.platform.test.annotations.SecurityTest;
 import android.test.AndroidTestCase;
 
+import java.io.File;
+import java.io.FileDescriptor;
+import java.io.FileOutputStream;
 import java.io.InputStream;
 
+import java.lang.Exception;
+
 import android.security.cts.R;
 
 @SecurityTest
 public class BitmapFactorySecurityTests extends AndroidTestCase {
-    private InputStream getResource(int resId) {
-        InputStream resource = mContext.getResources().openRawResource(R.raw.bug_38116746);
-        assertNotNull(resource);
-        return resource;
+    private FileDescriptor getResource(int resId) {
+        try {
+            InputStream is = mContext.getResources().openRawResource(resId);
+            assertNotNull(is);
+            File file = File.createTempFile("BitmapFactorySecurityFile" + resId, "img");
+            file.deleteOnExit();
+            FileOutputStream output = new FileOutputStream(file);
+            byte[] buffer = new byte[1024];
+            int readLength;
+            while ((readLength = is.read(buffer)) != -1) {
+                output.write(buffer, 0, readLength);
+            }
+            is.close();
+            output.close();
+            ParcelFileDescriptor pfd = ParcelFileDescriptor.open(file,
+                    ParcelFileDescriptor.MODE_READ_ONLY);
+            return pfd.getFileDescriptor();
+        } catch (Exception e) {
+            fail("Could not get resource " + resId + "! " + e);
+            return null;
+        }
     }
 
     /**
-     * Verifies that decoding a corrupt ICO does not run out of memory.
+     * Verifies that decoding a corrupt ICO does crash.
      */
     public void test_android_bug_38116746() {
-        InputStream exploitImage = getResource(R.raw.bug_38116746);
+        FileDescriptor exploitImage = getResource(R.raw.bug_38116746);
         try {
-            BitmapFactory.decodeStream(exploitImage);
+            BitmapFactory.decodeFileDescriptor(exploitImage);
         } catch (OutOfMemoryError e) {
             fail("OOM attempting to decode ICO");
         }
+
+        // This previously crashed in fread. No need to check the output.
+        BitmapFactory.decodeFileDescriptor(getResource(R.raw.b38116746_new));
     }
 }
diff --git a/tests/tests/security/src/android/security/cts/CertificateData.java b/tests/tests/security/src/android/security/cts/CertificateData.java
index b880f5f..68ec092 100644
--- a/tests/tests/security/src/android/security/cts/CertificateData.java
+++ b/tests/tests/security/src/android/security/cts/CertificateData.java
@@ -36,7 +36,6 @@
       "75:E0:AB:B6:13:85:12:27:1C:04:F8:5F:DD:DE:38:E4:B7:24:2E:FE",
       "DA:C9:02:4F:54:D8:F6:DF:94:93:5F:B1:73:26:38:CA:6A:D7:7C:13",
       "74:20:74:41:72:9C:DD:92:EC:79:31:D8:23:10:8D:C2:81:92:E2:BB",
-      "40:54:DA:6F:1C:3F:40:74:AC:ED:0F:EC:CD:DB:79:D1:53:FB:90:1D",
       "F4:8B:11:BF:DE:AB:BE:94:54:20:71:E6:41:DE:6B:BE:88:2B:40:B9",
       "58:E8:AB:B0:36:15:33:FB:80:F7:9B:1B:6D:29:D3:FF:8D:5F:00:F0",
       "55:A6:72:3E:CB:F2:EC:CD:C3:23:74:70:19:9D:2A:BE:11:E3:81:D1",
@@ -51,6 +50,7 @@
       "DA:FA:F7:FA:66:84:EC:06:8F:14:50:BD:C7:C2:81:A5:BC:A9:64:57",
       "74:F8:A3:C3:EF:E7:B3:90:06:4B:83:90:3C:21:64:60:20:E5:DF:CE",
       "31:43:64:9B:EC:CE:27:EC:ED:3A:3F:0B:8F:0D:E4:E8:91:DD:EE:CA",
+      "B7:AB:33:08:D1:EA:44:77:BA:14:80:12:5A:6F:BD:A9:36:49:0C:BB",
       "5F:43:E5:B1:BF:F8:78:8C:AC:1C:C7:CA:4A:9A:C6:22:2B:CC:34:C6",
       "2B:8F:1B:57:33:0D:BB:A2:D0:7A:6C:51:F7:0E:E9:0D:DA:B9:AD:8E",
       "79:5F:88:60:C5:AB:7C:3D:92:E6:CB:F4:8D:E1:45:CD:11:EF:60:0B",
@@ -64,7 +64,6 @@
       "59:AF:82:79:91:86:C7:B4:75:07:CB:CF:03:57:46:EB:04:DD:B7:16",
       "50:30:06:09:1D:97:D4:F5:AE:39:F7:CB:E7:92:7D:7D:65:2D:34:31",
       "FE:45:65:9B:79:03:5B:98:A1:61:B5:51:2E:AC:DA:58:09:48:22:4D",
-      "1B:4B:39:61:26:27:6B:64:91:A2:68:6D:D7:02:43:21:2D:1F:1D:96",
       "8C:F4:27:FD:79:0C:3A:D1:66:06:8D:E8:1E:57:EF:BB:93:22:72:D4",
       "2F:78:3D:25:52:18:A7:4A:65:39:71:B5:2C:A2:9C:45:15:6F:E9:19",
       "BA:29:41:60:77:98:3F:F4:F3:EF:F2:31:05:3B:2E:EA:6D:4D:45:FD",
@@ -73,6 +72,7 @@
       "36:79:CA:35:66:87:72:30:4D:30:A5:FB:87:3B:0F:A7:7B:B7:0D:54",
       "1B:8E:EA:57:96:29:1A:C9:39:EA:B8:0A:81:1A:73:73:C0:93:79:67",
       "6E:26:64:F3:56:BF:34:55:BF:D1:93:3F:7C:01:DE:D8:13:DA:8A:A6",
+      "74:3A:F0:52:9B:D0:32:A0:F4:4A:83:CD:D4:BA:A9:7B:7C:2E:C4:9A",
       "D8:EB:6B:41:51:92:59:E0:F3:E7:85:00:C0:3D:B6:88:97:C9:EE:FC",
       "66:31:BF:9E:F7:4F:9E:B6:C9:D5:A6:0C:BA:6A:BE:D1:F7:BD:EF:7B",
       "DE:3F:40:BD:50:93:D3:9B:6C:60:F6:DA:BC:07:62:01:00:89:76:C9",
@@ -83,14 +83,14 @@
       "43:13:BB:96:F1:D5:86:9B:C1:4E:6A:92:F6:CF:F6:34:69:87:82:37",
       "F1:8B:53:8D:1B:E9:03:B6:A6:F0:56:43:5B:17:15:89:CA:F3:6B:F2",
       "05:63:B8:63:0D:62:D7:5A:BB:C8:AB:1E:4B:DF:B5:A8:99:B2:4D:43",
-      "62:52:DC:40:F7:11:43:A2:2F:DE:9E:F7:34:8E:06:42:51:B1:81:18",
       "70:17:9B:86:8C:00:A4:FA:60:91:52:22:3F:9F:3E:32:BD:E0:05:62",
       "D1:EB:23:A4:6D:17:D6:8F:D9:25:64:C2:F1:F1:60:17:64:D8:E3:49",
       "B8:01:86:D1:EB:9C:86:A5:41:04:CF:30:54:F3:4C:52:B7:E5:58:C6",
-      "2E:14:DA:EC:28:F0:FA:1E:8E:38:9A:4E:AB:EB:26:C0:0A:D3:83:C3",
+      "4C:DD:51:A3:D1:F5:20:32:14:B0:C6:C5:32:23:03:91:C7:46:42:6D",
       "DE:28:F4:A4:FF:E5:B9:2F:A3:C5:03:D1:A3:49:A7:F9:96:2A:82:12",
       "0D:44:DD:8C:3C:8C:1A:1A:58:75:64:81:E9:0F:2E:2A:FF:B3:D2:6E",
       "CA:3A:FB:CF:12:40:36:4B:44:B2:16:20:88:80:48:39:19:93:7C:F7",
+      "FF:BD:CD:E7:82:C8:43:5E:3C:6F:26:86:5C:CA:A8:3A:45:5B:C3:0A",
       "13:2D:0D:45:53:4B:69:97:CD:B2:D5:C3:39:E2:55:76:60:9B:5C:C6",
       "5F:B7:EE:06:33:E2:59:DB:AD:0C:4C:9A:E6:D3:8F:1A:61:C7:DC:25",
       "49:0A:75:74:DE:87:0A:47:FE:58:EE:F6:C7:6B:EB:C6:0B:12:40:99",
@@ -98,6 +98,7 @@
       "29:36:21:02:8B:20:ED:02:F5:66:C5:32:D1:D6:ED:90:9F:45:00:2F",
       "37:9A:19:7B:41:85:45:35:0C:A6:03:69:F3:3C:2E:AF:47:4F:20:79",
       "FA:B7:EE:36:97:26:62:FB:2D:B0:2A:F6:BF:03:FD:E8:7C:4B:2F:9B",
+      "C3:19:7C:39:24:E6:54:AF:1B:C4:AB:20:95:7A:E2:C3:0E:13:02:6A",
       "9F:74:4E:9F:2B:4D:BA:EC:0F:31:2C:50:B6:56:3B:8E:2D:93:C3:11",
       "A1:4B:48:D9:43:EE:0A:0E:40:90:4F:3C:E0:A4:C0:91:93:51:5D:3F",
       "C9:A8:B9:E7:55:80:5E:58:E3:53:77:A7:25:EB:AF:C3:7B:27:CC:D7",
@@ -119,6 +120,7 @@
       "AA:DB:BC:22:23:8F:C4:01:A1:27:BB:38:DD:F4:1D:DB:08:9E:F0:12",
       "E2:52:FA:95:3F:ED:DB:24:60:BD:6E:28:F3:9C:CC:CF:5E:B3:3F:DE",
       "3B:C4:9F:48:F8:F3:73:A0:9C:1E:BD:F8:5B:B1:C3:65:C7:D8:11:B3",
+      "0F:36:38:5B:81:1A:25:C3:9B:31:4E:83:CA:E9:34:66:70:CC:74:B4",
       "28:90:3A:63:5B:52:80:FA:E6:77:4C:0B:6D:A7:D6:BA:A6:4A:F2:E8",
       "9C:BB:48:53:F6:A4:F6:D3:52:A4:E8:32:52:55:60:13:F5:AD:AF:65",
       "B1:BC:96:8B:D4:F4:9D:62:2A:A8:9A:81:F2:15:01:52:A4:1D:82:9C",
@@ -129,6 +131,7 @@
       "47:BE:AB:C9:22:EA:E8:0E:78:78:34:62:A7:9F:45:C2:54:FD:E6:8B",
       "3A:44:73:5A:E5:81:90:1F:24:86:61:46:1E:3B:9C:C4:5F:F5:3A:1B",
       "B3:1E:B1:B7:40:E3:6C:84:02:DA:DC:37:D4:4D:F5:D4:67:49:52:F9",
+      "58:D1:DF:95:95:67:6B:63:C0:F0:5B:1C:17:4D:8B:84:0B:C8:78:BD",
       "F5:17:A2:4F:9A:48:C6:C9:F8:A2:00:26:9F:DC:0F:48:2C:AB:30:89",
       "3B:C0:38:0B:33:C3:F6:A6:0C:86:15:22:93:D9:DF:F5:4B:81:C0:04",
       "03:9E:ED:B8:0B:E7:A0:3C:69:53:89:3B:20:D2:D9:32:3A:4C:2A:FD",
@@ -139,12 +142,12 @@
       "B8:23:6B:00:2F:1D:16:86:53:01:55:6C:11:A4:37:CA:EB:FF:C3:BB",
       "87:82:C6:C3:04:35:3B:CF:D2:96:92:D2:59:3E:7D:44:D9:34:FF:11",
       "59:0D:2D:7D:88:4F:40:2E:61:7E:A5:62:32:17:65:CF:17:D8:94:E9",
+      "B8:BE:6D:CB:56:F1:55:B9:63:D4:12:CA:4E:06:34:C7:94:B2:1C:C0",
       "AE:C5:FB:3F:C8:E1:BF:C4:E5:4F:03:07:5A:9A:E8:00:B7:F7:B6:FA",
       "DF:71:7E:AA:4A:D9:4E:C9:55:84:99:60:2D:48:DE:5F:BC:F0:3A:25",
       "F6:10:84:07:D6:F8:BB:67:98:0C:C2:E2:44:C2:EB:AE:1C:EF:63:BE",
       "AF:E5:D2:44:A8:D1:19:42:30:FF:47:9F:E2:F8:97:BB:CD:7A:8C:B4",
       "5F:3B:8C:F2:F8:10:B3:7D:78:B4:CE:EC:19:19:C3:73:34:B9:C7:74",
-      "F1:7F:6F:B6:31:DC:99:E3:A3:C8:7F:FE:1C:F1:81:10:88:D9:60:33",
       "9D:70:BB:01:A5:A4:A0:18:11:2E:F7:1C:01:B9:32:C5:34:E7:88:A8",
       "96:C9:1B:0B:95:B4:10:98:42:FA:D0:D8:22:79:FE:60:FA:B9:16:83",
       "4F:65:8E:1F:E9:06:D8:28:02:E9:54:47:41:C9:54:25:5D:69:CC:1A",
@@ -155,7 +158,6 @@
       "F9:B5:B6:32:45:5F:9C:BE:EC:57:5F:80:DC:E9:6E:2C:C7:B2:78:B7",
       "E6:21:F3:35:43:79:05:9A:4B:68:30:9D:8A:2F:74:22:15:87:EC:79",
       "89:DF:74:FE:5C:F4:0F:4A:80:F9:E3:37:7D:54:DA:91:E1:01:31:8E",
-      "E0:B4:32:2E:B2:F6:A5:68:B6:54:53:84:48:18:4A:50:36:87:43:84",
       "7E:04:DE:89:6A:3E:66:6D:00:E6:87:D3:3F:FA:D9:3B:E8:3D:34:9E",
       "6E:3A:55:A4:19:0C:19:5C:93:84:3C:C0:DB:72:2E:31:30:61:F0:B1",
       "4E:B6:D5:78:49:9B:1C:CF:5F:58:1E:AD:56:BE:3D:9B:67:44:A5:E5",
diff --git a/tests/tests/security/src/android/security/cts/DecodeTest.java b/tests/tests/security/src/android/security/cts/DecodeTest.java
index e64e37a..0e92310 100644
--- a/tests/tests/security/src/android/security/cts/DecodeTest.java
+++ b/tests/tests/security/src/android/security/cts/DecodeTest.java
@@ -38,4 +38,17 @@
         Bitmap bitmap = BitmapFactory.decodeStream(exploitImage);
         assertNull(bitmap);
     }
+
+    /**
+     * Verifies that the device fails to decode a truncated animated webp.
+     *
+     * Prior to fixing bug 67381469, decoding this file would crash. Instead, it should fail to
+     * decode.
+     */
+    @SecurityTest
+    public void test_android_bug_67381469() {
+        InputStream exploitImage = mContext.getResources().openRawResource(R.raw.bug_67381469);
+        Bitmap bitmap = BitmapFactory.decodeStream(exploitImage);
+        assertNull(bitmap);
+    }
 }
diff --git a/tests/tests/security/src/android/security/cts/OpenSSLEarlyCCSTest.java b/tests/tests/security/src/android/security/cts/OpenSSLEarlyCCSTest.java
deleted file mode 100644
index a6bad84..0000000
--- a/tests/tests/security/src/android/security/cts/OpenSSLEarlyCCSTest.java
+++ /dev/null
@@ -1,582 +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.security.cts;
-
-import android.platform.test.annotations.SecurityTest;
-import android.security.cts.OpenSSLHeartbleedTest.AlertMessage;
-import android.security.cts.OpenSSLHeartbleedTest.HandshakeMessage;
-import android.security.cts.OpenSSLHeartbleedTest.HardcodedCertX509KeyManager;
-import android.security.cts.OpenSSLHeartbleedTest.TlsProtocols;
-import android.security.cts.OpenSSLHeartbleedTest.TlsRecord;
-import android.security.cts.OpenSSLHeartbleedTest.TlsRecordReader;
-import android.security.cts.OpenSSLHeartbleedTest.TrustAllX509TrustManager;
-import android.test.InstrumentationTestCase;
-import android.util.Log;
-
-import java.io.ByteArrayInputStream;
-import java.io.IOException;
-import java.io.InputStream;
-import java.io.OutputStream;
-import java.net.ServerSocket;
-import java.net.Socket;
-import java.net.SocketAddress;
-import java.security.KeyFactory;
-import java.security.PrivateKey;
-import java.security.cert.CertificateFactory;
-import java.security.cert.X509Certificate;
-import java.security.spec.PKCS8EncodedKeySpec;
-import java.util.concurrent.Callable;
-import java.util.concurrent.ExecutionException;
-import java.util.concurrent.ExecutorService;
-import java.util.concurrent.Executors;
-import java.util.concurrent.Future;
-import java.util.concurrent.TimeUnit;
-
-import javax.net.ServerSocketFactory;
-import javax.net.SocketFactory;
-import javax.net.ssl.KeyManager;
-import javax.net.ssl.SSLContext;
-import javax.net.ssl.SSLServerSocket;
-import javax.net.ssl.SSLSocket;
-import javax.net.ssl.TrustManager;
-
-/**
- * Tests for the OpenSSL early ChangeCipherSpec (CCS) vulnerability (CVE-2014-0224).
- */
-@SecurityTest
-public class OpenSSLEarlyCCSTest extends InstrumentationTestCase {
-
-    // IMPLEMENTATION NOTE: This test spawns an SSLSocket client, SSLServerSocket server, and a
-    // Man-in-The-Middle (MiTM). The client connects to the MiTM which then connects to the server
-    // and starts forwarding all TLS records between the client and the server. In tests that check
-    // for the early CCS vulnerability, the MiTM also injects an early ChangeCipherSpec record into
-    // the traffic to which a correctly implemented peer is supposed to reply by immediately
-    // aborting the TLS handshake with an unexpected_message fatal alert.
-
-    // IMPLEMENTATION NOTE: This test spawns several background threads that perform network I/O
-    // on localhost. To ensure that these background threads are cleaned up at the end of the test,
-    // tearDown() kills the sockets they may be using. To aid this behavior, all Socket and
-    // ServerSocket instances are available as fields of this class. These fields should be accessed
-    // via setters and getters to avoid memory visibility issues due to concurrency.
-
-    private static final String TAG = OpenSSLEarlyCCSTest.class.getSimpleName();
-
-    private SSLServerSocket mServerListeningSocket;
-    private SSLSocket mServerSocket;
-    private SSLSocket mClientSocket;
-    private ServerSocket mMitmListeningSocket;
-    private Socket mMitmServerSocket;
-    private Socket mMitmClientSocket;
-    private ExecutorService mExecutorService;
-
-    private boolean mCCSWasInjected;
-    private TlsRecord mFirstRecordReceivedAfterCCSWasInjected;
-    private boolean mFirstRecordReceivedAfterCCSWasInjectedMayBeEncrypted;
-
-    @Override
-    protected void tearDown() throws Exception {
-        Log.i(TAG, "Tearing down");
-        try {
-            if (mExecutorService != null) {
-                mExecutorService.shutdownNow();
-            }
-            OpenSSLHeartbleedTest.closeQuietly(getServerListeningSocket());
-            OpenSSLHeartbleedTest.closeQuietly(getServerSocket());
-            OpenSSLHeartbleedTest.closeQuietly(getClientSocket());
-            OpenSSLHeartbleedTest.closeQuietly(getMitmListeningSocket());
-            OpenSSLHeartbleedTest.closeQuietly(getMitmServerSocket());
-            OpenSSLHeartbleedTest.closeQuietly(getMitmClientSocket());
-        } finally {
-            super.tearDown();
-            Log.i(TAG, "Tear down completed");
-        }
-    }
-
-    /**
-     * Tests that TLS handshake succeeds when the MiTM simply forwards all data without tampering
-     * with it. This is to catch issues unrelated to early CCS.
-     */
-    public void testWithoutEarlyCCS() throws Exception {
-        handshake(false, false);
-    }
-
-    /**
-     * Tests whether client sockets are vulnerable to early CCS.
-     */
-    public void testEarlyCCSInjectedIntoClient() throws Exception {
-        checkEarlyCCS(true);
-    }
-
-    /**
-     * Tests whether server sockets are vulnerable to early CCS.
-     */
-    public void testEarlyCCSInjectedIntoServer() throws Exception {
-        checkEarlyCCS(false);
-    }
-
-    /**
-     * Tests for the early CCS vulnerability.
-     *
-     * @param client {@code true} to test the client, {@code false} to test the server.
-     */
-    private void checkEarlyCCS(boolean client) throws Exception {
-        // IMPLEMENTATION NOTE: The MiTM is forwarding all TLS records between the client and the
-        // server unmodified. Additionally, the MiTM transmits an early ChangeCipherSpec to server
-        // (if "client" argument is false) right before client's ClientKeyExchange or to client (if
-        // "client" argument is true) right before server's Certificate. The peer is expected to
-        // to abort the handshake immediately with unexpected_message alert.
-        try {
-            handshake(true, client);
-            // TLS handshake succeeded
-            assertTrue("Early CCS injected", wasCCSInjected());
-            fail("Handshake succeeded despite early CCS having been injected");
-        } catch (ExecutionException e) {
-            // TLS handshake failed
-            assertTrue("Early CCS injected", wasCCSInjected());
-            TlsRecord firstRecordReceivedAfterCCSWasInjected =
-                    getFirstRecordReceivedAfterCCSWasInjected();
-            assertNotNull(
-                    "Nothing received after early CCS was injected",
-                    firstRecordReceivedAfterCCSWasInjected);
-            if (firstRecordReceivedAfterCCSWasInjected.protocol == TlsProtocols.ALERT) {
-                AlertMessage alert = AlertMessage.tryParse(firstRecordReceivedAfterCCSWasInjected);
-                if ((alert != null)
-                        && (alert.level == AlertMessage.LEVEL_FATAL)
-                        && (alert.description == AlertMessage.DESCRIPTION_UNEXPECTED_MESSAGE)) {
-                    // Expected/correct response to an early CCS
-                    return;
-                }
-            }
-            fail("SSLSocket is vulnerable to early CCS in " + ((client) ? "client" : "server")
-                    + " mode: unexpected record received after CCS was injected: "
-                    + getRecordInfo(
-                            getFirstRecordReceivedAfterCCSWasInjected(),
-                            getFirstRecordReceivedAfterCCSWasInjectedMayBeEncrypted()));
-        }
-    }
-
-    /**
-     * Starts the client, server, and the MiTM. Makes the client and server perform a TLS handshake
-     * and exchange application-level data. The MiTM injects a ChangeCipherSpec record if requested
-     * by {@code earlyCCSInjected}. The direction of the injected message is specified by
-     * {@code injectedIntoClient}.
-     */
-    private void handshake(
-            final boolean earlyCCSInjected,
-            final boolean injectedIntoClient) throws Exception {
-        mExecutorService = Executors.newFixedThreadPool(4);
-        setServerListeningSocket(serverBind());
-        final SocketAddress serverAddress = getServerListeningSocket().getLocalSocketAddress();
-        Log.i(TAG, "Server bound to " + serverAddress);
-
-        setMitmListeningSocket(mitmBind());
-        final SocketAddress mitmAddress = getMitmListeningSocket().getLocalSocketAddress();
-        Log.i(TAG, "MiTM bound to " + mitmAddress);
-
-        // Start the MiTM daemon in the background
-        mExecutorService.submit(new Callable<Void>() {
-            @Override
-            public Void call() throws Exception {
-                mitmAcceptAndForward(serverAddress, earlyCCSInjected, injectedIntoClient);
-                return null;
-            }
-        });
-        // Start the server in the background
-        Future<Void> serverFuture = mExecutorService.submit(new Callable<Void>() {
-            @Override
-            public Void call() throws Exception {
-                serverAcceptAndHandshake();
-                return null;
-            }
-        });
-        // Start the client in the background
-        Future<Void> clientFuture = mExecutorService.submit(new Callable<Void>() {
-            @Override
-            public Void call() throws Exception {
-                clientConnectAndHandshake(mitmAddress);
-                return null;
-            }
-        });
-
-        // Wait for both client and server to terminate, to ensure that we observe all the traffic
-        // exchanged between them. Throw an exception if one of them failed.
-        Log.i(TAG, "Waiting for client");
-        // Wait for the client, but don't yet throw an exception if it failed.
-        Exception clientException = null;
-        try {
-            clientFuture.get(10, TimeUnit.SECONDS);
-        } catch (Exception e) {
-            clientException = e;
-        }
-        Log.i(TAG, "Waiting for server");
-        // Wait for the server and throw an exception if it failed.
-        serverFuture.get(5, TimeUnit.SECONDS);
-        // Throw an exception if the client failed.
-        if (clientException != null) {
-            throw clientException;
-        }
-        Log.i(TAG, "Handshake completed and application data exchanged");
-    }
-
-    private void clientConnectAndHandshake(SocketAddress serverAddress) throws Exception {
-        SSLContext sslContext = SSLContext.getInstance("TLS");
-        sslContext.init(
-                null,
-                new TrustManager[] {new TrustAllX509TrustManager()},
-                null);
-        SSLSocket socket = (SSLSocket) sslContext.getSocketFactory().createSocket();
-        setClientSocket(socket);
-        try {
-            Log.i(TAG, "Client connecting to " + serverAddress);
-            socket.connect(serverAddress);
-            Log.i(TAG, "Client connected to server from " + socket.getLocalSocketAddress());
-            // Ensure a TLS handshake is performed and an exception is thrown if it fails.
-            socket.getOutputStream().write("client".getBytes());
-            socket.getOutputStream().flush();
-            Log.i(TAG, "Client sent request. Reading response");
-            int b = socket.getInputStream().read();
-            Log.i(TAG, "Client read response: " + b);
-        } catch (Exception e) {
-            // Make sure the test log includes exceptions from all parties involved.
-            Log.w(TAG, "Client failed", e);
-            throw e;
-          } finally {
-            socket.close();
-        }
-    }
-
-    private SSLServerSocket serverBind() throws Exception {
-        // Load the server's private key and cert chain
-        KeyFactory keyFactory = KeyFactory.getInstance("RSA");
-        PrivateKey privateKey = keyFactory.generatePrivate(new PKCS8EncodedKeySpec(
-                OpenSSLHeartbleedTest.readResource(
-                        getInstrumentation().getContext(), R.raw.openssl_heartbleed_test_key)));
-        CertificateFactory certFactory = CertificateFactory.getInstance("X.509");
-        X509Certificate[] certChain =  new X509Certificate[] {
-                (X509Certificate) certFactory.generateCertificate(
-                        new ByteArrayInputStream(OpenSSLHeartbleedTest.readResource(
-                                getInstrumentation().getContext(),
-                                R.raw.openssl_heartbleed_test_cert)))
-        };
-
-        // Initialize TLS context to use the private key and cert chain for server sockets
-        SSLContext sslContext = SSLContext.getInstance("TLS");
-        sslContext.init(
-                new KeyManager[] {new HardcodedCertX509KeyManager(privateKey, certChain)},
-                null,
-                null);
-
-        Log.i(TAG, "Server binding to local port");
-        return (SSLServerSocket) sslContext.getServerSocketFactory().createServerSocket(0);
-    }
-
-    private void serverAcceptAndHandshake() throws Exception {
-        SSLSocket socket = null;
-        SSLServerSocket serverSocket = getServerListeningSocket();
-        try {
-            Log.i(TAG, "Server listening for incoming connection");
-            socket = (SSLSocket) serverSocket.accept();
-            setServerSocket(socket);
-            Log.i(TAG, "Server accepted connection from " + socket.getRemoteSocketAddress());
-            // Ensure a TLS handshake is performed and an exception is thrown if it fails.
-            socket.getOutputStream().write("server".getBytes());
-            socket.getOutputStream().flush();
-            Log.i(TAG, "Server sent reply. Reading response");
-            int b = socket.getInputStream().read();
-            Log.i(TAG, "Server read response: " + b);
-        } catch (Exception e) {
-            // Make sure the test log includes exceptions from all parties involved.
-            Log.w(TAG, "Server failed", e);
-            throw e;
-        } finally {
-            if (socket != null) {
-                socket.close();
-            }
-        }
-    }
-
-    private ServerSocket mitmBind() throws Exception {
-        Log.i(TAG, "MiTM binding to local port");
-        return ServerSocketFactory.getDefault().createServerSocket(0);
-    }
-
-    /**
-     * Accepts the connection on the MiTM listening socket, forwards the TLS records between the
-     * client and the server, and, if requested, injects an early {@code ChangeCipherSpec} record.
-     *
-     * @param injectEarlyCCS whether to inject an early {@code ChangeCipherSpec} record.
-     * @param injectIntoClient when {@code injectEarlyCCS} is {@code true}, whether to inject the
-     *        {@code ChangeCipherSpec} record into client or into server.
-     */
-    private void mitmAcceptAndForward(
-            SocketAddress serverAddress,
-            final boolean injectEarlyCCS,
-            final boolean injectIntoClient) throws Exception {
-        Socket clientSocket = null;
-        Socket serverSocket = null;
-        ServerSocket listeningSocket = getMitmListeningSocket();
-        try {
-            Log.i(TAG, "MiTM waiting for incoming connection");
-            clientSocket = listeningSocket.accept();
-            setMitmClientSocket(clientSocket);
-            Log.i(TAG, "MiTM accepted connection from " + clientSocket.getRemoteSocketAddress());
-            serverSocket = SocketFactory.getDefault().createSocket();
-            setMitmServerSocket(serverSocket);
-            Log.i(TAG, "MiTM connecting to server " + serverAddress);
-            serverSocket.connect(serverAddress, 10000);
-            Log.i(TAG, "MiTM connected to server from " + serverSocket.getLocalSocketAddress());
-            final InputStream serverInputStream = serverSocket.getInputStream();
-            final OutputStream clientOutputStream = clientSocket.getOutputStream();
-            Future<Void> serverToClientTask = mExecutorService.submit(new Callable<Void>() {
-                @Override
-                public Void call() throws Exception {
-                    // Inject early ChangeCipherSpec before Certificate, if requested
-                    forwardTlsRecords(
-                            "MiTM S->C",
-                            serverInputStream,
-                            clientOutputStream,
-                            (injectEarlyCCS && injectIntoClient)
-                                      ? HandshakeMessage.TYPE_CERTIFICATE : -1);
-                    return null;
-                }
-            });
-            // Inject early ChangeCipherSpec before ClientKeyExchange, if requested
-            forwardTlsRecords(
-                    "MiTM C->S",
-                    clientSocket.getInputStream(),
-                    serverSocket.getOutputStream(),
-                    (injectEarlyCCS && !injectIntoClient)
-                            ? HandshakeMessage.TYPE_CLIENT_KEY_EXCHANGE : -1);
-            serverToClientTask.get(10, TimeUnit.SECONDS);
-        } catch (Exception e) {
-            // Make sure the test log includes exceptions from all parties involved.
-            Log.w(TAG, "MiTM failed", e);
-            throw e;
-        } finally {
-            OpenSSLHeartbleedTest.closeQuietly(clientSocket);
-            OpenSSLHeartbleedTest.closeQuietly(serverSocket);
-        }
-    }
-
-    /**
-     * Forwards TLS records from the provided {@code InputStream} to the provided
-     * {@code OutputStream}. If requested, injects an early {@code ChangeCipherSpec}.
-     */
-    private void forwardTlsRecords(
-            String logPrefix,
-            InputStream in,
-            OutputStream out,
-            int handshakeMessageTypeBeforeWhichToInjectEarlyCCS) throws Exception {
-        Log.i(TAG, logPrefix + ": record forwarding started");
-        boolean interestingRecordsLogged =
-                handshakeMessageTypeBeforeWhichToInjectEarlyCCS == -1;
-        try {
-            TlsRecordReader reader = new TlsRecordReader(in);
-            byte[] recordBytes;
-            // Fragments contained in records may be encrypted after a certain point in the
-            // handshake. Once they are encrypted, this MiTM cannot inspect their plaintext which.
-            boolean fragmentEncryptionMayBeEnabled = false;
-            while ((recordBytes = reader.readRecord()) != null) {
-                TlsRecord record = TlsRecord.parse(recordBytes);
-                forwardTlsRecord(logPrefix,
-                        recordBytes,
-                        record,
-                        fragmentEncryptionMayBeEnabled,
-                        out,
-                        interestingRecordsLogged,
-                        handshakeMessageTypeBeforeWhichToInjectEarlyCCS);
-                if (record.protocol == TlsProtocols.CHANGE_CIPHER_SPEC) {
-                    fragmentEncryptionMayBeEnabled = true;
-                }
-            }
-        } finally {
-            Log.d(TAG, logPrefix + ": record forwarding finished");
-        }
-    }
-
-    private void forwardTlsRecord(
-            String logPrefix,
-            byte[] recordBytes,
-            TlsRecord record,
-            boolean fragmentEncryptionMayBeEnabled,
-            OutputStream out,
-            boolean interestingRecordsLogged,
-            int handshakeMessageTypeBeforeWhichToInjectCCS) throws IOException {
-        // Save information about the record if it's of interest to this test
-        if (interestingRecordsLogged) {
-            if (wasCCSInjected()) {
-                setFirstRecordReceivedAfterCCSWasInjected(record, fragmentEncryptionMayBeEnabled);
-            }
-        } else if ((record.protocol == TlsProtocols.CHANGE_CIPHER_SPEC)
-                && (handshakeMessageTypeBeforeWhichToInjectCCS != -1)) {
-            // Do not forward original ChangeCipherSpec record(s) if we're injecting such a record
-            // ourselves. This is to make sure that the peer sees only one ChangeCipherSpec.
-            Log.i(TAG, logPrefix + ": Dropping TLS record. "
-                    + getRecordInfo(record, fragmentEncryptionMayBeEnabled));
-            return;
-        }
-
-        // Inject a ChangeCipherSpec, if necessary, before the specified handshake message type
-        if (handshakeMessageTypeBeforeWhichToInjectCCS != -1) {
-            if ((!fragmentEncryptionMayBeEnabled) && (OpenSSLHeartbleedTest.isHandshakeMessageType(
-                    record, handshakeMessageTypeBeforeWhichToInjectCCS))) {
-                Log.i(TAG, logPrefix + ": Injecting ChangeCipherSpec");
-                setCCSWasInjected();
-                out.write(createChangeCipherSpecRecord(record.versionMajor, record.versionMinor));
-                out.flush();
-            }
-        }
-
-        Log.i(TAG, logPrefix + ": Forwarding TLS record. "
-                + getRecordInfo(record, fragmentEncryptionMayBeEnabled));
-        out.write(recordBytes);
-        out.flush();
-    }
-
-    private static String getRecordInfo(TlsRecord record, boolean mayBeEncrypted) {
-        StringBuilder result = new StringBuilder();
-        result.append(getProtocolName(record.protocol))
-                .append(", ")
-                .append(getFragmentInfo(record, mayBeEncrypted));
-        return result.toString();
-    }
-
-    private static String getProtocolName(int protocol) {
-        switch (protocol) {
-            case TlsProtocols.ALERT:
-                return "alert";
-            case TlsProtocols.APPLICATION_DATA:
-                return "application data";
-            case TlsProtocols.CHANGE_CIPHER_SPEC:
-                return "change cipher spec";
-            case TlsProtocols.HANDSHAKE:
-                return "handshake";
-            default:
-                return String.valueOf(protocol);
-        }
-    }
-
-    private static String getFragmentInfo(TlsRecord record, boolean mayBeEncrypted) {
-        StringBuilder result = new StringBuilder();
-        if (mayBeEncrypted) {
-            result.append("encrypted?");
-        } else {
-            switch (record.protocol) {
-                case TlsProtocols.ALERT:
-                    result.append("level: " + ((record.fragment.length > 0)
-                            ? String.valueOf(record.fragment[0] & 0xff) : "n/a")
-                    + ", description: "
-                    + ((record.fragment.length > 1)
-                            ? String.valueOf(record.fragment[1] & 0xff) : "n/a"));
-                    break;
-                case TlsProtocols.APPLICATION_DATA:
-                    break;
-                case TlsProtocols.CHANGE_CIPHER_SPEC:
-                    result.append("payload: " + ((record.fragment.length > 0)
-                            ? String.valueOf(record.fragment[0] & 0xff) : "n/a"));
-                    break;
-                case TlsProtocols.HANDSHAKE:
-                    result.append("type: " + ((record.fragment.length > 0)
-                            ? String.valueOf(record.fragment[0] & 0xff) : "n/a"));
-                    break;
-            }
-        }
-        result.append(", ").append("fragment length: " + record.fragment.length);
-        return result.toString();
-    }
-
-    private synchronized void setServerListeningSocket(SSLServerSocket socket) {
-        mServerListeningSocket = socket;
-    }
-
-    private synchronized SSLServerSocket getServerListeningSocket() {
-        return mServerListeningSocket;
-    }
-
-    private synchronized void setServerSocket(SSLSocket socket) {
-        mServerSocket = socket;
-    }
-
-    private synchronized SSLSocket getServerSocket() {
-        return mServerSocket;
-    }
-
-    private synchronized void setClientSocket(SSLSocket socket) {
-        mClientSocket = socket;
-    }
-
-    private synchronized SSLSocket getClientSocket() {
-        return mClientSocket;
-    }
-
-    private synchronized void setMitmListeningSocket(ServerSocket socket) {
-        mMitmListeningSocket = socket;
-    }
-
-    private synchronized ServerSocket getMitmListeningSocket() {
-        return mMitmListeningSocket;
-    }
-
-    private synchronized void setMitmServerSocket(Socket socket) {
-        mMitmServerSocket = socket;
-    }
-
-    private synchronized Socket getMitmServerSocket() {
-        return mMitmServerSocket;
-    }
-
-    private synchronized void setMitmClientSocket(Socket socket) {
-        mMitmClientSocket = socket;
-    }
-
-    private synchronized Socket getMitmClientSocket() {
-        return mMitmClientSocket;
-    }
-
-    private synchronized void setCCSWasInjected() {
-        mCCSWasInjected = true;
-    }
-
-    private synchronized boolean wasCCSInjected() {
-        return mCCSWasInjected;
-    }
-
-    private synchronized void setFirstRecordReceivedAfterCCSWasInjected(
-            TlsRecord record, boolean mayBeEncrypted) {
-        if (mFirstRecordReceivedAfterCCSWasInjected == null) {
-            mFirstRecordReceivedAfterCCSWasInjected = record;
-            mFirstRecordReceivedAfterCCSWasInjectedMayBeEncrypted = mayBeEncrypted;
-        }
-    }
-
-    private synchronized TlsRecord getFirstRecordReceivedAfterCCSWasInjected() {
-        return mFirstRecordReceivedAfterCCSWasInjected;
-    }
-
-    private synchronized boolean getFirstRecordReceivedAfterCCSWasInjectedMayBeEncrypted() {
-        return mFirstRecordReceivedAfterCCSWasInjectedMayBeEncrypted;
-    }
-
-    private static byte[] createChangeCipherSpecRecord(int versionMajor, int versionMinor) {
-        TlsRecord record = new TlsRecord();
-        record.protocol = TlsProtocols.CHANGE_CIPHER_SPEC;
-        record.versionMajor = versionMajor;
-        record.versionMinor = versionMinor;
-        record.fragment = new byte[] {1};
-        return TlsRecord.unparse(record);
-    }
-}
diff --git a/tests/tests/security/src/android/security/cts/OpenSSLHeartbleedTest.java b/tests/tests/security/src/android/security/cts/OpenSSLHeartbleedTest.java
deleted file mode 100644
index 95a82a8..0000000
--- a/tests/tests/security/src/android/security/cts/OpenSSLHeartbleedTest.java
+++ /dev/null
@@ -1,983 +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.security.cts;
-
-import android.content.Context;
-import android.platform.test.annotations.SecurityTest;
-import android.test.InstrumentationTestCase;
-import android.util.Log;
-
-import java.io.ByteArrayInputStream;
-import java.io.ByteArrayOutputStream;
-import java.io.EOFException;
-import java.io.IOException;
-import java.io.InputStream;
-import java.io.OutputStream;
-import java.net.ServerSocket;
-import java.net.Socket;
-import java.net.SocketAddress;
-import java.security.KeyFactory;
-import java.security.Principal;
-import java.security.PrivateKey;
-import java.security.cert.CertificateException;
-import java.security.cert.CertificateFactory;
-import java.security.cert.X509Certificate;
-import java.security.spec.PKCS8EncodedKeySpec;
-import java.util.concurrent.Callable;
-import java.util.concurrent.ExecutionException;
-import java.util.concurrent.ExecutorService;
-import java.util.concurrent.Executors;
-import java.util.concurrent.Future;
-import java.util.concurrent.TimeUnit;
-
-import javax.net.ServerSocketFactory;
-import javax.net.SocketFactory;
-import javax.net.ssl.KeyManager;
-import javax.net.ssl.SSLContext;
-import javax.net.ssl.SSLException;
-import javax.net.ssl.SSLServerSocket;
-import javax.net.ssl.SSLSocket;
-import javax.net.ssl.TrustManager;
-import javax.net.ssl.X509KeyManager;
-import javax.net.ssl.X509TrustManager;
-
-/**
- * Tests for the OpenSSL Heartbleed vulnerability.
- */
-@SecurityTest
-public class OpenSSLHeartbleedTest extends InstrumentationTestCase {
-
-    // IMPLEMENTATION NOTE: This test spawns an SSLSocket client, SSLServerSocket server, and a
-    // Man-in-The-Middle (MiTM). The client connects to the MiTM which then connects to the server
-    // and starts forwarding all TLS records between the client and the server. In tests that check
-    // for the Heartbleed vulnerability, the MiTM also injects a HeartbeatRequest message into the
-    // traffic.
-
-    // IMPLEMENTATION NOTE: This test spawns several background threads that perform network I/O
-    // on localhost. To ensure that these background threads are cleaned up at the end of the test
-    // tearDown() kills the sockets they may be using. To aid this behavior, all Socket and
-    // ServerSocket instances are available as fields of this class. These fields should be accessed
-    // via setters and getters to avoid memory visibility issues due to concurrency.
-
-    private static final String TAG = OpenSSLHeartbleedTest.class.getSimpleName();
-
-    private SSLServerSocket mServerListeningSocket;
-    private SSLSocket mServerSocket;
-    private SSLSocket mClientSocket;
-    private ServerSocket mMitmListeningSocket;
-    private Socket mMitmServerSocket;
-    private Socket mMitmClientSocket;
-    private ExecutorService mExecutorService;
-
-    private boolean mHeartbeatRequestWasInjected;
-    private boolean mHeartbeatResponseWasDetetected;
-    private int mFirstDetectedFatalAlertDescription = -1;
-
-    @Override
-    protected void tearDown() throws Exception {
-        Log.i(TAG, "Tearing down");
-        if (mExecutorService != null) {
-            mExecutorService.shutdownNow();
-        }
-        closeQuietly(getServerListeningSocket());
-        closeQuietly(getServerSocket());
-        closeQuietly(getClientSocket());
-        closeQuietly(getMitmListeningSocket());
-        closeQuietly(getMitmServerSocket());
-        closeQuietly(getMitmClientSocket());
-        super.tearDown();
-        Log.i(TAG, "Tear down completed");
-    }
-
-    /**
-     * Tests that TLS handshake succeeds when the MiTM simply forwards all data without tampering
-     * with it. This is to catch issues unrelated to TLS heartbeats.
-     */
-    public void testWithoutHeartbeats() throws Exception {
-        handshake(false, false);
-    }
-
-    /**
-     * Tests whether client sockets are vulnerable to Heartbleed.
-     */
-    public void testClientHeartbleed() throws Exception {
-        checkHeartbleed(true);
-    }
-
-    /**
-     * Tests whether server sockets are vulnerable to Heartbleed.
-     */
-    public void testServerHeartbleed() throws Exception {
-        checkHeartbleed(false);
-    }
-
-    /**
-     * Tests for Heartbleed.
-     *
-     * @param client {@code true} to test the client, {@code false} to test the server.
-     */
-    private void checkHeartbleed(boolean client) throws Exception {
-        // IMPLEMENTATION NOTE: The MiTM is forwarding all TLS records between the client and the
-        // server unmodified. Additionally, the MiTM transmits a malformed HeartbeatRequest to
-        // server (if "client" argument is false) right after client's ClientKeyExchange or to
-        // client (if "client" argument is true) right after server's ServerHello. The peer is
-        // expected to either ignore the HeartbeatRequest (if heartbeats are supported) or to abort
-        // the handshake with unexpected_message alert (if heartbeats are not supported).
-        try {
-            handshake(true, client);
-        } catch (ExecutionException e) {
-            assertFalse(
-                    "SSLSocket is vulnerable to Heartbleed in " + ((client) ? "client" : "server")
-                            + " mode",
-                    wasHeartbeatResponseDetected());
-            if (e.getCause() instanceof SSLException) {
-                // TLS handshake or data exchange failed. Check whether the error was caused by
-                // fatal alert unexpected_message
-                int alertDescription = getFirstDetectedFatalAlertDescription();
-                if (alertDescription == -1) {
-                    fail("Handshake failed without a fatal alert");
-                }
-                assertEquals(
-                        "First fatal alert description received from server",
-                        AlertMessage.DESCRIPTION_UNEXPECTED_MESSAGE,
-                        alertDescription);
-                return;
-            } else {
-                throw e;
-            }
-        }
-
-        // TLS handshake succeeded
-        assertFalse(
-                "SSLSocket is vulnerable to Heartbleed in " + ((client) ? "client" : "server")
-                        + " mode",
-                wasHeartbeatResponseDetected());
-        assertTrue("HeartbeatRequest not injected", wasHeartbeatRequestInjected());
-    }
-
-    /**
-     * Starts the client, server, and the MiTM. Makes the client and server perform a TLS handshake
-     * and exchange application-level data. The MiTM injects a HeartbeatRequest message if requested
-     * by {@code heartbeatRequestInjected}. The direction of the injected message is specified by
-     * {@code injectedIntoClient}.
-     */
-    private void handshake(
-            final boolean heartbeatRequestInjected,
-            final boolean injectedIntoClient) throws Exception {
-        mExecutorService = Executors.newFixedThreadPool(4);
-        setServerListeningSocket(serverBind());
-        final SocketAddress serverAddress = getServerListeningSocket().getLocalSocketAddress();
-        Log.i(TAG, "Server bound to " + serverAddress);
-
-        setMitmListeningSocket(mitmBind());
-        final SocketAddress mitmAddress = getMitmListeningSocket().getLocalSocketAddress();
-        Log.i(TAG, "MiTM bound to " + mitmAddress);
-
-        // Start the MiTM daemon in the background
-        mExecutorService.submit(new Callable<Void>() {
-            @Override
-            public Void call() throws Exception {
-                mitmAcceptAndForward(
-                        serverAddress,
-                        heartbeatRequestInjected,
-                        injectedIntoClient);
-                return null;
-            }
-        });
-        // Start the server in the background
-        Future<Void> serverFuture = mExecutorService.submit(new Callable<Void>() {
-            @Override
-            public Void call() throws Exception {
-                serverAcceptAndHandshake();
-                return null;
-            }
-        });
-        // Start the client in the background
-        Future<Void> clientFuture = mExecutorService.submit(new Callable<Void>() {
-            @Override
-            public Void call() throws Exception {
-                clientConnectAndHandshake(mitmAddress);
-                return null;
-            }
-        });
-
-        // Wait for both client and server to terminate, to ensure that we observe all the traffic
-        // exchanged between them. Throw an exception if one of them failed.
-        Log.i(TAG, "Waiting for client");
-        // Wait for the client, but don't yet throw an exception if it failed.
-        Exception clientException = null;
-        try {
-            clientFuture.get(10, TimeUnit.SECONDS);
-        } catch (Exception e) {
-            clientException = e;
-        }
-        Log.i(TAG, "Waiting for server");
-        // Wait for the server and throw an exception if it failed.
-        serverFuture.get(5, TimeUnit.SECONDS);
-        // Throw an exception if the client failed.
-        if (clientException != null) {
-            throw clientException;
-        }
-        Log.i(TAG, "Handshake completed and application data exchanged");
-    }
-
-    private void clientConnectAndHandshake(SocketAddress serverAddress) throws Exception {
-        SSLContext sslContext = SSLContext.getInstance("TLS");
-        sslContext.init(
-                null,
-                new TrustManager[] {new TrustAllX509TrustManager()},
-                null);
-        SSLSocket socket = (SSLSocket) sslContext.getSocketFactory().createSocket();
-        setClientSocket(socket);
-        try {
-            Log.i(TAG, "Client connecting to " + serverAddress);
-            socket.connect(serverAddress);
-            Log.i(TAG, "Client connected to server from " + socket.getLocalSocketAddress());
-            // Ensure a TLS handshake is performed and an exception is thrown if it fails.
-            socket.getOutputStream().write("client".getBytes());
-            socket.getOutputStream().flush();
-            Log.i(TAG, "Client sent request. Reading response");
-            int b = socket.getInputStream().read();
-            Log.i(TAG, "Client read response: " + b);
-        } catch (Exception e) {
-            Log.w(TAG, "Client failed", e);
-            throw e;
-          } finally {
-            socket.close();
-        }
-    }
-
-    public SSLServerSocket serverBind() throws Exception {
-        // Load the server's private key and cert chain
-        KeyFactory keyFactory = KeyFactory.getInstance("RSA");
-        PrivateKey privateKey = keyFactory.generatePrivate(new PKCS8EncodedKeySpec(
-                readResource(
-                        getInstrumentation().getContext(), R.raw.openssl_heartbleed_test_key)));
-        CertificateFactory certFactory = CertificateFactory.getInstance("X.509");
-        X509Certificate[] certChain =  new X509Certificate[] {
-                (X509Certificate) certFactory.generateCertificate(
-                        new ByteArrayInputStream(readResource(
-                                getInstrumentation().getContext(),
-                                R.raw.openssl_heartbleed_test_cert)))
-        };
-
-        // Initialize TLS context to use the private key and cert chain for server sockets
-        SSLContext sslContext = SSLContext.getInstance("TLS");
-        sslContext.init(
-                new KeyManager[] {new HardcodedCertX509KeyManager(privateKey, certChain)},
-                null,
-                null);
-
-        Log.i(TAG, "Server binding to local port");
-        return (SSLServerSocket) sslContext.getServerSocketFactory().createServerSocket(0);
-    }
-
-    private void serverAcceptAndHandshake() throws Exception {
-        SSLSocket socket = null;
-        SSLServerSocket serverSocket = getServerListeningSocket();
-        try {
-            Log.i(TAG, "Server listening for incoming connection");
-            socket = (SSLSocket) serverSocket.accept();
-            setServerSocket(socket);
-            Log.i(TAG, "Server accepted connection from " + socket.getRemoteSocketAddress());
-            // Ensure a TLS handshake is performed and an exception is thrown if it fails.
-            socket.getOutputStream().write("server".getBytes());
-            socket.getOutputStream().flush();
-            Log.i(TAG, "Server sent reply. Reading response");
-            int b = socket.getInputStream().read();
-            Log.i(TAG, "Server read response: " + b);
-        } catch (Exception e) {
-          Log.w(TAG, "Server failed", e);
-          throw e;
-        } finally {
-            if (socket != null) {
-                socket.close();
-            }
-        }
-    }
-
-    private ServerSocket mitmBind() throws Exception {
-        Log.i(TAG, "MiTM binding to local port");
-        return ServerSocketFactory.getDefault().createServerSocket(0);
-    }
-
-    /**
-     * Accepts the connection on the MiTM listening socket, forwards the TLS records between the
-     * client and the server, and, if requested, injects a {@code HeartbeatRequest}.
-     *
-     * @param injectHeartbeat whether to inject a {@code HeartbeatRequest} message.
-     * @param injectIntoClient when {@code injectHeartbeat} is {@code true}, whether to inject the
-     *        {@code HeartbeatRequest} message into client or into server.
-     */
-    private void mitmAcceptAndForward(
-            SocketAddress serverAddress,
-            final boolean injectHeartbeat,
-            final boolean injectIntoClient) throws Exception {
-        Socket clientSocket = null;
-        Socket serverSocket = null;
-        ServerSocket listeningSocket = getMitmListeningSocket();
-        try {
-            Log.i(TAG, "MiTM waiting for incoming connection");
-            clientSocket = listeningSocket.accept();
-            setMitmClientSocket(clientSocket);
-            Log.i(TAG, "MiTM accepted connection from " + clientSocket.getRemoteSocketAddress());
-            serverSocket = SocketFactory.getDefault().createSocket();
-            setMitmServerSocket(serverSocket);
-            Log.i(TAG, "MiTM connecting to server " + serverAddress);
-            serverSocket.connect(serverAddress, 10000);
-            Log.i(TAG, "MiTM connected to server from " + serverSocket.getLocalSocketAddress());
-            final InputStream serverInputStream = serverSocket.getInputStream();
-            final OutputStream clientOutputStream = clientSocket.getOutputStream();
-            Future<Void> serverToClientTask = mExecutorService.submit(new Callable<Void>() {
-                @Override
-                public Void call() throws Exception {
-                    // Inject HeatbeatRequest after ServerHello, if requested
-                    forwardTlsRecords(
-                            "MiTM S->C",
-                            serverInputStream,
-                            clientOutputStream,
-                            (injectHeartbeat && injectIntoClient)
-                                    ? HandshakeMessage.TYPE_SERVER_HELLO : -1);
-                    return null;
-                }
-            });
-            // Inject HeatbeatRequest after ClientKeyExchange, if requested
-            forwardTlsRecords(
-                    "MiTM C->S",
-                    clientSocket.getInputStream(),
-                    serverSocket.getOutputStream(),
-                    (injectHeartbeat && !injectIntoClient)
-                            ? HandshakeMessage.TYPE_CLIENT_KEY_EXCHANGE : -1);
-            serverToClientTask.get(10, TimeUnit.SECONDS);
-        } catch (Exception e) {
-            Log.w(TAG, "MiTM failed", e);
-            throw e;
-          } finally {
-            closeQuietly(clientSocket);
-            closeQuietly(serverSocket);
-        }
-    }
-
-    /**
-     * Forwards TLS records from the provided {@code InputStream} to the provided
-     * {@code OutputStream}. If requested, injects a {@code HeartbeatMessage}.
-     */
-    private void forwardTlsRecords(
-            String logPrefix,
-            InputStream in,
-            OutputStream out,
-            int handshakeMessageTypeAfterWhichToInjectHeartbeatRequest) throws Exception {
-        Log.i(TAG, logPrefix + ": record forwarding started");
-        boolean interestingRecordsLogged =
-                handshakeMessageTypeAfterWhichToInjectHeartbeatRequest == -1;
-        try {
-            TlsRecordReader reader = new TlsRecordReader(in);
-            byte[] recordBytes;
-            // Fragments contained in records may be encrypted after a certain point in the
-            // handshake. Once they are encrypted, this MiTM cannot inspect their plaintext which.
-            boolean fragmentEncryptionMayBeEnabled = false;
-            while ((recordBytes = reader.readRecord()) != null) {
-                TlsRecord record = TlsRecord.parse(recordBytes);
-                forwardTlsRecord(logPrefix,
-                        recordBytes,
-                        record,
-                        fragmentEncryptionMayBeEnabled,
-                        out,
-                        interestingRecordsLogged,
-                        handshakeMessageTypeAfterWhichToInjectHeartbeatRequest);
-                if (record.protocol == TlsProtocols.CHANGE_CIPHER_SPEC) {
-                    fragmentEncryptionMayBeEnabled = true;
-                }
-            }
-        } catch (Exception e) {
-            Log.w(TAG, logPrefix + ": failed", e);
-            throw e;
-        } finally {
-            Log.d(TAG, logPrefix + ": record forwarding finished");
-        }
-    }
-
-    private void forwardTlsRecord(
-            String logPrefix,
-            byte[] recordBytes,
-            TlsRecord record,
-            boolean fragmentEncryptionMayBeEnabled,
-            OutputStream out,
-            boolean interestingRecordsLogged,
-            int handshakeMessageTypeAfterWhichToInjectHeartbeatRequest) throws IOException {
-        // Save information about the records if its of interest to this test
-        if (interestingRecordsLogged) {
-            switch (record.protocol) {
-                case TlsProtocols.ALERT:
-                    if (!fragmentEncryptionMayBeEnabled) {
-                        AlertMessage alert = AlertMessage.tryParse(record);
-                        if ((alert != null) && (alert.level == AlertMessage.LEVEL_FATAL)) {
-                            setFatalAlertDetected(alert.description);
-                        }
-                    }
-                    break;
-                case TlsProtocols.HEARTBEAT:
-                    // When TLS records are encrypted, we cannot determine whether a
-                    // heartbeat is a HeartbeatResponse. In our setup, the client and the
-                    // server are not expected to sent HeartbeatRequests. Thus, we err on
-                    // the side of caution and assume that any heartbeat message sent by
-                    // client or server is a HeartbeatResponse.
-                    Log.e(TAG, logPrefix
-                            + ": heartbeat response detected -- vulnerable to Heartbleed");
-                    setHeartbeatResponseWasDetected();
-                    break;
-            }
-        }
-
-        Log.i(TAG, logPrefix + ": Forwarding TLS record. "
-                + getRecordInfo(record, fragmentEncryptionMayBeEnabled));
-        out.write(recordBytes);
-        out.flush();
-
-        // Inject HeartbeatRequest, if necessary, after the specified handshake message type
-        if (handshakeMessageTypeAfterWhichToInjectHeartbeatRequest != -1) {
-            if ((!fragmentEncryptionMayBeEnabled) && (isHandshakeMessageType(
-                    record, handshakeMessageTypeAfterWhichToInjectHeartbeatRequest))) {
-                // The Heartbeat Request message below is malformed because its declared
-                // length of payload one byte larger than the actual payload. The peer is
-                // supposed to reject such messages.
-                byte[] payload = "arbitrary".getBytes("US-ASCII");
-                byte[] heartbeatRequestRecordBytes = createHeartbeatRequestRecord(
-                        record.versionMajor,
-                        record.versionMinor,
-                        payload.length + 1,
-                        payload);
-                Log.i(TAG, logPrefix + ": Injecting malformed HeartbeatRequest: "
-                        + getRecordInfo(
-                                TlsRecord.parse(heartbeatRequestRecordBytes), false));
-                setHeartbeatRequestWasInjected();
-                out.write(heartbeatRequestRecordBytes);
-                out.flush();
-            }
-        }
-    }
-
-    private static String getRecordInfo(TlsRecord record, boolean mayBeEncrypted) {
-        StringBuilder result = new StringBuilder();
-        result.append(getProtocolName(record.protocol))
-                .append(", ")
-                .append(getFragmentInfo(record, mayBeEncrypted));
-        return result.toString();
-    }
-
-    private static String getProtocolName(int protocol) {
-        switch (protocol) {
-            case TlsProtocols.ALERT:
-                return "alert";
-            case TlsProtocols.APPLICATION_DATA:
-                return "application data";
-            case TlsProtocols.CHANGE_CIPHER_SPEC:
-                return "change cipher spec";
-            case TlsProtocols.HANDSHAKE:
-                return "handshake";
-            case TlsProtocols.HEARTBEAT:
-                return "heatbeat";
-            default:
-                return String.valueOf(protocol);
-        }
-    }
-
-    private static String getFragmentInfo(TlsRecord record, boolean mayBeEncrypted) {
-        StringBuilder result = new StringBuilder();
-        if (mayBeEncrypted) {
-            result.append("encrypted?");
-        } else {
-            switch (record.protocol) {
-                case TlsProtocols.ALERT:
-                    result.append("level: " + ((record.fragment.length > 0)
-                            ? String.valueOf(record.fragment[0] & 0xff) : "n/a")
-                    + ", description: "
-                    + ((record.fragment.length > 1)
-                            ? String.valueOf(record.fragment[1] & 0xff) : "n/a"));
-                    break;
-                case TlsProtocols.APPLICATION_DATA:
-                    break;
-                case TlsProtocols.CHANGE_CIPHER_SPEC:
-                    result.append("payload: " + ((record.fragment.length > 0)
-                            ? String.valueOf(record.fragment[0] & 0xff) : "n/a"));
-                    break;
-                case TlsProtocols.HANDSHAKE:
-                    result.append("type: " + ((record.fragment.length > 0)
-                            ? String.valueOf(record.fragment[0] & 0xff) : "n/a"));
-                    break;
-                case TlsProtocols.HEARTBEAT:
-                    result.append("type: " + ((record.fragment.length > 0)
-                            ? String.valueOf(record.fragment[0] & 0xff) : "n/a")
-                            + ", payload length: "
-                            + ((record.fragment.length >= 3)
-                                    ? String.valueOf(
-                                            getUnsignedShortBigEndian(record.fragment, 1))
-                                    : "n/a"));
-                    break;
-            }
-        }
-        result.append(", ").append("fragment length: " + record.fragment.length);
-        return result.toString();
-    }
-
-    private synchronized void setServerListeningSocket(SSLServerSocket socket) {
-        mServerListeningSocket = socket;
-    }
-
-    private synchronized SSLServerSocket getServerListeningSocket() {
-        return mServerListeningSocket;
-    }
-
-    private synchronized void setServerSocket(SSLSocket socket) {
-        mServerSocket = socket;
-    }
-
-    private synchronized SSLSocket getServerSocket() {
-        return mServerSocket;
-    }
-
-    private synchronized void setClientSocket(SSLSocket socket) {
-        mClientSocket = socket;
-    }
-
-    private synchronized SSLSocket getClientSocket() {
-        return mClientSocket;
-    }
-
-    private synchronized void setMitmListeningSocket(ServerSocket socket) {
-        mMitmListeningSocket = socket;
-    }
-
-    private synchronized ServerSocket getMitmListeningSocket() {
-        return mMitmListeningSocket;
-    }
-
-    private synchronized void setMitmServerSocket(Socket socket) {
-        mMitmServerSocket = socket;
-    }
-
-    private synchronized Socket getMitmServerSocket() {
-        return mMitmServerSocket;
-    }
-
-    private synchronized void setMitmClientSocket(Socket socket) {
-        mMitmClientSocket = socket;
-    }
-
-    private synchronized Socket getMitmClientSocket() {
-        return mMitmClientSocket;
-    }
-
-    private synchronized void setHeartbeatRequestWasInjected() {
-        mHeartbeatRequestWasInjected = true;
-    }
-
-    private synchronized boolean wasHeartbeatRequestInjected() {
-        return mHeartbeatRequestWasInjected;
-    }
-
-    private synchronized void setHeartbeatResponseWasDetected() {
-        mHeartbeatResponseWasDetetected = true;
-    }
-
-    private synchronized boolean wasHeartbeatResponseDetected() {
-        return mHeartbeatResponseWasDetetected;
-    }
-
-    private synchronized void setFatalAlertDetected(int description) {
-        if (mFirstDetectedFatalAlertDescription == -1) {
-            mFirstDetectedFatalAlertDescription = description;
-        }
-    }
-
-    private synchronized int getFirstDetectedFatalAlertDescription() {
-        return mFirstDetectedFatalAlertDescription;
-    }
-
-    public static abstract class TlsProtocols {
-        public static final int CHANGE_CIPHER_SPEC = 20;
-        public static final int ALERT = 21;
-        public static final int HANDSHAKE = 22;
-        public static final int APPLICATION_DATA = 23;
-        public static final int HEARTBEAT = 24;
-        private TlsProtocols() {}
-    }
-
-    public static class TlsRecord {
-        public int protocol;
-        public int versionMajor;
-        public int versionMinor;
-        public byte[] fragment;
-
-        public static TlsRecord parse(byte[] record) throws IOException {
-            TlsRecord result = new TlsRecord();
-            if (record.length < TlsRecordReader.RECORD_HEADER_LENGTH) {
-                throw new IOException("Record too short: " + record.length);
-            }
-            result.protocol = record[0] & 0xff;
-            result.versionMajor = record[1] & 0xff;
-            result.versionMinor = record[2] & 0xff;
-            int fragmentLength = getUnsignedShortBigEndian(record, 3);
-            int actualFragmentLength = record.length - TlsRecordReader.RECORD_HEADER_LENGTH;
-            if (fragmentLength != actualFragmentLength) {
-                throw new IOException("Fragment length mismatch. Expected: " + fragmentLength
-                        + ", actual: " + actualFragmentLength);
-            }
-            result.fragment = new byte[fragmentLength];
-            System.arraycopy(
-                    record, TlsRecordReader.RECORD_HEADER_LENGTH,
-                    result.fragment, 0,
-                    fragmentLength);
-            return result;
-        }
-
-        public static byte[] unparse(TlsRecord record) {
-            byte[] result = new byte[TlsRecordReader.RECORD_HEADER_LENGTH + record.fragment.length];
-            result[0] = (byte) record.protocol;
-            result[1] = (byte) record.versionMajor;
-            result[2] = (byte) record.versionMinor;
-            putUnsignedShortBigEndian(result, 3, record.fragment.length);
-            System.arraycopy(
-                    record.fragment, 0,
-                    result, TlsRecordReader.RECORD_HEADER_LENGTH,
-                    record.fragment.length);
-            return result;
-        }
-    }
-
-    public static final boolean isHandshakeMessageType(TlsRecord record, int type) {
-        HandshakeMessage handshake = HandshakeMessage.tryParse(record);
-        if (handshake == null) {
-            return false;
-        }
-        return handshake.type == type;
-    }
-
-    public static class HandshakeMessage {
-        public static final int TYPE_SERVER_HELLO = 2;
-        public static final int TYPE_CERTIFICATE = 11;
-        public static final int TYPE_CLIENT_KEY_EXCHANGE = 16;
-
-        public int type;
-
-        /**
-         * Parses the provided TLS record as a handshake message.
-         *
-         * @return alert message or {@code null} if the record does not contain a handshake message.
-         */
-        public static HandshakeMessage tryParse(TlsRecord record) {
-            if (record.protocol != TlsProtocols.HANDSHAKE) {
-                return null;
-            }
-            if (record.fragment.length < 1) {
-                return null;
-            }
-            HandshakeMessage result = new HandshakeMessage();
-            result.type = record.fragment[0] & 0xff;
-            return result;
-        }
-    }
-
-    public static class AlertMessage {
-        public static final int LEVEL_FATAL = 2;
-        public static final int DESCRIPTION_UNEXPECTED_MESSAGE = 10;
-
-        public int level;
-        public int description;
-
-        /**
-         * Parses the provided TLS record as an alert message.
-         *
-         * @return alert message or {@code null} if the record does not contain an alert message.
-         */
-        public static AlertMessage tryParse(TlsRecord record) {
-            if (record.protocol != TlsProtocols.ALERT) {
-                return null;
-            }
-            if (record.fragment.length < 2) {
-                return null;
-            }
-            AlertMessage result = new AlertMessage();
-            result.level = record.fragment[0] & 0xff;
-            result.description = record.fragment[1] & 0xff;
-            return result;
-        }
-    }
-
-    private static abstract class HeartbeatProtocol {
-        private HeartbeatProtocol() {}
-
-        private static final int MESSAGE_TYPE_REQUEST = 1;
-        @SuppressWarnings("unused")
-        private static final int MESSAGE_TYPE_RESPONSE = 2;
-
-        private static final int MESSAGE_HEADER_LENGTH = 3;
-        private static final int MESSAGE_PADDING_LENGTH = 16;
-    }
-
-    private static byte[] createHeartbeatRequestRecord(
-            int versionMajor, int versionMinor,
-            int declaredPayloadLength, byte[] payload) {
-
-        byte[] fragment = new byte[HeartbeatProtocol.MESSAGE_HEADER_LENGTH
-                + payload.length + HeartbeatProtocol.MESSAGE_PADDING_LENGTH];
-        fragment[0] = HeartbeatProtocol.MESSAGE_TYPE_REQUEST;
-        putUnsignedShortBigEndian(fragment, 1, declaredPayloadLength); // payload_length
-        TlsRecord record = new TlsRecord();
-        record.protocol = TlsProtocols.HEARTBEAT;
-        record.versionMajor = versionMajor;
-        record.versionMinor = versionMinor;
-        record.fragment = fragment;
-        return TlsRecord.unparse(record);
-    }
-
-    /**
-     * Reader of TLS records.
-     */
-    public static class TlsRecordReader {
-        private static final int MAX_RECORD_LENGTH = 16384;
-        public static final int RECORD_HEADER_LENGTH = 5;
-
-        private final InputStream in;
-        private final byte[] buffer;
-        private int firstBufferedByteOffset;
-        private int bufferedByteCount;
-
-        public TlsRecordReader(InputStream in) {
-            this.in = in;
-            buffer = new byte[MAX_RECORD_LENGTH];
-        }
-
-        /**
-         * Reads the next TLS record.
-         *
-         * @return TLS record or {@code null} if EOF was encountered before any bytes of a record
-         *         could be read.
-         */
-        public byte[] readRecord() throws IOException {
-            // Ensure that a TLS record header (or more) is in the buffer.
-            if (bufferedByteCount < RECORD_HEADER_LENGTH) {
-                boolean eofPermittedInstead = (bufferedByteCount == 0);
-                boolean eofEncounteredInstead =
-                        !readAtLeast(RECORD_HEADER_LENGTH, eofPermittedInstead);
-                if (eofEncounteredInstead) {
-                    // End of stream reached exactly before a TLS record start.
-                    return null;
-                }
-            }
-
-            // TLS record header (or more) is in the buffer.
-            // Ensure that the rest of the record is in the buffer.
-            int fragmentLength = getUnsignedShortBigEndian(buffer, firstBufferedByteOffset + 3);
-            int recordLength = RECORD_HEADER_LENGTH + fragmentLength;
-            if (recordLength > MAX_RECORD_LENGTH) {
-                throw new IOException("TLS record too long: " + recordLength);
-            }
-            if (bufferedByteCount < recordLength) {
-                readAtLeast(recordLength - bufferedByteCount, false);
-            }
-
-            // TLS record (or more) is in the buffer.
-            byte[] record = new byte[recordLength];
-            System.arraycopy(buffer, firstBufferedByteOffset, record, 0, recordLength);
-            firstBufferedByteOffset += recordLength;
-            bufferedByteCount -= recordLength;
-            return record;
-        }
-
-        /**
-         * Reads at least the specified number of bytes from the underlying {@code InputStream} into
-         * the {@code buffer}.
-         *
-         * <p>Bytes buffered but not yet returned to the client in the {@code buffer} are relocated
-         * to the start of the buffer to make space if necessary.
-         *
-         * @param eofPermittedInstead {@code true} if it's permitted for an EOF to be encountered
-         *        without any bytes having been read.
-         *
-         * @return {@code true} if the requested number of bytes (or more) has been read,
-         *         {@code false} if {@code eofPermittedInstead} was {@code true} and EOF was
-         *         encountered when no bytes have yet been read.
-         */
-        private boolean readAtLeast(int size, boolean eofPermittedInstead) throws IOException {
-            ensureRemainingBufferCapacityAtLeast(size);
-            boolean firstAttempt = true;
-            while (size > 0) {
-                int chunkSize = in.read(
-                        buffer,
-                        firstBufferedByteOffset + bufferedByteCount,
-                        buffer.length - (firstBufferedByteOffset + bufferedByteCount));
-                if (chunkSize == -1) {
-                    if ((firstAttempt) && (eofPermittedInstead)) {
-                        return false;
-                    } else {
-                        throw new EOFException("Premature EOF");
-                    }
-                }
-                firstAttempt = false;
-                bufferedByteCount += chunkSize;
-                size -= chunkSize;
-            }
-            return true;
-        }
-
-        /**
-         * Ensures that there is enough capacity in the buffer to store the specified number of
-         * bytes at the {@code firstBufferedByteOffset + bufferedByteCount} offset.
-         */
-        private void ensureRemainingBufferCapacityAtLeast(int size) throws IOException {
-            int bufferCapacityRemaining =
-                    buffer.length - (firstBufferedByteOffset + bufferedByteCount);
-            if (bufferCapacityRemaining >= size) {
-                return;
-            }
-            // Insufficient capacity at the end of the buffer.
-            if (firstBufferedByteOffset > 0) {
-                // Some of the bytes at the start of the buffer have already been returned to the
-                // client of this reader. Check if moving the remaining buffered bytes to the start
-                // of the buffer will make enough space at the end of the buffer.
-                bufferCapacityRemaining += firstBufferedByteOffset;
-                if (bufferCapacityRemaining >= size) {
-                    System.arraycopy(buffer, firstBufferedByteOffset, buffer, 0, bufferedByteCount);
-                    firstBufferedByteOffset = 0;
-                    return;
-                }
-            }
-
-            throw new IOException("Insuffucient remaining capacity in the buffer. Requested: "
-                    + size + ", remaining: " + bufferCapacityRemaining);
-        }
-    }
-
-    private static int getUnsignedShortBigEndian(byte[] buf, int offset) {
-        return ((buf[offset] & 0xff) << 8) | (buf[offset + 1] & 0xff);
-    }
-
-    private static void putUnsignedShortBigEndian(byte[] buf, int offset, int value) {
-        buf[offset] = (byte) ((value >>> 8) & 0xff);
-        buf[offset + 1] = (byte) (value & 0xff);
-    }
-
-    // IMPLEMENTATION NOTE: We can't implement just one closeQueietly(Closeable) because on some
-    // older Android platforms Socket did not implement these interfaces. To make this patch easy to
-    // apply to these older platforms, we declare all the variants of closeQuietly that are needed
-    // without relying on the Closeable interface.
-
-    private static void closeQuietly(InputStream in) {
-        if (in != null) {
-            try {
-                in.close();
-            } catch (IOException ignored) {}
-        }
-    }
-
-    public static void closeQuietly(ServerSocket socket) {
-        if (socket != null) {
-            try {
-                socket.close();
-            } catch (IOException ignored) {}
-        }
-    }
-
-    public static void closeQuietly(Socket socket) {
-        if (socket != null) {
-            try {
-                socket.close();
-            } catch (IOException ignored) {}
-        }
-    }
-
-    public static byte[] readResource(Context context, int resId) throws IOException {
-        ByteArrayOutputStream result = new ByteArrayOutputStream();
-        InputStream in = null;
-        byte[] buf = new byte[16 * 1024];
-        try {
-            in = context.getResources().openRawResource(resId);
-            int chunkSize;
-            while ((chunkSize = in.read(buf)) != -1) {
-                result.write(buf, 0, chunkSize);
-            }
-            return result.toByteArray();
-        } finally {
-            closeQuietly(in);
-        }
-    }
-
-    /**
-     * {@link X509TrustManager} which trusts all certificate chains.
-     */
-    public static class TrustAllX509TrustManager implements X509TrustManager {
-        @Override
-        public void checkClientTrusted(X509Certificate[] chain, String authType)
-                throws CertificateException {
-        }
-
-        @Override
-        public void checkServerTrusted(X509Certificate[] chain, String authType)
-                throws CertificateException {
-        }
-
-        @Override
-        public X509Certificate[] getAcceptedIssuers() {
-            return new X509Certificate[0];
-        }
-    }
-
-    /**
-     * {@link X509KeyManager} which uses the provided private key and cert chain for all sockets.
-     */
-    public static class HardcodedCertX509KeyManager implements X509KeyManager {
-
-        private final PrivateKey mPrivateKey;
-        private final X509Certificate[] mCertChain;
-
-        HardcodedCertX509KeyManager(PrivateKey privateKey, X509Certificate[] certChain) {
-            mPrivateKey = privateKey;
-            mCertChain = certChain;
-        }
-
-        @Override
-        public String chooseClientAlias(String[] keyType, Principal[] issuers, Socket socket) {
-            return null;
-        }
-
-        @Override
-        public String chooseServerAlias(String keyType, Principal[] issuers, Socket socket) {
-            return "singleton";
-        }
-
-        @Override
-        public X509Certificate[] getCertificateChain(String alias) {
-            return mCertChain;
-        }
-
-        @Override
-        public String[] getClientAliases(String keyType, Principal[] issuers) {
-            return null;
-        }
-
-        @Override
-        public PrivateKey getPrivateKey(String alias) {
-            return mPrivateKey;
-        }
-
-        @Override
-        public String[] getServerAliases(String keyType, Principal[] issuers) {
-            return new String[] {"singleton"};
-        }
-    }
-}
diff --git a/tests/tests/security/src/android/security/cts/OutputConfigurationTest.java b/tests/tests/security/src/android/security/cts/OutputConfigurationTest.java
new file mode 100644
index 0000000..d1b263f
--- /dev/null
+++ b/tests/tests/security/src/android/security/cts/OutputConfigurationTest.java
@@ -0,0 +1,102 @@
+/*
+ * 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.security.cts;
+
+import android.graphics.SurfaceTexture;
+import android.hardware.camera2.params.OutputConfiguration;
+import android.os.Parcel;
+import android.platform.test.annotations.SecurityTest;
+import android.test.AndroidTestCase;
+import android.util.Size;
+import android.view.Surface;
+import android.view.TextureView;
+
+/**
+ * Verify that OutputConfiguration's fields propagate through parcel properly.
+ */
+@SecurityTest
+public class OutputConfigurationTest extends AndroidTestCase {
+    public void testSharedSurfaceOutputConfigurationBasic() throws Exception {
+        SurfaceTexture outputTexture = new SurfaceTexture(/* random texture ID */ 5);
+        Surface surface = new Surface(outputTexture);
+
+        //Test OutputConfiguration with a surface.
+        OutputConfiguration outputConfig = new OutputConfiguration(surface);
+        outputConfig.enableSurfaceSharing();
+        Parcel p;
+        p = Parcel.obtain();
+        outputConfig.writeToParcel(p, 0);
+        p.setDataPosition(0);
+        OutputConfiguration parcelledOutput = OutputConfiguration.CREATOR.createFromParcel(p);
+        assertEquals("Number of surfaces should be 1",
+                parcelledOutput.getSurfaces().size(),
+                1);
+        assertEquals("Number of surfaces shouldn't change",
+                parcelledOutput.getSurfaces().size(),
+                outputConfig.getSurfaces().size());
+        assertEquals("surfaceGroupId shouldn't change",
+                parcelledOutput.getSurfaceGroupId(),
+                outputConfig.getSurfaceGroupId());
+        // addSurface shouldn't throw exception because surface sharing is enabled.
+        SurfaceTexture outputTexture2 = new SurfaceTexture(/* random texture ID */ 6);
+        Surface surface2 = new Surface(outputTexture2);
+        parcelledOutput.addSurface(surface2);
+        p.recycle();
+
+        //Test OutputConfiguration with surface group id.
+        outputConfig = new OutputConfiguration(
+                /* random surface groupd id */1, surface);
+        p = Parcel.obtain();
+        outputConfig.writeToParcel(p, 0);
+        p.setDataPosition(0);
+        parcelledOutput = OutputConfiguration.CREATOR.createFromParcel(p);
+        assertEquals("Number of surfaces should be 1",
+                parcelledOutput.getSurfaces().size(),
+                1);
+        assertEquals("Number of surfaces shouldn't change",
+                parcelledOutput.getSurfaces().size(),
+                outputConfig.getSurfaces().size());
+        assertEquals("surfaceGroupdId shouldn't change",
+                parcelledOutput.getSurfaceGroupId(),
+                outputConfig.getSurfaceGroupId());
+        try {
+            parcelledOutput.addSurface(surface);
+            fail("should get IllegalStateException due to OutputConfiguration not shared");
+        } catch (IllegalStateException e) {
+            // expected exception
+        }
+        p.recycle();
+
+        //Test OutputConfiguration with deferred surface.
+        Size surfaceSize = new Size(10, 10);
+        outputConfig = new OutputConfiguration(
+                surfaceSize, SurfaceTexture.class);
+        p = Parcel.obtain();
+        outputConfig.writeToParcel(p, 0);
+        p.setDataPosition(0);
+        parcelledOutput = OutputConfiguration.CREATOR.createFromParcel(p);
+        assertEquals("Number of surfaces should be 0",
+                parcelledOutput.getSurfaces().size(), 0);
+        assertEquals("Number of surfaces shouldn't change",
+                parcelledOutput.getSurfaces().size(),
+                outputConfig.getSurfaces().size());
+        assertEquals("surfaceGroupdId shouldn't change",
+                parcelledOutput.getSurfaceGroupId(),
+                outputConfig.getSurfaceGroupId());
+        p.recycle();
+    }
+}
diff --git a/tests/tests/security/src/android/security/cts/SeccompTest.java b/tests/tests/security/src/android/security/cts/SeccompTest.java
deleted file mode 100644
index 745aa87..0000000
--- a/tests/tests/security/src/android/security/cts/SeccompTest.java
+++ /dev/null
@@ -1,103 +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.security.cts;
-
-import android.test.AndroidTestCase;
-
-import com.android.compatibility.common.util.CpuFeatures;
-
-import junit.framework.TestCase;
-
-/**
- * Verify that the seccomp policy is enforced
- */
-public class SeccompTest extends AndroidTestCase {
-
-    static {
-        System.loadLibrary("ctssecurity_jni");
-    }
-
-    public void testCTSSyscallBlocked() {
-        if (CpuFeatures.isArm64Cpu()) {
-            testBlocked(217); // __NR_add_key
-            testBlocked(219); // __NR_keyctl
-            testAllowed(56); // __NR_openat
-
-            // b/35034743 - do not remove test without reading bug
-            testAllowed(267); // __NR_fstatfs64
-        } else if (CpuFeatures.isArmCpu()) {
-            testBlocked(309); // __NR_add_key
-            testBlocked(311); // __NR_keyctl
-            testAllowed(322); // __NR_openat
-
-            // b/35906875 - do not remove test without reading bug
-            testAllowed(316); // __NR_inotify_init
-        } else if (CpuFeatures.isX86_64Cpu()) {
-            testBlocked(248); // __NR_add_key
-            testBlocked(250); // __NR_keyctl
-            testAllowed(257); // __NR_openat
-        } else if (CpuFeatures.isX86Cpu()) {
-            testBlocked(286); // __NR_add_key
-            testBlocked(288); // __NR_keyctl
-            testAllowed(295); // __NR_openat
-        } else if (CpuFeatures.isMips64Cpu()) {
-            testBlocked(5239); // __NR_add_key
-            testBlocked(5241); // __NR_keyctl
-            testAllowed(5247); // __NR_openat
-        } else if (CpuFeatures.isMipsCpu()) {
-            testBlocked(4280); // __NR_add_key
-            testBlocked(4282); // __NR_keyctl
-            testAllowed(4288); // __NR_openat
-        } else {
-            fail("Unsupported OS");
-        }
-    }
-
-    public void testCTSSwapOnOffBlocked() {
-        if (CpuFeatures.isArm64Cpu()) {
-            testBlocked(224); // __NR_swapon
-            testBlocked(225); // __NR_swapoff
-        } else if (CpuFeatures.isArmCpu()) {
-            testBlocked(87);  // __NR_swapon
-            testBlocked(115); // __NR_swapoff
-        } else if (CpuFeatures.isX86_64Cpu()) {
-            testBlocked(167); // __NR_swapon
-            testBlocked(168); // __NR_swapoff
-        } else if (CpuFeatures.isX86Cpu()) {
-            testBlocked(87);  // __NR_swapon
-            testBlocked(115); // __NR_swapoff
-        } else if (CpuFeatures.isMips64Cpu()) {
-            testBlocked(5162); // __NR_swapon
-            testBlocked(5163); // __NR_swapoff
-        } else if (CpuFeatures.isMipsCpu()) {
-            testBlocked(4087); // __NR_swapon
-            testBlocked(4115); // __NR_swapoff
-        } else {
-            fail("Unsupported OS");
-        }
-    }
-
-    private void testBlocked(int nr) {
-        assertTrue("Syscall " + nr + " allowed", testSyscallBlocked(nr));
-    }
-
-    private void testAllowed(int nr) {
-        assertFalse("Syscall " + nr + " blocked", testSyscallBlocked(nr));
-    }
-
-    private static final native boolean testSyscallBlocked(int nr);
-}
diff --git a/tests/tests/security/src/android/security/cts/StagefrightTest.java b/tests/tests/security/src/android/security/cts/StagefrightTest.java
index 87f1c0b..fb493dc 100644
--- a/tests/tests/security/src/android/security/cts/StagefrightTest.java
+++ b/tests/tests/security/src/android/security/cts/StagefrightTest.java
@@ -45,8 +45,11 @@
 import android.view.Surface;
 import android.webkit.cts.CtsTestServer;
 
+import java.io.BufferedInputStream;
 import java.io.FileInputStream;
 import java.io.IOException;
+import java.io.InputStream;
+import java.net.URL;
 import java.nio.ByteBuffer;
 import java.util.ArrayList;
 import java.util.HashMap;
@@ -436,12 +439,38 @@
         CtsTestServer server = new CtsTestServer(context);
         String rname = resources.getResourceEntryName(rid);
         String url = server.getAssetUrl("raw/" + rname);
+        verifyServer(rid, url);
         doStagefrightTestMediaPlayer(url);
         doStagefrightTestMediaCodec(url);
         doStagefrightTestMediaMetadataRetriever(url);
         server.shutdown();
     }
 
+    // verify that CtsTestServer is functional by retrieving the asset
+    // and comparing it to the resource
+    private void verifyServer(final int rid, final String uri) throws Exception {
+        Log.i(TAG, "checking server");
+        URL url = new URL(uri);
+        InputStream in1 = new BufferedInputStream(url.openStream());
+
+        AssetFileDescriptor fd = getInstrumentation().getContext().getResources()
+                        .openRawResourceFd(rid);
+        InputStream in2 = new BufferedInputStream(fd.createInputStream());
+
+        while (true) {
+            int b1 = in1.read();
+            int b2 = in2.read();
+            assertEquals("CtsTestServer fail", b1, b2);
+            if (b1 < 0) {
+                break;
+            }
+        }
+
+        in1.close();
+        in2.close();
+        Log.i(TAG, "checked server");
+    }
+
     private void doStagefrightTestANR(final int rid) throws Exception {
         doStagefrightTestMediaPlayerANR(rid, null);
     }
diff --git a/tests/tests/selinux/selinuxTargetSdk/Android.mk b/tests/tests/selinux/selinuxTargetSdk/Android.mk
index a0923ee..13f1ac9 100755
--- a/tests/tests/selinux/selinuxTargetSdk/Android.mk
+++ b/tests/tests/selinux/selinuxTargetSdk/Android.mk
@@ -21,7 +21,7 @@
 LOCAL_STATIC_JAVA_LIBRARIES := \
     compatibility-device-util \
     ctstestrunner
-LOCAL_JAVA_LIBRARIES := legacy-android-test
+LOCAL_JAVA_LIBRARIES := android.test.base.stubs
 LOCAL_SRC_FILES := $(call all-java-files-under, src)
 LOCAL_PACKAGE_NAME := CtsSelinuxTargetSdkTestCases
 LOCAL_SDK_VERSION := current
diff --git a/tests/tests/selinux/selinuxTargetSdk2/Android.mk b/tests/tests/selinux/selinuxTargetSdk2/Android.mk
index f475f9f..45d570e 100755
--- a/tests/tests/selinux/selinuxTargetSdk2/Android.mk
+++ b/tests/tests/selinux/selinuxTargetSdk2/Android.mk
@@ -20,8 +20,8 @@
 LOCAL_COMPATIBILITY_SUITE := cts vts general-tests
 LOCAL_STATIC_JAVA_LIBRARIES := \
     compatibility-device-util \
-    ctstestrunner \
-    legacy-android-test
+    ctstestrunner
+LOCAL_JAVA_LIBRARIES := android.test.base.stubs
 LOCAL_SRC_FILES := $(call all-java-files-under, src)
 LOCAL_PACKAGE_NAME := CtsSelinuxTargetSdk2TestCases
 LOCAL_SDK_VERSION := current
diff --git a/tests/tests/shortcutmanager/Android.mk b/tests/tests/shortcutmanager/Android.mk
index 29333aa..d9328b2 100755
--- a/tests/tests/shortcutmanager/Android.mk
+++ b/tests/tests/shortcutmanager/Android.mk
@@ -29,7 +29,7 @@
     ub-uiautomator \
     ShortcutManagerTestUtils
 
-LOCAL_JAVA_LIBRARIES := android.test.runner
+LOCAL_JAVA_LIBRARIES := android.test.runner android.test.base.stubs
 
 LOCAL_SRC_FILES := $(call all-java-files-under, src) \
     $(call all-java-files-under, common/src)
diff --git a/tests/tests/shortcutmanager/packages/launchermanifest/AndroidManifest.xml b/tests/tests/shortcutmanager/packages/launchermanifest/AndroidManifest.xml
index 1872a7d..ec688fe 100755
--- a/tests/tests/shortcutmanager/packages/launchermanifest/AndroidManifest.xml
+++ b/tests/tests/shortcutmanager/packages/launchermanifest/AndroidManifest.xml
@@ -20,6 +20,8 @@
     android:sharedUserId="android.content.pm.cts.shortcutmanager.packages">
 
     <application>
+        <uses-library android:name="android.test.runner" />
+
         <activity android:name="Launcher">
             <intent-filter>
                 <action android:name="android.intent.action.MAIN" />
diff --git a/tests/tests/shortcutmanager/packages/launchermanifest_nonshared/AndroidManifest.xml b/tests/tests/shortcutmanager/packages/launchermanifest_nonshared/AndroidManifest.xml
index c43d574..90d0df5 100755
--- a/tests/tests/shortcutmanager/packages/launchermanifest_nonshared/AndroidManifest.xml
+++ b/tests/tests/shortcutmanager/packages/launchermanifest_nonshared/AndroidManifest.xml
@@ -19,6 +19,8 @@
     package="android.content.pm.cts.shortcutmanager.packages">
 
     <application>
+        <uses-library android:name="android.test.runner" />
+
         <activity android:name="Launcher">
             <intent-filter>
                 <action android:name="android.intent.action.MAIN" />
diff --git a/tests/tests/shortcutmanager/packages/packagemanifest/AndroidManifest.xml b/tests/tests/shortcutmanager/packages/packagemanifest/AndroidManifest.xml
index 18a118e..a5a0060 100755
--- a/tests/tests/shortcutmanager/packages/packagemanifest/AndroidManifest.xml
+++ b/tests/tests/shortcutmanager/packages/packagemanifest/AndroidManifest.xml
@@ -20,6 +20,8 @@
     android:sharedUserId="android.content.pm.cts.shortcutmanager.packages">
 
     <application>
+        <uses-library android:name="android.test.runner" />
+
         <activity android:name="Launcher"
             android:enabled="true">
             <intent-filter>
diff --git a/tests/tests/shortcutmanager/packages/packagemanifest_nonshared/AndroidManifest.xml b/tests/tests/shortcutmanager/packages/packagemanifest_nonshared/AndroidManifest.xml
index ee92b8c..d2cc04a 100755
--- a/tests/tests/shortcutmanager/packages/packagemanifest_nonshared/AndroidManifest.xml
+++ b/tests/tests/shortcutmanager/packages/packagemanifest_nonshared/AndroidManifest.xml
@@ -19,6 +19,8 @@
     package="android.content.pm.cts.shortcutmanager.packages">
 
     <application>
+        <uses-library android:name="android.test.runner" />
+
         <activity android:name="Launcher" android:enabled="true" android:exported="false">
         </activity>
     </application>
diff --git a/tests/tests/shortcutmanager/packages/src/android/content/pm/cts/shortcutmanager/packages/ShortcutConfirmPin.java b/tests/tests/shortcutmanager/packages/src/android/content/pm/cts/shortcutmanager/packages/ShortcutConfirmPin.java
index 9a2659b..727828e 100644
--- a/tests/tests/shortcutmanager/packages/src/android/content/pm/cts/shortcutmanager/packages/ShortcutConfirmPin.java
+++ b/tests/tests/shortcutmanager/packages/src/android/content/pm/cts/shortcutmanager/packages/ShortcutConfirmPin.java
@@ -108,6 +108,7 @@
                 return;
             }
             ReplyUtil.sendSuccessReply(this, replyAction);
+            Log.e(TAG, "Sent reply");
         } catch (Exception e) {
             Log.e(TAG, "Caught exception", e);
             if (replyAction != null) {
diff --git a/tests/tests/shortcutmanager/src/android/content/pm/cts/shortcutmanager/ShortcutManagerClientApiTest.java b/tests/tests/shortcutmanager/src/android/content/pm/cts/shortcutmanager/ShortcutManagerClientApiTest.java
index bab2674..56969bb 100644
--- a/tests/tests/shortcutmanager/src/android/content/pm/cts/shortcutmanager/ShortcutManagerClientApiTest.java
+++ b/tests/tests/shortcutmanager/src/android/content/pm/cts/shortcutmanager/ShortcutManagerClientApiTest.java
@@ -234,14 +234,14 @@
     }
 
     public void testSetDynamicShortcuts() {
-        runWithCaller(mPackageContext1, () -> {
+        runWithCallerWithStrictMode(mPackageContext1, () -> {
             assertTrue(getManager().setDynamicShortcuts(list(
                     makeShortcut("s1", "title1"),
                     makeShortcut("s2", "title2"),
                     makeShortcut("s3", "title3"))));
         });
 
-        runWithCaller(mPackageContext1, () -> {
+        runWithCallerWithStrictMode(mPackageContext1, () -> {
             assertWith(getManager().getDynamicShortcuts())
                     .areAllEnabled()
                     .areAllDynamic()
@@ -262,12 +262,12 @@
         });
 
         // Publish from different package.
-        runWithCaller(mPackageContext2, () -> {
+        runWithCallerWithStrictMode(mPackageContext2, () -> {
             assertTrue(getManager().setDynamicShortcuts(list(
                     makeShortcut("s1x", "title1x"))));
         });
 
-        runWithCaller(mPackageContext2, () -> {
+        runWithCallerWithStrictMode(mPackageContext2, () -> {
             assertWith(getManager().getDynamicShortcuts())
                     .areAllEnabled()
                     .areAllDynamic()
@@ -282,7 +282,7 @@
         });
 
         // Package 1 still has the same shortcuts.
-        runWithCaller(mPackageContext1, () -> {
+        runWithCallerWithStrictMode(mPackageContext1, () -> {
             assertWith(getManager().getDynamicShortcuts())
                     .areAllEnabled()
                     .areAllDynamic()
@@ -302,12 +302,12 @@
                     .isEmpty();
         });
 
-        runWithCaller(mPackageContext1, () -> {
+        runWithCallerWithStrictMode(mPackageContext1, () -> {
             assertTrue(getManager().setDynamicShortcuts(list(
                     makeShortcut("s2", "title2-updated"))));
         });
 
-        runWithCaller(mPackageContext1, () -> {
+        runWithCallerWithStrictMode(mPackageContext1, () -> {
             assertWith(getManager().getDynamicShortcuts())
                     .areAllEnabled()
                     .areAllDynamic()
@@ -321,11 +321,11 @@
                     .isEmpty();
         });
 
-        runWithCaller(mPackageContext1, () -> {
+        runWithCallerWithStrictMode(mPackageContext1, () -> {
             assertTrue(getManager().setDynamicShortcuts(list()));
         });
 
-        runWithCaller(mPackageContext1, () -> {
+        runWithCallerWithStrictMode(mPackageContext1, () -> {
             assertWith(getManager().getDynamicShortcuts())
                     .isEmpty();
             assertWith(getManager().getPinnedShortcuts())
@@ -335,7 +335,7 @@
         });
 
         // Package2 still has the same shortcuts.
-        runWithCaller(mPackageContext2, () -> {
+        runWithCallerWithStrictMode(mPackageContext2, () -> {
             assertWith(getManager().getDynamicShortcuts())
                     .areAllEnabled()
                     .areAllDynamic()
@@ -361,7 +361,7 @@
                 getTestContext().getResources(), R.drawable.black_16x64));
         final Icon icon6 = Icon.createWithAdaptiveBitmap(BitmapFactory.decodeResource(
                 getTestContext().getResources(), R.drawable.black_32x32));
-        runWithCaller(mPackageContext1, () -> {
+        runWithCallerWithStrictMode(mPackageContext1, () -> {
             final ShortcutInfo source = makeShortcutBuilder("s1")
                     .setShortLabel("shortlabel")
                     .setLongLabel("longlabel")
@@ -392,7 +392,7 @@
                     icon1);
         });
 
-        runWithCaller(mPackageContext1, () -> {
+        runWithCallerWithStrictMode(mPackageContext1, () -> {
             // No fields updated.
             final ShortcutInfo updated = makeShortcutBuilder("s1")
                     .setShortLabel("xxx")
@@ -418,7 +418,7 @@
                     getIconAsLauncher(mLauncherContext1, mPackageContext1.getPackageName(), "s1"));
         });
 
-        runWithCaller(mPackageContext1, () -> {
+        runWithCallerWithStrictMode(mPackageContext1, () -> {
             final ShortcutInfo source = makeShortcutBuilder("s1")
                     .setShortLabel("shortlabel")
                     .setLongLabel("longlabel")
@@ -450,7 +450,7 @@
         });
 
         // paranoid icon check
-        runWithCaller(mPackageContext1, () -> {
+        runWithCallerWithStrictMode(mPackageContext1, () -> {
             // No fields updated.
             final ShortcutInfo updated = makeShortcutBuilder("s1")
                     .setShortLabel("xxx")
@@ -469,7 +469,7 @@
                     icon2);
         });
 
-        runWithCaller(mPackageContext1, () -> {
+        runWithCallerWithStrictMode(mPackageContext1, () -> {
             // No fields updated.
             final ShortcutInfo updated = makeShortcutBuilder("s1")
                     .setShortLabel("xxx")
@@ -489,7 +489,7 @@
         });
 
 
-        runWithCaller(mPackageContext1, () -> {
+        runWithCallerWithStrictMode(mPackageContext1, () -> {
             // No fields updated.
             final ShortcutInfo updated = makeShortcutBuilder("s1")
                     .setShortLabel("xxx")
@@ -507,7 +507,7 @@
             assertIconDimensions(mLauncherContext1, mPackageContext1.getPackageName(), "s1",
                     icon4);
         });
-        runWithCaller(mPackageContext1, () -> {
+        runWithCallerWithStrictMode(mPackageContext1, () -> {
             // No fields updated.
             final ShortcutInfo updated = makeShortcutBuilder("s1")
                     .setShortLabel("xxx")
@@ -525,7 +525,7 @@
             assertIconDimensions(mLauncherContext1, mPackageContext1.getPackageName(), "s1",
                     icon5);
         });
-        runWithCaller(mPackageContext1, () -> {
+        runWithCallerWithStrictMode(mPackageContext1, () -> {
             // No fields updated.
             final ShortcutInfo updated = makeShortcutBuilder("s1")
                     .setShortLabel("xxx")
@@ -547,18 +547,18 @@
 
     public void testSetDynamicShortcuts_wasPinned() throws Exception {
         // Create s1 as a floating pinned shortcut.
-        runWithCaller(mPackageContext1, () -> {
+        runWithCallerWithStrictMode(mPackageContext1, () -> {
             assertTrue(getManager().setDynamicShortcuts(list(
                     makeShortcut("s1"))));
         });
 
         setDefaultLauncher(getInstrumentation(), mLauncherContext1);
 
-        runWithCaller(mLauncherContext1, () -> {
+        runWithCallerWithStrictMode(mLauncherContext1, () -> {
             getLauncherApps().pinShortcuts(mPackageContext1.getPackageName(),
                     list("s1"), getUserHandle());
         });
-        runWithCaller(mPackageContext1, () -> {
+        runWithCallerWithStrictMode(mPackageContext1, () -> {
             getManager().removeDynamicShortcuts(list("s1"));
 
             assertWith(getManager().getDynamicShortcuts())
@@ -572,14 +572,14 @@
     }
 
     public void testAddDynamicShortcuts() {
-        runWithCaller(mPackageContext1, () -> {
+        runWithCallerWithStrictMode(mPackageContext1, () -> {
             assertTrue(getManager().addDynamicShortcuts(list(
                     makeShortcut("s1", "title1"),
                     makeShortcut("s2", "title2"),
                     makeShortcut("s3", "title3"))));
         });
 
-        runWithCaller(mPackageContext1, () -> {
+        runWithCallerWithStrictMode(mPackageContext1, () -> {
             assertWith(getManager().getDynamicShortcuts())
                     .areAllEnabled()
                     .areAllDynamic()
@@ -600,12 +600,12 @@
         });
 
         // Publish from different package.
-        runWithCaller(mPackageContext2, () -> {
+        runWithCallerWithStrictMode(mPackageContext2, () -> {
             assertTrue(getManager().addDynamicShortcuts(list(
                     makeShortcut("s1x", "title1x"))));
         });
 
-        runWithCaller(mPackageContext2, () -> {
+        runWithCallerWithStrictMode(mPackageContext2, () -> {
             assertWith(getManager().getDynamicShortcuts())
                     .areAllEnabled()
                     .areAllDynamic()
@@ -620,7 +620,7 @@
         });
 
         // Package 1 still has the same shortcuts.
-        runWithCaller(mPackageContext1, () -> {
+        runWithCallerWithStrictMode(mPackageContext1, () -> {
             assertWith(getManager().getDynamicShortcuts())
                     .areAllEnabled()
                     .areAllDynamic()
@@ -640,12 +640,12 @@
                     .isEmpty();
         });
 
-        runWithCaller(mPackageContext1, () -> {
+        runWithCallerWithStrictMode(mPackageContext1, () -> {
             assertTrue(getManager().addDynamicShortcuts(list(
                     makeShortcut("s2", "title2-updated"))));
         });
 
-        runWithCaller(mPackageContext1, () -> {
+        runWithCallerWithStrictMode(mPackageContext1, () -> {
             assertWith(getManager().getDynamicShortcuts())
                     .areAllEnabled()
                     .areAllDynamic()
@@ -665,11 +665,11 @@
                     .isEmpty();
         });
 
-        runWithCaller(mPackageContext1, () -> {
+        runWithCallerWithStrictMode(mPackageContext1, () -> {
             assertTrue(getManager().addDynamicShortcuts(list()));
         });
 
-        runWithCaller(mPackageContext1, () -> {
+        runWithCallerWithStrictMode(mPackageContext1, () -> {
             assertWith(getManager().getDynamicShortcuts())
                     .areAllEnabled()
                     .areAllDynamic()
@@ -690,7 +690,7 @@
         });
 
         // Package2 still has the same shortcuts.
-        runWithCaller(mPackageContext2, () -> {
+        runWithCallerWithStrictMode(mPackageContext2, () -> {
             assertWith(getManager().getDynamicShortcuts())
                     .areAllEnabled()
                     .areAllDynamic()
@@ -713,7 +713,7 @@
         final Icon icon3 = loadPackageDrawableIcon(mPackageContext1, "black_64x16");
         final Icon icon4 = loadPackageDrawableIcon(mPackageContext1, "black_64x64");
 
-        runWithCaller(mPackageContext1, () -> {
+        runWithCallerWithStrictMode(mPackageContext1, () -> {
             final ShortcutInfo source = makeShortcutBuilder("s1")
                     .setShortLabel("shortlabel")
                     .setLongLabel("longlabel")
@@ -744,7 +744,7 @@
                     icon1);
         });
 
-        runWithCaller(mPackageContext1, () -> {
+        runWithCallerWithStrictMode(mPackageContext1, () -> {
             // No fields updated.
             final ShortcutInfo updated = makeShortcutBuilder("s1")
                     .setShortLabel("xxx")
@@ -770,7 +770,7 @@
                     getIconAsLauncher(mLauncherContext1, mPackageContext1.getPackageName(), "s1"));
         });
 
-        runWithCaller(mPackageContext1, () -> {
+        runWithCallerWithStrictMode(mPackageContext1, () -> {
             final ShortcutInfo source = makeShortcutBuilder("s1")
                     .setShortLabel("shortlabel")
                     .setLongLabel("longlabel")
@@ -802,7 +802,7 @@
         });
 
         // paranoid icon check
-        runWithCaller(mPackageContext1, () -> {
+        runWithCallerWithStrictMode(mPackageContext1, () -> {
             // No fields updated.
             final ShortcutInfo updated = makeShortcutBuilder("s1")
                     .setShortLabel("xxx")
@@ -821,7 +821,7 @@
                     icon2);
         });
 
-        runWithCaller(mPackageContext1, () -> {
+        runWithCallerWithStrictMode(mPackageContext1, () -> {
             // No fields updated.
             final ShortcutInfo updated = makeShortcutBuilder("s1")
                     .setShortLabel("xxx")
@@ -841,7 +841,7 @@
         });
 
 
-        runWithCaller(mPackageContext1, () -> {
+        runWithCallerWithStrictMode(mPackageContext1, () -> {
             // No fields updated.
             final ShortcutInfo updated = makeShortcutBuilder("s1")
                     .setShortLabel("xxx")
@@ -863,18 +863,18 @@
 
     public void testAddDynamicShortcuts_wasPinned() throws Exception {
         // Create s1 as a floating pinned shortcut.
-        runWithCaller(mPackageContext1, () -> {
+        runWithCallerWithStrictMode(mPackageContext1, () -> {
             assertTrue(getManager().setDynamicShortcuts(list(
                     makeShortcut("s1"))));
         });
 
         setDefaultLauncher(getInstrumentation(), mLauncherContext1);
 
-        runWithCaller(mLauncherContext1, () -> {
+        runWithCallerWithStrictMode(mLauncherContext1, () -> {
             getLauncherApps().pinShortcuts(mPackageContext1.getPackageName(),
                     list("s1"), getUserHandle());
         });
-        runWithCaller(mPackageContext1, () -> {
+        runWithCallerWithStrictMode(mPackageContext1, () -> {
             getManager().removeDynamicShortcuts(list("s1"));
 
             assertWith(getManager().getDynamicShortcuts())
@@ -888,13 +888,13 @@
     }
 
     public void testUpdateShortcut() {
-        runWithCaller(mPackageContext1, () -> {
+        runWithCallerWithStrictMode(mPackageContext1, () -> {
             assertTrue(getManager().setDynamicShortcuts(list(
                     makeShortcut("s1", "1a"),
                     makeShortcut("s2", "2a"),
                     makeShortcut("s3", "3a"))));
         });
-        runWithCaller(mPackageContext2, () -> {
+        runWithCallerWithStrictMode(mPackageContext2, () -> {
             assertTrue(getManager().setDynamicShortcuts(list(
                     makeShortcut("s1", "1b"),
                     makeShortcut("s2", "2b"),
@@ -903,21 +903,21 @@
 
         setDefaultLauncher(getInstrumentation(), mLauncherContext1);
 
-        runWithCaller(mLauncherContext1, () -> {
+        runWithCallerWithStrictMode(mLauncherContext1, () -> {
             getLauncherApps().pinShortcuts(mPackageContext1.getPackageName(),
                     list("s2", "s3"), getUserHandle());
             getLauncherApps().pinShortcuts(mPackageContext2.getPackageName(),
                     list("s1", "s2"), getUserHandle());
         });
-        runWithCaller(mPackageContext1, () -> {
+        runWithCallerWithStrictMode(mPackageContext1, () -> {
             getManager().removeDynamicShortcuts(list("s3"));
         });
-        runWithCaller(mPackageContext2, () -> {
+        runWithCallerWithStrictMode(mPackageContext2, () -> {
             getManager().removeDynamicShortcuts(list("s1"));
         });
 
         // Check the current status.
-        runWithCaller(mPackageContext1, () -> {
+        runWithCallerWithStrictMode(mPackageContext1, () -> {
             assertWith(getManager().getDynamicShortcuts())
                     .areAllEnabled()
                     .areAllDynamic()
@@ -943,7 +943,7 @@
             assertWith(getManager().getManifestShortcuts())
                     .isEmpty();
         });
-        runWithCaller(mPackageContext2, () -> {
+        runWithCallerWithStrictMode(mPackageContext2, () -> {
             assertWith(getManager().getDynamicShortcuts())
                     .areAllEnabled()
                     .areAllDynamic()
@@ -971,21 +971,21 @@
         });
 
         // finally, call update.
-        runWithCaller(mPackageContext1, () -> {
+        runWithCallerWithStrictMode(mPackageContext1, () -> {
             assertTrue(getManager().updateShortcuts(list(
                     makeShortcut("s1", "upd1a"),
                     makeShortcut("s2", "upd2a"),
                     makeShortcut("xxx") // doen't exist -> ignored.
                     )));
         });
-        runWithCaller(mPackageContext2, () -> {
+        runWithCallerWithStrictMode(mPackageContext2, () -> {
             assertTrue(getManager().updateShortcuts(list(
                     makeShortcut("s1", "upd1b"),
                     makeShortcut("s2", "upd2b"))));
         });
 
         // check.
-        runWithCaller(mPackageContext1, () -> {
+        runWithCallerWithStrictMode(mPackageContext1, () -> {
             assertWith(getManager().getDynamicShortcuts())
                     .areAllEnabled()
                     .areAllDynamic()
@@ -1011,7 +1011,7 @@
             assertWith(getManager().getManifestShortcuts())
                     .isEmpty();
         });
-        runWithCaller(mPackageContext2, () -> {
+        runWithCallerWithStrictMode(mPackageContext2, () -> {
             assertWith(getManager().getDynamicShortcuts())
                     .areAllEnabled()
                     .areAllDynamic()
@@ -1047,7 +1047,7 @@
         final Icon icon3 = loadPackageDrawableIcon(mPackageContext1, "black_64x16");
         final Icon icon4 = loadPackageDrawableIcon(mPackageContext1, "black_64x64");
 
-        runWithCaller(mPackageContext1, () -> {
+        runWithCallerWithStrictMode(mPackageContext1, () -> {
             final ShortcutInfo source = makeShortcutBuilder("s1")
                     .setShortLabel("shortlabel")
                     .setLongLabel("longlabel")
@@ -1078,7 +1078,7 @@
                     icon1);
         });
 
-        runWithCaller(mPackageContext1, () -> {
+        runWithCallerWithStrictMode(mPackageContext1, () -> {
             // No fields updated.
             final ShortcutInfo updated = makeShortcutBuilder("s1")
                     .build();
@@ -1102,7 +1102,7 @@
                     icon1);
         });
 
-        runWithCaller(mPackageContext1, () -> {
+        runWithCallerWithStrictMode(mPackageContext1, () -> {
             final ShortcutInfo updated = makeShortcutBuilder("s1")
                     .setShortLabel("x")
                     .build();
@@ -1126,7 +1126,7 @@
                     icon1);
         });
 
-        runWithCaller(mPackageContext1, () -> {
+        runWithCallerWithStrictMode(mPackageContext1, () -> {
             final ShortcutInfo updated = makeShortcutBuilder("s1")
                     .setLongLabel("y")
                     .build();
@@ -1150,7 +1150,7 @@
                     icon1);
         });
 
-        runWithCaller(mPackageContext1, () -> {
+        runWithCallerWithStrictMode(mPackageContext1, () -> {
             final ShortcutInfo updated = makeShortcutBuilder("s1")
                     .setActivity(getActivity("Launcher2"))
                     .build();
@@ -1174,7 +1174,7 @@
                     icon1);
         });
 
-        runWithCaller(mPackageContext1, () -> {
+        runWithCallerWithStrictMode(mPackageContext1, () -> {
             final ShortcutInfo updated = makeShortcutBuilder("s1")
                     .setDisabledMessage("z")
                     .build();
@@ -1198,7 +1198,7 @@
                     icon1);
         });
 
-        runWithCaller(mPackageContext1, () -> {
+        runWithCallerWithStrictMode(mPackageContext1, () -> {
             final ShortcutInfo updated = makeShortcutBuilder("s1")
                     .setIntents(new Intent[]{new Intent("main")})
                     .build();
@@ -1222,7 +1222,7 @@
                     icon1);
         });
 
-        runWithCaller(mPackageContext1, () -> {
+        runWithCallerWithStrictMode(mPackageContext1, () -> {
             final ShortcutInfo updated = makeShortcutBuilder("s1")
                     .setExtras(makePersistableBundle("ek1", "X"))
                     .build();
@@ -1246,7 +1246,7 @@
                     icon1);
         });
 
-        runWithCaller(mPackageContext1, () -> {
+        runWithCallerWithStrictMode(mPackageContext1, () -> {
             final ShortcutInfo updated = makeShortcutBuilder("s1")
                     .setCategories(set("dog"))
                     .build();
@@ -1270,7 +1270,7 @@
                     icon1);
         });
 
-        runWithCaller(mPackageContext1, () -> {
+        runWithCallerWithStrictMode(mPackageContext1, () -> {
             final ShortcutInfo updated = makeShortcutBuilder("s1")
                     .setIcon(icon2)
                     .build();
@@ -1295,7 +1295,7 @@
         });
 
         // More paranoid tests with icons.
-        runWithCaller(mPackageContext1, () -> {
+        runWithCallerWithStrictMode(mPackageContext1, () -> {
             final ShortcutInfo updated = makeShortcutBuilder("s1")
                     .setIcon(icon1)
                     .build();
@@ -1307,7 +1307,7 @@
         });
 
         // More paranoid tests with icons.
-        runWithCaller(mPackageContext1, () -> {
+        runWithCallerWithStrictMode(mPackageContext1, () -> {
             final ShortcutInfo updated = makeShortcutBuilder("s1")
                     .setIcon(icon3)
                     .build();
@@ -1318,7 +1318,7 @@
                     icon3);
         });
 
-        runWithCaller(mPackageContext1, () -> {
+        runWithCallerWithStrictMode(mPackageContext1, () -> {
             final ShortcutInfo updated = makeShortcutBuilder("s1")
                     .setIcon(icon4)
                     .build();
@@ -1329,7 +1329,7 @@
                     icon4);
         });
 
-        runWithCaller(mPackageContext1, () -> {
+        runWithCallerWithStrictMode(mPackageContext1, () -> {
             final ShortcutInfo updated = makeShortcutBuilder("s1")
                     .setIcon(icon1)
                     .build();
@@ -1352,13 +1352,13 @@
     }
 
     public void testDisableAndEnableShortcut() {
-        runWithCaller(mPackageContext1, () -> {
+        runWithCallerWithStrictMode(mPackageContext1, () -> {
             assertTrue(getManager().setDynamicShortcuts(list(
                     makeShortcut("s1", "1a"),
                     makeShortcut("s2", "2a"),
                     makeShortcut("s3", "3a"))));
         });
-        runWithCaller(mPackageContext2, () -> {
+        runWithCallerWithStrictMode(mPackageContext2, () -> {
             assertTrue(getManager().setDynamicShortcuts(list(
                     makeShortcut("s1", "1b"),
                     makeShortcut("s2", "2b"),
@@ -1367,21 +1367,21 @@
 
         setDefaultLauncher(getInstrumentation(), mLauncherContext1);
 
-        runWithCaller(mLauncherContext1, () -> {
+        runWithCallerWithStrictMode(mLauncherContext1, () -> {
             getLauncherApps().pinShortcuts(mPackageContext1.getPackageName(),
                     list("s2", "s3"), getUserHandle());
             getLauncherApps().pinShortcuts(mPackageContext2.getPackageName(),
                     list("s1", "s2"), getUserHandle());
         });
-        runWithCaller(mPackageContext1, () -> {
+        runWithCallerWithStrictMode(mPackageContext1, () -> {
             getManager().removeDynamicShortcuts(list("s3"));
         });
-        runWithCaller(mPackageContext2, () -> {
+        runWithCallerWithStrictMode(mPackageContext2, () -> {
             getManager().removeDynamicShortcuts(list("s1"));
         });
 
         // Check the current status.
-        runWithCaller(mPackageContext1, () -> {
+        runWithCallerWithStrictMode(mPackageContext1, () -> {
             assertWith(getManager().getDynamicShortcuts())
                     .areAllEnabled()
                     .areAllDynamic()
@@ -1407,7 +1407,7 @@
             assertWith(getManager().getManifestShortcuts())
                     .isEmpty();
         });
-        runWithCaller(mPackageContext2, () -> {
+        runWithCallerWithStrictMode(mPackageContext2, () -> {
             assertWith(getManager().getDynamicShortcuts())
                     .areAllEnabled()
                     .areAllDynamic()
@@ -1435,15 +1435,15 @@
         });
 
         // finally, call disable.
-        runWithCaller(mPackageContext1, () -> {
+        runWithCallerWithStrictMode(mPackageContext1, () -> {
             getManager().disableShortcuts(list("s1", "s3"));
         });
-        runWithCaller(mPackageContext2, () -> {
+        runWithCallerWithStrictMode(mPackageContext2, () -> {
             getManager().disableShortcuts(list("s1", "s2"), "custom message");
         });
 
         // check
-        runWithCaller(mPackageContext1, () -> {
+        runWithCallerWithStrictMode(mPackageContext1, () -> {
             assertWith(getManager().getDynamicShortcuts())
                     .areAllEnabled()
                     .areAllDynamic()
@@ -1469,7 +1469,7 @@
                     .isEmpty();
         });
 
-        runWithCaller(mPackageContext2, () -> {
+        runWithCallerWithStrictMode(mPackageContext2, () -> {
             assertWith(getManager().getDynamicShortcuts())
                     .areAllEnabled()
                     .areAllDynamic()
@@ -1496,10 +1496,10 @@
         });
 
         // try re-enable
-        runWithCaller(mPackageContext1, () -> {
+        runWithCallerWithStrictMode(mPackageContext1, () -> {
             getManager().enableShortcuts(list("s3"));
         });
-        runWithCaller(mPackageContext1, () -> {
+        runWithCallerWithStrictMode(mPackageContext1, () -> {
             assertWith(getManager().getDynamicShortcuts())
                     .areAllEnabled()
                     .areAllDynamic()
@@ -1524,11 +1524,11 @@
         });
 
         // Re-publish will implicitly re-enable.
-        runWithCaller(mPackageContext2, () -> {
+        runWithCallerWithStrictMode(mPackageContext2, () -> {
             getManager().addDynamicShortcuts(list(makeShortcut("s2", "re-published")));
         });
 
-        runWithCaller(mPackageContext2, () -> {
+        runWithCallerWithStrictMode(mPackageContext2, () -> {
             assertWith(getManager().getDynamicShortcuts())
                     .areAllEnabled()
                     .areAllDynamic()
@@ -1550,7 +1550,7 @@
     }
 
     public void testImmutableShortcuts() {
-        runWithCaller(mPackageContext1, () -> {
+        runWithCallerWithStrictMode(mPackageContext1, () -> {
             enableManifestActivity("Launcher_manifest_2", true);
 
             retryUntil(() -> getManager().getManifestShortcuts().size() == 2,
@@ -1558,11 +1558,11 @@
         });
         setDefaultLauncher(getInstrumentation(), mLauncherContext1);
 
-        runWithCaller(mLauncherContext1, () -> {
+        runWithCallerWithStrictMode(mLauncherContext1, () -> {
             getLauncherApps().pinShortcuts(mPackageContext1.getPackageName(),
                     list("ms21"), getUserHandle());
         });
-        runWithCaller(mPackageContext1, () -> {
+        runWithCallerWithStrictMode(mPackageContext1, () -> {
             enableManifestActivity("Launcher_manifest_1", true);
             enableManifestActivity("Launcher_manifest_2", false);
 
@@ -1570,7 +1570,7 @@
                     "Manifest shortcuts didn't show up");
         });
         setDefaultLauncher(getInstrumentation(), mLauncherContext1);
-        runWithCaller(mPackageContext1, () -> {
+        runWithCallerWithStrictMode(mPackageContext1, () -> {
             assertWith(getManager().getDynamicShortcuts())
                     .isEmpty();
             assertWith(getManager().getPinnedShortcuts())
@@ -1610,7 +1610,7 @@
     public void testManifestDefinition() throws Exception {
         final Icon iconMs21 = loadPackageDrawableIcon(mPackageContext1, "black_16x16");
 
-        runWithCaller(mPackageContext1, () -> {
+        runWithCallerWithStrictMode(mPackageContext1, () -> {
             enableManifestActivity("Launcher_manifest_2", true);
 
             retryUntil(() -> getManager().getManifestShortcuts().size() > 0,
@@ -1687,7 +1687,7 @@
     }
 
     public void testDynamicIntents() {
-        runWithCaller(mPackageContext1, () -> {
+        runWithCallerWithStrictMode(mPackageContext1, () -> {
 
             final ShortcutInfo s1 = makeShortcutBuilder("s1")
                     .setShortLabel("shortlabel")
@@ -1754,7 +1754,7 @@
     }
 
     public void testManifestWithErrors() {
-        runWithCaller(mPackageContext1, () -> {
+        runWithCallerWithStrictMode(mPackageContext1, () -> {
             enableManifestActivity("Launcher_manifest_error_1", true);
             enableManifestActivity("Launcher_manifest_error_2", true);
             enableManifestActivity("Launcher_manifest_error_3", true);
@@ -1772,7 +1772,7 @@
     }
 
     public void testManifestDisabled() {
-        runWithCaller(mPackageContext1, () -> {
+        runWithCallerWithStrictMode(mPackageContext1, () -> {
             enableManifestActivity("Launcher_manifest_4a", true);
 
             retryUntil(() -> getManager().getManifestShortcuts().size() > 0,
@@ -1786,11 +1786,11 @@
         });
         setDefaultLauncher(getInstrumentation(), mLauncherContext1);
 
-        runWithCaller(mLauncherContext1, () -> {
+        runWithCallerWithStrictMode(mLauncherContext1, () -> {
             getLauncherApps().pinShortcuts(mPackageContext1.getPackageName(),
                     list("ms41", "ms42"), getUserHandle());
         });
-        runWithCaller(mPackageContext1, () -> {
+        runWithCallerWithStrictMode(mPackageContext1, () -> {
             enableManifestActivity("Launcher_manifest_4b", true);
             enableManifestActivity("Launcher_manifest_4a", false);
 
diff --git a/tests/tests/shortcutmanager/src/android/content/pm/cts/shortcutmanager/ShortcutManagerConfigActivityTest.java b/tests/tests/shortcutmanager/src/android/content/pm/cts/shortcutmanager/ShortcutManagerConfigActivityTest.java
index 0614d84..cc8df51 100644
--- a/tests/tests/shortcutmanager/src/android/content/pm/cts/shortcutmanager/ShortcutManagerConfigActivityTest.java
+++ b/tests/tests/shortcutmanager/src/android/content/pm/cts/shortcutmanager/ShortcutManagerConfigActivityTest.java
@@ -42,7 +42,7 @@
     public void testGetShortcutConfigActivityList() throws Exception {
         setDefaultLauncher(getInstrumentation(), mLauncherContext1);
 
-        runWithCaller(mLauncherContext1, () -> {
+        runWithCallerWithStrictMode(mLauncherContext1, () -> {
             assertNotNull(getConfigActivity());
             assertNotNull(getLauncherApps().getShortcutConfigActivityIntent(getConfigActivity()));
         });
@@ -50,7 +50,7 @@
         // Get config activity works even for non-default activity.
         setDefaultLauncher(getInstrumentation(), mLauncherContext4);
 
-        runWithCaller(mLauncherContext1, () -> {
+        runWithCallerWithStrictMode(mLauncherContext1, () -> {
             assertNotNull(getConfigActivity());
             // throws exception when default launcher is different.
             assertExpectException(SecurityException.class, null, () ->
@@ -61,7 +61,7 @@
     public void testCorrectIntentSenderCreated() throws Throwable {
         setDefaultLauncher(getInstrumentation(), mLauncherContext1);
         final AtomicReference<IntentSender> sender = new AtomicReference<>();
-        runWithCaller(mLauncherContext1, () ->
+        runWithCallerWithStrictMode(mLauncherContext1, () ->
             sender.set(getLauncherApps().getShortcutConfigActivityIntent(getConfigActivity())));
 
         Instrumentation.ActivityMonitor monitor =
@@ -87,7 +87,7 @@
     public void testCreateShortcutResultIntent_defaultLauncher() throws Exception {
         setDefaultLauncher(getInstrumentation(), mLauncherContext1);
         PinItemRequest request = getShortcutRequestForPackage1();
-        runWithCaller(mLauncherContext1, () -> {
+        runWithCallerWithStrictMode(mLauncherContext1, () -> {
             assertTrue(request.isValid());
             assertTrue(request.accept());
         });
@@ -99,7 +99,7 @@
 
         setDefaultLauncher(getInstrumentation(), mLauncherContext4);
         // Launcher1 can still access the request
-        runWithCaller(mLauncherContext1, () -> {
+        runWithCallerWithStrictMode(mLauncherContext1, () -> {
             assertTrue(request.isValid());
             assertTrue(request.accept());
         });
@@ -110,7 +110,7 @@
         PinItemRequest request = getShortcutRequestForPackage1();
 
         // Launcher1 can still access the request
-        runWithCaller(mLauncherContext1, () -> {
+        runWithCallerWithStrictMode(mLauncherContext1, () -> {
             assertFalse(request.isValid());
             assertExpectException(SecurityException.class, null, request::accept);
         });
@@ -118,7 +118,7 @@
 
     private PinItemRequest getShortcutRequestForPackage1() {
         final AtomicReference<PinItemRequest> result = new AtomicReference<>();
-        runWithCaller(mPackageContext1, () -> {
+        runWithCallerWithStrictMode(mPackageContext1, () -> {
             final ShortcutInfo shortcut = makeShortcutBuilder(SHORTCUT_ID)
                     .setShortLabel("label1")
                     .setIntent(new Intent(Intent.ACTION_MAIN))
diff --git a/tests/tests/shortcutmanager/src/android/content/pm/cts/shortcutmanager/ShortcutManagerCtsTestsBase.java b/tests/tests/shortcutmanager/src/android/content/pm/cts/shortcutmanager/ShortcutManagerCtsTestsBase.java
index 1f45a7f..aa0ea527 100644
--- a/tests/tests/shortcutmanager/src/android/content/pm/cts/shortcutmanager/ShortcutManagerCtsTestsBase.java
+++ b/tests/tests/shortcutmanager/src/android/content/pm/cts/shortcutmanager/ShortcutManagerCtsTestsBase.java
@@ -33,12 +33,13 @@
 import android.graphics.drawable.Icon;
 import android.os.Bundle;
 import android.os.PersistableBundle;
+import android.os.StrictMode;
+import android.os.StrictMode.ThreadPolicy;
 import android.os.UserHandle;
 import android.support.annotation.NonNull;
 import android.test.InstrumentationTestCase;
 import android.text.TextUtils;
 
-import java.security.SecureRandom;
 import java.util.ArrayList;
 import java.util.HashMap;
 import java.util.List;
@@ -50,6 +51,14 @@
 
     private static final boolean DUMPSYS_IN_TEARDOWN = false; // DO NOT SUBMIT WITH true
 
+    /**
+     * Whether to enable strict mode or not.
+     *
+     * TODO Enable it after fixing b/68051728. Somehow violations would happen on the dashboard
+     * only and can't reproduce it locally.
+     */
+    private static final boolean ENABLE_STRICT_MODE = false;
+
     private static class SpoofingContext extends ContextWrapper {
         private final String mPackageName;
 
@@ -276,6 +285,33 @@
         return mCurrentLauncherApps;
     }
 
+    protected void runWithStrictMode(Runnable r) {
+        final ThreadPolicy oldPolicy = StrictMode.getThreadPolicy();
+        try {
+            if (ENABLE_STRICT_MODE) {
+                StrictMode.setThreadPolicy(new StrictMode.ThreadPolicy.Builder()
+                        .detectAll()
+                        .penaltyDeath()
+                        .build());
+            }
+            r.run();
+        } finally {
+            StrictMode.setThreadPolicy(oldPolicy);
+        }
+    }
+
+    protected void runWithNoStrictMode(Runnable r) {
+        final ThreadPolicy oldPolicy = StrictMode.getThreadPolicy();
+        try {
+            StrictMode.setThreadPolicy(new StrictMode.ThreadPolicy.Builder()
+                    .permitAll()
+                    .build());
+            r.run();
+        } finally {
+            StrictMode.setThreadPolicy(oldPolicy);
+        }
+    }
+
     protected void runWithCaller(Context callerContext, Runnable r) {
         final Context prev = mCurrentCallerPackage;
 
@@ -286,6 +322,14 @@
         setCurrentCaller(prev);
     }
 
+    protected void runWithCallerWithStrictMode(Context callerContext, Runnable r) {
+        runWithCaller(callerContext, () -> runWithStrictMode(r));
+    }
+
+    protected void runWithCallerWithNoStrictMode(Context callerContext, Runnable r) {
+        runWithCaller(callerContext, () -> runWithNoStrictMode(r));
+    }
+
     public static Bundle makeBundle(Object... keysAndValues) {
         assertTrue((keysAndValues.length % 2) == 0);
 
@@ -437,11 +481,11 @@
 
     protected Drawable getIconAsLauncher(Context launcherContext, String packageName,
             String shortcutId, boolean withBadge) {
-        setDefaultLauncher(getInstrumentation(), launcherContext);
+        runWithNoStrictMode(() -> setDefaultLauncher(getInstrumentation(), launcherContext));
 
         final AtomicReference<Drawable> ret = new AtomicReference<>();
 
-        runWithCaller(launcherContext, () -> {
+        runWithCallerWithNoStrictMode(launcherContext, () -> {
             final ShortcutQuery q = new ShortcutQuery()
                     .setQueryFlags(ShortcutQuery.FLAG_MATCH_DYNAMIC
                                     | ShortcutQuery.FLAG_MATCH_MANIFEST
diff --git a/tests/tests/shortcutmanager/src/android/content/pm/cts/shortcutmanager/ShortcutManagerLauncherApiTest.java b/tests/tests/shortcutmanager/src/android/content/pm/cts/shortcutmanager/ShortcutManagerLauncherApiTest.java
index e527bf9..f27a60a 100644
--- a/tests/tests/shortcutmanager/src/android/content/pm/cts/shortcutmanager/ShortcutManagerLauncherApiTest.java
+++ b/tests/tests/shortcutmanager/src/android/content/pm/cts/shortcutmanager/ShortcutManagerLauncherApiTest.java
@@ -40,7 +40,7 @@
     }
 
     public void testPinShortcuts() {
-        runWithCaller(mPackageContext1, () -> {
+        runWithCallerWithStrictMode(mPackageContext1, () -> {
             enableManifestActivity("Launcher_manifest_1", true);
             enableManifestActivity("Launcher_manifest_2", true);
 
@@ -59,7 +59,7 @@
                     .areAllDynamic()
                     .areAllEnabled();
         });
-        runWithCaller(mPackageContext2, () -> {
+        runWithCallerWithStrictMode(mPackageContext2, () -> {
             enableManifestActivity("Launcher_manifest_1", true);
             enableManifestActivity("Launcher_manifest_3", true);
 
@@ -81,7 +81,7 @@
 
         setDefaultLauncher(getInstrumentation(), mLauncherContext1);
 
-        runWithCaller(mLauncherContext1, () -> {
+        runWithCallerWithStrictMode(mLauncherContext1, () -> {
             getLauncherApps().pinShortcuts(
                     mPackageContext1.getPackageName(),
                     list("s1", "s2", "s3", "ms1", "ms21"), getUserHandle());
@@ -90,11 +90,11 @@
                     list("s2", "s3", "ms1", "ms31"), getUserHandle());
         });
 
-        runWithCaller(mPackageContext1, () -> {
+        runWithCallerWithStrictMode(mPackageContext1, () -> {
             getManager().removeDynamicShortcuts(list("s1", "s2"));
         });
 
-        runWithCaller(mPackageContext2, () -> {
+        runWithCallerWithStrictMode(mPackageContext2, () -> {
             enableManifestActivity("Launcher_manifest_3", false);
 
             retryUntil(() -> getManager().getManifestShortcuts().size() == 1,
@@ -104,7 +104,7 @@
         });
 
         // Check the result.
-        runWithCaller(mPackageContext1, () -> {
+        runWithCallerWithStrictMode(mPackageContext1, () -> {
             assertWith(getManager().getDynamicShortcuts())
                     .haveIds("s3", "s4", "s5")
                     .areAllEnabled();
@@ -116,7 +116,7 @@
                     .areAllEnabled();
         });
 
-        runWithCaller(mPackageContext2, () -> {
+        runWithCallerWithStrictMode(mPackageContext2, () -> {
             assertWith(getManager().getDynamicShortcuts())
                     .haveIds("s3", "s4", "s5")
                     .areAllEnabled();
@@ -142,7 +142,7 @@
         Thread.sleep(2);
         final long time1 = System.currentTimeMillis();
 
-        runWithCaller(mPackageContext1, () -> {
+        runWithCallerWithStrictMode(mPackageContext1, () -> {
             assertTrue(getManager().updateShortcuts(list(
                     makeShortcut("s3"))));
 
@@ -156,12 +156,12 @@
         Thread.sleep(2);
         final long time2 = System.currentTimeMillis();
 
-        runWithCaller(mPackageContext1, () -> {
+        runWithCallerWithStrictMode(mPackageContext1, () -> {
             assertTrue(getManager().updateShortcuts(list(
                     makeShortcutWithRank("s4", 999))));
         });
 
-        runWithCaller(mPackageContext2, () -> {
+        runWithCallerWithStrictMode(mPackageContext2, () -> {
             setTargetActivityOverride("Launcher_manifest_1");
 
             assertTrue(getManager().updateShortcuts(list(
@@ -171,7 +171,7 @@
         Thread.sleep(2);
         final long time3 = System.currentTimeMillis();
 
-        runWithCaller(mLauncherContext1, () -> {
+        runWithCallerWithStrictMode(mLauncherContext1, () -> {
             assertWith(getShortcutsAsLauncher(
                     FLAG_MATCH_DYNAMIC,
                     mPackageContext1.getPackageName(),
@@ -311,7 +311,7 @@
 
         final Icon icon5 = loadPackageDrawableIcon(mPackageContext1, "black_16x16");
 
-        runWithCaller(mPackageContext1, () -> {
+        runWithCallerWithStrictMode(mPackageContext1, () -> {
             enableManifestActivity("Launcher_manifest_2", true);
 
             retryUntil(() -> getManager().getManifestShortcuts().size() == 2,
@@ -357,7 +357,7 @@
         final Icon icon2 = Icon.createWithAdaptiveBitmap(BitmapFactory.decodeResource(
             getTestContext().getResources(), R.drawable.black_32x32));
 
-        runWithCaller(mPackageContext1, () -> {
+        runWithCallerWithStrictMode(mPackageContext1, () -> {
             enableManifestActivity("Launcher_manifest_2", true);
 
             retryUntil(() -> getManager().getManifestShortcuts().size() == 2,
diff --git a/tests/tests/shortcutmanager/src/android/content/pm/cts/shortcutmanager/ShortcutManagerMaxCountTest.java b/tests/tests/shortcutmanager/src/android/content/pm/cts/shortcutmanager/ShortcutManagerMaxCountTest.java
index 3e75b4a..8b7ae4e 100644
--- a/tests/tests/shortcutmanager/src/android/content/pm/cts/shortcutmanager/ShortcutManagerMaxCountTest.java
+++ b/tests/tests/shortcutmanager/src/android/content/pm/cts/shortcutmanager/ShortcutManagerMaxCountTest.java
@@ -29,7 +29,7 @@
      * Basic tests: single app, single activity, no manifest shortcuts.
      */
     public void testNumDynamicShortcuts() {
-        runWithCaller(mPackageContext1, () -> {
+        runWithCallerWithStrictMode(mPackageContext1, () -> {
             assertTrue(getManager().setDynamicShortcuts(list(makeShortcut("s1"))));
             assertTrue(getManager().setDynamicShortcuts(list(
                     makeShortcut("s1"),
@@ -101,7 +101,7 @@
      * Manifest shortcuts are included in the count too.
      */
     public void testWithManifest() throws Exception {
-        runWithCaller(mPackageContext1, () -> {
+        runWithCallerWithStrictMode(mPackageContext1, () -> {
             enableManifestActivity("Launcher_manifest_1", true);
             enableManifestActivity("Launcher_manifest_2", true);
 
@@ -110,7 +110,7 @@
 
         });
 
-        runWithCaller(mPackageContext1, () -> {
+        runWithCallerWithStrictMode(mPackageContext1, () -> {
             assertWith(getManager().getManifestShortcuts())
                     .haveIds("ms1", "ms21", "ms22")
                     .areAllManifest()
@@ -134,7 +134,7 @@
         testNumDynamicShortcuts();
 
         // Launcher_manifest_1 has one manifest, so can only add 4 dynamic shortcuts.
-        runWithCaller(mPackageContext1, () -> {
+        runWithCallerWithStrictMode(mPackageContext1, () -> {
             setTargetActivityOverride("Launcher_manifest_1");
 
             assertTrue(getManager().setDynamicShortcuts(list(
@@ -163,7 +163,7 @@
         });
 
         // Launcher_manifest_2 has two manifests, so can only add 3.
-        runWithCaller(mPackageContext1, () -> {
+        runWithCallerWithStrictMode(mPackageContext1, () -> {
             setTargetActivityOverride("Launcher_manifest_2");
 
             assertTrue(getManager().addDynamicShortcuts(list(
@@ -188,7 +188,7 @@
     }
 
     public void testChangeActivity() {
-        runWithCaller(mPackageContext1, () -> {
+        runWithCallerWithStrictMode(mPackageContext1, () -> {
             setTargetActivityOverride("Launcher");
             assertTrue(getManager().setDynamicShortcuts(list(
                     makeShortcut("s1"),
@@ -262,7 +262,7 @@
     }
 
     public void testWithPinned() {
-        runWithCaller(mPackageContext1, () -> {
+        runWithCallerWithStrictMode(mPackageContext1, () -> {
             assertTrue(getManager().setDynamicShortcuts(list(
                     makeShortcut("s1"),
                     makeShortcut("s2"),
@@ -274,12 +274,12 @@
 
         setDefaultLauncher(getInstrumentation(), mLauncherContext1);
 
-        runWithCaller(mLauncherContext1, () -> {
+        runWithCallerWithStrictMode(mLauncherContext1, () -> {
             getLauncherApps().pinShortcuts(mPackageContext1.getPackageName(),
                     list("s1", "s2", "s3", "s4", "s5"), getUserHandle());
         });
 
-        runWithCaller(mPackageContext1, () -> {
+        runWithCallerWithStrictMode(mPackageContext1, () -> {
             assertTrue(getManager().setDynamicShortcuts(list(
                     makeShortcut("s6"),
                     makeShortcut("s7"),
diff --git a/tests/tests/shortcutmanager/src/android/content/pm/cts/shortcutmanager/ShortcutManagerMultiLauncherTest.java b/tests/tests/shortcutmanager/src/android/content/pm/cts/shortcutmanager/ShortcutManagerMultiLauncherTest.java
index 78dbccd..5ed5d52 100644
--- a/tests/tests/shortcutmanager/src/android/content/pm/cts/shortcutmanager/ShortcutManagerMultiLauncherTest.java
+++ b/tests/tests/shortcutmanager/src/android/content/pm/cts/shortcutmanager/ShortcutManagerMultiLauncherTest.java
@@ -16,6 +16,7 @@
 package android.content.pm.cts.shortcutmanager;
 
 import static android.content.pm.LauncherApps.ShortcutQuery.FLAG_GET_KEY_FIELDS_ONLY;
+import static android.content.pm.LauncherApps.ShortcutQuery.FLAG_MATCH_PINNED_BY_ANY_LAUNCHER;
 import static android.content.pm.LauncherApps.ShortcutQuery.FLAG_MATCH_DYNAMIC;
 import static android.content.pm.LauncherApps.ShortcutQuery.FLAG_MATCH_MANIFEST;
 import static android.content.pm.LauncherApps.ShortcutQuery.FLAG_MATCH_PINNED;
@@ -266,6 +267,18 @@
                     .revertToOriginalList()
                     .selectByIds("ms32")
                     .areAllDisabled();
+
+            // Make sure "ALL_PINNED" doesn't work without the permission.
+            assertWith(getShortcutsAsLauncher(
+                    FLAG_MATCH_PINNED | FLAG_GET_KEY_FIELDS_ONLY
+                            | FLAG_MATCH_PINNED_BY_ANY_LAUNCHER,
+                    mPackageContext1.getPackageName()))
+                    .haveIds("s3", "s4", "ms22");
+            assertWith(getShortcutsAsLauncher(
+                    FLAG_MATCH_PINNED | FLAG_GET_KEY_FIELDS_ONLY
+                            | FLAG_MATCH_PINNED_BY_ANY_LAUNCHER,
+                    mPackageContext2.getPackageName()))
+                    .haveIds("s1", "s2", "s3", "ms32");
         });
     }
 }
diff --git a/tests/tests/shortcutmanager/src/android/content/pm/cts/shortcutmanager/ShortcutManagerRequestPinTest.java b/tests/tests/shortcutmanager/src/android/content/pm/cts/shortcutmanager/ShortcutManagerRequestPinTest.java
index 756cf80..c04c0af 100644
--- a/tests/tests/shortcutmanager/src/android/content/pm/cts/shortcutmanager/ShortcutManagerRequestPinTest.java
+++ b/tests/tests/shortcutmanager/src/android/content/pm/cts/shortcutmanager/ShortcutManagerRequestPinTest.java
@@ -40,6 +40,8 @@
 public class ShortcutManagerRequestPinTest extends ShortcutManagerCtsTestsBase {
     private static final String TAG = "ShortcutMRPT";
 
+    private static final String SHORTCUT_ID = "s12345";
+
     public void testIsRequestPinShortcutSupported() {
 
         // Launcher 1 supports it.
@@ -61,11 +63,11 @@
      * A test for {@link ShortcutManager#requestPinShortcut}, a very simple case.
      */
     public void testRequestPinShortcut() {
+        Log.i(TAG, "Testing with launcher1.");
+
         setDefaultLauncher(getInstrumentation(), mLauncherContext1);
 
-        final String SHORTCUT_ID = "s12345";
-
-        runWithCaller(mPackageContext1, () -> {
+        runWithCallerWithStrictMode(mPackageContext1, () -> {
             assertTrue(getManager().isRequestPinShortcutSupported());
 
             ReplyUtil.invokeAndWaitForReply(getTestContext(), (replyAction) -> {
@@ -79,12 +81,19 @@
                         .setExtras(extras)
                         .build();
 
+                // Note: Because requestPinShortcut() won't update the shortcut, but we need to
+                // update the extras that contains the broadcast ID, we need to update the shortcut
+                // manually here before requestPinShortcut().
+
+                // This is only needed when a shortcut is already published with the same ID.
+                assertTrue(getManager().updateShortcuts(list(shortcut)));
+
                 Log.i(TAG, "Calling requestPinShortcut...");
                 assertTrue(getManager().requestPinShortcut(shortcut, /* intent sender */ null));
                 Log.i(TAG, "Done.");
             });
         });
-        runWithCaller(mLauncherContext1, () -> {
+        runWithCallerWithStrictMode(mLauncherContext1, () -> {
             final ShortcutQuery query = new ShortcutQuery()
                     .setPackage(mPackageContext1.getPackageName())
                     .setShortcutIds(list(SHORTCUT_ID))
@@ -117,6 +126,183 @@
                     .areAllMutable()
                     ;
         });
+
+        Log.i(TAG, "Done testing with launcher1.");
+    }
+
+    public void testRequestPinShortcut_multiLaunchers() {
+        testRequestPinShortcut();
+
+        Log.i(TAG, "Testing with launcher2.");
+
+        setDefaultLauncher(getInstrumentation(), mLauncherContext2);
+
+        runWithCallerWithStrictMode(mPackageContext1, () -> {
+            ReplyUtil.invokeAndWaitForReply(getTestContext(), (replyAction) -> {
+                final PersistableBundle extras = new PersistableBundle();
+                extras.putString(Constants.EXTRA_REPLY_ACTION, replyAction);
+                extras.putString(Constants.LABEL, "label1");
+
+                final ShortcutInfo shortcut = makeShortcutBuilder(SHORTCUT_ID)
+                        .setExtras(extras)
+                        .build();
+
+                // Note: Because requestPinShortcut() won't update the shortcut, but we need to
+                // update the extras that contains the broadcast ID, we need to update the shortcut
+                // manually here before requestPinShortcut().
+                assertTrue(getManager().updateShortcuts(list(shortcut)));
+
+                Log.i(TAG, "Calling requestPinShortcut...");
+                assertTrue(getManager().requestPinShortcut(shortcut, /* intent sender */ null));
+                Log.i(TAG, "Done.");
+            });
+        });
+        runWithCallerWithStrictMode(mLauncherContext2, () -> {
+            final ShortcutQuery query = new ShortcutQuery()
+                    .setPackage(mPackageContext1.getPackageName())
+                    .setShortcutIds(list(SHORTCUT_ID))
+                    .setQueryFlags(ShortcutQuery.FLAG_MATCH_DYNAMIC
+                            | ShortcutQuery.FLAG_MATCH_PINNED | ShortcutQuery.FLAG_MATCH_MANIFEST);
+            Log.i(TAG, "Waiting for shortcut to be visible to launcher...");
+            retryUntil(() -> {
+                final List<ShortcutInfo> shortcuts = getLauncherApps().getShortcuts(query,
+                        android.os.Process.myUserHandle());
+                if (shortcuts == null) {
+                    // Launcher not responded yet.
+                    return false;
+                }
+                assertWith(shortcuts)
+                        .haveIds(SHORTCUT_ID)
+                        .areAllPinned()
+                        .areAllNotDynamic()
+                        .areAllNotManifest();
+                return true;
+            }, "Shortcut still not pinned");
+        });
+        Log.i(TAG, "Done testing with launcher2.");
+    }
+
+    public void testRequestPinShortcut_multiLaunchers_withDynamic() {
+        setDefaultLauncher(getInstrumentation(), mLauncherContext1);
+
+        // Publish as a dynamic shortcut first, then call requestPin.
+        ShortcutInfo shortcut = makeShortcutBuilder(SHORTCUT_ID)
+                .setShortLabel("label1")
+                .setIntent(new Intent(Intent.ACTION_MAIN))
+                .build();
+        assertTrue(getManager().setDynamicShortcuts(list(shortcut)));
+
+        // ==============================================================
+        Log.i(TAG, "Testing with launcher1.");
+
+        assertTrue(getManager().isRequestPinShortcutSupported());
+
+        runWithCallerWithStrictMode(mPackageContext1, () -> {
+            ReplyUtil.invokeAndWaitForReply(getTestContext(), (replyAction) -> {
+                final PersistableBundle extras = new PersistableBundle();
+                extras.putString(Constants.EXTRA_REPLY_ACTION, replyAction);
+                extras.putString(Constants.LABEL, "label1");
+
+                final ShortcutInfo shortcut2 = makeShortcutBuilder(SHORTCUT_ID)
+                        .setExtras(extras)
+                        .build();
+
+                // Note: Because requestPinShortcut() won't update the shortcut, but we need to
+                // update the extras that contains the broadcast ID, we need to update the shortcut
+                // manually here before requestPinShortcut().
+
+                // This is only needed when a shortcut is already published with the same ID.
+                assertTrue(getManager().updateShortcuts(list(shortcut2)));
+
+                Log.i(TAG, "Calling requestPinShortcut...");
+                assertTrue(getManager().requestPinShortcut(shortcut2, /* intent sender */ null));
+                Log.i(TAG, "Done.");
+            });
+        });
+        runWithCallerWithStrictMode(mLauncherContext1, () -> {
+            final ShortcutQuery query = new ShortcutQuery()
+                    .setPackage(mPackageContext1.getPackageName())
+                    .setShortcutIds(list(SHORTCUT_ID))
+                    .setQueryFlags(ShortcutQuery.FLAG_MATCH_DYNAMIC
+                            | ShortcutQuery.FLAG_MATCH_PINNED | ShortcutQuery.FLAG_MATCH_MANIFEST);
+            Log.i(TAG, "Waiting for shortcut to be visible to launcher...");
+            retryUntil(() -> {
+                final List<ShortcutInfo> shortcuts = getLauncherApps().getShortcuts(query,
+                        android.os.Process.myUserHandle());
+                if (shortcuts == null) {
+                    // Launcher not responded yet.
+                    return false;
+                }
+                assertWith(shortcuts)
+                        .haveIds(SHORTCUT_ID)
+                        .areAllPinned()
+                        .areAllDynamic()
+                        .areAllNotManifest();
+                return true;
+            }, "Shortcut still not pinned");
+        });
+        // ==============================================================
+        Log.i(TAG, "Testing with launcher2.");
+        setDefaultLauncher(getInstrumentation(), mLauncherContext2);
+
+        assertTrue(getManager().isRequestPinShortcutSupported());
+
+        runWithCallerWithStrictMode(mPackageContext1, () -> {
+            ReplyUtil.invokeAndWaitForReply(getTestContext(), (replyAction) -> {
+                final PersistableBundle extras = new PersistableBundle();
+                extras.putString(Constants.EXTRA_REPLY_ACTION, replyAction);
+                extras.putString(Constants.LABEL, "label1");
+
+                final ShortcutInfo shortcut2 = makeShortcutBuilder(SHORTCUT_ID)
+                        .setExtras(extras)
+                        .build();
+
+                // Note: Because requestPinShortcut() won't update the shortcut, but we need to
+                // update the extras that contains the broadcast ID, we need to update the shortcut
+                // manually here before requestPinShortcut().
+
+                // This is only needed when a shortcut is already published with the same ID.
+                assertTrue(getManager().updateShortcuts(list(shortcut2)));
+
+                Log.i(TAG, "Calling requestPinShortcut...");
+                assertTrue(getManager().requestPinShortcut(shortcut2, /* intent sender */ null));
+                Log.i(TAG, "Done.");
+            });
+        });
+        runWithCallerWithStrictMode(mLauncherContext2, () -> {
+            final ShortcutQuery query = new ShortcutQuery()
+                    .setPackage(mPackageContext1.getPackageName())
+                    .setShortcutIds(list(SHORTCUT_ID))
+                    .setQueryFlags(ShortcutQuery.FLAG_MATCH_DYNAMIC
+                            | ShortcutQuery.FLAG_MATCH_PINNED | ShortcutQuery.FLAG_MATCH_MANIFEST);
+            Log.i(TAG, "Waiting for shortcut to be visible to launcher...");
+            retryUntil(() -> {
+                final List<ShortcutInfo> shortcuts = getLauncherApps().getShortcuts(query,
+                        android.os.Process.myUserHandle());
+                if (shortcuts == null) {
+                    // Launcher not responded yet.
+                    return false;
+                }
+                assertWith(shortcuts)
+                        .haveIds(SHORTCUT_ID)
+                        .areAllPinned()
+                        .areAllDynamic()
+                        .areAllNotManifest();
+                return true;
+            }, "Shortcut still not pinned");
+        });
+
+        runWithCaller(mPackageContext1, () -> {
+            assertWith(getManager().getPinnedShortcuts())
+                    .forShortcutWithId(SHORTCUT_ID, si -> {
+                        assertEquals("label1", si.getShortLabel());
+                    })
+                    .areAllPinned()
+                    .areAllDynamic()
+                    .areAllNotManifest()
+                    .areAllMutable()
+            ;
+        });
     }
 
     /**
diff --git a/tests/tests/shortcutmanager/src/android/content/pm/cts/shortcutmanager/ShortcutManagerStartShortcutTest.java b/tests/tests/shortcutmanager/src/android/content/pm/cts/shortcutmanager/ShortcutManagerStartShortcutTest.java
index 9cf1f89..a66e8ed 100644
--- a/tests/tests/shortcutmanager/src/android/content/pm/cts/shortcutmanager/ShortcutManagerStartShortcutTest.java
+++ b/tests/tests/shortcutmanager/src/android/content/pm/cts/shortcutmanager/ShortcutManagerStartShortcutTest.java
@@ -53,7 +53,7 @@
 
         ShortcutLaunchedActivity.setExpectedOrder(expectedActions);
 
-        runWithCaller(launcher, () -> {
+        runWithCallerWithStrictMode(launcher, () -> {
             getLauncherApps().startShortcut(client.getPackageName(), id, rect, options,
                     getUserHandle());
         });
@@ -73,7 +73,7 @@
 
     private void assertShortcutCantStart(Context launcher, Context client, String id,
             Class<? extends Throwable> exceptionClass) {
-        runWithCaller(launcher, () -> {
+        runWithCallerWithStrictMode(launcher, () -> {
             assertExpectException(exceptionClass, "", () -> {
 
                 getLauncherApps().startShortcut(client.getPackageName(), id, null, null,
@@ -96,7 +96,7 @@
                 .setFlags(Intent.FLAG_ACTIVITY_NO_ANIMATION)
                 .putExtra("k1", "v1");
 
-        runWithCaller(mPackageContext1, () -> {
+        runWithCallerWithStrictMode(mPackageContext1, () -> {
             assertTrue(getManager().addDynamicShortcuts(list(
                     makeShortcutBuilder("s1").setShortLabel("abc")
                             .setIntent(i).build()
@@ -128,7 +128,7 @@
                 .setComponent(mLaunchedActivity)
                 .putExtra("kx", "vx");
 
-        runWithCaller(mPackageContext1, () -> {
+        runWithCallerWithStrictMode(mPackageContext1, () -> {
             assertTrue(getManager().addDynamicShortcuts(list(
                     makeShortcutBuilder("s1").setShortLabel("abc")
                             .setIntents(new Intent[]{i1, i2, i3}).build()
@@ -182,7 +182,7 @@
         testStartMultiple();
 
         // then remove it.
-        runWithCaller(mPackageContext1, () -> {
+        runWithCallerWithStrictMode(mPackageContext1, () -> {
             getManager().removeAllDynamicShortcuts();
         });
 
@@ -198,7 +198,7 @@
                 .setFlags(Intent.FLAG_ACTIVITY_NO_ANIMATION)
                 .putExtra("k1", "v1");
 
-        runWithCaller(mPackageContext1, () -> {
+        runWithCallerWithStrictMode(mPackageContext1, () -> {
             assertTrue(getManager().addDynamicShortcuts(list(
                     makeShortcutBuilder("s1").setShortLabel("abc")
                             .setIntent(i).build()
@@ -219,13 +219,13 @@
         setDefaultLauncher(getInstrumentation(), mLauncherContext1);
 
         // then pin it.
-        runWithCaller(mLauncherContext1, () -> {
+        runWithCallerWithStrictMode(mLauncherContext1, () -> {
             getLauncherApps().pinShortcuts(mPackageContext1.getPackageName(),
                     list("s1"), getUserHandle());
         });
 
         // Then remove it.
-        runWithCaller(mPackageContext1, () -> {
+        runWithCallerWithStrictMode(mPackageContext1, () -> {
             getManager().removeAllDynamicShortcuts();
         });
 
@@ -241,7 +241,7 @@
         setDefaultLauncher(getInstrumentation(), mLauncherContext1);
 
         // then pin it.
-        runWithCaller(mLauncherContext1, () -> {
+        runWithCallerWithStrictMode(mLauncherContext1, () -> {
             getLauncherApps().pinShortcuts(mPackageContext1.getPackageName(),
                     list("s1"), getUserHandle());
         });
@@ -257,7 +257,7 @@
         assertShortcutStarts(mLauncherContext2, mPackageContext1, "s1", EXPECTED_ACTIONS_SINGLE);
 
         // Then remove it.
-        runWithCaller(mPackageContext1, () -> {
+        runWithCallerWithStrictMode(mPackageContext1, () -> {
             getManager().removeAllDynamicShortcuts();
         });
 
@@ -304,7 +304,7 @@
         Intent i = new Intent(Intent.ACTION_MAIN)
                 .setComponent(new ComponentName("abc", "def"));
 
-        runWithCaller(mPackageContext1, () -> {
+        runWithCallerWithStrictMode(mPackageContext1, () -> {
             assertTrue(getManager().addDynamicShortcuts(list(
                     makeShortcutBuilder("s1").setShortLabel("abc")
                             .setIntent(i).build()
@@ -329,7 +329,7 @@
                         "android.content.pm.cts.shortcutmanager.packages.package4",
                         "android.content.pm.cts.shortcutmanager.packages.Launcher"));
 
-        runWithCaller(mPackageContext1, () -> {
+        runWithCallerWithStrictMode(mPackageContext1, () -> {
             assertTrue(getManager().addDynamicShortcuts(list(
                     makeShortcutBuilder("s1").setShortLabel("abc")
                             .setIntent(i).build()
diff --git a/tests/tests/shortcutmanager/src/android/content/pm/cts/shortcutmanager/ShortcutManagerUsageTest.java b/tests/tests/shortcutmanager/src/android/content/pm/cts/shortcutmanager/ShortcutManagerUsageTest.java
index 064ba58..50ce615 100644
--- a/tests/tests/shortcutmanager/src/android/content/pm/cts/shortcutmanager/ShortcutManagerUsageTest.java
+++ b/tests/tests/shortcutmanager/src/android/content/pm/cts/shortcutmanager/ShortcutManagerUsageTest.java
@@ -75,7 +75,7 @@
 
     public void testReportShortcutUsed() throws InterruptedException {
 
-        runWithCaller(mPackageContext1, () -> {
+        runWithCallerWithStrictMode(mPackageContext1, () -> {
             enableManifestActivity("Launcher_manifest_2", true);
 
             retryUntil(() -> getManager().getManifestShortcuts().size() > 0,
@@ -89,13 +89,13 @@
         final String idManifest = "ms21";
         final String idNonexistance = "nonexistence";
 
-        runWithCaller(mPackageContext1, () -> {
+        runWithCallerWithStrictMode(mPackageContext1, () -> {
             assertTrue(getManager().setDynamicShortcuts(list(
                     makeShortcut(id1),
                     makeShortcut(id2)
             )));
         });
-        runWithCaller(mPackageContext2, () -> {
+        runWithCallerWithStrictMode(mPackageContext2, () -> {
             assertTrue(getManager().setDynamicShortcuts(list(
                     makeShortcut(id1),
                     makeShortcut(id3)
@@ -106,7 +106,7 @@
 
         // Report usage.
         final long start1 = System.currentTimeMillis() - USAGE_STATS_RANGE_ALLOWANCE;
-        runWithCaller(mPackageContext2, () -> getManager().reportShortcutUsed(id3));
+        runWithCallerWithStrictMode(mPackageContext2, () -> getManager().reportShortcutUsed(id3));
         final long end1 = System.currentTimeMillis() + USAGE_STATS_RANGE_ALLOWANCE;
 
         // Check the log.
@@ -115,7 +115,7 @@
 
         // Report usage.
         final long start2 = System.currentTimeMillis() - USAGE_STATS_RANGE_ALLOWANCE;
-        runWithCaller(mPackageContext1, () -> getManager().reportShortcutUsed(id1));
+        runWithCallerWithStrictMode(mPackageContext1, () -> getManager().reportShortcutUsed(id1));
         final long end2 = System.currentTimeMillis() + USAGE_STATS_RANGE_ALLOWANCE;
 
         // Check the log.
@@ -124,8 +124,8 @@
 
         // Report usage.
         final long start3 = System.currentTimeMillis() - USAGE_STATS_RANGE_ALLOWANCE;
-        runWithCaller(mPackageContext1, () -> getManager().reportShortcutUsed(idNonexistance));
-        runWithCaller(mPackageContext1, () -> getManager().reportShortcutUsed(idManifest));
+        runWithCallerWithStrictMode(mPackageContext1, () -> getManager().reportShortcutUsed(idNonexistance));
+        runWithCallerWithStrictMode(mPackageContext1, () -> getManager().reportShortcutUsed(idManifest));
         final long end3 = System.currentTimeMillis() + USAGE_STATS_RANGE_ALLOWANCE;
 
         // Check the log.
diff --git a/tests/tests/shortcutmanager/throttling/AndroidManifest.xml b/tests/tests/shortcutmanager/throttling/AndroidManifest.xml
index fcbc167..0d444d4 100644
--- a/tests/tests/shortcutmanager/throttling/AndroidManifest.xml
+++ b/tests/tests/shortcutmanager/throttling/AndroidManifest.xml
@@ -22,6 +22,8 @@
     <uses-sdk android:minSdkVersion="25" android:targetSdkVersion="25" />
 
     <application>
+        <uses-library android:name="android.test.runner" />
+
         <receiver
             android:name="ShortcutManagerThrottlingTestReceiver"
             android:exported="true">
diff --git a/tests/tests/simpleperf/AndroidTest.xml b/tests/tests/simpleperf/AndroidTest.xml
index 89faa0f..eeb6f99 100644
--- a/tests/tests/simpleperf/AndroidTest.xml
+++ b/tests/tests/simpleperf/AndroidTest.xml
@@ -14,6 +14,7 @@
      limitations under the License.
 -->
 <configuration description="Config for CTS Simpleperf test cases">
+    <option name="test-suite-tag" value="cts" />
     <option name="config-descriptor:metadata" key="component" value="bionic" />
     <target_preparer class="com.android.compatibility.common.tradefed.targetprep.FilePusher">
         <option name="cleanup" value="true" />
diff --git a/tests/tests/simpleperf/CtsSimpleperfDebugApp/Android.mk b/tests/tests/simpleperf/CtsSimpleperfDebugApp/Android.mk
index 2a172b5..4f097a4 100644
--- a/tests/tests/simpleperf/CtsSimpleperfDebugApp/Android.mk
+++ b/tests/tests/simpleperf/CtsSimpleperfDebugApp/Android.mk
@@ -20,7 +20,9 @@
 
 LOCAL_MODULE_PATH := $(TARGET_OUT_DATA_APPS)
 
-LOCAL_STATIC_JAVA_LIBRARIES := ctstestrunner legacy-android-test
+LOCAL_STATIC_JAVA_LIBRARIES := ctstestrunner
+
+LOCAL_JAVA_LIBRARIES := android.test.base.stubs
 
 LOCAL_SRC_FILES := $(call all-java-files-under, .)
 
diff --git a/tests/tests/slice/Android.mk b/tests/tests/slice/Android.mk
new file mode 100644
index 0000000..0e29781
--- /dev/null
+++ b/tests/tests/slice/Android.mk
@@ -0,0 +1,44 @@
+# Copyright (C) 2008 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#      http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+LOCAL_PATH:= $(call my-dir)
+
+include $(CLEAR_VARS)
+
+# don't include this package in any target
+LOCAL_MODULE_TAGS := optional
+# and when built explicitly put it in the data partition
+LOCAL_MODULE_PATH := $(TARGET_OUT_DATA_APPS)
+
+# Tag this module as a cts test artifact
+LOCAL_COMPATIBILITY_SUITE := cts vts general-tests
+
+LOCAL_JAVA_LIBRARIES := android.test.runner
+
+LOCAL_STATIC_JAVA_LIBRARIES := \
+    android-support-test \
+    compatibility-device-util \
+    ctsdeviceutillegacy \
+    ctstestrunner \
+    mockito-target-minus-junit4 \
+    platform-test-annotations \
+    ub-uiautomator
+
+LOCAL_SRC_FILES := $(call all-java-files-under, src)
+
+LOCAL_PACKAGE_NAME := CtsSliceTestCases
+
+include $(BUILD_CTS_PACKAGE)
+
+include $(call all-makefiles-under,$(LOCAL_PATH))
diff --git a/tests/tests/slice/AndroidManifest.xml b/tests/tests/slice/AndroidManifest.xml
new file mode 100644
index 0000000..87253c5
--- /dev/null
+++ b/tests/tests/slice/AndroidManifest.xml
@@ -0,0 +1,42 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ * Copyright (C) 2007 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ -->
+
+<manifest xmlns:android="http://schemas.android.com/apk/res/android"
+    package="android.slice.cts">
+
+    <uses-permission android:name="android.permission.BIND_SLICE" />
+
+    <application android:label="Android TestCase"
+                android:icon="@drawable/size_48x48"
+                android:maxRecents="1"
+                android:multiArch="true"
+                android:supportsRtl="true">
+        <uses-library android:name="android.test.runner" />
+
+        <provider android:name=".SliceProvider"
+            android:authorities="android.slice.cts"
+            android:process=":slice_process" />
+    </application>
+
+    <instrumentation android:name="android.support.test.runner.AndroidJUnitRunner"
+                     android:targetPackage="android.slice.cts"
+                     android:label="CTS tests of android.slice">
+        <meta-data android:name="listener"
+            android:value="com.android.cts.runner.CtsTestRunListener" />
+    </instrumentation>
+
+</manifest>
diff --git a/tests/tests/slice/AndroidTest.xml b/tests/tests/slice/AndroidTest.xml
new file mode 100644
index 0000000..ceae60a
--- /dev/null
+++ b/tests/tests/slice/AndroidTest.xml
@@ -0,0 +1,31 @@
+<?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 Slice 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="CtsSliceTestCases.apk" />
+    </target_preparer>
+    <target_preparer class="com.android.tradefed.targetprep.RunCommandTargetPreparer">
+        <option name="run-command" value="pm grant android.slice.cts android.permission.BIND_SLICE" />
+        <option name="teardown-command" value="pm revoke android.slice.cts android.permission.BIND_SLICE"/>
+    </target_preparer>
+    <test class="com.android.tradefed.testtype.AndroidJUnitTest" >
+        <option name="package" value="android.slice.cts" />
+        <option name="runtime-hint" value="1m" />
+    </test>
+</configuration>
diff --git a/tests/tests/slice/res/drawable/size_48x48.jpg b/tests/tests/slice/res/drawable/size_48x48.jpg
new file mode 100644
index 0000000..5c2291e
--- /dev/null
+++ b/tests/tests/slice/res/drawable/size_48x48.jpg
Binary files differ
diff --git a/tests/tests/slice/src/android/slice/cts/SliceProvider.java b/tests/tests/slice/src/android/slice/cts/SliceProvider.java
new file mode 100644
index 0000000..393af18
--- /dev/null
+++ b/tests/tests/slice/src/android/slice/cts/SliceProvider.java
@@ -0,0 +1,117 @@
+/*
+ * 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.slice.cts;
+
+import android.app.PendingIntent;
+import android.content.Intent;
+import android.graphics.drawable.Icon;
+import android.net.Uri;
+import android.app.slice.Slice;
+import android.app.slice.Slice.Builder;
+import android.os.Bundle;
+import android.os.Parcel;
+import android.os.Parcelable;
+
+public class SliceProvider extends android.app.slice.SliceProvider {
+
+    @Override
+    public boolean onCreate() {
+        return true;
+    }
+
+    @Override
+    public Slice onBindSlice(Uri sliceUri) {
+        switch (sliceUri.getPath()) {
+            case "/set_flag":
+                SliceTest.sFlag = true;
+                break;
+            case "/subslice":
+                Builder b = new Builder(sliceUri);
+                return b.addSubSlice(new Slice.Builder(b).build(), "subslice").build();
+            case "/text":
+                return new Slice.Builder(sliceUri).addText("Expected text", "text").build();
+            case "/icon":
+                return new Slice.Builder(sliceUri).addIcon(
+                        Icon.createWithResource(getContext(), R.drawable.size_48x48), "icon").build();
+            case "/action":
+                Builder builder = new Builder(sliceUri);
+                Slice subSlice = new Slice.Builder(builder).build();
+                PendingIntent broadcast = PendingIntent.getBroadcast(getContext(), 0,
+                        new Intent(getContext().getPackageName() + ".action"), 0);
+                return builder.addAction(broadcast, subSlice, "action").build();
+            case "/int":
+                return new Slice.Builder(sliceUri).addInt(0xff121212, "int").build();
+            case "/timestamp":
+                return new Slice.Builder(sliceUri).addTimestamp(43, "timestamp").build();
+            case "/hints":
+                return new Slice.Builder(sliceUri)
+                        .addHints(Slice.HINT_LIST)
+                        .addText("Text", null, Slice.HINT_TITLE)
+                        .addIcon(Icon.createWithResource(getContext(), R.drawable.size_48x48),
+                                null, Slice.HINT_NO_TINT, Slice.HINT_LARGE)
+                        .build();
+            case "/bundle":
+                Bundle b1 = new Bundle();
+                b1.putParcelable("a", new TestParcel());
+                return new Slice.Builder(sliceUri).addBundle(b1, "bundle").build();
+        }
+        return new Slice.Builder(sliceUri).build();
+    }
+
+    public static class TestParcel implements Parcelable {
+
+        private final int mValue;
+
+        public TestParcel() {
+            mValue = 42;
+        }
+
+        protected TestParcel(Parcel in) {
+            mValue = in.readInt();
+        }
+
+        @Override
+        public void writeToParcel(Parcel dest, int flags) {
+            dest.writeInt(mValue);
+        }
+
+        @Override
+        public int describeContents() {
+            return 0;
+        }
+
+        @Override
+        public boolean equals(Object obj) {
+            try {
+                TestParcel p = (TestParcel) obj;
+                return p.mValue == mValue;
+            } catch (ClassCastException e) {
+                return false;
+            }
+        }
+
+        public static final Creator<TestParcel> CREATOR = new Creator<TestParcel>() {
+            @Override
+            public TestParcel createFromParcel(Parcel in) {
+                return new TestParcel(in);
+            }
+
+            @Override
+            public TestParcel[] newArray(int size) {
+                return new TestParcel[size];
+            }
+        };
+    }
+}
diff --git a/tests/tests/slice/src/android/slice/cts/SliceTest.java b/tests/tests/slice/src/android/slice/cts/SliceTest.java
new file mode 100644
index 0000000..6b3ee00
--- /dev/null
+++ b/tests/tests/slice/src/android/slice/cts/SliceTest.java
@@ -0,0 +1,209 @@
+/*
+ * 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.slice.cts;
+
+import static org.junit.Assert.assertArrayEquals;
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertNotEquals;
+import static org.junit.Assert.assertTrue;
+
+import android.app.PendingIntent.CanceledException;
+import android.content.BroadcastReceiver;
+import android.content.Context;
+import android.content.Intent;
+import android.content.IntentFilter;
+import android.graphics.drawable.Icon;
+import android.net.Uri;
+import android.app.slice.Slice;
+import android.app.slice.SliceItem;
+import android.os.Bundle;
+import android.slice.cts.SliceProvider.TestParcel;
+import android.support.test.InstrumentationRegistry;
+import android.support.test.runner.AndroidJUnit4;
+
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+import java.util.Arrays;
+import java.util.Collections;
+import java.util.concurrent.CountDownLatch;
+import java.util.concurrent.TimeUnit;
+
+@RunWith(AndroidJUnit4.class)
+public class SliceTest {
+
+    public static boolean sFlag = false;
+
+    private static final Uri BASE_URI = Uri.parse("content://android.slice.cts/");
+    private final Context mContext = InstrumentationRegistry.getContext();
+
+    @Test
+    public void testProcess() {
+        sFlag = false;
+        Slice.bindSlice(mContext.getContentResolver(),
+                BASE_URI.buildUpon().appendPath("set_flag").build(),
+                Collections.emptyList());
+        assertFalse(sFlag);
+    }
+
+    @Test
+    public void testType() {
+        assertEquals(SliceProvider.SLICE_TYPE,
+                mContext.getContentResolver().getType(BASE_URI));
+    }
+
+    @Test
+    public void testSliceUri() {
+        Slice s = Slice.bindSlice(mContext.getContentResolver(), BASE_URI,
+                Collections.emptyList());
+        assertEquals(BASE_URI, s.getUri());
+    }
+
+    @Test
+    public void testSubSlice() {
+        Uri uri = BASE_URI.buildUpon().appendPath("subslice").build();
+        Slice s = Slice.bindSlice(mContext.getContentResolver(), uri,
+                Collections.emptyList());
+        assertEquals(uri, s.getUri());
+        assertEquals(1, s.getItems().size());
+
+        SliceItem item = s.getItems().get(0);
+        assertEquals(SliceItem.FORMAT_SLICE, item.getFormat());
+        assertEquals("subslice", item.getSubType());
+        // The item should start with the same Uri as the parent, but be different.
+        assertTrue(item.getSlice().getUri().toString().startsWith(uri.toString()));
+        assertNotEquals(uri, item.getSlice().getUri());
+    }
+
+    @Test
+    public void testText() {
+        Uri uri = BASE_URI.buildUpon().appendPath("text").build();
+        Slice s = Slice.bindSlice(mContext.getContentResolver(), uri,
+                Collections.emptyList());
+        assertEquals(uri, s.getUri());
+        assertEquals(1, s.getItems().size());
+
+        SliceItem item = s.getItems().get(0);
+        assertEquals(SliceItem.FORMAT_TEXT, item.getFormat());
+        // TODO: Test spannables here.
+        assertEquals("Expected text", item.getText());
+    }
+
+    @Test
+    public void testIcon() {
+        Uri uri = BASE_URI.buildUpon().appendPath("icon").build();
+        Slice s = Slice.bindSlice(mContext.getContentResolver(), uri,
+                Collections.emptyList());
+        assertEquals(uri, s.getUri());
+        assertEquals(1, s.getItems().size());
+
+        SliceItem item = s.getItems().get(0);
+        assertEquals(SliceItem.FORMAT_IMAGE, item.getFormat());
+        assertEquals(Icon.createWithResource(mContext, R.drawable.size_48x48).toString(),
+                item.getIcon().toString());
+    }
+
+    @Test
+    public void testAction() {
+        sFlag = false;
+        CountDownLatch latch = new CountDownLatch(1);
+        BroadcastReceiver receiver = new BroadcastReceiver() {
+            @Override
+            public void onReceive(Context context, Intent intent) {
+                sFlag = true;
+                latch.countDown();
+            }
+        };
+        mContext.registerReceiver(receiver,
+                new IntentFilter(mContext.getPackageName() + ".action"));
+        Uri uri = BASE_URI.buildUpon().appendPath("action").build();
+        Slice s = Slice.bindSlice(mContext.getContentResolver(), uri,
+                Collections.emptyList());
+        assertEquals(uri, s.getUri());
+        assertEquals(1, s.getItems().size());
+
+        SliceItem item = s.getItems().get(0);
+        assertEquals(SliceItem.FORMAT_ACTION, item.getFormat());
+        try {
+            item.getAction().send();
+        } catch (CanceledException e) {
+        }
+
+        try {
+            latch.await(100, TimeUnit.MILLISECONDS);
+        } catch (InterruptedException e) {
+            e.printStackTrace();
+        }
+        assertTrue(sFlag);
+        mContext.unregisterReceiver(receiver);
+    }
+
+    @Test
+    public void testInt() {
+        Uri uri = BASE_URI.buildUpon().appendPath("int").build();
+        Slice s = Slice.bindSlice(mContext.getContentResolver(), uri,
+                Collections.emptyList());
+        assertEquals(uri, s.getUri());
+        assertEquals(1, s.getItems().size());
+
+        SliceItem item = s.getItems().get(0);
+        assertEquals(SliceItem.FORMAT_INT, item.getFormat());
+        assertEquals(0xff121212, item.getInt());
+    }
+
+    @Test
+    public void testTimestamp() {
+        Uri uri = BASE_URI.buildUpon().appendPath("timestamp").build();
+        Slice s = Slice.bindSlice(mContext.getContentResolver(), uri,
+                Collections.emptyList());
+        assertEquals(uri, s.getUri());
+        assertEquals(1, s.getItems().size());
+
+        SliceItem item = s.getItems().get(0);
+        assertEquals(SliceItem.FORMAT_TIMESTAMP, item.getFormat());
+        assertEquals(43, item.getTimestamp());
+    }
+
+    @Test
+    public void testHints() {
+        // Note this tests that hints are propagated through to the client but not that any specific
+        // hints have any effects.
+        Uri uri = BASE_URI.buildUpon().appendPath("hints").build();
+        Slice s = Slice.bindSlice(mContext.getContentResolver(), uri,
+                Collections.emptyList());
+        assertEquals(uri, s.getUri());
+
+        assertEquals(Arrays.asList(Slice.HINT_LIST), s.getHints());
+        assertEquals(Arrays.asList(Slice.HINT_TITLE), s.getItems().get(0).getHints());
+        assertEquals(Arrays.asList(Slice.HINT_NO_TINT, Slice.HINT_LARGE),
+                s.getItems().get(1).getHints());
+    }
+
+    @Test
+    public void testBundle() {
+        Uri uri = BASE_URI.buildUpon().appendPath("bundle").build();
+        Slice s = Slice.bindSlice(mContext.getContentResolver(), uri,
+                Collections.emptyList());
+        assertEquals(uri, s.getUri());
+        assertEquals(1, s.getItems().size());
+
+        SliceItem item = s.getItems().get(0);
+        assertEquals(SliceItem.FORMAT_BUNDLE, item.getFormat());
+        Bundle b = item.getBundle();
+        b.setClassLoader(getClass().getClassLoader());
+        assertEquals(new TestParcel(), b.getParcelable("a"));
+    }
+}
diff --git a/tests/tests/speech/Android.mk b/tests/tests/speech/Android.mk
index 05f9388..1093467 100755
--- a/tests/tests/speech/Android.mk
+++ b/tests/tests/speech/Android.mk
@@ -23,8 +23,9 @@
 
 LOCAL_STATIC_JAVA_LIBRARIES := \
     ctstestrunner \
-    android-support-test \
-    legacy-android-test
+    android-support-test
+
+LOCAL_JAVA_LIBRARIES := android.test.base
 
 LOCAL_SRC_FILES := $(call all-java-files-under, src)
 
diff --git a/tests/tests/speech/AndroidTest.xml b/tests/tests/speech/AndroidTest.xml
index 9cc3132..adc4fab 100644
--- a/tests/tests/speech/AndroidTest.xml
+++ b/tests/tests/speech/AndroidTest.xml
@@ -14,6 +14,7 @@
      limitations under the License.
 -->
 <configuration description="Config for CTS Speech test cases">
+    <option name="test-suite-tag" value="cts" />
     <option name="config-descriptor:metadata" key="component" value="framework" />
     <target_preparer class="com.android.tradefed.targetprep.suite.SuiteApkInstaller">
         <option name="cleanup-apks" value="true" />
diff --git a/tests/tests/speech/src/android/speech/tts/cts/StubTextToSpeechService.java b/tests/tests/speech/src/android/speech/tts/cts/StubTextToSpeechService.java
index 857ffd8..6b3abf6 100644
--- a/tests/tests/speech/src/android/speech/tts/cts/StubTextToSpeechService.java
+++ b/tests/tests/speech/src/android/speech/tts/cts/StubTextToSpeechService.java
@@ -15,6 +15,7 @@
  */
 package android.speech.tts.cts;
 
+import android.os.ConditionVariable;
 import android.media.AudioFormat;
 import android.os.ConditionVariable;
 import android.speech.tts.SynthesisCallback;
@@ -37,6 +38,10 @@
     // Object that onSynthesizeText will #block on, if set to non-null
     public static volatile ConditionVariable sSynthesizeTextWait;
 
+    // Condition variable that onSynthesizeText will #open when it started
+    // synethesizing, if set to non-null.
+    public static volatile ConditionVariable sSynthesizeTextStartEvent;
+
     private ArrayList<Locale> supportedLanguages = new ArrayList<Locale>();
     private ArrayList<Locale> supportedCountries = new ArrayList<Locale>();
     private ArrayList<Locale> GBFallbacks = new ArrayList<Locale>();
@@ -80,6 +85,11 @@
             return;
         }
 
+        final ConditionVariable synthesizeTextStartEvent = sSynthesizeTextStartEvent;
+        if (synthesizeTextStartEvent != null) {
+            sSynthesizeTextStartEvent.open();
+        }
+
         final ConditionVariable synthesizeTextWait = sSynthesizeTextWait;
         if (synthesizeTextWait != null) {
             synthesizeTextWait.block(10000);  // 10s timeout
diff --git a/tests/tests/speech/src/android/speech/tts/cts/TextToSpeechServiceTest.java b/tests/tests/speech/src/android/speech/tts/cts/TextToSpeechServiceTest.java
index 4d9faad..8e54d31 100644
--- a/tests/tests/speech/src/android/speech/tts/cts/TextToSpeechServiceTest.java
+++ b/tests/tests/speech/src/android/speech/tts/cts/TextToSpeechServiceTest.java
@@ -117,6 +117,27 @@
         }
     }
 
+    public void testSpeakStopBehindOtherAudioPlayback() throws Exception {
+        final ConditionVariable synthesizeTextWait = new ConditionVariable();
+        final ConditionVariable synthesizeTextStartEvent = new ConditionVariable();
+        StubTextToSpeechService.sSynthesizeTextWait = synthesizeTextWait;
+        StubTextToSpeechService.sSynthesizeTextStartEvent = synthesizeTextStartEvent;
+
+        // Make the audio playback queue busy by putting a 30s of silence.
+        getTts().stop();
+        getTts().playSilentUtterance(30000, TextToSpeech.QUEUE_ADD, "silence");
+
+        // speak(), wait it to starting in the service, and stop().
+        int result = getTts().speak(UTTERANCE, TextToSpeech.QUEUE_ADD, null, "stop");
+        assertEquals("speak() failed", TextToSpeech.SUCCESS, result);
+        assertTrue("synthesis not started", synthesizeTextStartEvent.block(10000));
+        getTts().stop();
+
+        // Wake up the Stubs #onSynthesizeSpeech (one that will be stopped in-progress)
+        synthesizeTextWait.open();
+
+        assertTrue("speak() stop callback timeout", mTts.waitForStop("stop"));
+    }
 
     public void testMediaPlayerFails() throws Exception {
         File sampleFile = new File(Environment.getExternalStorageDirectory(), "notsound.wav");
diff --git a/tests/tests/systemintents/Android.mk b/tests/tests/systemintents/Android.mk
index 573e2ee..09ac3314 100644
--- a/tests/tests/systemintents/Android.mk
+++ b/tests/tests/systemintents/Android.mk
@@ -27,7 +27,7 @@
 
 LOCAL_PACKAGE_NAME := CtsSystemIntentTestCases
 
-LOCAL_STATIC_JAVA_LIBRARIES := ctstestrunner legacy-android-test
+LOCAL_STATIC_JAVA_LIBRARIES := ctstestrunner android-support-test
 
 LOCAL_SDK_VERSION := test_current
 
diff --git a/tests/tests/systemintents/AndroidTest.xml b/tests/tests/systemintents/AndroidTest.xml
index 37f9895..4f0cc36 100644
--- a/tests/tests/systemintents/AndroidTest.xml
+++ b/tests/tests/systemintents/AndroidTest.xml
@@ -15,6 +15,7 @@
   ~ limitations under the License
   -->
 <configuration description="Config for CTS system intent test cases">
+    <option name="test-suite-tag" value="cts" />
     <option name="config-descriptor:metadata" key="component" value="framework" />
     <option name="not-shardable" value="true" />
     <target_preparer class="com.android.tradefed.targetprep.suite.SuiteApkInstaller">
diff --git a/tests/tests/systemintents/src/android/systemintents/cts/TestSystemIntents.java b/tests/tests/systemintents/src/android/systemintents/cts/TestSystemIntents.java
index c572629..086f2cb 100644
--- a/tests/tests/systemintents/src/android/systemintents/cts/TestSystemIntents.java
+++ b/tests/tests/systemintents/src/android/systemintents/cts/TestSystemIntents.java
@@ -16,6 +16,8 @@
 
 package android.systemintents.cts;
 
+import static org.junit.Assert.assertTrue;
+
 import android.content.Intent;
 import android.content.pm.PackageManager;
 import android.content.pm.ResolveInfo;
@@ -23,14 +25,17 @@
 import android.net.Uri;
 import android.provider.Settings;
 import android.support.test.filters.MediumTest;
-import android.test.AndroidTestCase;
+import android.support.test.runner.AndroidJUnit4;
+import android.support.test.InstrumentationRegistry;
 import android.util.Log;
 
-import org.junit.Rule;
 import org.junit.Test;
+import org.junit.runner.RunWith;
 
 @MediumTest
-public class TestSystemIntents extends AndroidTestCase {
+@RunWith(AndroidJUnit4.class)
+public class TestSystemIntents {
+
     /*
      * List of activity intents defined by the system.  Activities to handle each of these
      * intents must all exist.
@@ -56,7 +61,6 @@
         }
     }
 
-    @Rule
     private final IntentEntry[] mTestIntents = {
             /* Settings-namespace intent actions */
             new IntentEntry(0, new Intent(Settings.ACTION_SETTINGS)),
@@ -76,7 +80,7 @@
 
     @Test
     public void testSystemIntents() {
-        final PackageManager pm = getContext().getPackageManager();
+        final PackageManager pm = InstrumentationRegistry.getContext().getPackageManager();
         int productFlags = 0;
 
         if (pm.hasSystemFeature(PackageManager.FEATURE_LEANBACK)) {
@@ -87,7 +91,7 @@
             productFlags |= EXCLUDE_NON_TELEPHONY;
         }
 
-        final Configuration config = getContext().getResources().getConfiguration();
+        final Configuration config = InstrumentationRegistry.getContext().getResources().getConfiguration();
         if ((config.uiMode & Configuration.UI_MODE_TYPE_WATCH) != 0) {
             productFlags |= EXCLUDE_WATCH;
         }
@@ -99,4 +103,4 @@
             }
         }
     }
-}
\ No newline at end of file
+}
diff --git a/tests/tests/systemui/Android.mk b/tests/tests/systemui/Android.mk
index 6ba1e77..073e829 100644
--- a/tests/tests/systemui/Android.mk
+++ b/tests/tests/systemui/Android.mk
@@ -29,7 +29,6 @@
 LOCAL_STATIC_JAVA_LIBRARIES := \
     ctstestrunner \
     android-support-test \
-    legacy-android-test \
     ub-uiautomator
 
 LOCAL_SRC_FILES := $(call all-java-files-under, src)
diff --git a/tests/tests/telecom/Android.mk b/tests/tests/telecom/Android.mk
index 8a243eb..8f46fd7 100644
--- a/tests/tests/telecom/Android.mk
+++ b/tests/tests/telecom/Android.mk
@@ -27,8 +27,9 @@
 LOCAL_STATIC_JAVA_LIBRARIES := \
 	compatibility-device-util \
 	ctstestrunner \
-	android-support-test \
-	legacy-android-test
+	android-support-test
+
+LOCAL_JAVA_LIBRARIES := android.test.base.stubs
 
 LOCAL_SRC_FILES := $(call all-java-files-under, src)
 
diff --git a/tests/tests/telecom/AndroidTest.xml b/tests/tests/telecom/AndroidTest.xml
index fb60586..46de0b7 100644
--- a/tests/tests/telecom/AndroidTest.xml
+++ b/tests/tests/telecom/AndroidTest.xml
@@ -14,6 +14,7 @@
      limitations under the License.
 -->
 <configuration description="Configuration for Telecom Tests">
+    <option name="test-suite-tag" value="cts" />
     <option name="config-descriptor:metadata" key="component" value="telecom" />
     <target_preparer class="com.android.compatibility.common.tradefed.targetprep.TokenRequirement">
         <option name="token" value="sim-card" />
diff --git a/tests/tests/telecom/src/android/telecom/cts/BaseTelecomTestWithMockServices.java b/tests/tests/telecom/src/android/telecom/cts/BaseTelecomTestWithMockServices.java
index ea29552..321d0b5 100644
--- a/tests/tests/telecom/src/android/telecom/cts/BaseTelecomTestWithMockServices.java
+++ b/tests/tests/telecom/src/android/telecom/cts/BaseTelecomTestWithMockServices.java
@@ -100,6 +100,7 @@
 
         @Override
         public void onCallStateChanged(int state, String number) {
+            Log.i(TAG, "onCallStateChanged: state=" + state + ", number=%s" + number);
             mCallStates.add(Pair.create(state, number));
             mCallbackSemaphore.release();
         }
@@ -571,7 +572,11 @@
     void verifyPhoneStateListenerCallbacksForCall(int expectedCallState) throws Exception {
         assertTrue(mPhoneStateListener.mCallbackSemaphore.tryAcquire(
                 TestUtils.WAIT_FOR_PHONE_STATE_LISTENER_CALLBACK_TIMEOUT_S, TimeUnit.SECONDS));
-        Pair<Integer, String> callState = mPhoneStateListener.mCallStates.get(0);
+        // Get the most recent callback; it is possible that there was an initial state reported due
+        // to the fact that TelephonyManager will sometimes give an initial state back to the caller
+        // when the listener is registered.
+        Pair<Integer, String> callState = mPhoneStateListener.mCallStates.get(
+                mPhoneStateListener.mCallStates.size() - 1);
         assertEquals(expectedCallState, (int) callState.first);
         assertEquals(getTestNumber().getSchemeSpecificPart(), callState.second);
     }
diff --git a/tests/tests/telecom2/Android.mk b/tests/tests/telecom2/Android.mk
index 8e9c8a5d..5b22a92 100644
--- a/tests/tests/telecom2/Android.mk
+++ b/tests/tests/telecom2/Android.mk
@@ -26,8 +26,9 @@
 
 LOCAL_STATIC_JAVA_LIBRARIES := \
 	compatibility-device-util \
-	ctstestrunner \
-	legacy-android-test
+	ctstestrunner
+
+LOCAL_JAVA_LIBRARIES := android.test.base.stubs
 
 src_dirs := src \
     ../telecom/src/android/telecom/cts/SelfManagedConnection.java \
diff --git a/tests/tests/telecom2/AndroidTest.xml b/tests/tests/telecom2/AndroidTest.xml
index f4d8e86..d38f24b 100644
--- a/tests/tests/telecom2/AndroidTest.xml
+++ b/tests/tests/telecom2/AndroidTest.xml
@@ -14,6 +14,7 @@
      limitations under the License.
 -->
 <configuration description="Configuration for Telecom2 Tests">
+    <option name="test-suite-tag" value="cts" />
     <option name="config-descriptor:metadata" key="component" value="telecom" />
     <option name="not-shardable" value="true" />
     <target_preparer class="com.android.tradefed.targetprep.suite.SuiteApkInstaller">
diff --git a/tests/tests/telecom3/Android.mk b/tests/tests/telecom3/Android.mk
index 86cb859..3138e6c 100644
--- a/tests/tests/telecom3/Android.mk
+++ b/tests/tests/telecom3/Android.mk
@@ -26,6 +26,8 @@
 
 LOCAL_STATIC_JAVA_LIBRARIES := compatibility-device-util ctstestrunner
 
+LOCAL_JAVA_LIBRARIES := android.test.base.stubs
+
 src_dirs := src \
     ../telecom/src/android/telecom/cts/SelfManagedConnection.java \
     ../telecom/src/android/telecom/cts/CtsSelfManagedConnectionService.java \
diff --git a/tests/tests/telecom3/AndroidTest.xml b/tests/tests/telecom3/AndroidTest.xml
index 354581a..75b1d0d 100644
--- a/tests/tests/telecom3/AndroidTest.xml
+++ b/tests/tests/telecom3/AndroidTest.xml
@@ -14,6 +14,7 @@
      limitations under the License.
 -->
 <configuration description="Configuration for Telecom3 Tests">
+    <option name="test-suite-tag" value="cts" />
     <option name="config-descriptor:metadata" key="component" value="telecom" />
     <option name="not-shardable" value="true" />
     <target_preparer class="com.android.tradefed.targetprep.suite.SuiteApkInstaller">
diff --git a/tests/tests/telephony/Android.mk b/tests/tests/telephony/Android.mk
index 7bd88fe..2e350b0 100644
--- a/tests/tests/telephony/Android.mk
+++ b/tests/tests/telephony/Android.mk
@@ -26,8 +26,7 @@
 
 LOCAL_STATIC_JAVA_LIBRARIES := \
     ctstestrunner \
-    compatibility-device-util \
-    legacy-android-test
+    compatibility-device-util
 
 LOCAL_HOST_SHARED_LIBRARIES := compatibility-device-telephony-preconditions
 
@@ -45,6 +44,7 @@
 # uncomment when b/13250611 is fixed
 #LOCAL_SDK_VERSION := current
 LOCAL_JAVA_LIBRARIES += android.test.runner
+LOCAL_JAVA_LIBRARIES += android.test.base
 
 include $(BUILD_CTS_PACKAGE)
 include $(call all-makefiles-under,$(LOCAL_PATH))
diff --git a/tests/tests/telephony/AndroidTest.xml b/tests/tests/telephony/AndroidTest.xml
index 1e48ebf..a696dfc 100644
--- a/tests/tests/telephony/AndroidTest.xml
+++ b/tests/tests/telephony/AndroidTest.xml
@@ -14,6 +14,7 @@
      limitations under the License.
 -->
 <configuration description="Config for CTS Telephony test cases">
+    <option name="test-suite-tag" value="cts" />
     <target_preparer class="android.telephony.cts.preconditions.TelephonyPreparer">
         <option name="apk" value="CtsTelephonyPreparerApp.apk" />
         <option name="package" value="android.telephony.cts.preconditions.app" />
diff --git a/tests/tests/telephony/EmbmsMiddlewareTestApp/Android.mk b/tests/tests/telephony/EmbmsMiddlewareTestApp/Android.mk
index cbd18b2..748b6963 100644
--- a/tests/tests/telephony/EmbmsMiddlewareTestApp/Android.mk
+++ b/tests/tests/telephony/EmbmsMiddlewareTestApp/Android.mk
@@ -14,7 +14,7 @@
 
 LOCAL_MODULE_TAGS := optional
 LOCAL_SDK_VERSION := test_current
-LOCAL_COMPATIBILITY_SUITE := cts
+LOCAL_COMPATIBILITY_SUITE := cts vts
 LOCAL_MODULE_PATH := $(TARGET_OUT_DATA_APPS)
 
 LOCAL_PROGUARD_ENABLED := disabled
diff --git a/tests/tests/telephony/preconditions/app/Android.mk b/tests/tests/telephony/preconditions/app/Android.mk
index a5fa396..fb64cd2 100644
--- a/tests/tests/telephony/preconditions/app/Android.mk
+++ b/tests/tests/telephony/preconditions/app/Android.mk
@@ -31,6 +31,8 @@
                                 compatibility-device-util \
                                 compatibility-device-preconditions
 
+LOCAL_JAVA_LIBRARIES := android.test.base.stubs
+
 # 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/SmsManagerTest.java b/tests/tests/telephony/src/android/telephony/cts/SmsManagerTest.java
index 8ffee99..74f9d13 100755
--- a/tests/tests/telephony/src/android/telephony/cts/SmsManagerTest.java
+++ b/tests/tests/telephony/src/android/telephony/cts/SmsManagerTest.java
@@ -283,6 +283,24 @@
         }
     }
 
+
+    public void testSmsNotPersisted_failsWithoutCarrierPermissions() throws Exception {
+        if (!mPackageManager.hasSystemFeature(PackageManager.FEATURE_TELEPHONY)) {
+            return;
+        }
+
+        assertFalse("[RERUN] SIM card does not provide phone number. Use a suitable SIM Card.",
+                TextUtils.isEmpty(mDestAddr));
+
+        try {
+            getSmsManager().sendTextMessageWithoutPersisting(mDestAddr, null /*scAddress */,
+                    mDestAddr, mSentIntent, mDeliveredIntent);
+            fail("We should get a SecurityException due to not having carrier privileges");
+        } catch (SecurityException e) {
+            // Success
+        }
+    }
+
     private void init() {
         mSendReceiver.reset();
         mDeliveryReceiver.reset();
diff --git a/tests/tests/telephony/src/android/telephony/cts/TelephonyManagerTest.java b/tests/tests/telephony/src/android/telephony/cts/TelephonyManagerTest.java
index 79097e0..d0b219d 100644
--- a/tests/tests/telephony/src/android/telephony/cts/TelephonyManagerTest.java
+++ b/tests/tests/telephony/src/android/telephony/cts/TelephonyManagerTest.java
@@ -190,6 +190,7 @@
         mTelephonyManager.getPhoneCount();
         mTelephonyManager.getDataEnabled();
         mTelephonyManager.getNetworkSpecifier();
+        mTelephonyManager.getNai();
         TelecomManager telecomManager = (TelecomManager) getContext()
                 .getSystemService(Context.TELECOM_SERVICE);
         PhoneAccountHandle defaultAccount = telecomManager
@@ -358,12 +359,12 @@
     }
 
     private void assertSerialNumber() {
-        assertNotNull("Non-telephony devices must have a Build.SERIAL number.",
-                Build.SERIAL);
+        assertNotNull("Non-telephony devices must have a Build.getSerial() number.",
+                Build.getSerial());
         assertTrue("Hardware id must be no longer than 20 characters.",
-                Build.SERIAL.length() <= 20);
+                Build.getSerial().length() <= 20);
         assertTrue("Hardware id must be alphanumeric.",
-                Pattern.matches("[0-9A-Za-z]+", Build.SERIAL));
+                Pattern.matches("[0-9A-Za-z]+", Build.getSerial()));
     }
 
     private void assertMacAddress(String macAddress) {
@@ -509,7 +510,7 @@
 
         if (mPackageManager.hasSystemFeature(PackageManager.FEATURE_TELEPHONY)) {
             if (!TextUtils.isEmpty(meid)) {
-                assertMeidEsn(meid);
+                assertImei(meid);
             } else {
                 // If MEID is empty, then IMEI must not be empty. A phone should have either a
                 // IMEI or MEID. The validation of IMEI will be checked by testGetImei().
diff --git a/tests/tests/telephony2/Android.mk b/tests/tests/telephony2/Android.mk
index ec72916..ee03500 100644
--- a/tests/tests/telephony2/Android.mk
+++ b/tests/tests/telephony2/Android.mk
@@ -24,8 +24,7 @@
 
 LOCAL_STATIC_JAVA_LIBRARIES := \
     ctstestrunner \
-    compatibility-device-util \
-    legacy-android-test
+    compatibility-device-util
 
 LOCAL_SRC_FILES := $(call all-java-files-under, src)
 
@@ -35,5 +34,6 @@
 LOCAL_COMPATIBILITY_SUITE := cts vts general-tests
 
 LOCAL_JAVA_LIBRARIES += android.test.runner
+LOCAL_JAVA_LIBRARIES += android.test.base
 
 include $(BUILD_CTS_PACKAGE)
diff --git a/tests/tests/telephony2/AndroidTest.xml b/tests/tests/telephony2/AndroidTest.xml
index 78d8d6f..27b9070 100644
--- a/tests/tests/telephony2/AndroidTest.xml
+++ b/tests/tests/telephony2/AndroidTest.xml
@@ -14,6 +14,7 @@
      limitations under the License.
 -->
 <configuration description="Config for CTS Telephony test cases">
+    <option name="test-suite-tag" value="cts" />
     <option name="config-descriptor:metadata" key="component" value="telecom" />
     <option name="not-shardable" value="true" />
     <target_preparer class="com.android.tradefed.targetprep.suite.SuiteApkInstaller">
diff --git a/tests/tests/text/Android.mk b/tests/tests/text/Android.mk
index 15a7bcc..7800ee9 100644
--- a/tests/tests/text/Android.mk
+++ b/tests/tests/text/Android.mk
@@ -21,7 +21,7 @@
 # and when built explicitly put it in the data partition
 LOCAL_MODULE_PATH := $(TARGET_OUT_DATA_APPS)
 
-LOCAL_JAVA_LIBRARIES := android.test.runner
+LOCAL_JAVA_LIBRARIES := android.test.runner android.test.base.stubs
 
 LOCAL_STATIC_JAVA_LIBRARIES += \
     compatibility-device-util \
diff --git a/tests/tests/text/AndroidManifest.xml b/tests/tests/text/AndroidManifest.xml
index 40a0b50..6a998a8 100644
--- a/tests/tests/text/AndroidManifest.xml
+++ b/tests/tests/text/AndroidManifest.xml
@@ -19,6 +19,7 @@
     package="android.text.cts">
 
     <uses-permission android:name="android.permission.DISABLE_KEYGUARD" />
+    <uses-permission android:name="android.permission.WRITE_SETTINGS" />
 
     <application android:maxRecents="1">
         <uses-library android:name="android.test.runner" />
diff --git a/tests/tests/text/AndroidTest.xml b/tests/tests/text/AndroidTest.xml
index 2b8c733..296fa08 100644
--- a/tests/tests/text/AndroidTest.xml
+++ b/tests/tests/text/AndroidTest.xml
@@ -14,6 +14,7 @@
      limitations under the License.
 -->
 <configuration description="Config for CTS Text test cases">
+    <option name="test-suite-tag" value="cts" />
     <option name="config-descriptor:metadata" key="component" value="uitoolkit" />
     <target_preparer class="com.android.tradefed.targetprep.suite.SuiteApkInstaller">
         <option name="cleanup-apks" value="true" />
diff --git a/tests/tests/text/OWNERS b/tests/tests/text/OWNERS
new file mode 100644
index 0000000..0f85e1f
--- /dev/null
+++ b/tests/tests/text/OWNERS
@@ -0,0 +1,4 @@
+siyamed@google.com
+nona@google.com
+clarabayarri@google.com
+toki@google.com
diff --git a/tests/tests/text/assets/ellipsis_test_font.ttf b/tests/tests/text/assets/ellipsis_test_font.ttf
new file mode 100644
index 0000000..ad0d79b
--- /dev/null
+++ b/tests/tests/text/assets/ellipsis_test_font.ttf
Binary files differ
diff --git a/tests/tests/text/assets/ellipsis_test_font.ttx b/tests/tests/text/assets/ellipsis_test_font.ttx
new file mode 100644
index 0000000..0c74b47
--- /dev/null
+++ b/tests/tests/text/assets/ellipsis_test_font.ttx
@@ -0,0 +1,238 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- Copyright (C) 2017 The Android Open Source Project
+
+     Licensed under the Apache License, Version 2.0 (the "License");
+     you may not use this file except in compliance with the License.
+     You may obtain a copy of the License at
+
+          http://www.apache.org/licenses/LICENSE-2.0
+
+     Unless required by applicable law or agreed to in writing, software
+     distributed under the License is distributed on an "AS IS" BASIS
+     WITHOUT 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="ellipsis"/>
+  </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="Fri Mar 17 07:26:00 2017"/>
+    <macStyle value="00000000 00000000"/>
+    <lowestRecPPEM value="7"/>
+    <fontDirectionHint value="2"/>
+    <glyphDataFormat value="0"/>
+  </head>
+
+  <hhea>
+    <tableVersion value="0x10000"/>
+    <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="0"/>
+    <mtx name="a" width="1000" lsb="0"/>
+    <mtx name="ellipsis" width="1000" lsb="0"/>
+  </hmtx>
+
+  <cmap>
+    <tableVersion version="0"/>
+    <cmap_format_4 platformID="3" platEncID="10" language="0">
+      <map code="0x0061" name="a" /> <!-- LATIN SMALL LETTER A -->
+      <map code="0x2026" name="ellipsis" /> <!-- HORIZONTAL ELLIPSIS -->
+    </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="0" yMax="0" />
+    <TTGlyph name="ellipsis" xMin="0" yMin="0" xMax="0" yMax="0" />
+  </glyf>
+
+  <name>
+    <namerecord nameID="0" platformID="3" platEncID="1" langID="0x409">
+      Copyright (C) 2017 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>
+
+  <GPOS>
+    <Version value="0x00010000"/>
+    <ScriptList>
+      <!-- ScriptCount=1 -->
+      <ScriptRecord index="0">
+        <ScriptTag value="DFLT"/>
+        <Script>
+          <DefaultLangSys>
+            <ReqFeatureIndex value="65535"/>
+            <!-- FeatureCount=1 -->
+            <FeatureIndex index="0" value="0"/>
+          </DefaultLangSys>
+          <!-- LangSysCount=0 -->
+        </Script>
+      </ScriptRecord>
+    </ScriptList>
+    <FeatureList>
+      <!-- FeatureCount=1 -->
+      <FeatureRecord index="0">
+        <FeatureTag value="kern"/>
+        <Feature>
+          <!-- LookupCount=1 -->
+          <LookupListIndex index="0" value="0"/>
+        </Feature>
+      </FeatureRecord>
+    </FeatureList>
+    <LookupList>
+      <!-- LookupCount=1 -->
+      <Lookup index="0">
+        <LookupType value="2"/>
+        <LookupFlag value="0"/>
+        <PairPos index="0" Format="1">
+          <Coverage Format="1">
+            <Glyph value="a"/>
+            <Glyph value="ellipsis"/>
+          </Coverage>
+          <ValueFormat1 value="4"/>
+          <ValueFormat2 value="0"/>
+          <PairSet index="0">
+            <PairValueRecord index="0">
+              <SecondGlyph value="ellipsis"/>
+              <Value1 XAdvance="500"/>
+            </PairValueRecord>
+          </PairSet>
+          <PairSet index="1">
+            <PairValueRecord index="0">
+              <SecondGlyph value="a"/>
+              <Value1 XAdvance="500"/>
+            </PairValueRecord>
+          </PairSet>
+        </PairPos>
+      </Lookup>
+    </LookupList>
+  </GPOS>
+</ttFont>
diff --git a/tests/tests/text/assets/fonts/StaticLayoutLineBreakingTestFont.ttf b/tests/tests/text/assets/fonts/StaticLayoutLineBreakingTestFont.ttf
new file mode 100644
index 0000000..1bad6fe
--- /dev/null
+++ b/tests/tests/text/assets/fonts/StaticLayoutLineBreakingTestFont.ttf
Binary files differ
diff --git a/tests/tests/text/assets/fonts/StaticLayoutLineBreakingTestFont.ttx b/tests/tests/text/assets/fonts/StaticLayoutLineBreakingTestFont.ttx
new file mode 100644
index 0000000..0cf0f79
--- /dev/null
+++ b/tests/tests/text/assets/fonts/StaticLayoutLineBreakingTestFont.ttx
@@ -0,0 +1,207 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- Copyright (C) 2017 The Android Open Source Project
+
+     Licensed under the Apache License, Version 2.0 (the "License");
+     you may not use this file except in compliance with the License.
+     You may obtain a copy of the License at
+
+          http://www.apache.org/licenses/LICENSE-2.0
+
+     Unless required by applicable law or agreed to in writing, software
+     distributed under the License is distributed on an "AS IS" BASIS
+     WITHOUT 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="0em"/>
+    <GlyphID id="2" name="1em"/>
+    <GlyphID id="3" name="3em"/>
+    <GlyphID id="4" name="5em"/>
+    <GlyphID id="5" name="7em"/>
+    <GlyphID id="6" name="10em"/>
+    <GlyphID id="7" name="50em"/>
+    <GlyphID id="8" name="100em"/>
+  </GlyphOrder>
+
+  <head>
+    <tableVersion value="1.0"/>
+    <fontRevision value="1.0"/>
+    <checkSumAdjustment value="0x640cdb2f"/>
+    <magicNumber value="0x5f0f3cf5"/>
+    <flags value="00000000 00000011"/>
+    <unitsPerEm value="100"/>
+    <created value="Fri Mar 17 07:26:00 2017"/>
+    <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="50" lsb="0"/>
+    <mtx name="0em" width="0" lsb="0"/>
+    <mtx name="1em" width="100" lsb="0"/>
+    <mtx name="3em" width="300" lsb="0"/>
+    <mtx name="5em" width="500" lsb="0"/>
+    <mtx name="7em" width="700" lsb="0"/>
+    <mtx name="10em" width="1000" lsb="0"/>
+    <mtx name="50em" width="5000" lsb="0"/>
+    <mtx name="100em" width="10000" lsb="0"/>
+  </hmtx>
+
+  <cmap>
+    <tableVersion version="0"/>
+    <cmap_format_12 format="12" reserved="0" length="6" nGroups="1" platformID="3" platEncID="10" language="0">
+      <map code="0x0020" name="10em" />
+      <map code="0x002e" name="10em" />  <!-- . -->
+      <map code="0x0043" name="100em" />  <!-- C -->
+      <map code="0x0049" name="1em" />  <!-- I -->
+      <map code="0x004c" name="50em" />  <!-- L -->
+      <map code="0x0056" name="5em" />  <!-- V -->
+      <map code="0x0058" name="10em" />  <!-- X -->
+      <map code="0x005f" name="0em" /> <!-- _ -->
+      <map code="0xfffd" name="7em" /> <!-- REPLACEMENT CHAR -->
+      <map code="0x10331" name="10em" />
+    </cmap_format_12>
+  </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="0em" 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" />
+    <TTGlyph name="5em" xMin="0" yMin="0" xMax="0" yMax="0" />
+    <TTGlyph name="7em" xMin="0" yMin="0" xMax="0" yMax="0" />
+    <TTGlyph name="10em" xMin="0" yMin="0" xMax="0" yMax="0" />
+    <TTGlyph name="50em" xMin="0" yMin="0" xMax="0" yMax="0" />
+    <TTGlyph name="100em" xMin="0" yMin="0" xMax="0" yMax="0" />
+  </glyf>
+
+  <name>
+    <namerecord nameID="1" platformID="1" platEncID="0" langID="0x0" unicode="True">
+      Font for StaticLayoutLineBreakingTest
+    </namerecord>
+    <namerecord nameID="2" platformID="1" platEncID="0" langID="0x0" unicode="True">
+      Regular
+    </namerecord>
+    <namerecord nameID="4" platformID="1" platEncID="0" langID="0x0" unicode="True">
+      Font for StaticLayoutLineBreakingTest
+    </namerecord>
+    <namerecord nameID="6" platformID="1" platEncID="0" langID="0x0" unicode="True">
+      SampleFont-Regular
+    </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>
+  </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/text/res/layout/keylistener_layout.xml b/tests/tests/text/res/layout/keylistener_layout.xml
index 84a7a14..e26e815 100644
--- a/tests/tests/text/res/layout/keylistener_layout.xml
+++ b/tests/tests/text/res/layout/keylistener_layout.xml
@@ -19,6 +19,7 @@
     android:id="@+id/keylistener_textview"
     android:layout_width="wrap_content"
     android:layout_height="wrap_content"
-    android:textSize="10dp"
-/>
+    android:textSize="10dp">
+    <requestFocus />
+</android.text.method.cts.EditTextNoIme>
 
diff --git a/tests/tests/text/src/android/text/cts/BoringLayoutTest.java b/tests/tests/text/src/android/text/cts/BoringLayoutTest.java
index e78fb3e..f2a7b5a 100644
--- a/tests/tests/text/src/android/text/cts/BoringLayoutTest.java
+++ b/tests/tests/text/src/android/text/cts/BoringLayoutTest.java
@@ -23,6 +23,7 @@
 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.mockito.Matchers.any;
 import static org.mockito.Matchers.anyFloat;
 import static org.mockito.Matchers.anyInt;
@@ -36,6 +37,8 @@
 import android.graphics.Bitmap.Config;
 import android.graphics.Canvas;
 import android.graphics.Paint;
+import android.graphics.Typeface;
+import android.support.test.InstrumentationRegistry;
 import android.support.test.filters.SmallTest;
 import android.support.test.runner.AndroidJUnit4;
 import android.text.BoringLayout;
@@ -345,6 +348,91 @@
                 anyInt(), anyInt(), anyFloat(), anyFloat(), any(Paint.class));
     }
 
+    @Test
+    public void testEllipsize_End() {
+        // When we try to ellipsize "aaaa" into a thinner 3.4 em space, we originally think "aa…"
+        // would fit, but after measuring the new text, we find that it doesn't and we need to
+        // retry.
+        final float size = 100.0f;
+        final int allocatedWidth = (int) (3.4f * size);
+        final BoringLayout layout = new BoringLayout(
+                "aaaa",
+                getTextPaintForEllipsize(size),
+                allocatedWidth,
+                DEFAULT_ALIGN,
+                SPACING_MULT_NO_SCALE,
+                SPACING_ADD_NO_SCALE,
+                createMetrics(0, 0, 0, 0, allocatedWidth, 0),
+                false /* includepad */,
+                TextUtils.TruncateAt.END,
+                allocatedWidth);
+        assertEquals("a\u2026\uFEFF\uFEFF", layout.getText().toString());
+    }
+
+    @Test
+    public void testEllipsize_Start() {
+        // When we try to ellipsize "aaaa" into a thinner 3.4 em space, we originally think "…aa"
+        // would fit, but after measuring the new text, we find that it doesn't and we need to
+        // retry.
+        final float size = 100.0f;
+        final int allocatedWidth = (int) (3.4f * size);
+        final BoringLayout layout = new BoringLayout(
+                "aaaa",
+                getTextPaintForEllipsize(size),
+                allocatedWidth,
+                DEFAULT_ALIGN,
+                SPACING_MULT_NO_SCALE,
+                SPACING_ADD_NO_SCALE,
+                createMetrics(0, 0, 0, 0, allocatedWidth, 0),
+                false /* includepad */,
+                TextUtils.TruncateAt.START,
+                allocatedWidth);
+        assertEquals("\u2026\uFEFF\uFEFFa", layout.getText().toString());
+    }
+
+    @Test
+    public void testEllipsize_Middle() {
+        // When we try to ellipsize "aaaaaa" into a thinner 5.9 em space, we originally think
+        // "aa…aa" would fit, but after measuring the new text, we find that it doesn't and we need
+        // to retry.
+        final float size = 100.0f;
+        final int allocatedWidth = (int) (5.9f * size);
+        final BoringLayout layout = new BoringLayout(
+                "aaaaaa",
+                getTextPaintForEllipsize(size),
+                allocatedWidth,
+                DEFAULT_ALIGN,
+                SPACING_MULT_NO_SCALE,
+                SPACING_ADD_NO_SCALE,
+                createMetrics(0, 0, 0, 0, allocatedWidth, 0),
+                false /* includepad */,
+                TextUtils.TruncateAt.MIDDLE,
+                allocatedWidth);
+        final String ellipsizedString = layout.getText().toString();
+        assertTrue("aa\u2026\uFEFF\uFEFFa".equals(ellipsizedString)
+                || "a\u2026\uFEFF\uFEFFaa".equals(ellipsizedString));
+    }
+
+    private TextPaint getTextPaintForEllipsize(float size) {
+        // The font used in this method has two glyphs defined for "a" and ellipsis. Both are one
+        // em wide. But the glyphs are kerned: whenever the "a" is followed or preceded by an
+        // ellipsis, half an em is added between them as kerning. This means that:
+        // "aaaa" is 4 ems wide,
+        // "aaa…" is 4.5 ems wide,
+        // "aa…" is 3.5 ems wide,
+        // "a…" is 2.5 ems wide,
+        // "aa…aa" is 6 ems wide,
+        // "aa…a" is 5 ems wide,
+        // "a…aa" is 5 ems wide,
+        // "a…a" is 4 ems wide,
+        // "…a" is 2.5 ems wide.
+        final TextPaint paint = new TextPaint();
+        paint.setTypeface(Typeface.createFromAsset(
+                InstrumentationRegistry.getTargetContext().getAssets(), "ellipsis_test_font.ttf"));
+        paint.setTextSize(size);
+        return paint;
+    }
+
     private static Metrics createMetrics(
             final int top,
             final int ascent,
diff --git a/tests/tests/text/src/android/text/cts/DynamicLayoutTest.java b/tests/tests/text/src/android/text/cts/DynamicLayoutTest.java
index 02139c5..ec36b0e 100644
--- a/tests/tests/text/src/android/text/cts/DynamicLayoutTest.java
+++ b/tests/tests/text/src/android/text/cts/DynamicLayoutTest.java
@@ -17,21 +17,25 @@
 package android.text.cts;
 
 import static android.text.Layout.Alignment.ALIGN_NORMAL;
+import static android.text.Spanned.SPAN_EXCLUSIVE_EXCLUSIVE;
 
 import static org.junit.Assert.assertEquals;
 import static org.junit.Assert.assertFalse;
 import static org.junit.Assert.assertNotNull;
 import static org.junit.Assert.assertTrue;
 import static org.junit.Assert.fail;
+import static org.mockito.Mockito.mock;
 
 import android.graphics.Paint.FontMetricsInt;
 import android.support.test.filters.SmallTest;
 import android.support.test.runner.AndroidJUnit4;
 import android.text.DynamicLayout;
 import android.text.Layout;
+import android.text.SpannableStringBuilder;
 import android.text.StaticLayout;
 import android.text.TextPaint;
 import android.text.TextUtils;
+import android.text.style.TypefaceSpan;
 
 import org.junit.Before;
 import org.junit.Test;
@@ -44,6 +48,7 @@
     private static final float SPACING_MULT_NO_SCALE = 1.0f;
     private static final float SPACING_ADD_NO_SCALE = 0.0f;
     private static final int DEFAULT_OUTER_WIDTH = 150;
+    private static final int ELLIPSIZE_WIDTH = 8;
     private static final CharSequence SINGLELINE_CHAR_SEQUENCE = "......";
     private static final String[] TEXT = {"CharSequence\n", "Char\tSequence\n", "CharSequence"};
     private static final CharSequence MULTLINE_CHAR_SEQUENCE = TEXT[0] + TEXT[1] + TEXT[2];
@@ -110,7 +115,7 @@
                 DEFAULT_ALIGN,
                 SPACING_MULT_NO_SCALE,
                 SPACING_ADD_NO_SCALE,
-                true,
+                true /* include pad */,
                 TextUtils.TruncateAt.START,
                 DEFAULT_OUTER_WIDTH);
         assertEquals(0, dynamicLayout.getEllipsisCount(LINE1));
@@ -118,6 +123,24 @@
         assertEquals(DEFAULT_OUTER_WIDTH, dynamicLayout.getEllipsizedWidth());
     }
 
+    // This could cause a crash in an older version of ellipsization code.
+    @Test
+    public void testEllipsisWithReflow() {
+        final String text = "Ham & Cheese.sandwich";
+        final int width = 1 << 20;
+        final int ellipsizedWidth = 2 * (int) mDefaultPaint.getTextSize();
+        final DynamicLayout dynamicLayout = new DynamicLayout(text,
+                text,
+                mDefaultPaint,
+                width,
+                DEFAULT_ALIGN,
+                SPACING_MULT_NO_SCALE,
+                SPACING_ADD_NO_SCALE,
+                true /* include pad */,
+                TextUtils.TruncateAt.END,
+                ellipsizedWidth);
+    }
+
     /*
      * Test whether include the padding to calculate the layout.
      * 1. Include padding while calculate the layout.
@@ -194,6 +217,62 @@
         assertEquals(TEXT[0].length() + TEXT[1].length(), mDynamicLayout.getLineStart(LINE2));
     }
 
+    @Test
+    public void testLineSpacing() {
+        SpannableStringBuilder text = new SpannableStringBuilder("a\nb\nc");
+        final float spacingMultiplier = 2f;
+        final float spacingAdd = 4;
+        final int width = 1000;
+        final TextPaint textPaint = new TextPaint();
+        // create the DynamicLayout
+        final DynamicLayout dynamicLayout = new DynamicLayout(text,
+                textPaint,
+                width,
+                ALIGN_NORMAL,
+                spacingMultiplier,
+                spacingAdd,
+                false /*includepad*/);
+
+        // create a StaticLayout with same text, this will define the expectations
+        Layout expected = createStaticLayout(text.toString(), textPaint, width, spacingAdd,
+                spacingMultiplier);
+        assertLineSpecs(expected, dynamicLayout);
+
+        // add a new line to the end, DynamicLayout will re-calculate
+        text = text.append("\nd");
+        expected = createStaticLayout(text.toString(), textPaint, width, spacingAdd,
+                spacingMultiplier);
+        assertLineSpecs(expected, dynamicLayout);
+
+        // insert a next line and a char as the new second line
+        text = text.insert(TextUtils.indexOf(text, '\n') + 1, "a1\n");
+        expected = createStaticLayout(text.toString(), textPaint, width, spacingAdd,
+                spacingMultiplier);
+        assertLineSpecs(expected, dynamicLayout);
+    }
+
+    @Test
+    public void testLineSpacing_textEndingWithNextLine() {
+        final SpannableStringBuilder text = new SpannableStringBuilder("a\n");
+        final float spacingMultiplier = 2f;
+        final float spacingAdd = 4f;
+        final int width = 1000;
+        final TextPaint textPaint = new TextPaint();
+        // create the DynamicLayout
+        final DynamicLayout dynamicLayout = new DynamicLayout(text,
+                textPaint,
+                width,
+                ALIGN_NORMAL,
+                spacingMultiplier,
+                spacingAdd,
+                false /*includepad*/);
+
+        // create a StaticLayout with same text, this will define the expectations
+        final Layout expected = createStaticLayout(text.toString(), textPaint, width, spacingAdd,
+                spacingMultiplier);
+        assertLineSpecs(expected, dynamicLayout);
+    }
+
     private Layout createStaticLayout(CharSequence text, TextPaint textPaint, int width,
             float spacingAdd, float spacingMultiplier) {
         return StaticLayout.Builder.obtain(text, 0,
@@ -253,4 +332,110 @@
                 spacingMultiplier);
         assertLineSpecs(expected, dynamicLayout);
     }
+
+    @Test
+    public void testBuilder_obtain() {
+        final DynamicLayout.Builder builder = DynamicLayout.Builder.obtain(MULTLINE_CHAR_SEQUENCE,
+                mDefaultPaint, DEFAULT_OUTER_WIDTH);
+        final DynamicLayout layout = builder.build();
+        // Check values passed to obtain().
+        assertEquals(MULTLINE_CHAR_SEQUENCE, layout.getText());
+        assertEquals(mDefaultPaint, layout.getPaint());
+        assertEquals(DEFAULT_OUTER_WIDTH, layout.getWidth());
+        // Check default values.
+        assertEquals(Layout.Alignment.ALIGN_NORMAL, layout.getAlignment());
+        assertEquals(0.0f, layout.getSpacingAdd(), 0.0f);
+        assertEquals(1.0f, layout.getSpacingMultiplier(), 0.0f);
+        assertEquals(DEFAULT_OUTER_WIDTH, layout.getEllipsizedWidth());
+    }
+
+    @Test(expected = NullPointerException.class)
+    public void testBuilder_obtainWithNullText() {
+        final DynamicLayout.Builder builder = DynamicLayout.Builder.obtain(null, mDefaultPaint, 0);
+        final DynamicLayout layout = builder.build();
+    }
+
+    @Test(expected = NullPointerException.class)
+    public void testBuilder_obtainWithNullPaint() {
+        final DynamicLayout.Builder builder = DynamicLayout.Builder.obtain(MULTLINE_CHAR_SEQUENCE,
+                null, 0);
+        final DynamicLayout layout = builder.build();
+    }
+
+    @Test
+    public void testBuilder_setDisplayTest() {
+        final DynamicLayout.Builder builder = DynamicLayout.Builder.obtain(MULTLINE_CHAR_SEQUENCE,
+                mDefaultPaint, DEFAULT_OUTER_WIDTH);
+        builder.setDisplayText(SINGLELINE_CHAR_SEQUENCE);
+        final DynamicLayout layout = builder.build();
+        assertEquals(SINGLELINE_CHAR_SEQUENCE, layout.getText());
+    }
+
+    @Test
+    public void testBuilder_setAlignment() {
+        final DynamicLayout.Builder builder = DynamicLayout.Builder.obtain(MULTLINE_CHAR_SEQUENCE,
+                mDefaultPaint, DEFAULT_OUTER_WIDTH);
+        builder.setAlignment(DEFAULT_ALIGN);
+        final DynamicLayout layout = builder.build();
+        assertEquals(DEFAULT_ALIGN, layout.getAlignment());
+    }
+
+    @Test
+    public void testBuilder_setLineSpacing() {
+        final DynamicLayout.Builder builder = DynamicLayout.Builder.obtain(MULTLINE_CHAR_SEQUENCE,
+                mDefaultPaint, DEFAULT_OUTER_WIDTH);
+        builder.setLineSpacing(1.0f, 2.0f);
+        final DynamicLayout layout = builder.build();
+        assertEquals(1.0f, layout.getSpacingAdd(), 0.0f);
+        assertEquals(2.0f, layout.getSpacingMultiplier(), 0.0f);
+    }
+
+    @Test
+    public void testBuilder_ellipsization() {
+        final DynamicLayout.Builder builder = DynamicLayout.Builder.obtain(MULTLINE_CHAR_SEQUENCE,
+                mDefaultPaint, DEFAULT_OUTER_WIDTH);
+        builder.setEllipsize(TextUtils.TruncateAt.END)
+                .setEllipsizedWidth(ELLIPSIZE_WIDTH);
+        final DynamicLayout layout = builder.build();
+        assertEquals(ELLIPSIZE_WIDTH, layout.getEllipsizedWidth());
+        assertEquals(DEFAULT_OUTER_WIDTH, layout.getWidth());
+        for (int i = 0; i < TEXT.length; i++) {
+            if (i == TEXT.length - 1) { // last line
+                assertTrue(layout.getEllipsisCount(i) > 0);
+            } else {
+                assertEquals(0, layout.getEllipsisCount(i));
+            }
+        }
+    }
+
+    @Test
+    public void testBuilder_otherSetters() {
+        // Setter methods that cannot be directly tested.
+        // setBreakStrategy, setHyphenationFrequency, setIncludePad, and setJustificationMode.
+        final DynamicLayout.Builder builder = DynamicLayout.Builder.obtain(MULTLINE_CHAR_SEQUENCE,
+                mDefaultPaint, DEFAULT_OUTER_WIDTH);
+        builder.setBreakStrategy(Layout.BREAK_STRATEGY_HIGH_QUALITY)
+                .setHyphenationFrequency(Layout.HYPHENATION_FREQUENCY_FULL)
+                .setIncludePad(true)
+                .setJustificationMode(Layout.JUSTIFICATION_MODE_INTER_WORD);
+        final DynamicLayout layout = builder.build();
+        assertNotNull(layout);
+    }
+
+    @Test
+    public void testReflow_afterSpanChangedShouldNotThrowException() {
+        final SpannableStringBuilder builder = new SpannableStringBuilder("crash crash crash!!");
+
+        final TypefaceSpan span = mock(TypefaceSpan.class);
+        builder.setSpan(span, 1, 4, SPAN_EXCLUSIVE_EXCLUSIVE);
+
+        final DynamicLayout layout = DynamicLayout.Builder.obtain(builder,
+                new TextPaint(), Integer.MAX_VALUE).build();
+        try {
+            builder.insert(1, "Hello there\n\n");
+        } catch (Throwable e) {
+            throw new RuntimeException("Inserting text into DynamicLayout should not crash", e);
+        }
+    }
+
 }
diff --git a/tests/tests/text/src/android/text/cts/PremeasuredTextTest.java b/tests/tests/text/src/android/text/cts/PremeasuredTextTest.java
new file mode 100644
index 0000000..3d3ecd0
--- /dev/null
+++ b/tests/tests/text/src/android/text/cts/PremeasuredTextTest.java
@@ -0,0 +1,276 @@
+/*
+ * 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.text.cts;
+
+import static org.junit.Assert.assertEquals;
+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.support.test.filters.SmallTest;
+import android.support.test.runner.AndroidJUnit4;
+import android.text.PremeasuredText;
+import android.text.SpannableStringBuilder;
+import android.text.Spanned;
+import android.text.TextDirectionHeuristic;
+import android.text.TextDirectionHeuristics;
+import android.text.TextPaint;
+import android.text.style.LocaleSpan;
+
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+import java.util.Locale;
+
+@SmallTest
+@RunWith(AndroidJUnit4.class)
+public class PremeasuredTextTest {
+
+    private static final CharSequence NULL_CHAR_SEQUENCE = null;
+    private static final String STRING = "Hello, World!";
+    private static final String MULTIPARA_STRING = "Hello,\nWorld!";
+
+    private static final int SPAN_START = 3;
+    private static final int SPAN_END = 7;
+    private static final LocaleSpan SPAN = new LocaleSpan(Locale.US);
+    private static final Spanned SPANNED;
+    static {
+        final SpannableStringBuilder ssb = new SpannableStringBuilder(STRING);
+        ssb.setSpan(SPAN, SPAN_START, SPAN_END, Spanned.SPAN_INCLUSIVE_EXCLUSIVE);
+        SPANNED = ssb;
+    }
+
+    private static final TextPaint PAINT = new TextPaint();
+
+    private static final TextDirectionHeuristic LTR = TextDirectionHeuristics.LTR;
+
+    @Test
+    public void testBuild() {
+        assertNotNull(PremeasuredText.build(STRING, PAINT, LTR));
+        assertNotNull(PremeasuredText.build(STRING, PAINT, LTR, 0, STRING.length()));
+        assertNotNull(PremeasuredText.build(STRING, PAINT, LTR, 1, STRING.length() - 1));
+
+        assertNotNull(PremeasuredText.build(SPANNED, PAINT, LTR));
+        assertNotNull(PremeasuredText.build(SPANNED, PAINT, LTR, 0, STRING.length()));
+        assertNotNull(PremeasuredText.build(SPANNED, PAINT, LTR, 1, STRING.length() - 1));
+    }
+
+    @Test
+    public void testBuild_withNull() {
+        try {
+            PremeasuredText.build(NULL_CHAR_SEQUENCE, PAINT, LTR);
+            fail();
+        } catch (NullPointerException e) {
+            // pass
+        }
+        try {
+            PremeasuredText.build(NULL_CHAR_SEQUENCE, PAINT, LTR, 0, 0);
+            fail();
+        } catch (NullPointerException e) {
+            // pass
+        }
+
+        try {
+            PremeasuredText.build(STRING, null, LTR);
+            fail();
+        } catch (NullPointerException e) {
+            // pass
+        }
+        try {
+            PremeasuredText.build(STRING, null, LTR, 0, 1);
+            fail();
+        } catch (NullPointerException e) {
+            // pass
+        }
+
+        try {
+            PremeasuredText.build(STRING, PAINT, null);
+            fail();
+        } catch (NullPointerException e) {
+            // pass
+        }
+        try {
+            PremeasuredText.build(STRING, PAINT, null, 0, 1);
+            fail();
+        } catch (NullPointerException e) {
+            // pass
+        }
+    }
+
+    @Test
+    public void testBuild_withInvalidRange() {
+        try {
+            PremeasuredText.build(STRING, PAINT, LTR, -1, -1);
+            fail();
+        } catch (IllegalArgumentException e) {
+            // pass
+        }
+        try {
+            PremeasuredText.build(STRING, PAINT, LTR, 100000, 100000);
+            fail();
+        } catch (IllegalArgumentException e) {
+            // pass
+        }
+    }
+
+    @Test
+    public void testCharSequenceInteferface() {
+        final CharSequence s = PremeasuredText.build(STRING, PAINT, LTR);
+        assertEquals(STRING.length(), s.length());
+        assertEquals('H', s.charAt(0));
+        assertEquals('e', s.charAt(1));
+        assertEquals('l', s.charAt(2));
+        assertEquals('l', s.charAt(3));
+        assertEquals('o', s.charAt(4));
+        assertEquals(',', s.charAt(5));
+        assertEquals("Hello, World!", s.toString());
+
+        // Even measure the part of the text, the CharSequence interface still works for original
+        // text.
+        // TODO: Should this work like substring?
+        final CharSequence s2 = PremeasuredText.build(STRING, PAINT, LTR, 7, STRING.length());
+        assertEquals(STRING.length(), s2.length());
+        assertEquals('H', s2.charAt(0));
+        assertEquals('e', s2.charAt(1));
+        assertEquals('l', s2.charAt(2));
+        assertEquals('l', s2.charAt(3));
+        assertEquals('o', s2.charAt(4));
+        assertEquals(',', s2.charAt(5));
+        assertEquals("Hello, World!", s2.toString());
+
+        final CharSequence s3 = s.subSequence(0, 3);
+        assertEquals(3, s3.length());
+        assertEquals('H', s3.charAt(0));
+        assertEquals('e', s3.charAt(1));
+        assertEquals('l', s3.charAt(2));
+
+    }
+
+    @Test
+    public void testSpannedInterface_Spanned() {
+        final Spanned s = PremeasuredText.build(SPANNED, PAINT, LTR);
+        final LocaleSpan[] spans = s.getSpans(0, s.length(), LocaleSpan.class);
+        assertNotNull(spans);
+        assertEquals(1, spans.length);
+        assertEquals(SPAN, spans[0]);
+
+        assertEquals(SPAN_START, s.getSpanStart(SPAN));
+        assertEquals(SPAN_END, s.getSpanEnd(SPAN));
+        assertTrue((s.getSpanFlags(SPAN) & Spanned.SPAN_INCLUSIVE_EXCLUSIVE) != 0);
+
+        assertEquals(SPAN_START, s.nextSpanTransition(0, s.length(), LocaleSpan.class));
+        assertEquals(SPAN_END, s.nextSpanTransition(SPAN_START, s.length(), LocaleSpan.class));
+
+        final Spanned s2 = PremeasuredText.build(SPANNED, PAINT, LTR, 7, SPANNED.length());
+        final LocaleSpan[] spans2 = s2.getSpans(0, s2.length(), LocaleSpan.class);
+        assertNotNull(spans2);
+        assertEquals(1, spans2.length);
+        assertEquals(SPAN, spans2[0]);
+
+        assertEquals(SPAN_START, s2.getSpanStart(SPAN));
+        assertEquals(SPAN_END, s2.getSpanEnd(SPAN));
+        assertTrue((s2.getSpanFlags(SPAN) & Spanned.SPAN_INCLUSIVE_EXCLUSIVE) != 0);
+
+        assertEquals(SPAN_START, s2.nextSpanTransition(0, s2.length(), LocaleSpan.class));
+        assertEquals(SPAN_END, s2.nextSpanTransition(SPAN_START, s2.length(), LocaleSpan.class));
+    }
+
+    @Test
+    public void testSpannedInterface_String() {
+        final Spanned s = PremeasuredText.build(STRING, PAINT, LTR);
+        LocaleSpan[] spans = s.getSpans(0, s.length(), LocaleSpan.class);
+        assertNotNull(spans);
+        assertEquals(0, spans.length);
+
+        assertEquals(-1, s.getSpanStart(SPAN));
+        assertEquals(-1, s.getSpanEnd(SPAN));
+        assertEquals(0, s.getSpanFlags(SPAN));
+
+        assertEquals(s.length(), s.nextSpanTransition(0, s.length(), LocaleSpan.class));
+    }
+
+    @Test
+    public void testGetText() {
+        assertSame(STRING, PremeasuredText.build(STRING, PAINT, LTR).getText());
+        assertSame(SPANNED, PremeasuredText.build(SPANNED, PAINT, LTR).getText());
+
+        assertSame(STRING, PremeasuredText.build(STRING, PAINT, LTR, 1, 5).getText());
+        assertSame(SPANNED, PremeasuredText.build(SPANNED, PAINT, LTR, 1, 5).getText());
+    }
+
+    @Test
+    public void testGetStartEnd() {
+        assertEquals(0, PremeasuredText.build(STRING, PAINT, LTR).getStart());
+        assertEquals(STRING.length(), PremeasuredText.build(STRING, PAINT, LTR).getEnd());
+
+        assertEquals(1, PremeasuredText.build(STRING, PAINT, LTR, 1, 5).getStart());
+        assertEquals(5, PremeasuredText.build(STRING, PAINT, LTR, 1, 5).getEnd());
+
+        assertEquals(0, PremeasuredText.build(SPANNED, PAINT, LTR).getStart());
+        assertEquals(SPANNED.length(), PremeasuredText.build(SPANNED, PAINT, LTR).getEnd());
+
+        assertEquals(1, PremeasuredText.build(SPANNED, PAINT, LTR, 1, 5).getStart());
+        assertEquals(5, PremeasuredText.build(SPANNED, PAINT, LTR, 1, 5).getEnd());
+    }
+
+    @Test
+    public void testGetTextDir() {
+        assertSame(LTR, PremeasuredText.build(STRING, PAINT, LTR).getTextDir());
+        assertSame(LTR, PremeasuredText.build(SPANNED, PAINT, LTR).getTextDir());
+    }
+
+    @Test
+    public void testGetPaint() {
+        // No Paint equality functions. Check only not null.
+        assertNotNull(PremeasuredText.build(STRING, PAINT, LTR).getPaint());
+        assertNotNull(PremeasuredText.build(SPANNED, PAINT, LTR).getPaint());
+    }
+
+    @Test
+    public void testGetParagraphCount() {
+        final PremeasuredText pm = PremeasuredText.build(STRING, PAINT, LTR);
+        assertEquals(1, pm.getParagraphCount());
+        assertEquals(0, pm.getParagraphStart(0));
+        assertEquals(STRING.length(), pm.getParagraphEnd(0));
+
+        final PremeasuredText pm2 = PremeasuredText.build(STRING, PAINT, LTR, 1, 9);
+        assertEquals(1, pm2.getParagraphCount());
+        assertEquals(1, pm2.getParagraphStart(0));
+        assertEquals(9, pm2.getParagraphEnd(0));
+
+        final PremeasuredText pm3 = PremeasuredText.build(MULTIPARA_STRING, PAINT, LTR);
+        assertEquals(2, pm3.getParagraphCount());
+        assertEquals(0, pm3.getParagraphStart(0));
+        assertEquals(7, pm3.getParagraphEnd(0));
+        assertEquals(7, pm3.getParagraphStart(1));
+        assertEquals(pm3.length(), pm3.getParagraphEnd(1));
+
+        final PremeasuredText pm4 = PremeasuredText.build(MULTIPARA_STRING, PAINT, LTR, 1, 5);
+        assertEquals(1, pm4.getParagraphCount());
+        assertEquals(1, pm4.getParagraphStart(0));
+        assertEquals(5, pm4.getParagraphEnd(0));
+
+        final PremeasuredText pm5 = PremeasuredText.build(MULTIPARA_STRING, PAINT, LTR, 1, 9);
+        assertEquals(2, pm5.getParagraphCount());
+        assertEquals(1, pm5.getParagraphStart(0));
+        assertEquals(7, pm5.getParagraphEnd(0));
+        assertEquals(7, pm5.getParagraphStart(1));
+        assertEquals(9, pm5.getParagraphEnd(1));
+    }
+
+}
diff --git a/tests/tests/text/src/android/text/cts/SelectionTest.java b/tests/tests/text/src/android/text/cts/SelectionTest.java
index f920bda..2aac83b 100644
--- a/tests/tests/text/src/android/text/cts/SelectionTest.java
+++ b/tests/tests/text/src/android/text/cts/SelectionTest.java
@@ -23,6 +23,7 @@
 
 import android.support.test.filters.SmallTest;
 import android.support.test.runner.AndroidJUnit4;
+import android.text.Layout;
 import android.text.Selection;
 import android.text.SpannableStringBuilder;
 import android.text.StaticLayout;
@@ -275,6 +276,56 @@
     }
 
     @Test
+    public void testMoveUpAfterTyping() {
+        CharSequence text = "aaa\nmm";
+        SpannableStringBuilder builder = new SpannableStringBuilder(text);
+        StaticLayout layout = new StaticLayout(builder, new TextPaint(), 200,
+                Layout.Alignment.ALIGN_NORMAL, 0, 0, false);
+        assertEquals(-1, Selection.getSelectionStart(builder));
+        assertEquals(-1, Selection.getSelectionEnd(builder));
+
+        Selection.setSelection(builder, 1, 1);
+        assertTrue(Selection.moveDown(builder, layout));
+        assertEquals(5, Selection.getSelectionStart(builder));
+        assertEquals(5, Selection.getSelectionEnd(builder));
+
+        builder.insert(5, "mm");
+        layout = new StaticLayout(builder, new TextPaint(), 200, Layout.Alignment.ALIGN_NORMAL,
+                0, 0, false);
+        assertEquals(7, Selection.getSelectionStart(builder));
+        assertEquals(7, Selection.getSelectionEnd(builder));
+
+        assertTrue(Selection.moveUp(builder, layout));
+        assertEquals(3, Selection.getSelectionStart(builder));
+        assertEquals(3, Selection.getSelectionEnd(builder));
+    }
+
+    @Test
+    public void testMoveUpKeepsOriginalMemoryPosition() {
+        CharSequence text = "aa\nm";
+        SpannableStringBuilder builder = new SpannableStringBuilder(text);
+        StaticLayout layout = new StaticLayout(builder, new TextPaint(), 200,
+                Layout.Alignment.ALIGN_NORMAL, 0, 0, false);
+        assertEquals(-1, Selection.getSelectionStart(builder));
+        assertEquals(-1, Selection.getSelectionEnd(builder));
+        assertEquals(0,
+                builder.getSpans(0, builder.length(), Selection.MemoryTextWatcher.class).length);
+
+        Selection.setSelection(builder, 1, 1);
+        assertTrue(Selection.moveDown(builder, layout));
+        assertEquals(4, Selection.getSelectionStart(builder));
+        assertEquals(4, Selection.getSelectionEnd(builder));
+        assertEquals(1,
+                builder.getSpans(0, builder.length(), Selection.MemoryTextWatcher.class).length);
+
+        assertTrue(Selection.moveUp(builder, layout));
+        assertEquals(1, Selection.getSelectionStart(builder));
+        assertEquals(1, Selection.getSelectionEnd(builder));
+        assertEquals(1,
+                builder.getSpans(0, builder.length(), Selection.MemoryTextWatcher.class).length);
+    }
+
+    @Test
     public void testMoveDown() {
         CharSequence text = "hello,world\nGoogle";
         SpannableStringBuilder builder = new SpannableStringBuilder(text);
@@ -313,6 +364,340 @@
     }
 
     @Test
+    public void testMoveDownAfterTyping() {
+        CharSequence text = "mm\naaa";
+        SpannableStringBuilder builder = new SpannableStringBuilder(text);
+        StaticLayout layout = new StaticLayout(builder, new TextPaint(), 200,
+                Layout.Alignment.ALIGN_NORMAL, 0, 0, false);
+        assertEquals(-1, Selection.getSelectionStart(builder));
+        assertEquals(-1, Selection.getSelectionEnd(builder));
+
+        Selection.setSelection(builder, 4, 4);
+        assertTrue(Selection.moveUp(builder, layout));
+        assertEquals(1, Selection.getSelectionStart(builder));
+        assertEquals(1, Selection.getSelectionEnd(builder));
+
+        builder.insert(1, "mm");
+        layout = new StaticLayout(builder, new TextPaint(), 200, Layout.Alignment.ALIGN_NORMAL,
+                0, 0, false);
+        assertEquals(3, Selection.getSelectionStart(builder));
+        assertEquals(3, Selection.getSelectionEnd(builder));
+
+        assertTrue(Selection.moveDown(builder, layout));
+        assertEquals(8, Selection.getSelectionStart(builder));
+        assertEquals(8, Selection.getSelectionEnd(builder));
+    }
+
+    @Test
+    public void testMoveDownKeepsOriginalMemoryPosition() {
+        CharSequence text = "m\naa";
+        SpannableStringBuilder builder = new SpannableStringBuilder(text);
+        StaticLayout layout = new StaticLayout(builder, new TextPaint(), 200,
+                Layout.Alignment.ALIGN_NORMAL, 0, 0, false);
+        assertEquals(-1, Selection.getSelectionStart(builder));
+        assertEquals(-1, Selection.getSelectionEnd(builder));
+        assertEquals(0,
+                builder.getSpans(0, builder.length(), Selection.MemoryTextWatcher.class).length);
+
+        Selection.setSelection(builder, 3, 3);
+        assertTrue(Selection.moveUp(builder, layout));
+        assertEquals(1, Selection.getSelectionStart(builder));
+        assertEquals(1, Selection.getSelectionEnd(builder));
+        assertEquals(1,
+                builder.getSpans(0, builder.length(), Selection.MemoryTextWatcher.class).length);
+
+        assertTrue(Selection.moveDown(builder, layout));
+        assertEquals(3, Selection.getSelectionStart(builder));
+        assertEquals(3, Selection.getSelectionEnd(builder));
+        assertEquals(1,
+                builder.getSpans(0, builder.length(), Selection.MemoryTextWatcher.class).length);
+    }
+
+    @Test
+    public void testMemoryPositionResetByHorizontalMovement() {
+        CharSequence text = "m\naa";
+        SpannableStringBuilder builder = new SpannableStringBuilder(text);
+        StaticLayout layout = new StaticLayout(builder, new TextPaint(), 200,
+                Layout.Alignment.ALIGN_NORMAL, 0, 0, false);
+        assertEquals(-1, Selection.getSelectionStart(builder));
+        assertEquals(-1, Selection.getSelectionEnd(builder));
+        assertEquals(0,
+                builder.getSpans(0, builder.length(), Selection.MemoryTextWatcher.class).length);
+
+        Selection.setSelection(builder, 3, 3);
+        assertTrue(Selection.moveUp(builder, layout));
+        assertEquals(1, Selection.getSelectionStart(builder));
+        assertEquals(1, Selection.getSelectionEnd(builder));
+        assertEquals(1,
+                builder.getSpans(0, builder.length(), Selection.MemoryTextWatcher.class).length);
+
+        assertTrue(Selection.moveDown(builder, layout));
+        assertEquals(3, Selection.getSelectionStart(builder));
+        assertEquals(3, Selection.getSelectionEnd(builder));
+        assertEquals(1,
+                builder.getSpans(0, builder.length(), Selection.MemoryTextWatcher.class).length);
+
+        assertTrue(Selection.moveRight(builder, layout));
+        assertEquals(4, Selection.getSelectionStart(builder));
+        assertEquals(4, Selection.getSelectionEnd(builder));
+        assertEquals(0,
+                builder.getSpans(0, builder.length(), Selection.MemoryTextWatcher.class).length);
+
+        assertTrue(Selection.moveUp(builder, layout));
+        assertEquals(1, Selection.getSelectionStart(builder));
+        assertEquals(1, Selection.getSelectionEnd(builder));
+        assertEquals(1,
+                builder.getSpans(0, builder.length(), Selection.MemoryTextWatcher.class).length);
+
+        assertTrue(Selection.moveDown(builder, layout));
+        assertEquals(4, Selection.getSelectionStart(builder));
+        assertEquals(4, Selection.getSelectionEnd(builder));
+        assertEquals(1,
+                builder.getSpans(0, builder.length(), Selection.MemoryTextWatcher.class).length);
+
+        assertTrue(Selection.moveLeft(builder, layout));
+        assertEquals(3, Selection.getSelectionStart(builder));
+        assertEquals(3, Selection.getSelectionEnd(builder));
+        assertEquals(0,
+                builder.getSpans(0, builder.length(), Selection.MemoryTextWatcher.class).length);
+
+        assertTrue(Selection.moveUp(builder, layout));
+        assertEquals(1, Selection.getSelectionStart(builder));
+        assertEquals(1, Selection.getSelectionEnd(builder));
+        assertEquals(1,
+                builder.getSpans(0, builder.length(), Selection.MemoryTextWatcher.class).length);
+
+        Selection.setSelection(builder, 3, 3);
+        assertEquals(0,
+                builder.getSpans(0, builder.length(), Selection.MemoryTextWatcher.class).length);
+    }
+
+    @Test
+    public void testMemoryPositionResetByRemoveSelection() {
+        CharSequence text = "m\naa";
+        SpannableStringBuilder builder = new SpannableStringBuilder(text);
+        StaticLayout layout = new StaticLayout(builder, new TextPaint(), 200,
+                Layout.Alignment.ALIGN_NORMAL, 0, 0, false);
+        assertEquals(-1, Selection.getSelectionStart(builder));
+        assertEquals(-1, Selection.getSelectionEnd(builder));
+        assertEquals(0,
+                builder.getSpans(0, builder.length(), Selection.MemoryTextWatcher.class).length);
+
+        Selection.setSelection(builder, 3, 3);
+        assertTrue(Selection.moveUp(builder, layout));
+        assertEquals(1, Selection.getSelectionStart(builder));
+        assertEquals(1, Selection.getSelectionEnd(builder));
+        assertEquals(1,
+                builder.getSpans(0, builder.length(), Selection.MemoryTextWatcher.class).length);
+
+        Selection.removeSelection(builder);
+        assertEquals(-1, Selection.getSelectionStart(builder));
+        assertEquals(-1, Selection.getSelectionEnd(builder));
+        assertEquals(0,
+                builder.getSpans(0, builder.length(), Selection.MemoryTextWatcher.class).length);
+    }
+
+    @Test
+    public void testMultilineLengthMoveDown() {
+        CharSequence text = "a\n\na";
+        SpannableStringBuilder builder = new SpannableStringBuilder(text);
+        StaticLayout layout = new StaticLayout(text, new TextPaint(), 200, null, 0, 0, false);
+        assertEquals(-1, Selection.getSelectionStart(builder));
+        assertEquals(-1, Selection.getSelectionEnd(builder));
+
+        Selection.setSelection(builder, 1);
+        // Move down to empty line
+        assertTrue(Selection.moveDown(builder, layout));
+        assertEquals(2, Selection.getSelectionStart(builder));
+        assertEquals(2, Selection.getSelectionEnd(builder));
+
+        // Move down to third line
+        assertTrue(Selection.moveDown(builder, layout));
+        assertEquals(4, Selection.getSelectionStart(builder));
+        assertEquals(4, Selection.getSelectionEnd(builder));
+    }
+
+    @Test
+    public void testMultilineLengthExtendDown() {
+        CharSequence text = "Google\n\nhello, world";
+        SpannableStringBuilder builder = new SpannableStringBuilder(text);
+        StaticLayout layout = new StaticLayout(text, new TextPaint(), 200, null, 0, 0, false);
+        assertEquals(-1, Selection.getSelectionStart(builder));
+        assertEquals(-1, Selection.getSelectionEnd(builder));
+
+        Selection.setSelection(builder, 1, 3);
+        assertTrue(Selection.extendDown(builder, layout));
+        assertEquals(1, Selection.getSelectionStart(builder));
+        assertEquals(7, Selection.getSelectionEnd(builder));
+
+        assertTrue(Selection.extendDown(builder, layout));
+        assertEquals(1, Selection.getSelectionStart(builder));
+        assertEquals(15, Selection.getSelectionEnd(builder));
+
+        assertTrue(Selection.extendDown(builder, layout));
+        assertEquals(1, Selection.getSelectionStart(builder));
+        assertEquals(text.length(), Selection.getSelectionEnd(builder));
+    }
+
+    @Test
+    public void testExtendDownKeepsOriginalMemoryPosition() {
+        CharSequence text = "m\naa";
+        SpannableStringBuilder builder = new SpannableStringBuilder(text);
+        StaticLayout layout = new StaticLayout(builder, new TextPaint(), 200,
+                Layout.Alignment.ALIGN_NORMAL, 0, 0, false);
+        assertEquals(-1, Selection.getSelectionStart(builder));
+        assertEquals(-1, Selection.getSelectionEnd(builder));
+        assertEquals(0,
+                builder.getSpans(0, builder.length(), Selection.MemoryTextWatcher.class).length);
+
+        Selection.setSelection(builder, 3, 3);
+        assertTrue(Selection.extendUp(builder, layout));
+        assertEquals(3, Selection.getSelectionStart(builder));
+        assertEquals(1, Selection.getSelectionEnd(builder));
+        assertEquals(1,
+                builder.getSpans(0, builder.length(), Selection.MemoryTextWatcher.class).length);
+
+        assertTrue(Selection.extendDown(builder, layout));
+        assertEquals(3, Selection.getSelectionStart(builder));
+        assertEquals(3, Selection.getSelectionEnd(builder));
+        assertEquals(1,
+                builder.getSpans(0, builder.length(), Selection.MemoryTextWatcher.class).length);
+    }
+
+    @Test
+    public void testMultilineLengthMoveUp() {
+        CharSequence text = "a\n\na";
+        SpannableStringBuilder builder = new SpannableStringBuilder(text);
+        StaticLayout layout = new StaticLayout(text, new TextPaint(), 200, null, 0, 0, false);
+        assertEquals(-1, Selection.getSelectionStart(builder));
+        assertEquals(-1, Selection.getSelectionEnd(builder));
+
+        Selection.setSelection(builder, 4);
+        // Move up to empty line
+        assertTrue(Selection.moveUp(builder, layout));
+        assertEquals(2, Selection.getSelectionStart(builder));
+        assertEquals(2, Selection.getSelectionEnd(builder));
+
+        // Move up to first line
+        assertTrue(Selection.moveUp(builder, layout));
+        assertEquals(1, Selection.getSelectionStart(builder));
+        assertEquals(1, Selection.getSelectionEnd(builder));
+    }
+
+    @Test
+    public void testMultilineLengthExtendUp() {
+        CharSequence text = "Google\n\nhello, world";
+        SpannableStringBuilder builder = new SpannableStringBuilder(text);
+        StaticLayout layout = new StaticLayout(text, new TextPaint(), 200, null, 0, 0, false);
+        assertEquals(-1, Selection.getSelectionStart(builder));
+        assertEquals(-1, Selection.getSelectionEnd(builder));
+
+        assertTrue(Selection.extendUp(builder, layout));
+        assertEquals(-1, Selection.getSelectionStart(builder));
+        assertEquals(0, Selection.getSelectionEnd(builder));
+
+        Selection.setSelection(builder, 9, 16);
+        assertTrue(Selection.extendUp(builder, layout));
+        assertEquals(9, Selection.getSelectionStart(builder));
+        assertEquals(7, Selection.getSelectionEnd(builder));
+
+        assertTrue(Selection.extendUp(builder, layout));
+        assertEquals(9, Selection.getSelectionStart(builder));
+        assertEquals(4, Selection.getSelectionEnd(builder));
+
+        assertTrue(Selection.extendUp(builder, layout));
+        assertEquals(9, Selection.getSelectionStart(builder));
+        assertEquals(0, Selection.getSelectionEnd(builder));
+    }
+
+    @Test
+    public void testExtendUpKeepsOriginalMemoryPosition() {
+        CharSequence text = "aa\nm";
+        SpannableStringBuilder builder = new SpannableStringBuilder(text);
+        StaticLayout layout = new StaticLayout(builder, new TextPaint(), 200,
+                Layout.Alignment.ALIGN_NORMAL, 0, 0, false);
+        assertEquals(-1, Selection.getSelectionStart(builder));
+        assertEquals(-1, Selection.getSelectionEnd(builder));
+        assertEquals(0,
+                builder.getSpans(0, builder.length(), Selection.MemoryTextWatcher.class).length);
+
+        Selection.setSelection(builder, 1, 1);
+        assertTrue(Selection.extendDown(builder, layout));
+        assertEquals(1, Selection.getSelectionStart(builder));
+        assertEquals(4, Selection.getSelectionEnd(builder));
+        assertEquals(1,
+                builder.getSpans(0, builder.length(), Selection.MemoryTextWatcher.class).length);
+
+        assertTrue(Selection.extendUp(builder, layout));
+        assertEquals(1, Selection.getSelectionStart(builder));
+        assertEquals(1, Selection.getSelectionEnd(builder));
+        assertEquals(1,
+                builder.getSpans(0, builder.length(), Selection.MemoryTextWatcher.class).length);
+    }
+
+    @Test
+    public void testMultilineLengthMoveDownAfterSelection() {
+        CharSequence text = "aaaaa\n\naaaaa";
+        SpannableStringBuilder builder = new SpannableStringBuilder(text);
+        StaticLayout layout = new StaticLayout(text, new TextPaint(), 200, null, 0, 0, false);
+        assertEquals(-1, Selection.getSelectionStart(builder));
+        assertEquals(-1, Selection.getSelectionEnd(builder));
+
+        Selection.setSelection(builder, 3);
+        // Move down to empty line
+        assertTrue(Selection.moveDown(builder, layout));
+        assertEquals(6, Selection.getSelectionStart(builder));
+        assertEquals(6, Selection.getSelectionEnd(builder));
+
+        // Move down to third line
+        assertTrue(Selection.moveUp(builder, layout));
+        assertEquals(3, Selection.getSelectionStart(builder));
+        assertEquals(3, Selection.getSelectionEnd(builder));
+
+        Selection.setSelection(builder, 5);
+        // Move down to empty line
+        assertTrue(Selection.moveDown(builder, layout));
+        assertEquals(6, Selection.getSelectionStart(builder));
+        assertEquals(6, Selection.getSelectionEnd(builder));
+
+        // Move down to third line
+        assertTrue(Selection.moveUp(builder, layout));
+        assertEquals(5, Selection.getSelectionStart(builder));
+        assertEquals(5, Selection.getSelectionEnd(builder));
+    }
+
+    @Test
+    public void testMultilineLengthMoveUpAfterSelection() {
+        CharSequence text = "aaaaa\n\naaaaa";
+        SpannableStringBuilder builder = new SpannableStringBuilder(text);
+        StaticLayout layout = new StaticLayout(text, new TextPaint(), 200, null, 0, 0, false);
+        assertEquals(-1, Selection.getSelectionStart(builder));
+        assertEquals(-1, Selection.getSelectionEnd(builder));
+
+        Selection.setSelection(builder, 10);
+        // Move up to empty line
+        assertTrue(Selection.moveUp(builder, layout));
+        assertEquals(6, Selection.getSelectionStart(builder));
+        assertEquals(6, Selection.getSelectionEnd(builder));
+
+        // Move down to third line
+        assertTrue(Selection.moveDown(builder, layout));
+        assertEquals(10, Selection.getSelectionStart(builder));
+        assertEquals(10, Selection.getSelectionEnd(builder));
+
+        Selection.setSelection(builder, 12);
+        // Move up to empty line
+        assertTrue(Selection.moveUp(builder, layout));
+        assertEquals(6, Selection.getSelectionStart(builder));
+        assertEquals(6, Selection.getSelectionEnd(builder));
+
+        // Move down to third line
+        assertTrue(Selection.moveDown(builder, layout));
+        assertEquals(12, Selection.getSelectionStart(builder));
+        assertEquals(12, Selection.getSelectionEnd(builder));
+    }
+
+    @Test
     public void testExtendSelection() {
         CharSequence text = "hello, world";
         SpannableStringBuilder builder = new SpannableStringBuilder(text);
diff --git a/tests/tests/text/src/android/text/cts/StaticLayoutLineBreakingTest.java b/tests/tests/text/src/android/text/cts/StaticLayoutLineBreakingTest.java
new file mode 100644
index 0000000..adb8f97
--- /dev/null
+++ b/tests/tests/text/src/android/text/cts/StaticLayoutLineBreakingTest.java
@@ -0,0 +1,459 @@
+/*
+ * Copyright (C) 2012 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.text.cts;
+
+import static org.junit.Assert.assertEquals;
+
+import android.content.Context;;
+import android.graphics.Typeface;
+import android.support.test.InstrumentationRegistry;
+import android.support.test.filters.SmallTest;
+import android.support.test.runner.AndroidJUnit4;
+import android.text.Layout;
+import android.text.Layout.Alignment;
+import android.text.SpannableStringBuilder;
+import android.text.Spanned;
+import android.text.StaticLayout;
+import android.text.TextDirectionHeuristics;
+import android.text.TextPaint;
+import android.text.style.MetricAffectingSpan;
+import android.util.Log;
+
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+@SmallTest
+@RunWith(AndroidJUnit4.class)
+public class StaticLayoutLineBreakingTest {
+    // Span test are currently not supported because text measurement uses the MeasuredText
+    // internal mWorkPaint instead of the provided MockTestPaint.
+    private static final boolean SPAN_TESTS_SUPPORTED = false;
+    private static final boolean DEBUG = false;
+
+    private static final float SPACE_MULTI = 1.0f;
+    private static final float SPACE_ADD = 0.0f;
+    private static final int WIDTH = 100;
+    private static final Alignment ALIGN = Alignment.ALIGN_NORMAL;
+
+    private static final char SURR_FIRST = '\uD800';
+    private static final char SURR_SECOND = '\uDF31';
+
+    private static final int[] NO_BREAK = new int[] {};
+
+    private static final TextPaint sTextPaint = new TextPaint();
+
+    static {
+        // The test font has following coverage and width.
+        // U+0020: 10em
+        // U+002E (.): 10em
+        // U+0043 (C): 100em
+        // U+0049 (I): 1em
+        // U+004C (L): 50em
+        // U+0056 (V): 5em
+        // U+0058 (X): 10em
+        // U+005F (_): 0em
+        // U+FFFD (invalid surrogate will be replaced to this): 7em
+        // U+10331 (\uD800\uDF31): 10em
+        Context context = InstrumentationRegistry.getTargetContext();
+        sTextPaint.setTypeface(Typeface.createFromAsset(context.getAssets(),
+                  "fonts/StaticLayoutLineBreakingTestFont.ttf"));
+        sTextPaint.setTextSize(1.0f);  // Make 1em == 1px.
+    }
+
+    private static StaticLayout getStaticLayout(CharSequence source, int width,
+            int breakStrategy) {
+        return StaticLayout.Builder.obtain(source, 0, source.length(), sTextPaint, width)
+                .setAlignment(ALIGN)
+                .setLineSpacing(SPACE_ADD, SPACE_MULTI)
+                .setIncludePad(false)
+                .setBreakStrategy(breakStrategy)
+                .build();
+    }
+
+    private static int[] getBreaks(CharSequence source) {
+        return getBreaks(source, WIDTH, Layout.BREAK_STRATEGY_SIMPLE);
+    }
+
+    private static int[] getBreaks(CharSequence source, int width, int breakStrategy) {
+        final StaticLayout staticLayout = getStaticLayout(source, width, breakStrategy);
+
+        final int[] breaks = new int[staticLayout.getLineCount() - 1];
+        for (int line = 0; line < breaks.length; line++) {
+            breaks[line] = staticLayout.getLineEnd(line);
+        }
+        return breaks;
+    }
+
+    private static void debugLayout(CharSequence source, StaticLayout staticLayout) {
+        if (DEBUG) {
+            int count = staticLayout.getLineCount();
+            Log.i("SLLBTest", "\"" + source.toString() + "\": "
+                    + count + " lines");
+            for (int line = 0; line < count; line++) {
+                int lineStart = staticLayout.getLineStart(line);
+                int lineEnd = staticLayout.getLineEnd(line);
+                Log.i("SLLBTest", "Line " + line + " [" + lineStart + ".."
+                        + lineEnd + "]\t" + source.subSequence(lineStart, lineEnd));
+            }
+        }
+    }
+
+    private static void layout(CharSequence source, int[] breaks) {
+        layout(source, breaks, WIDTH);
+    }
+
+    private static void layout(CharSequence source, int[] breaks, int width) {
+        final int[] breakStrategies = {Layout.BREAK_STRATEGY_SIMPLE,
+                Layout.BREAK_STRATEGY_HIGH_QUALITY};
+        for (int breakStrategy : breakStrategies) {
+            final StaticLayout staticLayout = getStaticLayout(source, width, breakStrategy);
+
+            debugLayout(source, staticLayout);
+
+            final int lineCount = breaks.length + 1;
+            assertEquals("Number of lines", lineCount, staticLayout.getLineCount());
+
+            for (int line = 0; line < lineCount; line++) {
+                final int lineStart = staticLayout.getLineStart(line);
+                final int lineEnd = staticLayout.getLineEnd(line);
+
+                if (line == 0) {
+                    assertEquals("Line start for first line", 0, lineStart);
+                } else {
+                    assertEquals("Line start for line " + line, breaks[line - 1], lineStart);
+                }
+
+                if (line == lineCount - 1) {
+                    assertEquals("Line end for last line", source.length(), lineEnd);
+                } else {
+                    assertEquals("Line end for line " + line, breaks[line], lineEnd);
+                }
+            }
+        }
+    }
+
+    private static void layoutMaxLines(CharSequence source, int[] breaks, int maxLines) {
+        final StaticLayout staticLayout = StaticLayout.Builder
+                .obtain(source, 0, source.length(), sTextPaint, WIDTH)
+                .setAlignment(ALIGN)
+                .setTextDirection(TextDirectionHeuristics.LTR)
+                .setLineSpacing(SPACE_ADD, SPACE_MULTI)
+                .setIncludePad(false)
+                .setMaxLines(maxLines)
+                .build();
+
+        debugLayout(source, staticLayout);
+
+        final int lineCount = staticLayout.getLineCount();
+
+        for (int line = 0; line < lineCount; line++) {
+            int lineStart = staticLayout.getLineStart(line);
+            int lineEnd = staticLayout.getLineEnd(line);
+
+            if (line == 0) {
+                assertEquals("Line start for first line", 0, lineStart);
+            } else {
+                assertEquals("Line start for line " + line, breaks[line - 1], lineStart);
+            }
+
+            if (line == lineCount - 1 && line != breaks.length - 1) {
+                assertEquals("Line end for last line", source.length(), lineEnd);
+            } else {
+                assertEquals("Line end for line " + line, breaks[line], lineEnd);
+            }
+        }
+    }
+
+    private static final int MAX_SPAN_COUNT = 10;
+    private static final int[] sSpanStarts = new int[MAX_SPAN_COUNT];
+    private static final int[] sSpanEnds = new int[MAX_SPAN_COUNT];
+
+    private static MetricAffectingSpan getMetricAffectingSpan() {
+        return new MetricAffectingSpan() {
+            @Override
+            public void updateDrawState(TextPaint tp) { /* empty */ }
+
+            @Override
+            public void updateMeasureState(TextPaint p) { /* empty */ }
+        };
+    }
+
+    /**
+     * Replaces the "<...>" blocks by spans, assuming non overlapping, correctly defined spans
+     * @param text
+     * @return A CharSequence with '<' '>' replaced by MetricAffectingSpan
+     */
+    private static CharSequence spanify(String text) {
+        int startIndex = text.indexOf('<');
+        if (startIndex < 0) return text;
+
+        int spanCount = 0;
+        do {
+            int endIndex = text.indexOf('>');
+            if (endIndex < 0) throw new IllegalArgumentException("Unbalanced span markers");
+
+            text = text.substring(0, startIndex) + text.substring(startIndex + 1, endIndex)
+                    + text.substring(endIndex + 1);
+
+            sSpanStarts[spanCount] = startIndex;
+            sSpanEnds[spanCount] = endIndex - 2;
+            spanCount++;
+
+            startIndex = text.indexOf('<');
+        } while (startIndex >= 0);
+
+        SpannableStringBuilder result = new SpannableStringBuilder(text);
+        for (int i = 0; i < spanCount; i++) {
+            result.setSpan(getMetricAffectingSpan(), sSpanStarts[i], sSpanEnds[i],
+                    Spanned.SPAN_INCLUSIVE_INCLUSIVE);
+        }
+        return result;
+    }
+
+    @Test
+    public void testNoLineBreak() {
+        // Width lower than WIDTH
+        layout("", NO_BREAK);
+        layout("I", NO_BREAK);
+        layout("V", NO_BREAK);
+        layout("X", NO_BREAK);
+        layout("L", NO_BREAK);
+        layout("I VILI", NO_BREAK);
+        layout("XXXX", NO_BREAK);
+        layout("LXXXX", NO_BREAK);
+
+        // Width equal to WIDTH
+        layout("C", NO_BREAK);
+        layout("LL", NO_BREAK);
+        layout("L XXXX", NO_BREAK);
+        layout("XXXXXXXXXX", NO_BREAK);
+        layout("XXX XXXXXX", NO_BREAK);
+        layout("XXX XXXX X", NO_BREAK);
+        layout("XXX XXXXX ", NO_BREAK);
+        layout(" XXXXXXXX ", NO_BREAK);
+        layout("  XX  XXX ", NO_BREAK);
+        //      0123456789
+
+        // Width greater than WIDTH, but no break
+        layout("  XX  XXX  ", NO_BREAK);
+        layout("XX XXX XXX ", NO_BREAK);
+        layout("XX XXX XXX     ", NO_BREAK);
+        layout("XXXXXXXXXX     ", NO_BREAK);
+        //      01234567890
+    }
+
+    @Test
+    public void testOneLineBreak() {
+        //      01234567890
+        layout("XX XXX XXXX", new int[] {7});
+        layout("XX XXXX XXX", new int[] {8});
+        layout("XX XXXXX XX", new int[] {9});
+        layout("XX XXXXXX X", new int[] {10});
+        //      01234567890
+        layout("XXXXXXXXXXX", new int[] {10});
+        layout("XXXXXXXXX X", new int[] {10});
+        layout("XXXXXXXX XX", new int[] {9});
+        layout("XXXXXXX XXX", new int[] {8});
+        layout("XXXXXX XXXX", new int[] {7});
+        //      01234567890
+        layout("LL LL", new int[] {3});
+        layout("LLLL", new int[] {2});
+        layout("C C", new int[] {2});
+        layout("CC", new int[] {1});
+    }
+
+    @Test
+    public void testSpaceAtBreak() {
+        //      0123456789012
+        layout("XXXX XXXXX X", new int[] {11});
+        layout("XXXXXXXXXX X", new int[] {11});
+        layout("XXXXXXXXXV X", new int[] {11});
+        layout("C X", new int[] {2});
+    }
+
+    @Test
+    public void testMultipleSpacesAtBreak() {
+        //      0123456789012
+        layout("LXX XXXX", new int[] {4});
+        layout("LXX  XXXX", new int[] {5});
+        layout("LXX   XXXX", new int[] {6});
+        layout("LXX    XXXX", new int[] {7});
+        layout("LXX     XXXX", new int[] {8});
+    }
+
+    @Test
+    public void testZeroWidthCharacters() {
+        //      0123456789012345678901234
+        layout("X_X_X_X_X_X_X_X_X_X", NO_BREAK);
+        layout("___X_X_X_X_X_X_X_X_X_X___", NO_BREAK);
+        layout("C_X", new int[] {2});
+        layout("C__X", new int[] {3});
+    }
+
+    /**
+     * Note that when the text has spans, StaticLayout does not use the provided TextPaint to
+     * measure text runs anymore. This is probably a bug.
+     * To be able to use the fake sTextPaint and make this test pass, use mPaint instead of
+     * mWorkPaint in MeasuredText#addStyleRun
+     */
+    @Test
+    public void testWithSpans() {
+        if (!SPAN_TESTS_SUPPORTED) return;
+
+        layout(spanify("<012 456 89>"), NO_BREAK);
+        layout(spanify("012 <456> 89"), NO_BREAK);
+        layout(spanify("<012> <456>< 89>"), NO_BREAK);
+        layout(spanify("<012> <456> <89>"), NO_BREAK);
+
+        layout(spanify("<012> <456> <89>012"), new int[] {8});
+        layout(spanify("<012> <456> 89<012>"), new int[] {8});
+        layout(spanify("<012> <456> <89><012>"), new int[] {8});
+        layout(spanify("<012> <456> 89 <123>"), new int[] {11});
+        layout(spanify("<012> <456> 89< 123>"), new int[] {11});
+        layout(spanify("<012> <456> <89> <123>"), new int[] {11});
+        layout(spanify("012 456 89 <LXX> XX XX"), new int[] {11, 18});
+    }
+
+    /*
+     * Adding a span to the string should not change the layout, since the metrics are unchanged.
+     */
+    @Test
+    public void testWithOneSpan() {
+        if (!SPAN_TESTS_SUPPORTED) return;
+
+        String[] texts = new String[] { "0123", "012 456", "012 456 89 123", "012 45678 012",
+                "012 456 89012 456 89012", "0123456789012" };
+
+        MetricAffectingSpan metricAffectingSpan = getMetricAffectingSpan();
+
+        for (String text : texts) {
+            // Get the line breaks without any span
+            int[] breaks = getBreaks(text);
+
+            // Add spans on all possible offsets
+            for (int spanStart = 0; spanStart < text.length(); spanStart++) {
+                for (int spanEnd = spanStart; spanEnd < text.length(); spanEnd++) {
+                    SpannableStringBuilder ssb = new SpannableStringBuilder(text);
+                    ssb.setSpan(metricAffectingSpan, spanStart, spanEnd,
+                            Spanned.SPAN_INCLUSIVE_INCLUSIVE);
+                    layout(ssb, breaks);
+                }
+            }
+        }
+    }
+
+    @Test
+    public void testWithTwoSpans() {
+        if (!SPAN_TESTS_SUPPORTED) return;
+
+        String[] texts = new String[] { "0123", "012 456", "012 456 89 123", "012 45678 012",
+                "012 456 89012 456 89012", "0123456789012" };
+
+        MetricAffectingSpan metricAffectingSpan1 = getMetricAffectingSpan();
+        MetricAffectingSpan metricAffectingSpan2 = getMetricAffectingSpan();
+
+        for (String text : texts) {
+            // Get the line breaks without any span
+            int[] breaks = getBreaks(text);
+
+            // Add spans on all possible offsets
+            for (int spanStart1 = 0; spanStart1 < text.length(); spanStart1++) {
+                for (int spanEnd1 = spanStart1; spanEnd1 < text.length(); spanEnd1++) {
+                    SpannableStringBuilder ssb = new SpannableStringBuilder(text);
+                    ssb.setSpan(metricAffectingSpan1, spanStart1, spanEnd1,
+                            Spanned.SPAN_INCLUSIVE_INCLUSIVE);
+
+                    for (int spanStart2 = 0; spanStart2 < text.length(); spanStart2++) {
+                        for (int spanEnd2 = spanStart2; spanEnd2 < text.length(); spanEnd2++) {
+                            ssb.setSpan(metricAffectingSpan2, spanStart2, spanEnd2,
+                                    Spanned.SPAN_INCLUSIVE_INCLUSIVE);
+                            layout(ssb, breaks);
+                        }
+                    }
+                }
+            }
+        }
+    }
+
+    public static String replace(String string, char c, char r) {
+        return string.replaceAll(String.valueOf(c), String.valueOf(r));
+    }
+
+    @Test
+    public void testWithSurrogate() {
+        layout("LX" + SURR_FIRST + SURR_SECOND, NO_BREAK);
+        layout("LXXXX" + SURR_FIRST + SURR_SECOND, NO_BREAK);
+        // LXXXXI (91) + SURR_FIRST + SURR_SECOND (10). Do not break in the middle point of
+        // surrogatge pair.
+        layout("LXXXXI" + SURR_FIRST + SURR_SECOND, new int[] {6});
+
+        // LXXXXI (91) + SURR_SECOND (replaced with REPLACEMENT CHARACTER. width is 7px) fits.
+        // Break just after invalid trailing surrogate.
+        layout("LXXXXI" + SURR_SECOND + SURR_FIRST, new int[] {7});
+
+        layout("C" + SURR_FIRST + SURR_SECOND, new int[] {1});
+    }
+
+    @Test
+    public void testNarrowWidth() {
+        int[] widths = new int[] { 0, 4, 10 };
+        String[] texts = new String[] { "", "X", " ", "XX", " X", "XXX" };
+
+        for (String text: texts) {
+            // 15 is such that only one character will fit
+            int[] breaks = getBreaks(text, 15, Layout.BREAK_STRATEGY_SIMPLE);
+
+            // Width under 15 should all lead to the same line break
+            for (int width: widths) {
+                layout(text, breaks, width);
+            }
+        }
+    }
+
+    @Test
+    public void testNarrowWidthZeroWidth() {
+        int[] widths = new int[] { 1, 4 };
+        for (int width: widths) {
+            layout("X.", new int[] {1}, width);
+            layout("X__", NO_BREAK, width);
+            layout("X__X", new int[] {3}, width);
+            layout("X__X_", new int[] {3}, width);
+
+            layout("_", NO_BREAK, width);
+            layout("__", NO_BREAK, width);
+
+            // TODO: The line breaking algorithms break the line too frequently in the presence of
+            // zero-width characters. The following cases document how line-breaking should behave
+            // in some cases, where the current implementation does not seem reasonable. (Breaking
+            // between a zero-width character that start the line and a character with positive
+            // width does not make sense.) Line-breaking should be fixed so that all the following
+            // tests end up on one line, with no breaks.
+            // layout("_X", NO_BREAK, width);
+            // layout("_X_", NO_BREAK, width);
+            // layout("__X__", NO_BREAK, width);
+        }
+    }
+
+    @Test
+    public void testMaxLines() {
+        layoutMaxLines("C", NO_BREAK, 1);
+        layoutMaxLines("C C", new int[] {2}, 1);
+        layoutMaxLines("C C", new int[] {2}, 2);
+        layoutMaxLines("CC", new int[] {1}, 1);
+        layoutMaxLines("CC", new int[] {1}, 2);
+    }
+}
diff --git a/tests/tests/text/src/android/text/cts/StaticLayoutTest.java b/tests/tests/text/src/android/text/cts/StaticLayoutTest.java
index 4f022a5..5029dcf 100644
--- a/tests/tests/text/src/android/text/cts/StaticLayoutTest.java
+++ b/tests/tests/text/src/android/text/cts/StaticLayoutTest.java
@@ -22,22 +22,30 @@
 import static org.junit.Assert.assertTrue;
 import static org.junit.Assert.fail;
 
+import android.content.res.ColorStateList;
+import android.graphics.Bitmap;
+import android.graphics.Canvas;
 import android.graphics.Typeface;
+import android.support.test.InstrumentationRegistry;
 import android.support.test.filters.SmallTest;
+import android.support.test.filters.Suppress;
 import android.support.test.runner.AndroidJUnit4;
 import android.text.Editable;
 import android.text.Layout;
 import android.text.Layout.Alignment;
+import android.text.PremeasuredText;
 import android.text.SpannableString;
 import android.text.SpannableStringBuilder;
 import android.text.Spanned;
 import android.text.SpannedString;
 import android.text.StaticLayout;
+import android.text.TextDirectionHeuristics;
 import android.text.TextPaint;
 import android.text.TextUtils;
 import android.text.TextUtils.TruncateAt;
 import android.text.method.cts.EditorState;
 import android.text.style.StyleSpan;
+import android.text.style.TextAppearanceSpan;
 
 import org.junit.Before;
 import org.junit.Test;
@@ -59,6 +67,13 @@
     private static final int LINE_COUNT = 6;
     private static final int LARGER_THAN_LINE_COUNT  = 50;
 
+    private static final String LOREM_IPSUM = "Lorem ipsum dolor sit amet, consectetur adipiscing "
+            + "elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad "
+            + "minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea "
+            + "commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse "
+            + "cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non "
+            + "proident, sunt in culpa qui officia deserunt mollit anim id est laborum.";
+
     /* the first line must have one tab. the others not. totally 6 lines
      */
     private static final CharSequence LAYOUT_TEXT = "CharSe\tq\nChar"
@@ -203,8 +218,8 @@
             // setBreakStrategy, setHyphenationFrequency, setIncludePad, and setIndents.
             StaticLayout.Builder builder = StaticLayout.Builder.obtain(LAYOUT_TEXT, 0,
                     LAYOUT_TEXT.length(), mDefaultPaint, DEFAULT_OUTER_WIDTH);
-            builder.setBreakStrategy(StaticLayout.BREAK_STRATEGY_HIGH_QUALITY);
-            builder.setHyphenationFrequency(StaticLayout.HYPHENATION_FREQUENCY_FULL);
+            builder.setBreakStrategy(Layout.BREAK_STRATEGY_HIGH_QUALITY);
+            builder.setHyphenationFrequency(Layout.HYPHENATION_FREQUENCY_FULL);
             builder.setIncludePad(true);
             builder.setIndents(null, null);
             StaticLayout layout = builder.build();
@@ -213,6 +228,38 @@
     }
 
     @Test
+    public void testSetLineSpacing_whereLineEndsWithNextLine() {
+        final float spacingAdd = 10f;
+        final float spacingMult = 3f;
+
+        // two lines of text, with line spacing, first line will have the spacing, but last line
+        // wont have the spacing
+        final String tmpText = "a\nb";
+        StaticLayout.Builder builder = StaticLayout.Builder.obtain(tmpText, 0, tmpText.length(),
+                mDefaultPaint, DEFAULT_OUTER_WIDTH);
+        builder.setLineSpacing(spacingAdd, spacingMult).setIncludePad(false);
+        final StaticLayout comparisonLayout = builder.build();
+
+        assertEquals(2, comparisonLayout.getLineCount());
+        final int heightWithLineSpacing = comparisonLayout.getLineBottom(0)
+                - comparisonLayout.getLineTop(0);
+        final int heightWithoutLineSpacing = comparisonLayout.getLineBottom(1)
+                - comparisonLayout.getLineTop(1);
+        assertTrue(heightWithLineSpacing > heightWithoutLineSpacing);
+
+        final String text = "a\n";
+        // build the layout to be tested
+        builder = StaticLayout.Builder.obtain("a\n", 0, text.length(), mDefaultPaint,
+                DEFAULT_OUTER_WIDTH);
+        builder.setLineSpacing(spacingAdd, spacingMult).setIncludePad(false);
+        final StaticLayout layout = builder.build();
+
+        assertEquals(comparisonLayout.getLineCount(), layout.getLineCount());
+        assertEquals(heightWithLineSpacing, layout.getLineBottom(0) - layout.getLineTop(0));
+        assertEquals(heightWithoutLineSpacing, layout.getLineBottom(1) - layout.getLineTop(1));
+    }
+
+    @Test
     public void testBuilder_setJustificationMode() {
         StaticLayout.Builder builder = StaticLayout.Builder.obtain(LAYOUT_TEXT, 0,
                 LAYOUT_TEXT.length(), mDefaultPaint, DEFAULT_OUTER_WIDTH);
@@ -222,6 +269,7 @@
         // without causing any exceptions.
         assertNotNull(layout);
     }
+
     /*
      * Get the line number corresponding to the specified vertical position.
      *  If you ask for a position above 0, you get 0. above 0 means pixel above the fire line
@@ -1171,6 +1219,91 @@
         assertTrue(layout.getEllipsisStart(0) != 0);
     }
 
+    @Test
+    public void testEllipsize_retryEnd() {
+        final float size = 100.0f;
+
+        final int allocatedWidth = (int) (3.4f * size);
+        final String text = "aaaa";
+        final StaticLayout layout = StaticLayout.Builder.obtain(text, 0, text.length(),
+                getTextPaintForEllipsize(size), allocatedWidth)
+                .setEllipsize(TextUtils.TruncateAt.END)
+                .setEllipsizedWidth(allocatedWidth)
+                .setMaxLines(1)
+                .build();
+        assertEquals(1, layout.getEllipsisStart(0)); // After the first 'a'
+        assertEquals(3, layout.getEllipsisCount(0));
+    }
+
+    @Test
+    public void testEllipsize_retryEndRtl() {
+        final float size = 100.0f;
+
+        final int allocatedWidth = (int) (3.4f * size);
+        final String text = "\u202Eaaaa"; // U+202E is the RIGHT-TO-LEFT OVERRIDE.
+        final StaticLayout layout = StaticLayout.Builder.obtain(text, 0, text.length(),
+                getTextPaintForEllipsize(size), allocatedWidth)
+                .setEllipsize(TextUtils.TruncateAt.END)
+                .setEllipsizedWidth(allocatedWidth)
+                .setMaxLines(1)
+                .build();
+        assertEquals(2, layout.getEllipsisStart(0)); // After the first 'a'
+        assertEquals(3, layout.getEllipsisCount(0));
+    }
+
+    @Test
+    public void testEllipsize_retryStart() {
+        final float size = 100.0f;
+
+        final int allocatedWidth = (int) (3.4f * size);
+        final String text = "aaaa";
+        final StaticLayout layout = StaticLayout.Builder.obtain(text, 0, text.length(),
+                getTextPaintForEllipsize(size), allocatedWidth)
+                .setEllipsize(TextUtils.TruncateAt.START)
+                .setEllipsizedWidth(allocatedWidth)
+                .setMaxLines(1)
+                .build();
+        assertEquals(0, layout.getEllipsisStart(0));
+        assertEquals(3, layout.getEllipsisCount(0));
+    }
+
+    @Test
+    public void testEllipsize_retryMiddle() {
+        final float size = 100.0f;
+
+        final int allocatedWidth = (int) (5.9f * size);
+        final String text = "aaaaaa";
+        final StaticLayout layout = StaticLayout.Builder.obtain(text, 0, text.length(),
+                getTextPaintForEllipsize(size), allocatedWidth)
+                .setEllipsize(TextUtils.TruncateAt.MIDDLE)
+                .setEllipsizedWidth(allocatedWidth)
+                .setMaxLines(1)
+                .build();
+        final int ellipsisStart = layout.getEllipsisStart(0);
+        assertTrue(ellipsisStart == 1 || ellipsisStart == 2);
+        assertEquals(3, layout.getEllipsisCount(0));
+    }
+
+    private TextPaint getTextPaintForEllipsize(float size) {
+        // The font used in this method has two glyphs defined for "a" and ellipsis. Both are one
+        // em wide. But the glyphs are kerned: whenever the "a" is followed or preceded by an
+        // ellipsis, half an em is added between them as kerning. This means that:
+        // "aaaa" is 4 ems wide,
+        // "aaa…" is 4.5 ems wide,
+        // "aa…" is 3.5 ems wide,
+        // "a…" is 2.5 ems wide,
+        // "aa…aa" is 6 ems wide,
+        // "aa…a" is 5 ems wide,
+        // "a…aa" is 5 ems wide,
+        // "a…a" is 4 ems wide,
+        // "…a" is 2.5 ems wide.
+        final TextPaint paint = new TextPaint();
+        paint.setTypeface(Typeface.createFromAsset(
+                InstrumentationRegistry.getTargetContext().getAssets(), "ellipsis_test_font.ttf"));
+        paint.setTextSize(size);
+        return paint;
+    }
+
     @Test(expected = IndexOutOfBoundsException.class)
     public void testGetPrimary_shouldFail_whenOffsetIsOutOfBounds_withSpannable() {
         final String text = "1\n2\n3";
@@ -1190,4 +1323,78 @@
                 .setEllipsize(TruncateAt.END).build();
         layout.getPrimaryHorizontal(layout.getText().length());
     }
+
+    // TODO: Re-enable once http://b/65207701 is fixed.
+    @Test
+    @Suppress
+    public void testGetLineWidth() {
+        final float wholeWidth = mDefaultPaint.measureText(LOREM_IPSUM);
+        final int lineWidth = (int) (wholeWidth / 10.0f);  // Make 10 lines per paragraph.
+        final String multiParaTestString =
+                LOREM_IPSUM + "\n" + LOREM_IPSUM + "\n" + LOREM_IPSUM + "\n" + LOREM_IPSUM;
+        final Layout layout = StaticLayout.Builder.obtain(multiParaTestString, 0,
+                multiParaTestString.length(), mDefaultPaint, lineWidth)
+                .build();
+        for (int i = 0; i < layout.getLineCount(); i++) {
+            assertTrue(layout.getLineWidth(i) <= lineWidth);
+        }
+    }
+
+    // TODO: Re-enable once http://b/65207701 is fixed.
+    @Test
+    @Suppress
+    public void testIndent() {
+        final float wholeWidth = mDefaultPaint.measureText(LOREM_IPSUM);
+        final int lineWidth = (int) (wholeWidth / 10.0f);  // Make 10 lines per paragraph.
+        final int indentWidth = (int) (lineWidth * 0.3f);  // Make 30% indent.
+        final String multiParaTestString =
+                LOREM_IPSUM + "\n" + LOREM_IPSUM + "\n" + LOREM_IPSUM + "\n" + LOREM_IPSUM;
+        final Layout layout = StaticLayout.Builder.obtain(multiParaTestString, 0,
+                multiParaTestString.length(), mDefaultPaint, lineWidth)
+                .setIndents(new int[] { indentWidth }, null)
+                .build();
+        for (int i = 0; i < layout.getLineCount(); i++) {
+            assertTrue(layout.getLineWidth(i) <= lineWidth - indentWidth);
+        }
+    }
+
+    private static Bitmap drawToBitmap(Layout l) {
+        final Bitmap bmp = Bitmap.createBitmap(l.getWidth(), l.getHeight(), Bitmap.Config.RGB_565);
+        final Canvas c = new Canvas(bmp);
+
+        c.save();
+        c.translate(0, 0);
+        l.draw(c);
+        c.restore();
+        return bmp;
+    }
+
+    @Test
+    public void testPremeasured() {
+        final float wholeWidth = mDefaultPaint.measureText(LOREM_IPSUM);
+        final int lineWidth = (int) (wholeWidth / 10.0f);  // Make 10 lines per paragraph.
+
+        final ColorStateList textColor = ColorStateList.valueOf(0x88FF0000);
+        final TextAppearanceSpan span = new TextAppearanceSpan(
+                "serif", Typeface.BOLD, 64 /* text size */, textColor, textColor);
+        final SpannableStringBuilder ssb = new SpannableStringBuilder(
+                LOREM_IPSUM + "\n" + LOREM_IPSUM);
+        ssb.setSpan(span, 0, LOREM_IPSUM.length(), Spanned.SPAN_INCLUSIVE_EXCLUSIVE);
+
+        final Layout layout = StaticLayout.Builder.obtain(ssb, 0, ssb.length(), mDefaultPaint,
+                lineWidth).build();
+
+        final PremeasuredText premeasuredText = PremeasuredText.build(ssb, mDefaultPaint,
+                TextDirectionHeuristics.FIRSTSTRONG_LTR);
+        final Layout premLayout = StaticLayout.Builder.obtain(premeasuredText, 0,
+                premeasuredText.length(), mDefaultPaint, lineWidth)
+                .setTextDirection(TextDirectionHeuristics.FIRSTSTRONG_LTR).build();
+
+        assertEquals(layout.getHeight(), premLayout.getHeight(), 0.0f);
+
+        final Bitmap bmp = drawToBitmap(layout);
+        final Bitmap premBmp = drawToBitmap(premLayout);
+
+        assertTrue(bmp.sameAs(premBmp));  // Need to be pixel perfect.
+    }
 }
diff --git a/tests/tests/text/src/android/text/cts/TextUtilsTest.java b/tests/tests/text/src/android/text/cts/TextUtilsTest.java
index 2fd4e59..b0848bb 100644
--- a/tests/tests/text/src/android/text/cts/TextUtilsTest.java
+++ b/tests/tests/text/src/android/text/cts/TextUtilsTest.java
@@ -706,6 +706,83 @@
         }
     }
 
+    @Test
+    public void testEllipsize_retryEnd() {
+        // When we try to ellipsize "aaaa" into a thinner 3.4 em space, we originally think
+        // "aa…" would fit, but after measuring the new text, we find that it doesn't and we need
+        // to retry.
+        final float size = 100.0f;
+        final String text = "aaaa";
+
+        final CharSequence ellipsized = TextUtils.ellipsize(text, getTextPaintForEllipsize(size),
+                3.4f * size,
+                TextUtils.TruncateAt.END, false /* preserveLength */, null /* callback */);
+        assertEquals("a\u2026", ellipsized.toString());
+    }
+
+    @Test
+    public void testEllipsize_retryEndRtl() {
+        // When we try to ellipsize "aaaa" into a thinner 3.4 em space, we originally think
+        // "aa…" would fit, but after measuring the new text, we find that it doesn't and we need
+        // to retry.
+        final float size = 100.0f;
+        final String text = "\u202Eaaaa"; // U+202E is the RIGHT-TO-LEFT OVERRIDE.
+
+        final CharSequence ellipsized = TextUtils.ellipsize(text, getTextPaintForEllipsize(size),
+                3.4f * size,
+                TextUtils.TruncateAt.END, false /* preserveLength */, null /* callback */);
+        assertEquals("\u202Ea\u2026", ellipsized.toString());
+    }
+
+    @Test
+    public void testEllipsize_retryStart() {
+        // When we try to ellipsize "aaaa" into a thinner 3.4 em space, we originally think
+        // "…aa" would fit, but after measuring the new text, we find that it doesn't and we need
+        // to retry.
+        final float size = 100.0f;
+        final String text = "aaaa";
+
+        final CharSequence ellipsized = TextUtils.ellipsize(text, getTextPaintForEllipsize(size),
+                3.4f * size,
+                TextUtils.TruncateAt.START, false /* preserveLength */, null /* callback */);
+        assertEquals("\u2026a", ellipsized.toString());
+    }
+
+    @Test
+    public void testEllipsize_retryMiddle() {
+        // When we try to ellipsize "aaaaaa" into a thinner 5.9 em space, we originally think
+        // "aa…aa" would fit, but after measuring the new text, we find that it doesn't and we need
+        // to retry.
+        final float size = 100.0f;
+        final String text = "aaaaaa";
+
+        final CharSequence ellipsized = TextUtils.ellipsize(text, getTextPaintForEllipsize(size),
+                5.9f * size,
+                TextUtils.TruncateAt.MIDDLE, false /* preserveLength */, null /* callback */);
+        final String ellipsizedString = ellipsized.toString();
+        assertTrue("aa\u2026a".equals(ellipsizedString) || "a\u2026aa".equals(ellipsizedString));
+    }
+
+    private TextPaint getTextPaintForEllipsize(float size) {
+        // The font used in this method has two glyphs defined for "a" and ellipsis. Both are one
+        // em wide. But the glyphs are kerned: whenever the "a" is followed or preceded by an
+        // ellipsis, half an em is added between them as kerning. This means that:
+        // "aaaa" is 4 ems wide,
+        // "aaa…" is 4.5 ems wide,
+        // "aa…" is 3.5 ems wide,
+        // "a…" is 2.5 ems wide,
+        // "aa…aa" is 6 ems wide,
+        // "aa…a" is 5 ems wide,
+        // "a…aa" is 5 ems wide,
+        // "a…a" is 4 ems wide,
+        // "…a" is 2.5 ems wide.
+        final TextPaint paint = new TextPaint();
+        paint.setTypeface(Typeface.createFromAsset(
+                InstrumentationRegistry.getTargetContext().getAssets(), "ellipsis_test_font.ttf"));
+        paint.setTextSize(size);
+        return paint;
+    }
+
     /**
      * Get a blank string which is filled up by '\uFEFF'.
      *
@@ -1614,6 +1691,8 @@
         spannableStringTokens.add(new SpannableString("span 2"));
         spannableStringTokens.add(new SpannableString("span 3"));
         assertEquals("span 1;span 2;span 3", TextUtils.join(";", spannableStringTokens));
+
+        assertEquals("", TextUtils.join("|", new ArrayList<CharSequence>()));
     }
 
     @Test(expected=NullPointerException.class)
@@ -1636,6 +1715,8 @@
                 new SpannableString("span 2"),
                 new SpannableString("span 3") };
         assertEquals("span 1;span 2;span 3", TextUtils.join(";", spannableStringTokens));
+
+        assertEquals("", TextUtils.join("|", new String[0]));
     }
 
     @Test(expected=NullPointerException.class)
diff --git a/tests/tests/text/src/android/text/format/cts/DateFormatTest.java b/tests/tests/text/src/android/text/format/cts/DateFormatTest.java
index 13c9aa2..50c97b2 100644
--- a/tests/tests/text/src/android/text/format/cts/DateFormatTest.java
+++ b/tests/tests/text/src/android/text/format/cts/DateFormatTest.java
@@ -18,12 +18,15 @@
 
 import static org.junit.Assert.assertEquals;
 import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertNotEquals;
 import static org.junit.Assert.assertNotNull;
 import static org.junit.Assert.assertTrue;
 import static org.junit.Assert.fail;
 
 import android.app.UiAutomation;
 import android.content.Context;
+import android.content.res.Configuration;
+import android.os.LocaleList;
 import android.os.ParcelFileDescriptor;
 import android.provider.Settings;
 import android.support.annotation.NonNull;
@@ -312,6 +315,27 @@
         }
     }
 
+    @Test
+    public void test_ContextLocaleIsUsed() {
+        final Locale oldLocale = Locale.getDefault();
+
+        try {
+            Date date = new Date(YEAR_FROM_1900, MONTH, DAY);
+            Locale.setDefault(Locale.FRANCE);
+            final String javaResult = java.text.DateFormat.getDateInstance(
+                    java.text.DateFormat.LONG).format(date);
+
+            final Configuration config = new Configuration();
+            config.setLocales(new LocaleList(Locale.JAPAN));
+            final Context context = mContext.createConfigurationContext(config);
+            final String androidResult = DateFormat.getLongDateFormat(context).format(date);
+
+            assertNotEquals(javaResult, androidResult);
+        } finally {
+            Locale.setDefault(oldLocale);
+        }
+    }
+
     @NonNull
     private String getTimeFormat() throws IOException {
         return SystemUtil.runShellCommand(InstrumentationRegistry.getInstrumentation(),
diff --git a/tests/tests/text/src/android/text/method/cts/KeyListenerCtsActivity.java b/tests/tests/text/src/android/text/method/cts/KeyListenerCtsActivity.java
index 9c96519..4f26aea 100644
--- a/tests/tests/text/src/android/text/method/cts/KeyListenerCtsActivity.java
+++ b/tests/tests/text/src/android/text/method/cts/KeyListenerCtsActivity.java
@@ -18,7 +18,6 @@
 
 import android.app.Activity;
 import android.os.Bundle;
-import android.os.SystemClock;
 import android.text.cts.R;
 import android.text.method.BaseKeyListener;
 import android.text.method.DateKeyListener;
@@ -29,7 +28,6 @@
 import android.text.method.QwertyKeyListener;
 import android.text.method.TextKeyListener;
 import android.text.method.TimeKeyListener;
-import android.util.Log;
 
 /**
  * This Activity is used for testing:
@@ -55,47 +53,9 @@
  */
 
 public class KeyListenerCtsActivity extends Activity {
-    private boolean mHasWindowFocus = false;
-    private final Object mHasWindowFocusLock = new Object();
-
     @Override
     protected void onCreate(Bundle savedInstanceState) {
         super.onCreate(savedInstanceState);
         setContentView(R.layout.keylistener_layout);
     }
-
-    @Override
-    public void onWindowFocusChanged(boolean hasFocus) {
-        super.onWindowFocusChanged(hasFocus);
-        if (!hasFocus) {
-            Log.w("KeyListenerCtsActivity", "KeyListenerCtsActivity lost window focus");
-        }
-        synchronized(mHasWindowFocusLock) {
-            mHasWindowFocus = hasFocus;
-            mHasWindowFocusLock.notify();
-        }
-    }
-
-    /**
-     * Blocks the calling thread until the {@link KeyListenerCtsActivity} has window focus or the
-     * specified duration (in milliseconds) has passed.
-     */
-    public boolean waitForWindowFocus(long durationMillis) {
-        long elapsedMillis = SystemClock.elapsedRealtime();
-        synchronized(mHasWindowFocusLock) {
-            mHasWindowFocus = hasWindowFocus();
-            while (!mHasWindowFocus && durationMillis > 0) {
-                long newElapsedMillis = SystemClock.elapsedRealtime();
-                durationMillis -= (newElapsedMillis - elapsedMillis);
-                elapsedMillis = newElapsedMillis;
-                if (durationMillis > 0) {
-                    try {
-                        mHasWindowFocusLock.wait(durationMillis);
-                    } catch (InterruptedException e) {
-                    }
-                }
-            }
-            return mHasWindowFocus;
-        }
-    }
 }
diff --git a/tests/tests/text/src/android/text/method/cts/KeyListenerTestCase.java b/tests/tests/text/src/android/text/method/cts/KeyListenerTestCase.java
index 8d47c57..36f92e2 100644
--- a/tests/tests/text/src/android/text/method/cts/KeyListenerTestCase.java
+++ b/tests/tests/text/src/android/text/method/cts/KeyListenerTestCase.java
@@ -16,40 +16,86 @@
 
 package android.text.method.cts;
 
+import static android.provider.Settings.System.TEXT_AUTO_CAPS;
+
+import android.app.AppOpsManager;
 import android.app.Instrumentation;
+import android.content.ContentResolver;
+import android.content.Context;
+import android.provider.Settings;
 import android.support.test.InstrumentationRegistry;
 import android.support.test.rule.ActivityTestRule;
 import android.text.cts.R;
 import android.text.method.KeyListener;
+import android.util.Log;
 import android.view.KeyEvent;
 import android.widget.EditText;
 
 import com.android.compatibility.common.util.PollingCheck;
+import com.android.compatibility.common.util.SystemUtil;
 
 import org.junit.Before;
 import org.junit.Rule;
 
+import java.io.IOException;
+
 /**
  * Base class for various KeyListener tests.
  */
 public abstract class KeyListenerTestCase {
+    private static final String TAG = "KeyListenerTestCase";
+
     protected KeyListenerCtsActivity mActivity;
     protected Instrumentation mInstrumentation;
     protected EditText mTextView;
+    private int mAutoCapSetting;
 
     @Rule
     public ActivityTestRule<KeyListenerCtsActivity> mActivityRule =
             new ActivityTestRule<>(KeyListenerCtsActivity.class);
 
     @Before
-    public void setup() {
+    public void setup() throws IOException {
         mInstrumentation = InstrumentationRegistry.getInstrumentation();
         mActivity = mActivityRule.getActivity();
-        mTextView = (EditText) mActivity.findViewById(R.id.keylistener_textview);
+        mTextView = mActivity.findViewById(R.id.keylistener_textview);
 
         PollingCheck.waitFor(5000, mActivity::hasWindowFocus);
     }
 
+    protected void enableAutoCapSettings() throws IOException {
+        grantWriteSettingsPermission();
+        final Instrumentation instrumentation = InstrumentationRegistry.getInstrumentation();
+        final Context context = instrumentation.getContext();
+        instrumentation.runOnMainSync(() -> {
+            final ContentResolver resolver = context.getContentResolver();
+            mAutoCapSetting = Settings.System.getInt(resolver, TEXT_AUTO_CAPS, 1);
+            try {
+                Settings.System.putInt(resolver, TEXT_AUTO_CAPS, 1);
+            } catch (SecurityException e) {
+                Log.e(TAG, "Cannot set TEXT_AUTO_CAPS to 1", e);
+                // ignore
+            }
+        });
+        instrumentation.waitForIdleSync();
+    }
+
+    protected void resetAutoCapSettings() throws IOException {
+        grantWriteSettingsPermission();
+        final Instrumentation instrumentation = InstrumentationRegistry.getInstrumentation();
+        final Context context = instrumentation.getContext();
+        instrumentation.runOnMainSync(() -> {
+            final ContentResolver resolver = context.getContentResolver();
+            try {
+                Settings.System.putInt(resolver, TEXT_AUTO_CAPS, mAutoCapSetting);
+            } catch (SecurityException e) {
+                Log.e(TAG, "Cannot set TEXT_AUTO_CAPS to previous value", e);
+                // ignore
+            }
+        });
+        instrumentation.waitForIdleSync();
+    }
+
     /**
      * Synchronously sets mTextView's key listener on the UI thread.
      */
@@ -63,4 +109,10 @@
         return new KeyEvent(currentTime, currentTime, KeyEvent.ACTION_DOWN, keycode,
                 0 /* repeat */, metaState);
     }
+
+    private void grantWriteSettingsPermission() throws IOException {
+        SystemUtil.runShellCommand(InstrumentationRegistry.getInstrumentation(),
+                "appops set " + mActivity.getPackageName() + " "
+                        + AppOpsManager.OPSTR_WRITE_SETTINGS + " allow");
+    }
 }
diff --git a/tests/tests/text/src/android/text/method/cts/MultiTapKeyListenerTest.java b/tests/tests/text/src/android/text/method/cts/MultiTapKeyListenerTest.java
index c73b7fa..206c6a5 100644
--- a/tests/tests/text/src/android/text/method/cts/MultiTapKeyListenerTest.java
+++ b/tests/tests/text/src/android/text/method/cts/MultiTapKeyListenerTest.java
@@ -39,9 +39,13 @@
 import android.view.KeyEvent;
 import android.widget.TextView.BufferType;
 
+import org.junit.After;
+import org.junit.Before;
 import org.junit.Test;
 import org.junit.runner.RunWith;
 
+import java.io.IOException;
+
 @LargeTest
 @RunWith(AndroidJUnit4.class)
 public class MultiTapKeyListenerTest extends KeyListenerTestCase {
@@ -50,6 +54,17 @@
      */
     private static final long TIME_OUT = 3000;
 
+    @Before
+    public void setup() throws IOException {
+        super.setup();
+        enableAutoCapSettings();
+    }
+
+    @After
+    public void tearDown() throws IOException {
+        resetAutoCapSettings();
+    }
+
     @Test
     public void testConstructor() {
         new MultiTapKeyListener(Capitalize.NONE, true);
diff --git a/tests/tests/text/src/android/text/method/cts/QwertyKeyListenerTest.java b/tests/tests/text/src/android/text/method/cts/QwertyKeyListenerTest.java
index c2e684a..c527257 100644
--- a/tests/tests/text/src/android/text/method/cts/QwertyKeyListenerTest.java
+++ b/tests/tests/text/src/android/text/method/cts/QwertyKeyListenerTest.java
@@ -32,12 +32,28 @@
 import android.view.KeyEvent;
 import android.widget.TextView.BufferType;
 
+import org.junit.After;
+import org.junit.Before;
 import org.junit.Test;
 import org.junit.runner.RunWith;
 
+import java.io.IOException;
+
 @MediumTest
 @RunWith(AndroidJUnit4.class)
 public class QwertyKeyListenerTest extends KeyListenerTestCase {
+
+    @Before
+    public void setup() throws IOException {
+        super.setup();
+        enableAutoCapSettings();
+    }
+
+    @After
+    public void tearDown() throws IOException {
+        resetAutoCapSettings();
+    }
+
     @Test
     public void testConstructor() {
         new QwertyKeyListener(Capitalize.NONE, false);
diff --git a/tests/tests/theme/Android.mk b/tests/tests/theme/Android.mk
index 71d576d..ff8536b 100644
--- a/tests/tests/theme/Android.mk
+++ b/tests/tests/theme/Android.mk
@@ -24,7 +24,9 @@
 # When built, explicitly put it in the data partition.
 LOCAL_MODULE_PATH := $(TARGET_OUT_DATA_APPS)
 
-LOCAL_STATIC_JAVA_LIBRARIES := ctstestrunner legacy-android-test
+LOCAL_STATIC_JAVA_LIBRARIES := ctstestrunner
+
+LOCAL_JAVA_LIBRARIES := android.test.runner.stubs android.test.base.stubs
 
 LOCAL_SRC_FILES := $(call all-java-files-under, src)
 
diff --git a/tests/tests/theme/AndroidTest.xml b/tests/tests/theme/AndroidTest.xml
index 39da798..95a7a88 100644
--- a/tests/tests/theme/AndroidTest.xml
+++ b/tests/tests/theme/AndroidTest.xml
@@ -14,6 +14,7 @@
      limitations under the License.
 -->
 <configuration description="Config for CTS Theme test cases">
+    <option name="test-suite-tag" value="cts" />
     <option name="config-descriptor:metadata" key="component" value="uitoolkit" />
     <target_preparer class="com.android.tradefed.targetprep.suite.SuiteApkInstaller">
         <option name="cleanup-apks" value="true" />
diff --git a/tests/tests/toast/AndroidTest.xml b/tests/tests/toast/AndroidTest.xml
index 66ceb5d..0f00913 100644
--- a/tests/tests/toast/AndroidTest.xml
+++ b/tests/tests/toast/AndroidTest.xml
@@ -14,6 +14,7 @@
      limitations under the License.
 -->
 <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="not-shardable" value="true" />
     <target_preparer class="com.android.tradefed.targetprep.suite.SuiteApkInstaller">
diff --git a/tests/tests/transition/Android.mk b/tests/tests/transition/Android.mk
index cba0b45..0937fc1 100644
--- a/tests/tests/transition/Android.mk
+++ b/tests/tests/transition/Android.mk
@@ -31,8 +31,7 @@
     android-common \
     compatibility-device-util \
     ctstestrunner \
-    platform-test-annotations \
-    legacy-android-test
+    platform-test-annotations
 
 LOCAL_JAVA_LIBRARIES := android.test.runner
 
diff --git a/tests/tests/transition/AndroidTest.xml b/tests/tests/transition/AndroidTest.xml
index 4632cbe..e75e8de 100644
--- a/tests/tests/transition/AndroidTest.xml
+++ b/tests/tests/transition/AndroidTest.xml
@@ -14,6 +14,7 @@
      limitations under the License.
 -->
 <configuration description="Config for CTS Transition test cases">
+    <option name="test-suite-tag" value="cts" />
     <option name="config-descriptor:metadata" key="component" value="uitoolkit" />
     <target_preparer class="com.android.tradefed.targetprep.suite.SuiteApkInstaller">
         <option name="cleanup-apks" value="true" />
diff --git a/tests/tests/tv/Android.mk b/tests/tests/tv/Android.mk
index 30429d3..24cf139 100644
--- a/tests/tests/tv/Android.mk
+++ b/tests/tests/tv/Android.mk
@@ -27,7 +27,7 @@
 
 LOCAL_PACKAGE_NAME := CtsTvTestCases
 
-LOCAL_JAVA_LIBRARIES := android.test.runner
+LOCAL_JAVA_LIBRARIES := android.test.runner android.test.base.stubs
 
 LOCAL_STATIC_JAVA_LIBRARIES := compatibility-device-util ctstestrunner
 
diff --git a/tests/tests/uiautomation/Android.mk b/tests/tests/uiautomation/Android.mk
index eb6ed68..347c55f 100644
--- a/tests/tests/uiautomation/Android.mk
+++ b/tests/tests/uiautomation/Android.mk
@@ -23,7 +23,9 @@
 # Tag this module as a cts test artifact
 LOCAL_COMPATIBILITY_SUITE := cts vts general-tests
 
-LOCAL_STATIC_JAVA_LIBRARIES := ctstestrunner ub-uiautomator legacy-android-test
+LOCAL_STATIC_JAVA_LIBRARIES := ctstestrunner ub-uiautomator
+
+LOCAL_JAVA_LIBRARIES := android.test.base.stubs
 
 LOCAL_SRC_FILES := $(call all-java-files-under, src)
 
diff --git a/tests/tests/uiautomation/AndroidTest.xml b/tests/tests/uiautomation/AndroidTest.xml
index 183a08e..c4bcdd6 100644
--- a/tests/tests/uiautomation/AndroidTest.xml
+++ b/tests/tests/uiautomation/AndroidTest.xml
@@ -14,6 +14,7 @@
      limitations under the License.
 -->
 <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" />
     <target_preparer class="com.android.tradefed.targetprep.suite.SuiteApkInstaller">
         <option name="cleanup-apks" value="true" />
diff --git a/tests/tests/uiautomation/src/android/app/uiautomation/cts/UiAutomationTestA11yService.java b/tests/tests/uiautomation/src/android/app/uiautomation/cts/UiAutomationTestA11yService.java
index cf46157..ca598bc 100644
--- a/tests/tests/uiautomation/src/android/app/uiautomation/cts/UiAutomationTestA11yService.java
+++ b/tests/tests/uiautomation/src/android/app/uiautomation/cts/UiAutomationTestA11yService.java
@@ -49,7 +49,7 @@
 
     public boolean isConnected() {
         try {
-            if (getRootInActiveWindow() == null) {
+            if (getServiceInfo() == null) {
                 return false;
             }
             return true;
diff --git a/tests/tests/uidisolation/Android.mk b/tests/tests/uidisolation/Android.mk
index e9ee3e9..a820c95 100644
--- a/tests/tests/uidisolation/Android.mk
+++ b/tests/tests/uidisolation/Android.mk
@@ -24,9 +24,13 @@
 # Tag this module as a cts test artifact
 LOCAL_COMPATIBILITY_SUITE := cts vts general-tests
 
-LOCAL_STATIC_JAVA_LIBRARIES := ctstestrunner ctstestserver legacy-android-test
+LOCAL_STATIC_JAVA_LIBRARIES := ctstestrunner ctstestserver
 
-LOCAL_JAVA_LIBRARIES := org.apache.http.legacy
+LOCAL_JAVA_LIBRARIES := \
+    org.apache.http.legacy \
+    android.test.runner.stubs \
+    android.test.base.stubs \
+
 
 LOCAL_SRC_FILES := $(call all-java-files-under, src)
 
diff --git a/tests/tests/uidisolation/AndroidTest.xml b/tests/tests/uidisolation/AndroidTest.xml
index 02df495..0a83f12 100644
--- a/tests/tests/uidisolation/AndroidTest.xml
+++ b/tests/tests/uidisolation/AndroidTest.xml
@@ -14,6 +14,7 @@
      limitations under the License.
 -->
 <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="not-shardable" value="true" />
     <target_preparer class="com.android.tradefed.targetprep.suite.SuiteApkInstaller">
diff --git a/tests/tests/uirendering/Android.mk b/tests/tests/uirendering/Android.mk
index 9cc07b9..f94b611 100644
--- a/tests/tests/uirendering/Android.mk
+++ b/tests/tests/uirendering/Android.mk
@@ -31,8 +31,7 @@
     ctsdeviceutillegacy \
     ctstestrunner \
     mockito-target-minus-junit4 \
-    android-support-test \
-    legacy-android-test
+    android-support-test
 
 LOCAL_SRC_FILES := $(call all-java-files-under, src)
 
diff --git a/tests/tests/uirendering/res/drawable/circle.xml b/tests/tests/uirendering/res/drawable/circle.xml
new file mode 100644
index 0000000..9d47a8a
--- /dev/null
+++ b/tests/tests/uirendering/res/drawable/circle.xml
@@ -0,0 +1,29 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+  ~ Copyright (C) 2017 The Android Open Source Project
+  ~
+  ~ Licensed under the Apache License, Version 2.0 (the "License");
+  ~ you may not use this file except in compliance with the License.
+  ~ You may obtain a copy of the License at
+  ~
+  ~      http://www.apache.org/licenses/LICENSE-2.0
+  ~
+  ~ Unless required by applicable law or agreed to in writing, software
+  ~ distributed under the License is distributed on an "AS IS" BASIS,
+  ~ WITHOUT 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="90px"
+        android:width="90px"
+        android:viewportHeight="1"
+        android:viewportWidth="1" >
+
+    <group>
+        <path
+            android:name="box0"
+            android:pathData="m0,0.5a0.5,0.5 0 1,0 1,0a0.5,0.5 0 1,0 -1,0"
+            android:fillColor="#FF0000" />
+    </group>
+</vector>
\ No newline at end of file
diff --git a/tests/tests/uirendering/src/android/uirendering/cts/testclasses/AlphaBlendTest.java b/tests/tests/uirendering/src/android/uirendering/cts/testclasses/AlphaBlendTest.java
new file mode 100644
index 0000000..8c777f8
--- /dev/null
+++ b/tests/tests/uirendering/src/android/uirendering/cts/testclasses/AlphaBlendTest.java
@@ -0,0 +1,84 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.uirendering.cts.testclasses;
+
+import android.content.Context;
+import android.graphics.Canvas;
+import android.graphics.Color;
+import android.graphics.Point;
+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.testinfrastructure.ActivityTestBase;
+import android.uirendering.cts.testinfrastructure.ViewInitializer;
+import android.view.View;
+import android.view.ViewGroup;
+import android.widget.FrameLayout;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+@MediumTest
+@RunWith(AndroidJUnit4.class)
+public class AlphaBlendTest extends ActivityTestBase {
+
+    class ViewWithAlpha extends View {
+        public ViewWithAlpha(Context context) {
+            super(context);
+        }
+
+        @Override
+        protected void onDraw(Canvas canvas) {
+            canvas.drawColor(Color.RED);
+            canvas.drawColor(Color.BLUE);
+        }
+
+        @Override
+        public boolean hasOverlappingRendering() {
+            return false;
+        }
+    }
+
+    /*
+     * The following test verifies that a RED and BLUE paints on a non-overlapping view with a 0.5f
+     * alpha blends correctly with a BLACK parent (without using an offscreen surface).
+     */
+    @Test
+    public void testBlendAlphaNonOverlappingView() {
+
+        ViewInitializer initializer = new ViewInitializer() {
+
+            @Override
+            public void initializeView(View view) {
+                FrameLayout root = (FrameLayout) view.findViewById(R.id.frame_layout);
+                root.setBackgroundColor(Color.BLACK);
+
+                final ViewWithAlpha child = new ViewWithAlpha(view.getContext());
+
+                child.setLayoutParams(new FrameLayout.LayoutParams(
+                        ViewGroup.LayoutParams.FILL_PARENT,  ViewGroup.LayoutParams.FILL_PARENT));
+                child.setAlpha(0.5f);
+                root.addView(child);
+            }
+        };
+
+        createTest()
+            .addLayout(R.layout.frame_layout, initializer, true)
+            .runWithVerifier(new ColorVerifier(0xff40007f));
+    }
+}
+
diff --git a/tests/tests/uirendering/src/android/uirendering/cts/testclasses/ColorFilterTests.java b/tests/tests/uirendering/src/android/uirendering/cts/testclasses/ColorFilterTests.java
new file mode 100644
index 0000000..4fd227d
--- /dev/null
+++ b/tests/tests/uirendering/src/android/uirendering/cts/testclasses/ColorFilterTests.java
@@ -0,0 +1,59 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.uirendering.cts.testclasses;
+
+import android.graphics.Bitmap;
+import android.graphics.Canvas;
+import android.graphics.Color;
+import android.graphics.ColorMatrixColorFilter;
+import android.graphics.Paint;
+import android.support.test.filters.MediumTest;
+import android.support.test.runner.AndroidJUnit4;
+import android.uirendering.cts.bitmapverifiers.ColorVerifier;
+import android.uirendering.cts.testinfrastructure.ActivityTestBase;
+
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+@MediumTest
+@RunWith(AndroidJUnit4.class)
+public class ColorFilterTests extends ActivityTestBase {
+
+    @Test
+    public void testColorMatrix() {
+        createTest()
+                .addCanvasClient((canvas, width, height) -> {
+                    Bitmap whiteBitmap = Bitmap.createBitmap(width, height, Bitmap.Config.ARGB_8888);
+                    whiteBitmap.eraseColor(Color.WHITE);
+
+                    Paint paint = new Paint();
+                    canvas.drawBitmap(whiteBitmap, 0, 0, paint);
+
+                    paint.setAntiAlias(true);
+                    paint.setColorFilter(new ColorMatrixColorFilter(new float[] {
+                            -1, 0, 0, 0, 255,
+                            0, -1, 0, 0, 255,
+                            0, 0, -1, 0, 255,
+                            0, 0, 0, 1, 0
+                            }));
+                    canvas.drawBitmap(whiteBitmap, 0, 0, paint);
+
+                }, true)
+                .runWithVerifier(new ColorVerifier(Color.BLACK));
+    }
+
+}
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 ed0110a..ae8f57e 100644
--- a/tests/tests/uirendering/src/android/uirendering/cts/testclasses/LayerTests.java
+++ b/tests/tests/uirendering/src/android/uirendering/cts/testclasses/LayerTests.java
@@ -47,6 +47,7 @@
 import android.view.ViewTreeObserver;
 import android.widget.FrameLayout;
 
+import org.junit.Ignore;
 import org.junit.Test;
 import org.junit.runner.RunWith;
 
@@ -294,9 +295,9 @@
             .runWithVerifier(new RectVerifier(Color.WHITE, Color.GREEN, new Rect(40, 40, 70, 70)));
     }
 
-    // Note: This test will fail for Skia pipeline, but that is OK.
-    // TODO: delete this test when Skia pipeline is default and modify next test
+    // STOPSHIP: delete this test when Skia pipeline ships as the default and modify next test
     // testSaveLayerUnclippedWithColorFilterSW to run for both HW and SW
+    @Ignore
     @Test
     public void testSaveLayerUnclippedWithColorFilterHW() {
         // verify that HW can draw nested unclipped layers with chained color filters
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 ffb263d..f4db101 100644
--- a/tests/tests/uirendering/src/android/uirendering/cts/testclasses/VectorDrawableTests.java
+++ b/tests/tests/uirendering/src/android/uirendering/cts/testclasses/VectorDrawableTests.java
@@ -16,17 +16,29 @@
 
 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;
+import android.graphics.Point;
 import android.graphics.Rect;
 import android.graphics.drawable.VectorDrawable;
 import android.support.test.filters.MediumTest;
 import android.support.test.runner.AndroidJUnit4;
 import android.uirendering.cts.R;
 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.View;
+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)
@@ -44,5 +56,94 @@
                 .runWithVerifier(
                         new RectVerifier(Color.WHITE, Color.RED, new Rect(0, 0, 25, 25)));
     }
+
+    class VectorDrawableView extends View {
+        private VectorDrawable mVd;
+        private Rect mVdBounds;
+
+        public VectorDrawableView(Context context) {
+            super(context);
+            mVd = (VectorDrawable) context.getResources().getDrawable(R.drawable.circle, null);
+        }
+
+        @Override
+        protected void onDraw(Canvas canvas) {
+            mVd.setBounds(mVdBounds);
+            mVd.draw(canvas);
+        }
+
+        public void setVDSize(Rect vdBounds) {
+            mVdBounds = vdBounds;
+        }
+    }
+
+    /*
+     * The following test verifies that VectorDrawable.setBounds invalidates the bitmap cache.
+     */
+    @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();
+            }
+        };
+
+        createTest()
+            .addLayout(R.layout.frame_layout, initializer, true, testFinishedFence)
+            .runWithVerifier(new SamplePointVerifier(
+                new Point[] { new Point(0, 0) },
+                new int[] { 0xff0000ff }
+            ));
+    }
 }
 
diff --git a/tests/tests/util/Android.mk b/tests/tests/util/Android.mk
index ed7a61c..0ad2452 100644
--- a/tests/tests/util/Android.mk
+++ b/tests/tests/util/Android.mk
@@ -27,8 +27,7 @@
 LOCAL_STATIC_JAVA_LIBRARIES := \
     android-support-annotations \
     android-support-test \
-    ctstestrunner \
-    legacy-android-test
+    ctstestrunner
 
 LOCAL_SRC_FILES := $(call all-java-files-under, src)
 
diff --git a/tests/tests/util/AndroidTest.xml b/tests/tests/util/AndroidTest.xml
index b022356..8be8204 100644
--- a/tests/tests/util/AndroidTest.xml
+++ b/tests/tests/util/AndroidTest.xml
@@ -14,6 +14,7 @@
      limitations under the License.
 -->
 <configuration description="Config for CTS Util test cases">
+    <option name="test-suite-tag" value="cts" />
     <option name="config-descriptor:metadata" key="component" value="framework" />
     <target_preparer class="com.android.tradefed.targetprep.suite.SuiteApkInstaller">
         <option name="cleanup-apks" value="true" />
diff --git a/tests/tests/view/Android.mk b/tests/tests/view/Android.mk
index 533d98b..a678561 100644
--- a/tests/tests/view/Android.mk
+++ b/tests/tests/view/Android.mk
@@ -26,7 +26,7 @@
 
 LOCAL_MULTILIB := both
 
-LOCAL_JAVA_LIBRARIES := android.test.runner
+LOCAL_JAVA_LIBRARIES := android.test.runner android.test.base
 
 LOCAL_STATIC_JAVA_LIBRARIES := \
     android-support-test \
@@ -35,8 +35,7 @@
     ctstestrunner \
     mockito-target-minus-junit4 \
     platform-test-annotations \
-    ub-uiautomator \
-    legacy-android-test
+    ub-uiautomator
 
 LOCAL_JNI_SHARED_LIBRARIES := libctsview_jni libnativehelper_compat_libc++
 
diff --git a/tests/tests/view/AndroidManifest.xml b/tests/tests/view/AndroidManifest.xml
index 7c35627..6cddaf3 100644
--- a/tests/tests/view/AndroidManifest.xml
+++ b/tests/tests/view/AndroidManifest.xml
@@ -288,15 +288,6 @@
             </intent-filter>
         </activity>
 
-        <activity android:name="android.view.cts.PanicPressBackActivity"
-                  android:screenOrientation="locked"
-                  android:label="PanicPressBackActivity">
-            <intent-filter>
-                <action android:name="android.intent.action.MAIN" />
-                <category android:name="android.intent.category.FRAMEWORK_INSTRUMENTATION_TEST" />
-            </intent-filter>
-        </activity>
-
         <activity android:name="android.view.cts.DragDropActivity"
                   android:screenOrientation="portrait"
                   android:label="DragDropActivity">
@@ -348,6 +339,18 @@
                 <category android:name="android.intent.category.LAUNCHER" />
             </intent-filter>
         </activity>
+
+        <activity android:name="android.view.cts.KeyEventInterceptTestActivity">
+            <intent-filter>
+                <action android:name="android.intent.action.MAIN" />
+            </intent-filter>
+        </activity>
+
+        <activity android:name="android.view.cts.TouchDelegateTestActivity">
+            <intent-filter>
+                <action android:name="android.intent.action.MAIN" />
+            </intent-filter>
+        </activity>
     </application>
 
     <instrumentation android:name="android.support.test.runner.AndroidJUnitRunner"
diff --git a/tests/tests/view/AndroidTest.xml b/tests/tests/view/AndroidTest.xml
index 9ed52bc..0ae9f42 100644
--- a/tests/tests/view/AndroidTest.xml
+++ b/tests/tests/view/AndroidTest.xml
@@ -14,6 +14,7 @@
      limitations under the License.
 -->
 <configuration description="Config for CTS View test cases">
+    <option name="test-suite-tag" value="cts" />
     <option name="config-descriptor:metadata" key="component" value="uitoolkit" />
     <target_preparer class="com.android.tradefed.targetprep.suite.SuiteApkInstaller">
         <option name="cleanup-apks" value="true" />
diff --git a/tests/tests/view/res/layout/focus_handling_layout.xml b/tests/tests/view/res/layout/focus_handling_layout.xml
index 6966e53..d2f7778 100644
--- a/tests/tests/view/res/layout/focus_handling_layout.xml
+++ b/tests/tests/view/res/layout/focus_handling_layout.xml
@@ -22,14 +22,14 @@
 
     <View
         android:id="@+id/view1"
-        android:layout_width="wrap_content"
-        android:layout_height="wrap_content"
+        android:layout_width="50dp"
+        android:layout_height="30dp"
         android:text="@string/id_ok"/>
 
     <View
         android:id="@+id/view2"
-        android:layout_width="wrap_content"
-        android:layout_height="wrap_content"
+        android:layout_width="50dp"
+        android:layout_height="30dp"
         android:layout_toRightOf="@id/view1"
         android:layout_alignTop="@id/view1"
         android:nextFocusLeft="@id/view1"
@@ -37,8 +37,8 @@
 
     <View
         android:id="@+id/view3"
-        android:layout_width="wrap_content"
-        android:layout_height="wrap_content"
+        android:layout_width="50dp"
+        android:layout_height="30dp"
         android:layout_below="@id/view1"
         android:layout_alignLeft="@id/view1"
         android:nextFocusUp="@id/view1"
@@ -46,8 +46,8 @@
 
     <View
         android:id="@+id/view4"
-        android:layout_width="wrap_content"
-        android:layout_height="wrap_content"
+        android:layout_width="50dp"
+        android:layout_height="30dp"
         android:layout_toRightOf="@id/view3"
         android:layout_alignTop="@id/view3"
         android:layout_alignRight="@id/view2"
@@ -57,8 +57,8 @@
 
     <LinearLayout
         android:id="@+id/auto_test_area"
-        android:layout_width="wrap_content"
-        android:layout_height="wrap_content"
+        android:layout_width="match_parent"
+        android:layout_height="200dp"
         android:orientation="vertical"
         android:layout_below="@id/view4"
         android:layout_alignParentLeft="true">
diff --git a/tests/tests/view/res/layout/key_fallback_layout.xml b/tests/tests/view/res/layout/key_fallback_layout.xml
new file mode 100644
index 0000000..5d5cef1
--- /dev/null
+++ b/tests/tests/view/res/layout/key_fallback_layout.xml
@@ -0,0 +1,58 @@
+<?xml version="1.0" encoding="utf-8"?>
+<LinearLayout
+    xmlns:android="http://schemas.android.com/apk/res/android"
+    android:orientation="vertical"
+    android:layout_width="match_parent"
+    android:layout_height="match_parent">
+    <LinearLayout
+        android:orientation="horizontal"
+        android:layout_width="wrap_content"
+        android:layout_height="wrap_content">
+        <Button
+            android:id="@+id/higher_in_normal"
+            android:translationZ="3dp"
+            android:layout_width="wrap_content"
+            android:layout_height="wrap_content" />
+        <Button
+            android:layout_width="wrap_content"
+            android:layout_height="wrap_content" />
+        <Button
+            android:id="@+id/last_in_normal"
+            android:layout_width="wrap_content"
+            android:layout_height="wrap_content" />
+    </LinearLayout>
+    <LinearLayout
+        android:id="@+id/higher_group"
+        android:translationZ="1dp"
+        android:orientation="horizontal"
+        android:layout_width="wrap_content"
+        android:layout_height="wrap_content">
+        <Button
+            android:layout_width="wrap_content"
+            android:layout_height="wrap_content" />
+        <Button
+            android:id="@+id/lower_in_higher"
+            android:translationZ="-2dp"
+            android:layout_width="wrap_content"
+            android:layout_height="wrap_content" />
+        <Button
+            android:id="@+id/last_in_higher"
+            android:layout_width="wrap_content"
+            android:layout_height="wrap_content" />
+    </LinearLayout>
+    <LinearLayout
+        android:orientation="horizontal"
+        android:layout_width="wrap_content"
+        android:layout_height="wrap_content">
+        <Button
+            android:layout_width="wrap_content"
+            android:layout_height="wrap_content" />
+        <Button
+            android:layout_width="wrap_content"
+            android:layout_height="wrap_content" />
+        <Button
+            android:id="@+id/last_button"
+            android:layout_width="wrap_content"
+            android:layout_height="wrap_content" />
+    </LinearLayout>
+</LinearLayout>
\ No newline at end of file
diff --git a/tests/tests/view/res/layout/touch_delegate_test_activity_layout.xml b/tests/tests/view/res/layout/touch_delegate_test_activity_layout.xml
new file mode 100644
index 0000000..0780046
--- /dev/null
+++ b/tests/tests/view/res/layout/touch_delegate_test_activity_layout.xml
@@ -0,0 +1,28 @@
+<?xml version="1.0" encoding="utf-8"?>
+
+<!-- Copyright (C) 2017 The Android Open Source Project
+
+     Licensed under the Apache License, Version 2.0 (the "License");
+     you may not use this file except in compliance with the License.
+     You may obtain a copy of the License at
+
+         http://www.apache.org/licenses/LICENSE-2.0
+
+     Unless required by applicable law or agreed to in writing, software
+     distributed under the License is distributed on an "AS IS" BASIS,
+     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+     See the License for the specific language governing permissions and
+     limitations under the License.
+-->
+
+<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
+              android:id="@+id/layout"
+              android:layout_width="match_parent"
+              android:layout_height="match_parent">
+    <Button
+        android:id="@+id/button"
+        android:layout_width="wrap_content"
+        android:layout_height="wrap_content"
+        android:layout_centerInParent="true"
+    />
+</RelativeLayout>
\ No newline at end of file
diff --git a/tests/tests/view/src/android/view/cts/DragDropTest.java b/tests/tests/view/src/android/view/cts/DragDropTest.java
index 39ae2d3..d74acef 100644
--- a/tests/tests/view/src/android/view/cts/DragDropTest.java
+++ b/tests/tests/view/src/android/view/cts/DragDropTest.java
@@ -25,6 +25,8 @@
 import android.content.ClipData;
 import android.content.ClipDescription;
 import android.content.pm.PackageManager;
+import android.os.Parcel;
+import android.os.Parcelable;
 import android.os.SystemClock;
 import android.support.test.InstrumentationRegistry;
 import android.support.test.rule.ActivityTestRule;
@@ -35,8 +37,6 @@
 import android.view.View;
 import android.view.ViewGroup;
 
-import com.android.internal.util.ArrayUtils;
-
 import org.junit.After;
 import org.junit.Before;
 import org.junit.Rule;
@@ -44,9 +44,10 @@
 import org.junit.runner.RunWith;
 
 import java.util.ArrayList;
-import java.util.Objects;
+import java.util.Arrays;
 import java.util.concurrent.CountDownLatch;
 import java.util.concurrent.TimeUnit;
+import java.util.stream.IntStream;
 
 @RunWith(AndroidJUnit4.class)
 public class DragDropTest {
@@ -64,60 +65,81 @@
     private CountDownLatch mStartReceived;
     private CountDownLatch mEndReceived;
 
-    private static boolean equal(ClipDescription d1, ClipDescription d2) {
-        if ((d1 == null) != (d2 == null)) {
-            return false;
-        }
-        if (d1 == null) {
+    /**
+     * Check whether two objects have the same binary data when dumped into Parcels
+     * @return True if the objects are equal
+     */
+    private static boolean compareParcelables(Parcelable obj1, Parcelable obj2) {
+        if (obj1 == null && obj2 == null) {
             return true;
         }
-        return d1.getLabel().equals(d2.getLabel()) &&
-                d1.getMimeTypeCount() == 1 && d2.getMimeTypeCount() == 1 &&
-                d1.getMimeType(0).equals(d2.getMimeType(0));
-    }
-
-    private static boolean equal(ClipData.Item i1, ClipData.Item i2) {
-        return Objects.equals(i1.getIntent(), i2.getIntent()) &&
-                Objects.equals(i1.getHtmlText(), i2.getHtmlText()) &&
-                Objects.equals(i1.getText(), i2.getText()) &&
-                Objects.equals(i1.getUri(), i2.getUri());
-    }
-
-    private static boolean equal(ClipData d1, ClipData d2) {
-        if ((d1 == null) != (d2 == null)) {
+        if (obj1 == null || obj2 == null) {
             return false;
         }
-        if (d1 == null) {
-            return true;
-        }
-        return equal(d1.getDescription(), d2.getDescription()) &&
-                Objects.equals(d1.getIcon(), d2.getIcon()) &&
-                d1.getItemCount() == 1 && d2.getItemCount() == 1 &&
-                equal(d1.getItemAt(0), d2.getItemAt(0));
+        Parcel p1 = Parcel.obtain();
+        obj1.writeToParcel(p1, 0);
+        Parcel p2 = Parcel.obtain();
+        obj2.writeToParcel(p2, 0);
+        boolean result = Arrays.equals(p1.marshall(), p2.marshall());
+        p1.recycle();
+        p2.recycle();
+        return result;
     }
 
-    private static boolean equal(DragEvent ev1, DragEvent ev2) {
-        return ev1.getAction() == ev2.getAction() &&
-                ev1.getX() == ev2.getX() &&
-                ev1.getY() == ev2.getY() &&
-                equal(ev1.getClipData(), ev2.getClipData()) &&
-                equal(ev1.getClipDescription(), ev2.getClipDescription()) &&
-                Objects.equals(ev1.getDragAndDropPermissions(), ev2.getDragAndDropPermissions()) &&
-                Objects.equals(ev1.getLocalState(), ev2.getLocalState()) &&
-                ev1.getResult() == ev2.getResult();
-    }
+    private static final ClipDescription sClipDescription =
+            new ClipDescription("TestLabel", new String[]{"text/plain"});
+    private static final ClipData sClipData =
+            new ClipData(sClipDescription, new ClipData.Item("TestText"));
+    private static final Object sLocalState = new Object(); // just check if null or not
 
     class LogEntry {
-        public View v;
-        public DragEvent ev;
+        public View view;
 
-        public LogEntry(View v, DragEvent ev) {
-            this.v = v;
-            this.ev = DragEvent.obtain(ev);
+        // Public DragEvent fields
+        public int action; // DragEvent.getAction()
+        public float x; // DragEvent.getX()
+        public float y; // DragEvent.getY()
+        public ClipData clipData; // DragEvent.getClipData()
+        public ClipDescription clipDescription; // DragEvent.getClipDescription()
+        public Object localState; // DragEvent.getLocalState()
+        public boolean result; // DragEvent.getResult()
+
+        LogEntry(View v, int action, float x, float y, ClipData clipData,
+                ClipDescription clipDescription, Object localState, boolean result) {
+            this.view = v;
+            this.action = action;
+            this.x = x;
+            this.y = y;
+            this.clipData = clipData;
+            this.clipDescription = clipDescription;
+            this.localState = localState;
+            this.result = result;
         }
 
-        public boolean eq(LogEntry other) {
-            return v == other.v && equal(ev, other.ev);
+        @Override
+        public boolean equals(Object obj) {
+            if (this == obj) {
+                return true;
+            }
+            if (!(obj instanceof LogEntry)) {
+                return false;
+            }
+            final LogEntry other = (LogEntry) obj;
+            return view == other.view && action == other.action
+                    && x == other.x && y == other.y
+                    && compareParcelables(clipData, other.clipData)
+                    && compareParcelables(clipDescription, other.clipDescription)
+                    && localState == other.localState
+                    && result == other.result;
+        }
+
+        @Override
+        public String toString() {
+            StringBuilder sb = new StringBuilder();
+            sb.append("DragEvent {action=").append(action).append(" x=").append(x).append(" y=")
+                    .append(y).append(" result=").append(result).append("}")
+                    .append(" @ ").append(view);
+            return sb.toString();
         }
     }
 
@@ -126,19 +148,18 @@
     final private ArrayList<LogEntry> mActual = new ArrayList<LogEntry> ();
     final private ArrayList<LogEntry> mExpected = new ArrayList<LogEntry> ();
 
-    private static ClipDescription createClipDescription() {
-        return new ClipDescription("TestLabel", new String[]{"text/plain"});
+    private static ClipData obtainClipData(int action) {
+        if (action == DragEvent.ACTION_DROP) {
+            return sClipData;
+        }
+        return null;
     }
 
-    private static ClipData createClipData() {
-        return new ClipData(createClipDescription(), new ClipData.Item("TestText"));
-    }
-
-    static private DragEvent obtainDragEvent(int action, int x, int y, boolean result) {
-        final ClipDescription description =
-                action != DragEvent.ACTION_DRAG_ENDED ? createClipDescription() : null;
-        final ClipData data = action == DragEvent.ACTION_DROP ? createClipData() : null;
-        return DragEvent.obtain(action, x, y, null, description, data, null, result);
+    private static ClipDescription obtainClipDescription(int action) {
+        if (action == DragEvent.ACTION_DRAG_ENDED) {
+            return null;
+        }
+        return sClipDescription;
     }
 
     private void logEvent(View v, DragEvent ev) {
@@ -148,19 +169,23 @@
         if (ev.getAction() == DragEvent.ACTION_DRAG_ENDED) {
             mEndReceived.countDown();
         }
-        mActual.add(new LogEntry(v, ev));
+        mActual.add(new LogEntry(v, ev.getAction(), ev.getX(), ev.getY(), ev.getClipData(),
+                ev.getClipDescription(), ev.getLocalState(), ev.getResult()));
     }
 
     // Add expected event for a view, with zero coordinates.
     private void expectEvent5(int action, int viewId) {
         View v = mActivity.findViewById(viewId);
-        mExpected.add(new LogEntry(v, obtainDragEvent(action, 0, 0, false)));
+        mExpected.add(new LogEntry(v, action, 0, 0, obtainClipData(action),
+                obtainClipDescription(action), sLocalState, false));
     }
 
     // Add expected event for a view.
-    private void expectEndEvent(int viewId, int x, int y, boolean result) {
+    private void expectEndEvent(int viewId, float x, float y, boolean result) {
         View v = mActivity.findViewById(viewId);
-        mExpected.add(new LogEntry(v, obtainDragEvent(DragEvent.ACTION_DRAG_ENDED, x, y, result)));
+        int action = DragEvent.ACTION_DRAG_ENDED;
+        mExpected.add(new LogEntry(v, action, x, y, obtainClipData(action),
+                obtainClipDescription(action), sLocalState, result));
     }
 
     // Add expected successful-end event for a view.
@@ -173,9 +198,12 @@
     private void expectEndEventFailure6(int viewId, int releaseViewId) {
         View v = mActivity.findViewById(viewId);
         View release = mActivity.findViewById(releaseViewId);
-        int [] releaseLoc = release.getLocationOnScreen();
-        mExpected.add(new LogEntry(v, obtainDragEvent(DragEvent.ACTION_DRAG_ENDED,
-                releaseLoc[0] + 6, releaseLoc[1] + 6, false)));
+        int [] releaseLoc = new int[2];
+        release.getLocationOnScreen(releaseLoc);
+        int action = DragEvent.ACTION_DRAG_ENDED;
+        mExpected.add(new LogEntry(v, action,
+                releaseLoc[0] + 6, releaseLoc[1] + 6, obtainClipData(action),
+                obtainClipDescription(action), sLocalState, false));
     }
 
     // Add expected event for a view, with coordinates over view locationViewId, with the specified
@@ -183,11 +211,14 @@
     private void expectEventWithOffset(int action, int viewId, int locationViewId, int offset) {
         View v = mActivity.findViewById(viewId);
         View locationView = mActivity.findViewById(locationViewId);
-        int [] viewLocation = v.getLocationOnScreen();
-        int [] locationViewLocation = locationView.getLocationOnScreen();
-        mExpected.add(new LogEntry(v, obtainDragEvent(action,
+        int [] viewLocation = new int[2];
+        v.getLocationOnScreen(viewLocation);
+        int [] locationViewLocation = new int[2];
+        locationView.getLocationOnScreen(locationViewLocation);
+        mExpected.add(new LogEntry(v, action,
                 locationViewLocation[0] - viewLocation[0] + offset,
-                locationViewLocation[1] - viewLocation[1] + offset, false)));
+                locationViewLocation[1] - viewLocation[1] + offset, obtainClipData(action),
+                obtainClipDescription(action), sLocalState, false));
     }
 
     private void expectEvent5(int action, int viewId, int locationViewId) {
@@ -203,7 +234,8 @@
     private void injectMouseWithOffset(int viewId, int action, int offset) {
         runOnMain(() -> {
             View v = mActivity.findViewById(viewId);
-            int [] destLoc = v.getLocationOnScreen();
+            int [] destLoc = new int [2];
+            v.getLocationOnScreen(destLoc);
             long downTime = SystemClock.uptimeMillis();
             MotionEvent event = MotionEvent.obtain(downTime, downTime, action,
                     destLoc[0] + offset, destLoc[1] + offset, 1);
@@ -237,8 +269,7 @@
         StringBuilder sb = new StringBuilder();
         for (int i = 0; i < log.size(); ++i) {
             LogEntry e = log.get(i);
-            sb.append("#").append(i + 1).append(": ").append(e.ev).append(" @ ").
-                    append(e.v.toString()).append('\n');
+            sb.append("#").append(i + 1).append(": ").append(e).append('\n');
         }
         return sb.toString();
     }
@@ -263,7 +294,7 @@
             }
 
             for (int i = 0; i < mActual.size(); ++i) {
-                if (!mActual.get(i).eq(mExpected.get(i))) {
+                if (!mActual.get(i).equals(mExpected.get(i))) {
                     failWithLogs("Actual event #" + (i + 1) + " is different from expected");
                 }
             }
@@ -321,7 +352,7 @@
             // Start drag.
             View v = mActivity.findViewById(R.id.draggable);
             assertTrue("Couldn't start drag",
-                    v.startDragAndDrop(createClipData(), new View.DragShadowBuilder(v), null, 0));
+                    v.startDragAndDrop(sClipData, new View.DragShadowBuilder(v), sLocalState, 0));
         });
 
         try {
@@ -585,6 +616,11 @@
         verifyEventLog();
     }
 
+    private boolean drawableStateContains(int resourceId, int attr) {
+        return IntStream.of(mActivity.findViewById(resourceId).getDrawableState())
+                .anyMatch(x -> x == attr);
+    }
+
     /**
      * Tests that state_drag_hovered and state_drag_can_accept are set correctly.
      */
@@ -600,52 +636,38 @@
                 logEvent(v, ev);
                 return true;
             });
-            assertFalse(ArrayUtils.contains(
-                    mActivity.findViewById(R.id.inner).getDrawableState(),
-                    android.R.attr.state_drag_can_accept));
+            assertFalse(drawableStateContains(R.id.inner, android.R.attr.state_drag_can_accept));
         });
 
         startDrag();
 
         runOnMain(() -> {
-            assertFalse(ArrayUtils.contains(
-                    mActivity.findViewById(R.id.inner).getDrawableState(),
-                    android.R.attr.state_drag_hovered));
-            assertTrue(ArrayUtils.contains(
-                    mActivity.findViewById(R.id.inner).getDrawableState(),
-                    android.R.attr.state_drag_can_accept));
+            assertFalse(drawableStateContains(R.id.inner, android.R.attr.state_drag_hovered));
+            assertTrue(drawableStateContains(R.id.inner, android.R.attr.state_drag_can_accept));
         });
 
         // Move mouse into the view.
         injectMouse5(R.id.inner, MotionEvent.ACTION_MOVE);
         runOnMain(() -> {
-            assertTrue(ArrayUtils.contains(
-                    mActivity.findViewById(R.id.inner).getDrawableState(),
-                    android.R.attr.state_drag_hovered));
+            assertTrue(drawableStateContains(R.id.inner, android.R.attr.state_drag_hovered));
         });
 
         // Move out.
         injectMouse5(R.id.subcontainer, MotionEvent.ACTION_MOVE);
         runOnMain(() -> {
-            assertFalse(ArrayUtils.contains(
-                    mActivity.findViewById(R.id.inner).getDrawableState(),
-                    android.R.attr.state_drag_hovered));
+            assertFalse(drawableStateContains(R.id.inner, android.R.attr.state_drag_hovered));
         });
 
         // Move in.
         injectMouse5(R.id.inner, MotionEvent.ACTION_MOVE);
         runOnMain(() -> {
-            assertTrue(ArrayUtils.contains(
-                    mActivity.findViewById(R.id.inner).getDrawableState(),
-                    android.R.attr.state_drag_hovered));
+            assertTrue(drawableStateContains(R.id.inner, android.R.attr.state_drag_hovered));
         });
 
         // Release there.
         injectMouse5(R.id.inner, MotionEvent.ACTION_UP);
         runOnMain(() -> {
-            assertFalse(ArrayUtils.contains(
-                    mActivity.findViewById(R.id.inner).getDrawableState(),
-                    android.R.attr.state_drag_hovered));
+            assertFalse(drawableStateContains(R.id.inner, android.R.attr.state_drag_hovered));
         });
     }
 }
\ No newline at end of file
diff --git a/tests/tests/view/src/android/view/cts/FocusFinderTest.java b/tests/tests/view/src/android/view/cts/FocusFinderTest.java
index 0f2119e..2505111 100644
--- a/tests/tests/view/src/android/view/cts/FocusFinderTest.java
+++ b/tests/tests/view/src/android/view/cts/FocusFinderTest.java
@@ -426,7 +426,6 @@
 
         // While we don't want to test details, we should at least verify basic correctness
         // like "left-to-right" ordering in well-behaved layouts
-        assertEquals(mLayout.findFocus(), mTopLeft);
         verifyNextFocus(mTopLeft, View.FOCUS_FORWARD, mTopRight);
         verifyNextFocus(mTopRight, View.FOCUS_FORWARD, mBottomLeft);
         verifyNextFocus(mBottomLeft, View.FOCUS_FORWARD, mBottomRight);
diff --git a/tests/tests/view/src/android/view/cts/KeyEventInterceptTest.java b/tests/tests/view/src/android/view/cts/KeyEventInterceptTest.java
new file mode 100644
index 0000000..598552b
--- /dev/null
+++ b/tests/tests/view/src/android/view/cts/KeyEventInterceptTest.java
@@ -0,0 +1,110 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.view.cts;
+
+import static org.junit.Assert.fail;
+
+import android.app.Instrumentation;
+import android.app.UiAutomation;
+import android.os.SystemClock;
+import android.support.test.InstrumentationRegistry;
+import android.support.test.filters.MediumTest;
+import android.support.test.rule.ActivityTestRule;
+import android.support.test.runner.AndroidJUnit4;
+import android.view.KeyEvent;
+
+import com.android.compatibility.common.util.PollingCheck;
+
+import org.junit.Before;
+import org.junit.Rule;
+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.
+ * The test will fail if any of these keys are received by the activity.
+ */
+@MediumTest
+@RunWith(AndroidJUnit4.class)
+public class KeyEventInterceptTest {
+    private KeyEventInterceptTestActivity mActivity;
+    private Instrumentation mInstrumentation;
+
+    @Rule
+    public ActivityTestRule<KeyEventInterceptTestActivity> mActivityRule =
+            new ActivityTestRule<>(KeyEventInterceptTestActivity.class);
+
+    @Before
+    public void setup() {
+        mInstrumentation = InstrumentationRegistry.getInstrumentation();
+        mActivity = mActivityRule.getActivity();
+        PollingCheck.waitFor(mActivity::hasWindowFocus);
+    }
+
+    @Test
+    public void testKeyCodeAssist() {
+        testKey(KeyEvent.KEYCODE_ASSIST);
+    }
+
+    @Test
+    public void testKeyCodeVoiceAssist() {
+        testKey(KeyEvent.KEYCODE_VOICE_ASSIST);
+    }
+
+    @Test
+    public void testKeyCodeHome() {
+        testKey(KeyEvent.KEYCODE_HOME);
+    }
+
+    private void testKey(int keyCode) {
+        sendKey(keyCode);
+        assertKeyNotReceived();
+    }
+
+    private void sendKey(int keyCodeToSend) {
+        long downTime = SystemClock.uptimeMillis();
+        injectEvent(new KeyEvent(downTime, downTime, KeyEvent.ACTION_DOWN,
+                keyCodeToSend, 0, 0));
+        injectEvent(new KeyEvent(downTime, downTime + 1, KeyEvent.ACTION_UP,
+                keyCodeToSend, 0, 0));
+    }
+
+    private void injectEvent(KeyEvent event) {
+        final UiAutomation automation = mInstrumentation.getUiAutomation();
+        automation.injectInputEvent(event, true);
+        event.recycle();
+    }
+
+    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");
+        }
+    }
+}
diff --git a/tests/tests/view/src/android/view/cts/KeyEventInterceptTestActivity.java b/tests/tests/view/src/android/view/cts/KeyEventInterceptTestActivity.java
new file mode 100644
index 0000000..18e0713
--- /dev/null
+++ b/tests/tests/view/src/android/view/cts/KeyEventInterceptTestActivity.java
@@ -0,0 +1,40 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.view.cts;
+
+import android.app.Activity;
+import android.view.KeyEvent;
+
+import java.util.concurrent.BlockingQueue;
+import java.util.concurrent.LinkedBlockingDeque;
+
+public class KeyEventInterceptTestActivity extends Activity {
+    final BlockingQueue<KeyEvent> mKeyEvents = new LinkedBlockingDeque<>();
+
+    @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
+        mKeyEvents.add(event);
+        return true;
+    }
+}
diff --git a/tests/tests/view/src/android/view/cts/PanicPressBackActivity.java b/tests/tests/view/src/android/view/cts/PanicPressBackActivity.java
deleted file mode 100644
index bb52491..0000000
--- a/tests/tests/view/src/android/view/cts/PanicPressBackActivity.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.view.cts;
-
-import android.app.Activity;
-
-import java.util.concurrent.CountDownLatch;
-
-public class PanicPressBackActivity extends Activity {
-
-    private boolean mWasPaused;
-
-    public final CountDownLatch mWaitForPanicBackLatch = new CountDownLatch(1);
-
-    @Override
-    public void onBackPressed() {
-        // Prevent back press from exiting app
-    }
-
-    @Override
-    public void onPause() {
-        mWaitForPanicBackLatch.countDown();
-        super.onPause();
-    }
-}
\ No newline at end of file
diff --git a/tests/tests/view/src/android/view/cts/PanicPressBackTest.java b/tests/tests/view/src/android/view/cts/PanicPressBackTest.java
deleted file mode 100644
index 8a0bf31..0000000
--- a/tests/tests/view/src/android/view/cts/PanicPressBackTest.java
+++ /dev/null
@@ -1,116 +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.view.cts;
-
-import static junit.framework.TestCase.assertFalse;
-import static junit.framework.TestCase.assertTrue;
-
-import android.app.UiAutomation;
-import android.content.pm.PackageManager;
-import android.support.test.InstrumentationRegistry;
-import android.support.test.rule.ActivityTestRule;
-import android.support.test.runner.AndroidJUnit4;
-import android.view.KeyEvent;
-import android.view.ViewConfiguration;
-
-import org.junit.Before;
-import org.junit.Rule;
-import org.junit.Test;
-import org.junit.runner.RunWith;
-
-import java.util.concurrent.TimeUnit;
-
-@RunWith(AndroidJUnit4.class)
-public class PanicPressBackTest {
-    static final String TAG = "PanicPressBackTest";
-
-    @Rule
-    public ActivityTestRule<PanicPressBackActivity> mActivityRule =
-            new ActivityTestRule<>(PanicPressBackActivity.class);
-
-    private static final int PANIC_PRESS_COUNT = 4;
-    private PanicPressBackActivity mActivity;
-
-    @Before
-    public void setUp() {
-        mActivity = mActivityRule.getActivity();
-    }
-
-    /**
-     * Tests to ensure that the foregrounded app does not handle back button panic press on
-     * non-watch devices
-     */
-    @Test
-    public void testNonWatchBackPanicDoesNothing() throws Exception {
-        // Only run for non-watch devices
-        if (mActivity.getPackageManager().hasSystemFeature(PackageManager.FEATURE_WATCH)) {
-            return;
-        }
-
-        final UiAutomation automation = InstrumentationRegistry.getInstrumentation()
-                .getUiAutomation();
-
-        // Press back button PANIC_PRESS_COUNT times
-        long startTime = System.currentTimeMillis();
-        for (int i = 0; i < PANIC_PRESS_COUNT; ++i) {
-            long currentTime = startTime + i;
-            automation.injectInputEvent(new KeyEvent(currentTime, currentTime, KeyEvent.ACTION_DOWN,
-                    KeyEvent.KEYCODE_BACK, 0), true);
-            automation.injectInputEvent(new KeyEvent(currentTime, currentTime, KeyEvent.ACTION_UP,
-                    KeyEvent.KEYCODE_BACK, 0), true);
-        }
-
-        // Wait multi press time out plus some time to give the system time to respond
-        long timeoutMs = ViewConfiguration.getMultiPressTimeout() + TimeUnit.SECONDS.toMillis(1);
-
-        // Assert activity was not stopped, indicating panic press was not able to exit the app
-        assertFalse(mActivity.mWaitForPanicBackLatch.await(timeoutMs, TimeUnit.MILLISECONDS));
-    }
-
-    /**
-     * Tests to ensure that the foregrounded app does handle back button panic press on watch
-     * devices
-     */
-    @Test
-    public void testWatchBackPanicReceivesHomeRequest() throws Exception {
-        // Only run for watch devices
-        if (!mActivity.getPackageManager().hasSystemFeature(PackageManager.FEATURE_WATCH)) {
-            return;
-        }
-
-        final UiAutomation automation = InstrumentationRegistry.getInstrumentation()
-                .getUiAutomation();
-
-        // Press back button PANIC_PRESS_COUNT times
-        long startTime = System.currentTimeMillis();
-        for (int i = 0; i < PANIC_PRESS_COUNT; ++i) {
-            // Assert activity hasn't stopped yet
-            assertFalse(mActivity.mWaitForPanicBackLatch.await(0, TimeUnit.MILLISECONDS));
-            long currentTime = startTime + i;
-            automation.injectInputEvent(new KeyEvent(currentTime, currentTime, KeyEvent.ACTION_DOWN,
-                    KeyEvent.KEYCODE_BACK, 0), true);
-            automation.injectInputEvent(new KeyEvent(currentTime, currentTime, KeyEvent.ACTION_UP,
-                    KeyEvent.KEYCODE_BACK, 0), true);
-        }
-
-        // Wait multi press time out plus some time to give the system time to respond
-        long timeoutMs = ViewConfiguration.getMultiPressTimeout() + TimeUnit.SECONDS.toMillis(1);
-
-        // Assert activity was stopped, indicating that panic press was able to exit the app
-        assertTrue(mActivity.mWaitForPanicBackLatch.await(timeoutMs, TimeUnit.MILLISECONDS));
-    }
-}
\ No newline at end of file
diff --git a/tests/tests/view/src/android/view/cts/TooltipTest.java b/tests/tests/view/src/android/view/cts/TooltipTest.java
index 1717763..90ab139 100644
--- a/tests/tests/view/src/android/view/cts/TooltipTest.java
+++ b/tests/tests/view/src/android/view/cts/TooltipTest.java
@@ -182,9 +182,13 @@
         mInstrumentation.sendPointerSync(event);
     }
 
+    private void injectHoverMove(int source, View target, int offsetX, int offsetY) {
+        injectMotionEvent(obtainMotionEvent(
+                    source, target, MotionEvent.ACTION_HOVER_MOVE, offsetX,  offsetY));
+    }
+
     private void injectHoverMove(View target, int offsetX, int offsetY) {
-        injectMotionEvent(obtainMouseEvent(
-                target, MotionEvent.ACTION_HOVER_MOVE, offsetX,  offsetY));
+        injectHoverMove(InputDevice.SOURCE_MOUSE, target, offsetX,  offsetY);
     }
 
     private void injectHoverMove(View target) {
@@ -197,13 +201,18 @@
     }
 
     private static MotionEvent obtainMouseEvent(View target, int action, int offsetX, int offsetY) {
+        return obtainMotionEvent(InputDevice.SOURCE_MOUSE, target, action, offsetX, offsetY);
+    }
+
+    private static MotionEvent obtainMotionEvent(
+                int source, View target, int action, int offsetX, int offsetY) {
         final long eventTime = SystemClock.uptimeMillis();
         final int[] xy = new int[2];
         target.getLocationOnScreen(xy);
         MotionEvent event = MotionEvent.obtain(eventTime, eventTime, action,
                 xy[0] + target.getWidth() / 2 + offsetX, xy[1] + target.getHeight() / 2 + offsetY,
                 0);
-        event.setSource(InputDevice.SOURCE_MOUSE);
+        event.setSource(source);
         return event;
     }
 
@@ -250,17 +259,16 @@
     }
 
     @Test
-    public void testNoTooltipOnDisabledView() throws Throwable {
+    public void testTooltipOnDisabledView() throws Throwable {
         mActivityRule.runOnUiThread(() -> mTooltipView.setEnabled(false));
 
+        // Long click has no effect on a disabled view.
         injectLongClick(mTooltipView);
         assertFalse(hasTooltip(mTooltipView));
 
-        injectLongEnter(mTooltipView);
-        assertFalse(hasTooltip(mTooltipView));
-
+        // Hover does show the tooltip on a disabled view.
         injectLongHoverMove(mTooltipView);
-        assertFalse(hasTooltip(mTooltipView));
+        assertTrue(hasTooltip(mTooltipView));
     }
 
     @Test
@@ -656,7 +664,8 @@
         injectHoverMove(mTooltipView);
         mActivityRule.runOnUiThread(() -> mTooltipView.setEnabled(false));
         waitOut(ViewConfiguration.getHoverTooltipShowTimeout());
-        assertFalse(hasTooltip(mTooltipView));
+        // Disabled view still displays a hover tooltip.
+        assertTrue(hasTooltip(mTooltipView));
     }
 
     @Test
@@ -787,6 +796,76 @@
     }
 
     @Test
+    public void testMouseHoverWithJitter() throws Throwable {
+        testHoverWithJitter(InputDevice.SOURCE_MOUSE);
+    }
+
+    @Test
+    public void testStylusHoverWithJitter() throws Throwable {
+        testHoverWithJitter(InputDevice.SOURCE_STYLUS);
+    }
+
+    @Test
+    public void testTouchscreenHoverWithJitter() throws Throwable {
+        testHoverWithJitter(InputDevice.SOURCE_TOUCHSCREEN);
+    }
+
+    private void testHoverWithJitter(int source) {
+        final int hoverSlop = ViewConfiguration.get(mTooltipView.getContext()).getScaledHoverSlop();
+        if (hoverSlop == 0) {
+            // Zero hoverSlop makes this test redundant.
+            return;
+        }
+
+        final int tooltipTimeout = ViewConfiguration.getHoverTooltipShowTimeout();
+        final long halfTimeout = tooltipTimeout / 2;
+        assertTrue(halfTimeout + WAIT_MARGIN < tooltipTimeout);
+
+        // Imitate strong jitter (above hoverSlop threshold). No tooltip should be shown.
+        int jitterHigh = hoverSlop + 1;
+        assertTrue(jitterHigh <= mTooltipView.getWidth());
+        assertTrue(jitterHigh <= mTooltipView.getHeight());
+
+        injectHoverMove(source, mTooltipView, 0, 0);
+        waitOut(halfTimeout);
+        assertFalse(hasTooltip(mTooltipView));
+
+        injectHoverMove(source, mTooltipView, jitterHigh, 0);
+        waitOut(halfTimeout);
+        assertFalse(hasTooltip(mTooltipView));
+
+        injectHoverMove(source, mTooltipView, 0, 0);
+        waitOut(halfTimeout);
+        assertFalse(hasTooltip(mTooltipView));
+
+        injectHoverMove(source, mTooltipView, 0, jitterHigh);
+        waitOut(halfTimeout);
+        assertFalse(hasTooltip(mTooltipView));
+
+        // Jitter below threshold should be ignored and the tooltip should be shown.
+        injectHoverMove(source, mTooltipView, 0, 0);
+        waitOut(halfTimeout);
+        assertFalse(hasTooltip(mTooltipView));
+
+        int jitterLow = hoverSlop - 1;
+        injectHoverMove(source, mTooltipView, jitterLow, 0);
+        waitOut(halfTimeout);
+        assertTrue(hasTooltip(mTooltipView));
+
+        // Dismiss the tooltip
+        injectShortClick(mTooltipView);
+        assertFalse(hasTooltip(mTooltipView));
+
+        injectHoverMove(source, mTooltipView, 0, 0);
+        waitOut(halfTimeout);
+        assertFalse(hasTooltip(mTooltipView));
+
+        injectHoverMove(source, mTooltipView, 0, jitterLow);
+        waitOut(halfTimeout);
+        assertTrue(hasTooltip(mTooltipView));
+    }
+
+    @Test
     public void testTooltipInPopup() throws Throwable {
         TextView popupContent = new TextView(mActivity);
 
diff --git a/tests/tests/view/src/android/view/cts/TouchDelegateTest.java b/tests/tests/view/src/android/view/cts/TouchDelegateTest.java
index 1fd3d8c..8def4b0 100644
--- a/tests/tests/view/src/android/view/cts/TouchDelegateTest.java
+++ b/tests/tests/view/src/android/view/cts/TouchDelegateTest.java
@@ -16,24 +16,19 @@
 
 package android.view.cts;
 
-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 static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertNull;
 
-import android.app.Activity;
 import android.app.Instrumentation;
+import android.os.SystemClock;
 import android.support.test.InstrumentationRegistry;
-import android.support.test.annotation.UiThreadTest;
 import android.support.test.filters.MediumTest;
 import android.support.test.rule.ActivityTestRule;
 import android.support.test.runner.AndroidJUnit4;
+import android.view.InputDevice;
 import android.view.MotionEvent;
-import android.view.TouchDelegate;
-import android.view.View;
-import android.view.ViewGroup;
-import android.widget.Button;
-import android.widget.LinearLayout;
+
 
 import org.junit.Before;
 import org.junit.Rule;
@@ -44,37 +39,89 @@
 @RunWith(AndroidJUnit4.class)
 public class TouchDelegateTest {
     private Instrumentation mInstrumentation;
-    private Activity mActivity;
-    private Button mButton;
+    private TouchDelegateTestActivity mActivity;
 
     @Rule
-    public ActivityTestRule<MockActivity> mActivityRule =
-            new ActivityTestRule<>(MockActivity.class);
+    public ActivityTestRule<TouchDelegateTestActivity> mActivityRule =
+            new ActivityTestRule<>(TouchDelegateTestActivity.class);
 
     @Before
     public void setup() throws Throwable {
         mActivity = mActivityRule.getActivity();
+        mActivity.resetCounters();
         mInstrumentation = InstrumentationRegistry.getInstrumentation();
-
-        mButton = new Button(mActivity);
-        mActivityRule.runOnUiThread(() -> mActivity.addContentView(
-                mButton, new LinearLayout.LayoutParams(
-                        ViewGroup.LayoutParams.WRAP_CONTENT, ViewGroup.LayoutParams.WRAP_CONTENT)));
         mInstrumentation.waitForIdleSync();
     }
 
-    @UiThreadTest
     @Test
-    public void testOnTouchEvent() {
-        // test callback of onTouchEvent
-        final View view = new View(mActivity);
-        final TouchDelegate touchDelegate = mock(TouchDelegate.class);
-        view.setTouchDelegate(touchDelegate);
+    public void testParentClick() {
+        // If only clicking parent, button should not receive click
+        clickParent();
+        assertEquals(0, mActivity.getButtonClickCount());
+        assertEquals(1, mActivity.getParentClickCount());
 
-        final int xInside = (mButton.getLeft() + mButton.getRight()) / 3;
-        final int yInside = (mButton.getTop() + mButton.getBottom()) / 3;
+        // When clicking TouchDelegate area, both parent and button
+        // should receive DOWN and UP events. However, click will only be generated for the button
+        mActivity.resetCounters();
+        clickTouchDelegateArea();
+        assertEquals(1, mActivity.getButtonClickCount());
+        assertEquals(0, mActivity.getParentClickCount());
 
-        view.onTouchEvent(MotionEvent.obtain(0, 0, MotionEvent.ACTION_DOWN, xInside, yInside, 0));
-        verify(touchDelegate, times(1)).onTouchEvent(any(MotionEvent.class));
+        // Ensure parent can still receive clicks after TouchDelegate has been activated once
+        mActivity.resetCounters();
+        clickParent();
+        assertEquals(0, mActivity.getButtonClickCount());
+        assertEquals(1, mActivity.getParentClickCount());
+    }
+
+    @Test
+    public void testCancelEvent() {
+        // Ensure events with ACTION_CANCEL are received by the TouchDelegate
+        final long downTime = SystemClock.uptimeMillis();
+        dispatchMotionEventToActivity(MotionEvent.ACTION_DOWN, mActivity.touchDelegateY,
+                downTime);
+        dispatchMotionEventToActivity(MotionEvent.ACTION_CANCEL, mActivity.touchDelegateY,
+                downTime);
+        mInstrumentation.waitForIdleSync();
+
+        MotionEvent event = mActivity.removeOldestButtonEvent();
+        assertNotNull(event);
+        assertEquals(MotionEvent.ACTION_DOWN, event.getAction());
+        event.recycle();
+        event = mActivity.removeOldestButtonEvent();
+        assertNotNull(event);
+        assertEquals(MotionEvent.ACTION_CANCEL, event.getAction());
+        event.recycle();
+        assertNull(mActivity.removeOldestButtonEvent());
+        assertEquals(0, mActivity.getButtonClickCount());
+        assertEquals(0, mActivity.getParentClickCount());
+    }
+
+    private void clickParent() {
+        click(mActivity.parentViewY);
+    }
+
+    private void clickTouchDelegateArea() {
+        click(mActivity.touchDelegateY);
+    }
+
+    // Low-level input-handling functions for the activity
+
+    private void click(int y) {
+        final long downTime = SystemClock.uptimeMillis();
+        dispatchMotionEventToActivity(MotionEvent.ACTION_DOWN, y, downTime);
+        dispatchMotionEventToActivity(MotionEvent.ACTION_UP, y, downTime);
+        mInstrumentation.waitForIdleSync();
+    }
+
+    private void dispatchMotionEventToActivity(int action, int y, long downTime) {
+        mActivity.runOnUiThread(() -> {
+            final long eventTime = SystemClock.uptimeMillis();
+            final MotionEvent event = MotionEvent.obtain(downTime, eventTime, action,
+                    mActivity.x, y, 0);
+            event.setSource(InputDevice.SOURCE_TOUCHSCREEN);
+            mActivity.dispatchTouchEvent(event);
+            event.recycle();
+        });
     }
 }
diff --git a/tests/tests/view/src/android/view/cts/TouchDelegateTestActivity.java b/tests/tests/view/src/android/view/cts/TouchDelegateTestActivity.java
new file mode 100644
index 0000000..0f01a95
--- /dev/null
+++ b/tests/tests/view/src/android/view/cts/TouchDelegateTestActivity.java
@@ -0,0 +1,117 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.view.cts;
+
+import android.app.Activity;
+import android.graphics.Rect;
+import android.os.Bundle;
+import android.view.MotionEvent;
+import android.view.TouchDelegate;
+import android.view.View;
+import android.view.ViewTreeObserver;
+import android.widget.Button;
+
+import java.util.ArrayDeque;
+import java.util.Queue;
+
+/**
+ * The layout is:
+ *                                    |       <-- top edge of RelativeLayout (parent)
+ *                                    |
+ *                                    |
+ *                                    |       (touches in this region should go to parent only)
+ *                                    |
+ *                                    |
+ *                                    |       <-- TouchDelegate boundary
+ *                                    |
+ *                                    |
+ *                                    |       (touches in this region should go to button + parent)
+ *                                    |
+ *                                    |
+ *                                    |       <-- Button top boundary
+ *                                    |
+ */
+public class TouchDelegateTestActivity extends Activity {
+    // Counters for click events. Must use waitForIdleSync() before accessing these
+    private volatile int mParentClickCount = 0;
+    private volatile int mButtonClickCount = 0;
+    // Storage for MotionEvents received by the TouchDelegate
+    private Queue<MotionEvent> mButtonEvents = new ArrayDeque<>();
+
+    // Coordinates for injecting input events from the test
+    public int x; // common X coordinate for all input events - center of the screen
+    public int touchDelegateY; // Y coordinate for touches inside TouchDelegate area
+    public int parentViewY; // Y coordinate for touches inside parent area only
+
+    @Override
+    protected void onCreate(Bundle savedInstanceState) {
+        super.onCreate(savedInstanceState);
+        setContentView(R.layout.touch_delegate_test_activity_layout);
+
+        final Button button = findViewById(R.id.button);
+        final View parent = findViewById(R.id.layout);
+
+        parent.getViewTreeObserver().addOnGlobalLayoutListener(
+                new ViewTreeObserver.OnGlobalLayoutListener() {
+                    @Override
+                    public void onGlobalLayout() {
+                        final int[] parentLocation = new int[2];
+                        parent.getLocationOnScreen(parentLocation);
+                        final int[] buttonLocation = new int[2];
+                        button.getLocationOnScreen(buttonLocation);
+                        x = parentLocation[0] + parent.getWidth() / 2;
+                        final int gap = buttonLocation[1] - parentLocation[1];
+
+                        Rect rect = new Rect();
+                        button.getHitRect(rect);
+                        rect.top -= gap / 2; // TouchDelegate is halfway between button and parent
+                        parent.setTouchDelegate(new TouchDelegate(rect, button));
+                        touchDelegateY = buttonLocation[1] - gap / 4;
+                        parentViewY = parentLocation[1] + gap / 4;
+                    }
+                });
+
+        parent.setOnClickListener(v -> mParentClickCount++);
+        button.setOnClickListener(v -> mButtonClickCount++);
+        button.setOnTouchListener((v, event) -> {
+            mButtonEvents.add(MotionEvent.obtain(event));
+            return TouchDelegateTestActivity.super.onTouchEvent(event);
+        });
+    }
+
+    void resetCounters() {
+        mParentClickCount = 0;
+        mButtonClickCount = 0;
+        mButtonEvents.clear();
+    }
+
+    int getButtonClickCount() {
+        return mButtonClickCount;
+    }
+
+    int getParentClickCount() {
+        return mParentClickCount;
+    }
+
+    /**
+     * Remove and return the oldest MotionEvent. Caller must recycle the returned object.
+     * @return the oldest MotionEvent that the Button has received
+     */
+    MotionEvent removeOldestButtonEvent() {
+        return mButtonEvents.poll();
+    }
+}
diff --git a/tests/tests/view/src/android/view/cts/VelocityTrackerTest.java b/tests/tests/view/src/android/view/cts/VelocityTrackerTest.java
index 774aadb..955cf7c 100644
--- a/tests/tests/view/src/android/view/cts/VelocityTrackerTest.java
+++ b/tests/tests/view/src/android/view/cts/VelocityTrackerTest.java
@@ -174,7 +174,7 @@
     }
 
     private void addMovement() {
-        if (mTime >= mLastTime) {
+        if (mTime > mLastTime) {
             MotionEvent ev = MotionEvent.obtain(0L, mTime, MotionEvent.ACTION_MOVE, mPx, mPy, 0);
             mVelocityTracker.addMovement(ev);
             ev.recycle();
@@ -201,9 +201,9 @@
         if (errorVx > tolerance || errorVy > tolerance) {
             fail(String.format("Velocity exceeds tolerance of %6.1f%%: "
                     + "expected vx=%6.1f, vy=%6.1f. "
-                    + "actual vx=%6.1f (%6.1f%%), vy=%6.1f (%6.1f%%)",
+                    + "actual vx=%6.1f (%6.1f%%), vy=%6.1f (%6.1f%%). %s",
                     tolerance * 100.0f, mVx, mVy,
-                    estimatedVx, errorVx * 100.0f, estimatedVy, errorVy * 100.0f));
+                    estimatedVx, errorVx * 100.0f, estimatedVy, errorVy * 100.0f, message));
         }
     }
 
diff --git a/tests/tests/view/src/android/view/cts/ViewTest.java b/tests/tests/view/src/android/view/cts/ViewTest.java
index 831aa15..7c05d26 100644
--- a/tests/tests/view/src/android/view/cts/ViewTest.java
+++ b/tests/tests/view/src/android/view/cts/ViewTest.java
@@ -99,6 +99,7 @@
 import android.widget.Button;
 import android.widget.EditText;
 import android.widget.LinearLayout;
+import android.widget.TextView;
 
 import com.android.compatibility.common.util.CtsMouseUtil;
 import com.android.compatibility.common.util.CtsTouchUtils;
@@ -133,6 +134,7 @@
     private ViewTestCtsActivity mActivity;
     private Resources mResources;
     private MockViewParent mMockParent;
+    private Context mContext;
 
     @Rule
     public ActivityTestRule<ViewTestCtsActivity> mActivityRule =
@@ -145,11 +147,13 @@
     @Before
     public void setup() {
         mInstrumentation = InstrumentationRegistry.getInstrumentation();
+        mContext = mInstrumentation.getTargetContext();
         mActivity = mActivityRule.getActivity();
         PollingCheck.waitFor(mActivity::hasWindowFocus);
         mResources = mActivity.getResources();
         mMockParent = new MockViewParent(mActivity);
-        assertTrue(mActivity.waitForWindowFocus(5 * DateUtils.SECOND_IN_MILLIS));
+        PollingCheck.waitFor(5 * DateUtils.SECOND_IN_MILLIS, mActivity::hasWindowFocus);
+        assertTrue(mActivity.hasWindowFocus());
     }
 
     @Test
@@ -500,6 +504,31 @@
 
         Bitmap bitmap = BitmapFactory.decodeResource(mResources, R.drawable.icon_blue);
         assertNotNull(PointerIcon.create(bitmap, 0, 0));
+        assertNotNull(PointerIcon.create(bitmap, bitmap.getWidth() / 2, bitmap.getHeight() / 2));
+
+        try {
+            PointerIcon.create(bitmap, -1, 0);
+            fail("Hotspot x can not be < 0");
+        } catch (IllegalArgumentException ignore) {
+        }
+
+        try {
+            PointerIcon.create(bitmap, 0, -1);
+            fail("Hotspot y can not be < 0");
+        } catch (IllegalArgumentException ignore) {
+        }
+
+        try {
+            PointerIcon.create(bitmap, bitmap.getWidth(), 0);
+            fail("Hotspot x cannot be >= width");
+        } catch (IllegalArgumentException ignore) {
+        }
+
+        try {
+            PointerIcon.create(bitmap, 0, bitmap.getHeight());
+            fail("Hotspot x cannot be >= height");
+        } catch (IllegalArgumentException e) {
+        }
     }
 
     private void assertSystemPointerIcon(int style) {
@@ -2385,6 +2414,109 @@
     }
 
     @Test
+    public void testKeyFallback() throws Throwable {
+        MockFallbackListener listener = new MockFallbackListener();
+        ViewGroup viewGroup = (ViewGroup) mActivity.findViewById(R.id.viewlayout_root);
+        // Attaching a fallback handler
+        TextView mockView1 = new TextView(mActivity);
+        mockView1.addKeyFallbackListener(listener);
+
+        // Before the view is attached, it shouldn't respond to anything
+        mInstrumentation.sendKeyDownUpSync(KeyEvent.KEYCODE_B);
+        assertFalse(listener.fired());
+
+        // Once attached, it should start receiving fallback events
+        mActivityRule.runOnUiThread(() -> viewGroup.addView(mockView1));
+        mInstrumentation.waitForIdleSync();
+        mInstrumentation.sendKeyDownUpSync(KeyEvent.KEYCODE_B);
+        assertTrue(listener.fired());
+        listener.reset();
+
+        // If multiple on one view, last added should receive event first
+        MockFallbackListener listener2 = new MockFallbackListener();
+        listener2.mReturnVal = true;
+        mActivityRule.runOnUiThread(() -> mockView1.addKeyFallbackListener(listener2));
+        mInstrumentation.waitForIdleSync();
+        mInstrumentation.sendKeyDownUpSync(KeyEvent.KEYCODE_B);
+        assertTrue(listener2.fired());
+        assertFalse(listener.fired());
+        listener2.reset();
+
+        // If removed, it should not receive fallbacks anymore
+        mActivityRule.runOnUiThread(() -> {
+            mockView1.removeKeyFallbackListener(listener);
+            mockView1.removeKeyFallbackListener(listener2);
+        });
+        mInstrumentation.waitForIdleSync();
+        mInstrumentation.sendKeyDownUpSync(KeyEvent.KEYCODE_B);
+        assertFalse(listener.fired());
+
+        mActivityRule.runOnUiThread(() -> mActivity.setContentView(R.layout.key_fallback_layout));
+        mInstrumentation.waitForIdleSync();
+        View higherInNormal = mActivity.findViewById(R.id.higher_in_normal);
+        View higherGroup = mActivity.findViewById(R.id.higher_group);
+        View lowerInHigher = mActivity.findViewById(R.id.lower_in_higher);
+        View lastButton = mActivity.findViewById(R.id.last_button);
+        View lastInHigher = mActivity.findViewById(R.id.last_in_higher);
+        View lastInNormal = mActivity.findViewById(R.id.last_in_normal);
+
+        View[] allViews = new View[]{higherInNormal, higherGroup, lowerInHigher, lastButton,
+                lastInHigher, lastInNormal};
+
+        // Test ordering by depth
+        listener.mReturnVal = true;
+        mActivityRule.runOnUiThread(() -> {
+            for (View v : allViews) {
+                v.addKeyFallbackListener(listener);
+            }
+        });
+        mInstrumentation.waitForIdleSync();
+
+        mInstrumentation.sendKeyDownUpSync(KeyEvent.KEYCODE_B);
+        assertEquals(lastInHigher, listener.mLastView);
+        listener.reset();
+
+        mActivityRule.runOnUiThread(() -> lastInHigher.removeKeyFallbackListener(listener));
+        mInstrumentation.waitForIdleSync();
+        mInstrumentation.sendKeyDownUpSync(KeyEvent.KEYCODE_B);
+        assertEquals(lowerInHigher, listener.mLastView);
+        listener.reset();
+
+        mActivityRule.runOnUiThread(() -> lowerInHigher.removeKeyFallbackListener(listener));
+        mInstrumentation.waitForIdleSync();
+        mInstrumentation.sendKeyDownUpSync(KeyEvent.KEYCODE_B);
+        assertEquals(higherGroup, listener.mLastView);
+        listener.reset();
+
+        mActivityRule.runOnUiThread(() -> higherGroup.removeKeyFallbackListener(listener));
+        mInstrumentation.waitForIdleSync();
+        mInstrumentation.sendKeyDownUpSync(KeyEvent.KEYCODE_B);
+        assertEquals(lastButton, listener.mLastView);
+        listener.reset();
+
+        mActivityRule.runOnUiThread(() -> lastButton.removeKeyFallbackListener(listener));
+        mInstrumentation.waitForIdleSync();
+        mInstrumentation.sendKeyDownUpSync(KeyEvent.KEYCODE_B);
+        assertEquals(higherInNormal, listener.mLastView);
+        listener.reset();
+
+        mActivityRule.runOnUiThread(() -> higherInNormal.removeKeyFallbackListener(listener));
+        mInstrumentation.waitForIdleSync();
+        mInstrumentation.sendKeyDownUpSync(KeyEvent.KEYCODE_B);
+        assertEquals(lastInNormal, listener.mLastView);
+        listener.reset();
+
+        // Test "capture"
+        mActivityRule.runOnUiThread(() -> lastInNormal.requestFocus());
+        mInstrumentation.waitForIdleSync();
+        lastInNormal.setOnKeyListener((v, keyCode, event)
+                -> (keyCode == KeyEvent.KEYCODE_B && event.getAction() == KeyEvent.ACTION_UP));
+        mInstrumentation.sendKeyDownUpSync(KeyEvent.KEYCODE_B);
+        assertTrue(listener.fired()); // checks that both up and down were received
+        listener.reset();
+    }
+
+    @Test
     public void testWindowVisibilityChanged() throws Throwable {
         final MockView mockView = new MockView(mActivity);
         final ViewGroup viewGroup = (ViewGroup) mActivity.findViewById(R.id.viewlayout_root);
@@ -3884,7 +4016,9 @@
         final MockEditText editText = new MockEditText(mActivity);
 
         mActivityRule.runOnUiThread(() -> {
-            viewGroup.addView(editText);
+            // Give a fixed size since, on most devices, the edittext is off-screen
+            // and therefore doesn't get laid-out properly.
+            viewGroup.addView(editText, 100, 30);
             editText.requestFocus();
         });
         mInstrumentation.waitForIdleSync();
@@ -4340,6 +4474,42 @@
         event.recycle();
     }
 
+    @Test(expected = IllegalArgumentException.class)
+    public void testScaleXNaN() {
+        View view = new View(mContext);
+        view.setScaleX(Float.NaN);
+    }
+
+    @Test(expected = IllegalArgumentException.class)
+    public void testScaleXPositiveInfinity() {
+        View view = new View(mContext);
+        view.setScaleX(Float.POSITIVE_INFINITY);
+    }
+
+    @Test(expected = IllegalArgumentException.class)
+    public void testScaleXNegativeInfinity() {
+        View view = new View(mContext);
+        view.setScaleX(Float.NEGATIVE_INFINITY);
+    }
+
+    @Test(expected = IllegalArgumentException.class)
+    public void testScaleYNaN() {
+        View view = new View(mContext);
+        view.setScaleY(Float.NaN);
+    }
+
+    @Test(expected = IllegalArgumentException.class)
+    public void testScaleYPositiveInfinity() {
+        View view = new View(mContext);
+        view.setScaleY(Float.POSITIVE_INFINITY);
+    }
+
+    @Test(expected = IllegalArgumentException.class)
+    public void testScaleYNegativeInfinity() {
+        View view = new View(mContext);
+        view.setScaleY(Float.NEGATIVE_INFINITY);
+    }
+
     private static class MockDrawable extends Drawable {
         private boolean mCalledSetTint = false;
 
@@ -4691,6 +4861,29 @@
         public View firstChild;
     }
 
+    private static class MockFallbackListener implements View.OnKeyFallbackListener {
+        public View mLastView = null;
+        public boolean mGotUp = false;
+        public boolean mReturnVal = false;
+
+        @Override
+        public boolean onKeyFallback(View v, KeyEvent event) {
+            if (event.getAction() == KeyEvent.ACTION_DOWN) {
+                mLastView = v;
+            } else if (event.getAction() == KeyEvent.ACTION_UP) {
+                mGotUp = true;
+            }
+            return mReturnVal;
+        }
+        public void reset() {
+            mLastView = null;
+            mGotUp = false;
+        }
+        public boolean fired() {
+            return mLastView != null && mGotUp;
+        }
+    }
+
     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/ViewTestCtsActivity.java b/tests/tests/view/src/android/view/cts/ViewTestCtsActivity.java
index 9766fa2..9117925 100644
--- a/tests/tests/view/src/android/view/cts/ViewTestCtsActivity.java
+++ b/tests/tests/view/src/android/view/cts/ViewTestCtsActivity.java
@@ -18,52 +18,13 @@
 
 import android.app.Activity;
 import android.os.Bundle;
-import android.os.SystemClock;
-import android.util.Log;
 import android.view.cts.R;
 
 public class ViewTestCtsActivity extends Activity {
-    private boolean mHasWindowFocus = false;
-    private final Object mHasWindowFocusLock = new Object();
 
     @Override
     protected void onCreate(Bundle icicle) {
         super.onCreate(icicle);
         setContentView(R.layout.view_layout);
     }
-
-    @Override
-    public void onWindowFocusChanged(boolean hasFocus) {
-        super.onWindowFocusChanged(hasFocus);
-        if (!hasFocus) {
-            Log.w("ViewTestCtsActivity", "ViewTestCtsActivity lost window focus");
-        }
-        synchronized(mHasWindowFocusLock) {
-            mHasWindowFocus = hasFocus;
-            mHasWindowFocusLock.notify();
-        }
-    }
-
-    /**
-     * Blocks the calling thread until the {@link ViewTestCtsActivity} has window focus or the
-     * specified duration (in milliseconds) has passed.
-     */
-    public boolean waitForWindowFocus(long durationMillis) {
-        long elapsedMillis = SystemClock.elapsedRealtime();
-        synchronized(mHasWindowFocusLock) {
-            mHasWindowFocus = hasWindowFocus();
-            while (!mHasWindowFocus && durationMillis > 0) {
-                long newElapsedMillis = SystemClock.elapsedRealtime();
-                durationMillis -= (newElapsedMillis - elapsedMillis);
-                elapsedMillis = newElapsedMillis;
-                if (durationMillis > 0) {
-                    try {
-                        mHasWindowFocusLock.wait(durationMillis);
-                    } catch (InterruptedException e) {
-                    }
-                }
-            }
-            return mHasWindowFocus;
-        }
-    }
 }
diff --git a/tests/tests/view/src/android/view/cts/View_FocusHandlingTest.java b/tests/tests/view/src/android/view/cts/View_FocusHandlingTest.java
index 21fdd89..d3d600a 100644
--- a/tests/tests/view/src/android/view/cts/View_FocusHandlingTest.java
+++ b/tests/tests/view/src/android/view/cts/View_FocusHandlingTest.java
@@ -227,6 +227,175 @@
 
     @UiThreadTest
     @Test
+    public void testEnabledHandling() {
+        Activity activity = mActivityRule.getActivity();
+
+        View v1 = activity.findViewById(R.id.view1);
+        View v2 = activity.findViewById(R.id.view2);
+        View v3 = activity.findViewById(R.id.view3);
+        View v4 = activity.findViewById(R.id.view4);
+
+        for (View v : new View[]{v1, v2, v3, v4}) v.setFocusable(true);
+
+        assertTrue(v1.requestFocus());
+
+        // disabled view should not be focusable
+        assertTrue(v1.hasFocus());
+        v1.setEnabled(false);
+        assertFalse(v1.hasFocus());
+        v1.requestFocus();
+        assertFalse(v1.hasFocus());
+        v1.setEnabled(true);
+        v1.requestFocus();
+        assertTrue(v1.hasFocus());
+
+        // an enabled view should not take focus if not visible OR not enabled
+        v1.setEnabled(false);
+        v1.setVisibility(View.INVISIBLE);
+        assertFalse(v1.hasFocus());
+        v1.setEnabled(true);
+        v1.requestFocus();
+        assertFalse(v1.hasFocus());
+        v1.setEnabled(false);
+        v1.setVisibility(View.VISIBLE);
+        v1.requestFocus();
+        assertFalse(v1.hasFocus());
+        v1.setEnabled(true);
+        v1.requestFocus();
+        assertTrue(v1.hasFocus());
+
+        // test hasFocusable
+        ViewGroup parent = (ViewGroup) v1.getParent();
+        assertTrue(parent.hasFocusable());
+        for (View v : new View[]{v1, v2, v3, v4}) v.setEnabled(false);
+        assertFalse(v1.isFocused());
+        assertFalse(v2.isFocused());
+        assertFalse(v3.isFocused());
+        assertFalse(v4.isFocused());
+        assertFalse(parent.hasFocusable());
+
+        // a view enabled while nothing has focus should get focus.
+        for (View v : new View[]{v1, v2, v3, v4}) v.setEnabled(true);
+        assertEquals(true, v1.isFocused());
+    }
+
+    @Test
+    public void testSizeHandling() {
+        Activity activity = mActivityRule.getActivity();
+        Instrumentation instrumentation = InstrumentationRegistry.getInstrumentation();
+
+        View v5 = new Button(activity);
+        ViewGroup layout = activity.findViewById(R.id.auto_test_area);
+
+        // Test requestFocus before first layout focuses if non-0 size
+        activity.runOnUiThread(() -> {
+            layout.addView(v5, 30, 30);
+            assertTrue(isZeroSize(v5));
+            assertTrue(v5.requestFocus());
+        });
+        instrumentation.waitForIdleSync();
+        assertFalse(isZeroSize(v5));
+        assertTrue(v5.isFocused());
+
+        // Test resize to 0 defocuses
+        activity.runOnUiThread(() -> {
+            v5.setRight(v5.getLeft());
+            assertEquals(0, v5.getWidth());
+        });
+        instrumentation.waitForIdleSync();
+        assertTrue(isZeroSize(v5));
+        assertFalse(v5.isFocused());
+
+        // Test requestFocus on laid-out 0-size fails
+        activity.runOnUiThread(() -> assertFalse(v5.requestFocus()));
+
+        activity.runOnUiThread(() -> layout.removeAllViews());
+
+        // Test requestFocus before first layout focuses a child if non-0 size
+        LinearLayout ll0 = new LinearLayout(activity);
+        ll0.setFocusable(true);
+        ll0.setDescendantFocusability(ViewGroup.FOCUS_AFTER_DESCENDANTS);
+        View butInScroll = new Button(activity);
+        activity.runOnUiThread(() -> {
+            ll0.addView(butInScroll, 40, 40);
+            layout.addView(ll0, 100, 100);
+            assertTrue(isZeroSize(butInScroll));
+            assertTrue(ll0.requestFocus());
+        });
+        instrumentation.waitForIdleSync();
+        assertFalse(isZeroSize(butInScroll));
+        assertTrue(butInScroll.isFocused());
+
+        // Test focusableViewAvailable on resize to non-0 size
+        activity.runOnUiThread(() -> {
+            butInScroll.setRight(v5.getLeft());
+        });
+        instrumentation.waitForIdleSync();
+        assertTrue(isZeroSize(butInScroll));
+        assertTrue(ll0.isFocused());
+
+        activity.runOnUiThread(() -> layout.removeAllViews());
+        instrumentation.waitForIdleSync();
+
+        // Test requestFocus before first layout defocuses if still 0 size
+        LinearLayout ll = new LinearLayout(activity);
+        View zeroSizeBut = new Button(activity);
+        activity.runOnUiThread(() -> {
+            ll.addView(zeroSizeBut, 30, 0);
+            layout.addView(ll, 100, 100);
+            assertTrue(zeroSizeBut.requestFocus());
+        });
+        instrumentation.waitForIdleSync();
+        assertTrue(isZeroSize(zeroSizeBut));
+        assertFalse(zeroSizeBut.isFocused());
+
+        activity.runOnUiThread(() -> layout.removeAllViews());
+        instrumentation.waitForIdleSync();
+
+        // Test requestFocus before first layout focuses parent if child is still 0 size
+        LinearLayout ll2 = new LinearLayout(activity);
+        ll2.setFocusable(true);
+        ll2.setDescendantFocusability(ViewGroup.FOCUS_AFTER_DESCENDANTS);
+        View zeroButInAfter = new Button(activity);
+        activity.runOnUiThread(() -> {
+            ll2.addView(zeroButInAfter, 40, 0);
+            layout.addView(ll2, 100, 100);
+            assertTrue(ll2.requestFocus());
+            assertTrue(zeroButInAfter.isFocused());
+        });
+        instrumentation.waitForIdleSync();
+        assertTrue(isZeroSize(zeroButInAfter));
+        assertFalse(zeroButInAfter.isFocused());
+        assertTrue(ll2.isFocused());
+
+        activity.runOnUiThread(() -> layout.removeAllViews());
+        instrumentation.waitForIdleSync();
+
+        // Test that we don't focus anything above/outside of where we requested focus
+        LinearLayout ll3 = new LinearLayout(activity);
+        Button outside = new Button(activity);
+        LinearLayout sub = new LinearLayout(activity);
+        Button inside = new Button(activity);
+        activity.runOnUiThread(() -> {
+            ll3.addView(outside, 40, 40);
+            sub.addView(inside, 30, 0);
+            ll3.addView(sub, 40, 40);
+            layout.addView(ll3, 100, 100);
+            assertTrue(sub.requestFocus());
+            assertTrue(inside.isFocused());
+        });
+        instrumentation.waitForIdleSync();
+        assertTrue(isZeroSize(inside));
+        assertTrue(outside.isFocusable() && !isZeroSize(outside));
+        assertNull(layout.getRootView().findFocus());
+    }
+
+    private boolean isZeroSize(View view) {
+        return view.getWidth() <= 0 || view.getHeight() <= 0;
+    }
+
+    @UiThreadTest
+    @Test
     public void testFocusAuto() {
         Activity activity = mActivityRule.getActivity();
 
diff --git a/tests/tests/view/src/android/view/textclassifier/cts/TextClassificationManagerTest.java b/tests/tests/view/src/android/view/textclassifier/cts/TextClassificationManagerTest.java
index 71ca678..f066898 100644
--- a/tests/tests/view/src/android/view/textclassifier/cts/TextClassificationManagerTest.java
+++ b/tests/tests/view/src/android/view/textclassifier/cts/TextClassificationManagerTest.java
@@ -29,6 +29,7 @@
 import android.view.textclassifier.TextClassification;
 import android.view.textclassifier.TextClassificationManager;
 import android.view.textclassifier.TextClassifier;
+import android.view.textclassifier.TextLinks;
 import android.view.textclassifier.TextSelection;
 
 import org.junit.Before;
@@ -42,7 +43,10 @@
     private static final LocaleList LOCALES = LocaleList.forLanguageTags("en");
     private static final int START = 1;
     private static final int END = 3;
-    private static final String TEXT = "text";
+    // This text has lots of things that are probably entities in many cases.
+    private static final String TEXT = "An email address is test@example.com. A phone number"
+            + " might be +12122537077. Somebody lives at 123 Main Street, Mountain View, CA,"
+            + " and there's good stuff at https://www.android.com :)";
 
     private TextClassificationManager mManager;
     private TextClassifier mClassifier;
@@ -57,12 +61,14 @@
 
     @Test
     public void testSmartSelection() {
-        assertValidResult(mClassifier.suggestSelection(TEXT, START, END, LOCALES));
+        assertValidResult(mClassifier.suggestSelection(TEXT, START, END,
+                new TextSelection.Options().setDefaultLocales(LOCALES)));
     }
 
     @Test
     public void testClassifyText() {
-        assertValidResult(mClassifier.classifyText(TEXT, START, END, LOCALES));
+        assertValidResult(mClassifier.classifyText(TEXT, START, END,
+                new TextClassification.Options().setDefaultLocales(LOCALES)));
     }
 
     @Test
@@ -70,14 +76,16 @@
         mManager.setTextClassifier(TextClassifier.NO_OP);
         mClassifier = mManager.getTextClassifier();
 
-        final TextSelection selection = mClassifier.suggestSelection(TEXT, START, END, LOCALES);
+        final TextSelection selection = mClassifier.suggestSelection(TEXT, START, END,
+                new TextSelection.Options().setDefaultLocales(LOCALES));
         assertValidResult(selection);
         assertEquals(START, selection.getSelectionStartIndex());
         assertEquals(END, selection.getSelectionEndIndex());
         assertEquals(0, selection.getEntityCount());
 
         final TextClassification classification =
-                mClassifier.classifyText(TEXT, START, END, LOCALES);
+                mClassifier.classifyText(TEXT, START, END,
+                        new TextClassification.Options().setDefaultLocales(LOCALES));
         assertValidResult(classification);
         assertNull(classification.getText());
         assertEquals(0, classification.getEntityCount());
@@ -88,6 +96,11 @@
     }
 
     @Test
+    public void testGenerateLinks() {
+        assertValidResult(mClassifier.generateLinks(TEXT, null));
+    }
+
+    @Test
     public void testSetTextClassifier() {
         final TextClassifier classifier = mock(TextClassifier.class);
         mManager.setTextClassifier(classifier);
@@ -96,6 +109,8 @@
 
     private static void assertValidResult(TextSelection selection) {
         assertNotNull(selection);
+        assertTrue(selection.getSelectionStartIndex() >= 0);
+        assertTrue(selection.getSelectionEndIndex() > selection.getSelectionStartIndex());
         assertTrue(selection.getEntityCount() >= 0);
         for (int i = 0; i < selection.getEntityCount(); i++) {
             final String entity = selection.getEntity(i);
@@ -104,6 +119,7 @@
             assertTrue(confidenceScore >= 0);
             assertTrue(confidenceScore <= 1);
         }
+        assertNotNull(selection.getSignature());
     }
 
     private static void assertValidResult(TextClassification classification) {
@@ -116,6 +132,23 @@
             assertTrue(confidenceScore >= 0);
             assertTrue(confidenceScore <= 1);
         }
+        assertTrue(classification.getSecondaryActionsCount() >= 0);
+        assertNotNull(classification.getSignature());
+    }
+
+    private static void assertValidResult(TextLinks links) {
+        assertNotNull(links);
+        for (TextLinks.TextLink link : links.getLinks()) {
+            assertTrue(link.getEntityCount() > 0);
+            assertTrue(link.getStart() >= 0);
+            assertTrue(link.getStart() <= link.getEnd());
+            for (int i = 0; i < link.getEntityCount(); i++) {
+                String entityType = link.getEntity(i);
+                assertNotNull(entityType);
+                final float confidenceScore = link.getConfidenceScore(entityType);
+                assertTrue(confidenceScore >= 0);
+                assertTrue(confidenceScore <= 1);
+            }
+        }
     }
 }
-
diff --git a/tests/tests/view/src/android/view/textclassifier/cts/TextClassifierValueObjectsTest.java b/tests/tests/view/src/android/view/textclassifier/cts/TextClassifierValueObjectsTest.java
new file mode 100644
index 0000000..6af8378
--- /dev/null
+++ b/tests/tests/view/src/android/view/textclassifier/cts/TextClassifierValueObjectsTest.java
@@ -0,0 +1,260 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.view.textclassifier.cts;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNull;
+
+import android.content.Intent;
+import android.graphics.Color;
+import android.graphics.drawable.ColorDrawable;
+import android.graphics.drawable.Drawable;
+import android.os.LocaleList;
+import android.support.test.filters.SmallTest;
+import android.support.test.runner.AndroidJUnit4;
+import android.view.View;
+import android.view.textclassifier.TextClassification;
+import android.view.textclassifier.TextClassifier;
+import android.view.textclassifier.TextSelection;
+
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+/**
+ * TextClassifier value objects tests.
+ *
+ * <p>Contains unit tests for value objects passed to/from TextClassifier APIs.
+ */
+@SmallTest
+@RunWith(AndroidJUnit4.class)
+public class TextClassifierValueObjectsTest {
+
+    private static final double ACCEPTED_DELTA = 0.0000001;
+    private static final String TEXT = "abcdefghijklmnopqrstuvwxyz";
+    private static final int START = 5;
+    private static final int END = 20;
+    private static final String SIGNATURE = "sig.na-ture";
+    private static final LocaleList LOCALES = LocaleList.forLanguageTags("fr,en,de,es");
+
+    @Test
+    public void testTextSelection() {
+        final float addressScore = 0.1f;
+        final float emailScore = 0.9f;
+
+        final TextSelection selection = new TextSelection.Builder(START, END)
+                .setEntityType(TextClassifier.TYPE_ADDRESS, addressScore)
+                .setEntityType(TextClassifier.TYPE_EMAIL, emailScore)
+                .setSignature(SIGNATURE)
+                .build();
+
+        assertEquals(START, selection.getSelectionStartIndex());
+        assertEquals(END, selection.getSelectionEndIndex());
+        assertEquals(2, selection.getEntityCount());
+        assertEquals(TextClassifier.TYPE_EMAIL, selection.getEntity(0));
+        assertEquals(TextClassifier.TYPE_ADDRESS, selection.getEntity(1));
+        assertEquals(addressScore, selection.getConfidenceScore(TextClassifier.TYPE_ADDRESS),
+                ACCEPTED_DELTA);
+        assertEquals(emailScore, selection.getConfidenceScore(TextClassifier.TYPE_EMAIL),
+                ACCEPTED_DELTA);
+        assertEquals(0, selection.getConfidenceScore("random_type"), ACCEPTED_DELTA);
+        assertEquals(SIGNATURE, selection.getSignature());
+    }
+
+    @Test
+    public void testTextSelection_differentParams() {
+        final int start = 0;
+        final int end = 1;
+        final float confidenceScore = 0.5f;
+        final String signature = "2hukwu3m3k44f1gb0";
+
+        final TextSelection selection = new TextSelection.Builder(start, end)
+                .setEntityType(TextClassifier.TYPE_URL, confidenceScore)
+                .setSignature(signature)
+                .build();
+
+        assertEquals(start, selection.getSelectionStartIndex());
+        assertEquals(end, selection.getSelectionEndIndex());
+        assertEquals(1, selection.getEntityCount());
+        assertEquals(TextClassifier.TYPE_URL, selection.getEntity(0));
+        assertEquals(confidenceScore, selection.getConfidenceScore(TextClassifier.TYPE_URL),
+                ACCEPTED_DELTA);
+        assertEquals(0, selection.getConfidenceScore("random_type"), ACCEPTED_DELTA);
+        assertEquals(signature, selection.getSignature());
+    }
+
+    @Test
+    public void testTextSelection_defaultValues() {
+        TextSelection selection = new TextSelection.Builder(START, END).build();
+        assertEquals(0, selection.getEntityCount());
+        assertEquals("", selection.getSignature());
+    }
+
+    @Test
+    public void testTextSelection_prunedConfidenceScore() {
+        final float phoneScore = -0.1f;
+        final float prunedPhoneScore = 0f;
+        final float otherScore = 1.5f;
+        final float prunedOtherScore = 1.0f;
+
+        final TextSelection selection = new TextSelection.Builder(START, END)
+                .setEntityType(TextClassifier.TYPE_PHONE, phoneScore)
+                .setEntityType(TextClassifier.TYPE_OTHER, otherScore)
+                .build();
+
+        assertEquals(prunedPhoneScore, selection.getConfidenceScore(TextClassifier.TYPE_PHONE),
+                ACCEPTED_DELTA);
+        assertEquals(prunedOtherScore, selection.getConfidenceScore(TextClassifier.TYPE_OTHER),
+                ACCEPTED_DELTA);
+    }
+
+    @Test(expected = IllegalArgumentException.class)
+    public void testTextSelection_invalidStartParams() {
+        new TextSelection.Builder(-1 /* start */, END)
+                .build();
+    }
+
+    @Test(expected = IllegalArgumentException.class)
+    public void testTextSelection_invalidEndParams() {
+        new TextSelection.Builder(START, 0 /* end */)
+                .build();
+    }
+
+    @Test(expected = NullPointerException.class)
+    public void testTextSelection_invalidSignature() {
+        new TextSelection.Builder(START, END)
+                .setSignature(null)
+                .build();
+    }
+
+    @Test(expected = IndexOutOfBoundsException.class)
+    public void testTextSelection_entityIndexOutOfBounds() {
+        final TextSelection selection = new TextSelection.Builder(START, END).build();
+        final int outOfBoundsIndex = selection.getEntityCount();
+        selection.getEntity(outOfBoundsIndex);
+    }
+
+    @Test
+    public void testTextSelectionOptions() {
+        final TextSelection.Options options = new TextSelection.Options()
+                .setDefaultLocales(LOCALES);
+        assertEquals(LOCALES, options.getDefaultLocales());
+    }
+
+    @Test
+    public void testTextSelectionOptions_nullValues() {
+        final TextSelection.Options options = new TextSelection.Options()
+                .setDefaultLocales(null);
+        assertNull(options.getDefaultLocales());
+    }
+
+    @Test
+    public void testTextSelectionOptions_defaultValues() {
+        final TextSelection.Options options = new TextSelection.Options();
+        assertNull(options.getDefaultLocales());
+    }
+
+    @Test
+    public void testTextClassification() {
+        final float addressScore = 0.1f;
+        final float emailScore = 0.9f;
+        final Intent intent = new Intent();
+        final String label = "label";
+        final Drawable icon = new ColorDrawable(Color.RED);
+        final View.OnClickListener onClick = v -> { };
+        final Intent intent1 = new Intent();
+        final String label1 = "label1";
+        final Drawable icon1 = new ColorDrawable(Color.GREEN);
+        final View.OnClickListener onClick1 = v -> { };
+        final Intent intent2 = new Intent();
+        final String label2 = "label2";
+        final Drawable icon2 = new ColorDrawable(Color.BLUE);
+        final View.OnClickListener onClick2 = v -> { };
+
+        final TextClassification classification = new TextClassification.Builder()
+                .setText(TEXT)
+                .setEntityType(TextClassifier.TYPE_ADDRESS, addressScore)
+                .setEntityType(TextClassifier.TYPE_EMAIL, emailScore)
+                .setPrimaryAction(intent, label, icon, onClick)
+                .addSecondaryAction(intent1, label1, icon1, onClick1)
+                .addSecondaryAction(intent2, label2, icon2, onClick2)
+                .setSignature(SIGNATURE)
+                .build();
+
+        assertEquals(TEXT, classification.getText());
+        assertEquals(2, classification.getEntityCount());
+        assertEquals(TextClassifier.TYPE_EMAIL, classification.getEntity(0));
+        assertEquals(TextClassifier.TYPE_ADDRESS, classification.getEntity(1));
+        assertEquals(addressScore, classification.getConfidenceScore(TextClassifier.TYPE_ADDRESS),
+                ACCEPTED_DELTA);
+        assertEquals(emailScore, classification.getConfidenceScore(TextClassifier.TYPE_EMAIL),
+                ACCEPTED_DELTA);
+        assertEquals(0, classification.getConfidenceScore("random_type"), ACCEPTED_DELTA);
+
+        assertEquals(intent, classification.getIntent());
+        assertEquals(label, classification.getLabel());
+        assertEquals(icon, classification.getIcon());
+        assertEquals(onClick, classification.getOnClickListener());
+
+        assertEquals(2, classification.getSecondaryActionsCount());
+        assertEquals(intent1, classification.getSecondaryIntent(0));
+        assertEquals(label1, classification.getSecondaryLabel(0));
+        assertEquals(icon1, classification.getSecondaryIcon(0));
+        assertEquals(onClick1, classification.getSecondaryOnClickListener(0));
+        assertEquals(intent2, classification.getSecondaryIntent(1));
+        assertEquals(label2, classification.getSecondaryLabel(1));
+        assertEquals(icon2, classification.getSecondaryIcon(1));
+        assertEquals(onClick2, classification.getSecondaryOnClickListener(1));
+
+        assertEquals(SIGNATURE, classification.getSignature());
+    }
+
+    @Test
+    public void testTextClassification_defaultValues() {
+        final TextClassification classification = new TextClassification.Builder().build();
+
+        assertEquals(null, classification.getText());
+        assertEquals(0, classification.getEntityCount());
+        assertEquals(null, classification.getIntent());
+        assertEquals(null, classification.getLabel());
+        assertEquals(null, classification.getIcon());
+        assertEquals(null, classification.getOnClickListener());
+        assertEquals(0, classification.getSecondaryActionsCount());
+        assertEquals("", classification.getSignature());
+    }
+
+    @Test
+    public void testTextClassificationOptions() {
+        final TextClassification.Options options = new TextClassification.Options()
+                .setDefaultLocales(LOCALES);
+        assertEquals(LOCALES, options.getDefaultLocales());
+    }
+
+    @Test
+    public void testTextClassificationOptions_nullValues() {
+        final TextClassification.Options options = new TextClassification.Options()
+                .setDefaultLocales(null);
+        assertNull(options.getDefaultLocales());
+    }
+
+    @Test
+    public void testTextClassificationOptions_defaultValues() {
+        final TextClassification.Options options = new TextClassification.Options();
+        assertNull(options.getDefaultLocales());
+    }
+
+    // TODO: Add more tests.
+}
diff --git a/tests/tests/voiceinteraction/Android.mk b/tests/tests/voiceinteraction/Android.mk
index b83f4e9..e0708ff 100644
--- a/tests/tests/voiceinteraction/Android.mk
+++ b/tests/tests/voiceinteraction/Android.mk
@@ -23,6 +23,8 @@
 
 LOCAL_STATIC_JAVA_LIBRARIES := CtsVoiceInteractionCommon ctstestrunner compatibility-device-util
 
+LOCAL_JAVA_LIBRARIES := android.test.runner.stubs android.test.base.stubs
+
 LOCAL_SRC_FILES := $(call all-java-files-under, src)
 
 LOCAL_PACKAGE_NAME := CtsVoiceInteractionTestCases
diff --git a/tests/tests/voiceinteraction/service/src/android/voiceinteraction/service/MainInteractionSession.java b/tests/tests/voiceinteraction/service/src/android/voiceinteraction/service/MainInteractionSession.java
index 6ada1b8..203be4d 100644
--- a/tests/tests/voiceinteraction/service/src/android/voiceinteraction/service/MainInteractionSession.java
+++ b/tests/tests/voiceinteraction/service/src/android/voiceinteraction/service/MainInteractionSession.java
@@ -20,6 +20,7 @@
 import android.app.VoiceInteractor.Prompt;
 import android.content.Context;
 import android.content.Intent;
+import android.content.pm.LauncherApps;
 import android.os.AsyncTask;
 import android.os.Bundle;
 import android.service.voice.VoiceInteractionSession;
@@ -46,6 +47,10 @@
     public void onCreate() {
         super.onCreate();
         Intent sessionStarted = new Intent();
+        sessionStarted.addFlags(Intent.FLAG_RECEIVER_FOREGROUND);
+        if (!getContext().getSystemService(LauncherApps.class).hasShortcutHostPermission()) {
+            sessionStarted.putExtra("error", "Does not have shortcut permission");
+        }
         sessionStarted.setClassName("android.voiceinteraction.cts",
                 "android.voiceinteraction.cts.VoiceInteractionTestReceiver");
         getContext().sendBroadcast(sessionStarted);
diff --git a/tests/tests/voiceinteraction/src/android/voiceinteraction/cts/LocalVoiceInteractionTest.java b/tests/tests/voiceinteraction/src/android/voiceinteraction/cts/LocalVoiceInteractionTest.java
index 07236b2..66c945a 100644
--- a/tests/tests/voiceinteraction/src/android/voiceinteraction/cts/LocalVoiceInteractionTest.java
+++ b/tests/tests/voiceinteraction/src/android/voiceinteraction/cts/LocalVoiceInteractionTest.java
@@ -71,7 +71,6 @@
         if (!mHasFeature) {
             return;
         }
-        VoiceInteractionTestReceiver.sServiceStartedLatch.await(5, TimeUnit.SECONDS);
 
         assertTrue("Doesn't support LocalVoiceInteraction",
                 mTestActivity.isLocalVoiceInteractionSupported());
diff --git a/tests/tests/voiceinteraction/src/android/voiceinteraction/cts/VoiceInteractionTest.java b/tests/tests/voiceinteraction/src/android/voiceinteraction/cts/VoiceInteractionTest.java
index 77c9f26..1abd00f 100644
--- a/tests/tests/voiceinteraction/src/android/voiceinteraction/cts/VoiceInteractionTest.java
+++ b/tests/tests/voiceinteraction/src/android/voiceinteraction/cts/VoiceInteractionTest.java
@@ -87,7 +87,7 @@
     }
 
     public void testAll() throws Exception {
-        VoiceInteractionTestReceiver.sServiceStartedLatch.await(5, TimeUnit.SECONDS);
+        VoiceInteractionTestReceiver.waitSessionStarted(this, 5, TimeUnit.SECONDS);
 
         if (!mHasFeature) {
             Log.i(TAG, "The device doesn't support feature: " + FEATURE_VOICE_RECOGNIZERS);
diff --git a/tests/tests/voiceinteraction/src/android/voiceinteraction/cts/VoiceInteractionTestReceiver.java b/tests/tests/voiceinteraction/src/android/voiceinteraction/cts/VoiceInteractionTestReceiver.java
index 118f049..7f875c4 100644
--- a/tests/tests/voiceinteraction/src/android/voiceinteraction/cts/VoiceInteractionTestReceiver.java
+++ b/tests/tests/voiceinteraction/src/android/voiceinteraction/cts/VoiceInteractionTestReceiver.java
@@ -21,15 +21,31 @@
 import android.content.Intent;
 import android.util.Log;
 
+import junit.framework.TestCase;
+
 import java.util.concurrent.CountDownLatch;
+import java.util.concurrent.TimeUnit;
 
 public class VoiceInteractionTestReceiver extends BroadcastReceiver {
 
-    public static CountDownLatch sServiceStartedLatch = new CountDownLatch(1);
+    private static CountDownLatch sServiceStartedLatch = new CountDownLatch(1);
+    private static Intent sReceivedIntent;
+
+    public static void waitSessionStarted(TestCase testCase, long timeout, TimeUnit unit)
+            throws InterruptedException {
+        if (!sServiceStartedLatch.await(5, TimeUnit.SECONDS)) {
+            testCase.fail("Timed out waiting for session to start");
+        }
+        String error = sReceivedIntent.getStringExtra("error");
+        if (error != null) {
+            testCase.fail(error);
+        }
+    }
 
     @Override
     public void onReceive(Context context, Intent intent) {
         Log.i("VoiceInteractionTestReceiver", "Got broadcast that MainInteractionService started");
+        sReceivedIntent = intent;
         sServiceStartedLatch.countDown();
     }
 }
\ No newline at end of file
diff --git a/tests/tests/voiceinteraction/testapp/src/android/voiceinteraction/testapp/TestApp.java b/tests/tests/voiceinteraction/testapp/src/android/voiceinteraction/testapp/TestApp.java
index dbfb031..7c6cf53 100644
--- a/tests/tests/voiceinteraction/testapp/src/android/voiceinteraction/testapp/TestApp.java
+++ b/tests/tests/voiceinteraction/testapp/src/android/voiceinteraction/testapp/TestApp.java
@@ -135,6 +135,7 @@
 
     private void broadcastResults() {
         Intent intent = new Intent(Utils.BROADCAST_INTENT);
+        intent.addFlags(Intent.FLAG_RECEIVER_FOREGROUND);
         intent.putExtras(mTotalInfo);
         Log.i(TAG, "broadcasting: " + intent.toString() + ", Bundle = " + mTotalInfo.toString());
         sendOrderedBroadcast(intent, null, new DoneReceiver(),
diff --git a/tests/tests/voicesettings/Android.mk b/tests/tests/voicesettings/Android.mk
index cad36cb..9409073 100644
--- a/tests/tests/voicesettings/Android.mk
+++ b/tests/tests/voicesettings/Android.mk
@@ -23,6 +23,8 @@
 
 LOCAL_STATIC_JAVA_LIBRARIES := ctstestrunner compatibility-device-util
 
+LOCAL_JAVA_LIBRARIES := android.test.runner.stubs android.test.base.stubs
+
 LOCAL_SRC_FILES := $(call all-java-files-under, src)
 
 LOCAL_PACKAGE_NAME := CtsVoiceSettingsTestCases
diff --git a/tests/tests/voicesettings/AndroidTest.xml b/tests/tests/voicesettings/AndroidTest.xml
index 421dfb7..dfb50d8 100644
--- a/tests/tests/voicesettings/AndroidTest.xml
+++ b/tests/tests/voicesettings/AndroidTest.xml
@@ -14,6 +14,7 @@
      limitations under the License.
 -->
 <configuration description="Config for CTS Voice Settings test cases">
+    <option name="test-suite-tag" value="cts" />
     <option name="config-descriptor:metadata" key="component" value="framework" />
     <option name="not-shardable" value="true" />
     <target_preparer class="com.android.tradefed.targetprep.suite.SuiteApkInstaller">
diff --git a/tests/tests/webkit/Android.mk b/tests/tests/webkit/Android.mk
index c56f00e..5612c53 100644
--- a/tests/tests/webkit/Android.mk
+++ b/tests/tests/webkit/Android.mk
@@ -21,7 +21,11 @@
 # and when built explicitly put it in the data partition
 LOCAL_MODULE_PATH := $(TARGET_OUT_DATA_APPS)
 
-LOCAL_JAVA_LIBRARIES := android.test.runner org.apache.http.legacy
+LOCAL_JAVA_LIBRARIES := \
+    android.test.runner \
+    org.apache.http.legacy \
+    android.test.base \
+
 
 LOCAL_STATIC_JAVA_LIBRARIES := \
     compatibility-device-util \
diff --git a/tests/tests/webkit/AndroidManifest.xml b/tests/tests/webkit/AndroidManifest.xml
index 83775df..5168cda 100644
--- a/tests/tests/webkit/AndroidManifest.xml
+++ b/tests/tests/webkit/AndroidManifest.xml
@@ -22,7 +22,7 @@
     <uses-permission android:name="android.permission.ACCESS_LOCATION_EXTRA_COMMANDS"/>
     <uses-permission android:name="android.permission.INTERNET" />
     <uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" />
-    <application android:maxRecents="1">
+    <application android:maxRecents="1" android:usesCleartextTraffic="true">
         <provider android:name="android.webkit.cts.MockContentProvider"
                   android:exported="true"
                   android:authorities="android.webkit.cts.MockContentProvider" />
@@ -56,6 +56,14 @@
             </intent-filter>
         </activity>
 
+        <service android:name="android.webkit.cts.TestProcessServiceA"
+                 android:process=":testprocessA"
+                 android:exported="false" />
+
+        <service android:name="android.webkit.cts.TestProcessServiceB"
+                 android:process=":testprocessB"
+                 android:exported="false" />
+
         <meta-data android:name="android.webkit.WebView.EnableSafeBrowsing" android:value="false" />
 
     </application>
diff --git a/tests/tests/webkit/AndroidTest.xml b/tests/tests/webkit/AndroidTest.xml
index a3801e4..903aef7 100644
--- a/tests/tests/webkit/AndroidTest.xml
+++ b/tests/tests/webkit/AndroidTest.xml
@@ -14,6 +14,7 @@
      limitations under the License.
 -->
 <configuration description="Config for CTS Webkit test cases">
+    <option name="test-suite-tag" value="cts" />
     <option name="config-descriptor:metadata" key="component" value="webview" />
     <target_preparer class="com.android.compatibility.common.tradefed.targetprep.LocationCheck" />
     <target_preparer class="com.android.tradefed.targetprep.suite.SuiteApkInstaller">
diff --git a/tests/tests/webkit/src/android/webkit/cts/PostMessageTest.java b/tests/tests/webkit/src/android/webkit/cts/PostMessageTest.java
index ecc3e4f..0fa239d 100644
--- a/tests/tests/webkit/src/android/webkit/cts/PostMessageTest.java
+++ b/tests/tests/webkit/src/android/webkit/cts/PostMessageTest.java
@@ -101,12 +101,26 @@
 
     // Post a string message to main frame and make sure it is received.
     public void testSimpleMessageToMainFrame() throws Throwable {
+        verifyPostMessageToOrigin(Uri.parse(BASE_URI));
+    }
+
+    // Post a string message to main frame passing a wildcard as target origin
+    public void testWildcardOriginMatchesAnything() throws Throwable {
+        verifyPostMessageToOrigin(Uri.parse("*"));
+    }
+
+    // Post a string message to main frame passing an empty string as target origin
+    public void testEmptyStringOriginMatchesAnything() throws Throwable {
+        verifyPostMessageToOrigin(Uri.parse(""));
+    }
+
+    private void verifyPostMessageToOrigin(Uri origin) throws Throwable {
         if (!NullWebViewUtils.isWebViewAvailable()) {
             return;
         }
         loadPage(TITLE_FROM_POST_MESSAGE);
         WebMessage message = new WebMessage(WEBVIEW_MESSAGE);
-        mOnUiThread.postWebMessage(message, Uri.parse(BASE_URI));
+        mOnUiThread.postWebMessage(message, origin);
         waitForTitle(WEBVIEW_MESSAGE);
     }
 
diff --git a/tests/tests/webkit/src/android/webkit/cts/TestProcessClient.java b/tests/tests/webkit/src/android/webkit/cts/TestProcessClient.java
new file mode 100644
index 0000000..1854641
--- /dev/null
+++ b/tests/tests/webkit/src/android/webkit/cts/TestProcessClient.java
@@ -0,0 +1,175 @@
+/*
+ * 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.webkit.cts;
+
+
+import android.content.ComponentName;
+import android.content.Context;
+import android.content.Intent;
+import android.content.ServiceConnection;
+import android.os.Handler;
+import android.os.IBinder;
+import android.os.Looper;
+import android.os.Message;
+import android.os.Messenger;
+import android.os.RemoteException;
+
+import com.android.internal.annotations.GuardedBy;
+
+import junit.framework.Assert;
+import junit.framework.AssertionFailedError;
+
+class TestProcessClient extends Assert implements AutoCloseable, ServiceConnection {
+    private Context mContext;
+
+    private static final long CONNECT_TIMEOUT_MS = 5000;
+
+    private Object mLock = new Object();
+    @GuardedBy("mLock")
+    private Messenger mService;
+    @GuardedBy("mLock")
+    private Integer mLastResult;
+    @GuardedBy("mLock")
+    private Throwable mLastException;
+
+    private final Messenger mReplyHandler = new Messenger(new ReplyHandler(Looper.getMainLooper()));
+
+    public static TestProcessClient createProcessA(Context context) throws Throwable {
+        return new TestProcessClient(context, TestProcessServiceA.class);
+    }
+
+    public static TestProcessClient createProcessB(Context context) throws Throwable {
+        return new TestProcessClient(context, TestProcessServiceB.class);
+    }
+
+    /**
+     * Subclass this to implement test code to run on the service side.
+     */
+    static abstract class TestRunnable extends Assert {
+        public abstract void run(Context ctx);
+    }
+
+    static class ProcessFreshChecker extends TestRunnable {
+        private static Object sFreshLock = new Object();
+        @GuardedBy("sFreshLock")
+        private static boolean sFreshProcess = true;
+
+        @Override
+        public void run(Context ctx) {
+            synchronized (sFreshLock) {
+                if (!sFreshProcess) {
+                    fail("Service process was unexpectedly reused");
+                }
+                sFreshProcess = true;
+            }
+        }
+
+    }
+
+    private TestProcessClient(Context context, Class service) throws Throwable {
+        mContext = context;
+        Intent i = new Intent(context, service);
+        context.bindService(i, this, Context.BIND_AUTO_CREATE);
+        synchronized (mLock) {
+            if (mService == null) {
+                mLock.wait(CONNECT_TIMEOUT_MS);
+                if (mService == null) {
+                    fail("Timeout waiting for connection");
+                }
+            }
+        }
+
+        // Check that we're using an actual fresh process.
+        // 1000ms timeout is plenty since the service is already running.
+        run(ProcessFreshChecker.class, 1000);
+    }
+
+    public void run(Class runnableClass, long timeoutMs) throws Throwable {
+        Message m = Message.obtain(null, TestProcessService.MSG_RUN_TEST);
+        m.replyTo = mReplyHandler;
+        m.getData().putString(TestProcessService.TEST_CLASS_KEY, runnableClass.getName());
+        int result;
+        Throwable exception;
+        synchronized (mLock) {
+            mService.send(m);
+            if (mLastResult == null) {
+                mLock.wait(timeoutMs);
+                if (mLastResult == null) {
+                    fail("Timeout waiting for result");
+                }
+            }
+            result = mLastResult;
+            mLastResult = null;
+            exception = mLastException;
+            mLastException = null;
+        }
+        if (result == TestProcessService.REPLY_EXCEPTION) {
+            throw exception;
+        } else if (result != TestProcessService.REPLY_OK) {
+            fail("Unknown result from service: " + result);
+        }
+    }
+
+    public void close() {
+        synchronized (mLock) {
+            if (mService != null) {
+                try {
+                    mService.send(Message.obtain(null, TestProcessService.MSG_EXIT_PROCESS));
+                } catch (RemoteException e) {}
+                mService = null;
+                mContext.unbindService(this);
+            }
+        }
+    }
+
+    @Override
+    public void onServiceConnected(ComponentName className, IBinder service) {
+        synchronized (mLock) {
+            mService = new Messenger(service);
+            mLock.notify();
+        }
+    }
+
+    @Override
+    public void onServiceDisconnected(ComponentName className) {
+        synchronized (mLock) {
+            mService = null;
+            mContext.unbindService(this);
+            mLastResult = TestProcessService.REPLY_EXCEPTION;
+            mLastException = new AssertionFailedError("Service disconnected unexpectedly");
+            mLock.notify();
+        }
+    }
+
+    private class ReplyHandler extends Handler {
+        ReplyHandler(Looper looper) {
+            super(looper);
+        }
+
+        @Override
+        public void handleMessage(Message msg) {
+            synchronized (mLock) {
+                mLastResult = msg.what;
+                if (msg.what == TestProcessService.REPLY_EXCEPTION) {
+                    mLastException = (Throwable) msg.getData().getSerializable(
+                            TestProcessService.REPLY_EXCEPTION_KEY);
+                }
+                mLock.notify();
+            }
+        }
+    }
+}
diff --git a/tests/tests/webkit/src/android/webkit/cts/TestProcessService.java b/tests/tests/webkit/src/android/webkit/cts/TestProcessService.java
new file mode 100644
index 0000000..6ce8c4c
--- /dev/null
+++ b/tests/tests/webkit/src/android/webkit/cts/TestProcessService.java
@@ -0,0 +1,85 @@
+/*
+ * 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.webkit.cts;
+
+
+import android.app.Service;
+import android.content.Context;
+import android.content.Intent;
+import android.os.Handler;
+import android.os.IBinder;
+import android.os.Message;
+import android.os.Messenger;
+import android.os.RemoteException;
+import android.util.Log;
+
+import junit.framework.Assert;
+import junit.framework.AssertionFailedError;
+
+// Subclasses are the ones that get actually used, so make this abstract
+abstract class TestProcessService extends Service {
+    static final int MSG_RUN_TEST = 0;
+    static final int MSG_EXIT_PROCESS = 1;
+    static final String TEST_CLASS_KEY = "class";
+
+    static final int REPLY_OK = 0;
+    static final int REPLY_EXCEPTION = 1;
+    static final String REPLY_EXCEPTION_KEY = "exception";
+
+    @Override
+    public IBinder onBind(Intent intent) {
+        return mMessenger.getBinder();
+    }
+
+    final Messenger mMessenger = new Messenger(new IncomingHandler());
+
+    private class IncomingHandler extends Handler {
+        @Override
+        public void handleMessage(Message msg) {
+            if (msg.what == MSG_EXIT_PROCESS) {
+                System.exit(0);
+            }
+
+            try {
+                if (msg.what != MSG_RUN_TEST) {
+                    throw new AssertionFailedError("Unknown service message " + msg.what);
+                }
+
+                String testClassName = msg.getData().getString(TEST_CLASS_KEY);
+                Class testClass = Class.forName(testClassName);
+                TestProcessClient.TestRunnable test =
+                        (TestProcessClient.TestRunnable) testClass.newInstance();
+                test.run(TestProcessService.this);
+            } catch (Throwable t) {
+                try {
+                    Message m = Message.obtain(null, REPLY_EXCEPTION);
+                    m.getData().putSerializable(REPLY_EXCEPTION_KEY, t);
+                    msg.replyTo.send(m);
+                } catch (RemoteException e) {
+                    throw new RuntimeException(e);
+                }
+                return;
+            }
+
+            try {
+                msg.replyTo.send(Message.obtain(null, REPLY_OK));
+            } catch (RemoteException e) {
+                throw new RuntimeException(e);
+            }
+        }
+    }
+}
diff --git a/tests/tests/webkit/src/android/webkit/cts/TestProcessServiceA.java b/tests/tests/webkit/src/android/webkit/cts/TestProcessServiceA.java
new file mode 100644
index 0000000..14241f0
--- /dev/null
+++ b/tests/tests/webkit/src/android/webkit/cts/TestProcessServiceA.java
@@ -0,0 +1,20 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.webkit.cts;
+
+// We need two different instances of the same service, so make two subclasses.
+public class TestProcessServiceA extends TestProcessService {}
diff --git a/tests/tests/webkit/src/android/webkit/cts/TestProcessServiceB.java b/tests/tests/webkit/src/android/webkit/cts/TestProcessServiceB.java
new file mode 100644
index 0000000..d2376f7
--- /dev/null
+++ b/tests/tests/webkit/src/android/webkit/cts/TestProcessServiceB.java
@@ -0,0 +1,20 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.webkit.cts;
+
+// We need two different instances of the same service, so make two subclasses.
+public class TestProcessServiceB extends TestProcessService {}
diff --git a/tests/tests/webkit/src/android/webkit/cts/WebViewDataDirTest.java b/tests/tests/webkit/src/android/webkit/cts/WebViewDataDirTest.java
new file mode 100644
index 0000000..0843eaf
--- /dev/null
+++ b/tests/tests/webkit/src/android/webkit/cts/WebViewDataDirTest.java
@@ -0,0 +1,187 @@
+/*
+ * 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.webkit.cts;
+
+
+import android.content.Context;
+import android.test.AndroidTestCase;
+import android.webkit.CookieManager;
+import android.webkit.WebView;
+
+import com.android.compatibility.common.util.NullWebViewUtils;
+
+public class WebViewDataDirTest extends AndroidTestCase {
+
+    private static final long REMOTE_TIMEOUT_MS = 5000;
+    private static final String ALTERNATE_DIR_NAME = "test";
+    private static final String COOKIE_URL = "https://www.example.com/";
+    private static final String COOKIE_VALUE = "foo=main";
+    private static final String SET_COOKIE_PARAMS = "; Max-Age=86400";
+
+    static class TestDisableThenUseImpl extends TestProcessClient.TestRunnable {
+        @Override
+        public void run(Context ctx) {
+            WebView.disableWebView();
+            try {
+                new WebView(ctx);
+                fail("didn't throw IllegalStateException");
+            } catch (IllegalStateException e) {}
+        }
+    }
+
+    public void testDisableThenUse() throws Throwable {
+        if (!NullWebViewUtils.isWebViewAvailable()) {
+            return;
+        }
+
+        try (TestProcessClient process = TestProcessClient.createProcessA(getContext())) {
+            process.run(TestDisableThenUseImpl.class, REMOTE_TIMEOUT_MS);
+        }
+    }
+
+    static class TestUseThenDisableImpl extends TestProcessClient.TestRunnable {
+        @Override
+        public void run(Context ctx) {
+            new WebView(ctx);
+            try {
+                WebView.disableWebView();
+                fail("didn't throw IllegalStateException");
+            } catch (IllegalStateException e) {}
+        }
+    }
+
+    public void testUseThenDisable() throws Throwable {
+        if (!NullWebViewUtils.isWebViewAvailable()) {
+            return;
+        }
+
+        try (TestProcessClient process = TestProcessClient.createProcessA(getContext())) {
+            process.run(TestUseThenDisableImpl.class, REMOTE_TIMEOUT_MS);
+        }
+    }
+
+    static class TestUseThenChangeDirImpl extends TestProcessClient.TestRunnable {
+        @Override
+        public void run(Context ctx) {
+            new WebView(ctx);
+            try {
+                WebView.setDataDirectorySuffix("test");
+                fail("didn't throw IllegalStateException");
+            } catch (IllegalStateException e) {}
+        }
+    }
+
+    public void testUseThenChangeDir() throws Throwable {
+        if (!NullWebViewUtils.isWebViewAvailable()) {
+            return;
+        }
+
+        try (TestProcessClient process = TestProcessClient.createProcessA(getContext())) {
+            process.run(TestUseThenChangeDirImpl.class, REMOTE_TIMEOUT_MS);
+        }
+    }
+
+    static class TestInvalidDirImpl extends TestProcessClient.TestRunnable {
+        @Override
+        public void run(Context ctx) {
+            try {
+                WebView.setDataDirectorySuffix("no/path/separators");
+                fail("didn't throw IllegalArgumentException");
+            } catch (IllegalArgumentException e) {}
+        }
+    }
+
+    public void testInvalidDir() throws Throwable {
+        if (!NullWebViewUtils.isWebViewAvailable()) {
+            return;
+        }
+
+        try (TestProcessClient process = TestProcessClient.createProcessA(getContext())) {
+            process.run(TestInvalidDirImpl.class, REMOTE_TIMEOUT_MS);
+        }
+    }
+
+    static class WebViewInDefaultDir extends TestProcessClient.TestRunnable {
+        @Override
+        public void run(Context ctx) {
+            new WebView(ctx);
+        }
+    }
+
+    static class TestDefaultDirDisallowed extends TestProcessClient.TestRunnable {
+        @Override
+        public void run(Context ctx) {
+            try {
+                new WebView(ctx);
+                fail("didn't throw RuntimeException");
+            } catch (RuntimeException e) {}
+        }
+    }
+
+    public void testSameDirTwoProcesses() throws Throwable {
+        if (!NullWebViewUtils.isWebViewAvailable()) {
+            return;
+        }
+
+        try (TestProcessClient processA = TestProcessClient.createProcessA(getContext());
+             TestProcessClient processB = TestProcessClient.createProcessB(getContext())) {
+            processA.run(WebViewInDefaultDir.class, REMOTE_TIMEOUT_MS);
+            processB.run(TestDefaultDirDisallowed.class, REMOTE_TIMEOUT_MS);
+        }
+    }
+
+    static class SetCookieInDefaultDir extends TestProcessClient.TestRunnable {
+        @Override
+        public void run(Context ctx) {
+            CookieManager cm = CookieManager.getInstance();
+            cm.setCookie(COOKIE_URL, COOKIE_VALUE + SET_COOKIE_PARAMS);
+            cm.flush();
+        }
+    }
+
+    static class TestCookieInDefaultDir extends TestProcessClient.TestRunnable {
+        @Override
+        public void run(Context ctx) {
+            CookieManager cm = CookieManager.getInstance();
+            String cookie = cm.getCookie(COOKIE_URL);
+            assertEquals("wrong cookie in default cookie jar", COOKIE_VALUE, cookie);
+        }
+    }
+
+    static class TestCookieInAlternateDir extends TestProcessClient.TestRunnable {
+        @Override
+        public void run(Context ctx) {
+            WebView.setDataDirectorySuffix(ALTERNATE_DIR_NAME);
+            CookieManager cm = CookieManager.getInstance();
+            String cookie = cm.getCookie(COOKIE_URL);
+            assertNull("cookie leaked to alternate cookie jar", cookie);
+        }
+    }
+
+    public void testCookieJarsSeparate() throws Throwable {
+        if (!NullWebViewUtils.isWebViewAvailable()) {
+            return;
+        }
+
+        try (TestProcessClient processA = TestProcessClient.createProcessA(getContext());
+             TestProcessClient processB = TestProcessClient.createProcessB(getContext())) {
+            processA.run(SetCookieInDefaultDir.class, REMOTE_TIMEOUT_MS);
+            processA.run(TestCookieInDefaultDir.class, REMOTE_TIMEOUT_MS);
+            processB.run(TestCookieInAlternateDir.class, REMOTE_TIMEOUT_MS);
+        }
+    }
+}
diff --git a/tests/tests/webkit/src/android/webkit/cts/WebViewSslTest.java b/tests/tests/webkit/src/android/webkit/cts/WebViewSslTest.java
index 0708568..688fc33 100644
--- a/tests/tests/webkit/src/android/webkit/cts/WebViewSslTest.java
+++ b/tests/tests/webkit/src/android/webkit/cts/WebViewSslTest.java
@@ -898,8 +898,8 @@
     }
 
     private void clearClientCertPreferences() {
-       final AtomicBoolean cleared = new AtomicBoolean(false);
-        mOnUiThread.clearClientCertPreferences(new Runnable() {
+        final AtomicBoolean cleared = new AtomicBoolean(false);
+        WebView.clearClientCertPreferences(new Runnable() {
             @Override
             public void run() {
                 cleared.set(true);
diff --git a/tests/tests/webkit/src/android/webkit/cts/WebViewTest.java b/tests/tests/webkit/src/android/webkit/cts/WebViewTest.java
old mode 100755
new mode 100644
index e5939fd..32be1c9
--- a/tests/tests/webkit/src/android/webkit/cts/WebViewTest.java
+++ b/tests/tests/webkit/src/android/webkit/cts/WebViewTest.java
@@ -27,6 +27,7 @@
 import android.graphics.Color;
 import android.graphics.Picture;
 import android.graphics.Rect;
+import android.graphics.pdf.PdfRenderer;
 import android.net.Uri;
 import android.os.Bundle;
 import android.os.CancellationSignal;
@@ -38,6 +39,7 @@
 import android.os.StrictMode;
 import android.os.StrictMode.ThreadPolicy;
 import android.os.SystemClock;
+import android.platform.test.annotations.Presubmit;
 import android.print.PageRange;
 import android.print.PrintAttributes;
 import android.print.PrintDocumentAdapter;
@@ -212,6 +214,7 @@
         StrictMode.setThreadPolicy(oldPolicy);
     }
 
+    @Presubmit
     @UiThreadTest
     public void testConstructor() {
         if (!NullWebViewUtils.isWebViewAvailable()) {
@@ -272,7 +275,7 @@
         assertNotNull(CookieSyncManager.getInstance());
     }
 
-    @UiThreadTest
+    // Static methods should be safe to call on non-UI threads
     public void testFindAddress() {
         if (!NullWebViewUtils.isWebViewAvailable()) {
             return;
@@ -2562,7 +2565,8 @@
                     // Called on UI thread
                     @Override
                     public void onLayoutFinished(PrintDocumentInfo info, boolean changed) {
-                        savePrintedPage(adapter, descriptor, result);
+                        PageRange[] pageRanges = new PageRange[] {PageRange.ALL_PAGES};
+                        savePrintedPage(adapter, descriptor, pageRanges, result);
                     }
                 });
         try {
@@ -2580,6 +2584,57 @@
         }
     }
 
+    // Verify Print feature can create a PDF file with correct number of pages.
+    public void testPrintingPagesCount() throws Throwable {
+        if (!NullWebViewUtils.isWebViewAvailable()) {
+            return;
+        }
+        String content = "<html><head></head><body>";
+        for (int i = 0; i < 500; ++i) {
+            content += "<br />abcdefghijk<br />";
+        }
+        content += "</body></html>";
+        mOnUiThread.loadDataAndWaitForCompletion(content, "text/html", null);
+        final PrintDocumentAdapter adapter =  mOnUiThread.createPrintDocumentAdapter();
+        printDocumentStart(adapter);
+        PrintAttributes attributes = new PrintAttributes.Builder()
+                .setMediaSize(PrintAttributes.MediaSize.ISO_A4)
+                .setResolution(new PrintAttributes.Resolution("foo", "bar", 300, 300))
+                .setMinMargins(PrintAttributes.Margins.NO_MARGINS)
+                .build();
+        final WebViewCtsActivity activity = getActivity();
+        final File file = activity.getFileStreamPath(PRINTER_TEST_FILE);
+        final ParcelFileDescriptor descriptor = ParcelFileDescriptor.open(file,
+                ParcelFileDescriptor.parseMode("w"));
+        final FutureTask<Boolean> result =
+                new FutureTask<Boolean>(new Callable<Boolean>() {
+                            public Boolean call() {
+                                return true;
+                            }
+                        });
+        printDocumentLayout(adapter, null, attributes,
+                new LayoutResultCallback() {
+                    // Called on UI thread
+                    @Override
+                    public void onLayoutFinished(PrintDocumentInfo info, boolean changed) {
+                        PageRange[] pageRanges = new PageRange[] {
+                            new PageRange(1, 1), new PageRange(4, 7)
+                        };
+                        savePrintedPage(adapter, descriptor, pageRanges, result);
+                    }
+                });
+        try {
+            result.get(TEST_TIMEOUT, TimeUnit.MILLISECONDS);
+            assertTrue(file.length() > 0);
+            PdfRenderer renderer = new PdfRenderer(
+                ParcelFileDescriptor.open(file, ParcelFileDescriptor.MODE_READ_ONLY));
+            assertEquals(5, renderer.getPageCount());
+        } finally {
+            descriptor.close();
+            file.delete();
+        }
+    }
+
     public void testVisualStateCallbackCalled() throws Exception {
         // Check that the visual state callback is called correctly.
         if (!NullWebViewUtils.isWebViewAvailable()) {
@@ -2808,8 +2863,9 @@
     }
 
     private void savePrintedPage(final PrintDocumentAdapter adapter,
-            final ParcelFileDescriptor descriptor, final FutureTask<Boolean> result) {
-        adapter.onWrite(new PageRange[] {PageRange.ALL_PAGES}, descriptor,
+            final ParcelFileDescriptor descriptor, final PageRange[] pageRanges,
+            final FutureTask<Boolean> result) {
+        adapter.onWrite(pageRanges, descriptor,
                 new CancellationSignal(),
                 new WriteResultCallback() {
                     @Override
diff --git a/tests/tests/widget/Android.mk b/tests/tests/widget/Android.mk
index ec104d8..d18ec64 100644
--- a/tests/tests/widget/Android.mk
+++ b/tests/tests/widget/Android.mk
@@ -27,8 +27,7 @@
     android-common \
     compatibility-device-util \
     ctstestrunner \
-    platform-test-annotations \
-    legacy-android-test
+    platform-test-annotations
 
 LOCAL_JAVA_LIBRARIES := android.test.runner
 
diff --git a/tests/tests/widget/AndroidManifest.xml b/tests/tests/widget/AndroidManifest.xml
index f7fa1d4..3300265 100644
--- a/tests/tests/widget/AndroidManifest.xml
+++ b/tests/tests/widget/AndroidManifest.xml
@@ -19,6 +19,7 @@
     package="android.widget.cts">
 
     <uses-permission android:name="android.permission.DISABLE_KEYGUARD" />
+    <uses-permission android:name="android.permission.WRITE_SETTINGS" />
 
     <application android:label="Android TestCase"
             android:icon="@drawable/size_48x48"
@@ -329,6 +330,16 @@
             </intent-filter>
         </activity>
 
+        <activity android:name="android.widget.cts.TextClockCtsActivity"
+                  android:label="TextClockCtsActivity"
+                  android:screenOrientation="nosensor"
+                  android:windowSoftInputMode="stateAlwaysHidden">
+            <intent-filter>
+                <action android:name="android.intent.action.MAIN" />
+                <category android:name="android.intent.category.FRAMEWORK_INSTRUMENTATION_TEST" />
+            </intent-filter>
+        </activity>
+
         <activity android:name="android.widget.cts.TextViewCtsActivity"
                   android:label="TextViewCtsActivity"
                   android:screenOrientation="nosensor"
@@ -570,6 +581,14 @@
             </intent-filter>
         </activity>
 
+        <activity android:name="android.widget.cts.MagnifierCtsActivity"
+                  android:label="MagnifierCtsActivity">
+            <intent-filter>
+                <action android:name="android.intent.action.MAIN" />
+                <category android:name="android.intent.category.FRAMEWORK_INSTRUMENTATION_TEST" />
+            </intent-filter>
+        </activity>
+
         <receiver android:name="android.widget.cts.appwidget.MyAppWidgetProvider" >
             <intent-filter>
                 <action android:name="android.appwidget.action.APPWIDGET_UPDATE" />
diff --git a/tests/tests/widget/AndroidTest.xml b/tests/tests/widget/AndroidTest.xml
index 2eb0b86..a00e5c9 100644
--- a/tests/tests/widget/AndroidTest.xml
+++ b/tests/tests/widget/AndroidTest.xml
@@ -14,6 +14,7 @@
      limitations under the License.
 -->
 <configuration description="Config for CTS Widget test cases">
+    <option name="test-suite-tag" value="cts" />
     <option name="config-descriptor:metadata" key="component" value="uitoolkit" />
     <target_preparer class="com.android.tradefed.targetprep.suite.SuiteApkInstaller">
         <option name="cleanup-apks" value="true" />
diff --git a/tests/tests/widget/res/layout/autocompletetextview_layout.xml b/tests/tests/widget/res/layout/autocompletetextview_layout.xml
index 793dfb0..32eefb7 100644
--- a/tests/tests/widget/res/layout/autocompletetextview_layout.xml
+++ b/tests/tests/widget/res/layout/autocompletetextview_layout.xml
@@ -18,6 +18,7 @@
     android:orientation="vertical"
     android:layout_width="match_parent"
     android:layout_height="wrap_content">
+    <requestFocus />
 
     <TextView android:id="@+id/autocompletetv_title"
         android:layout_width="wrap_content"
diff --git a/tests/tests/widget/res/layout/list_popup_window.xml b/tests/tests/widget/res/layout/list_popup_window.xml
index bcf5893..fd9dcdf 100644
--- a/tests/tests/widget/res/layout/list_popup_window.xml
+++ b/tests/tests/widget/res/layout/list_popup_window.xml
@@ -19,6 +19,7 @@
     android:id="@+id/main_container"
     android:layout_width="match_parent"
     android:layout_height="match_parent">
+    <requestFocus />
 
     <android.widget.cts.MockViewForListPopupWindow
         android:id="@+id/anchor_upper_left"
diff --git a/tests/tests/widget/res/layout/magnifier_layout.xml b/tests/tests/widget/res/layout/magnifier_layout.xml
new file mode 100644
index 0000000..989d33d
--- /dev/null
+++ b/tests/tests/widget/res/layout/magnifier_layout.xml
@@ -0,0 +1,23 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+  ~ Copyright (C) 2017 The Android Open Source Project
+  ~
+  ~ Licensed under the Apache License, Version 2.0 (the "License");
+  ~ you may not use this file except in compliance with the License.
+  ~ You may obtain a copy of the License at
+  ~
+  ~      http://www.apache.org/licenses/LICENSE-2.0
+  ~
+  ~ Unless required by applicable law or agreed to in writing, software
+  ~ distributed under the License is distributed on an "AS IS" BASIS,
+  ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+  ~ See the License for the specific language governing permissions and
+  ~ limitations under the License
+  -->
+
+<LinearLayout
+    xmlns:android="http://schemas.android.com/apk/res/android"
+    android:id="@+id/magnifier_layout"
+    android:layout_width="wrap_content"
+    android:layout_height="wrap_content">
+</LinearLayout>
diff --git a/tests/tests/widget/res/layout/textclock_layout.xml b/tests/tests/widget/res/layout/textclock_layout.xml
new file mode 100644
index 0000000..a1acce8
--- /dev/null
+++ b/tests/tests/widget/res/layout/textclock_layout.xml
@@ -0,0 +1,21 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2017 The Android Open Source Project
+
+     Licensed under the Apache License, Version 2.0 (the "License");
+     you may not use this file except in compliance with the License.
+     You may obtain a copy of the License at
+
+         http://www.apache.org/licenses/LICENSE-2.0
+
+     Unless required by applicable law or agreed to in writing, software
+     distributed under the License is distributed on an "AS IS" BASIS,
+     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+     See the License for the specific language governing permissions and
+     limitations under the License.
+-->
+
+<TextClock
+    xmlns:android="http://schemas.android.com/apk/res/android"
+    android:id="@+id/textclock"
+    android:layout_width="match_parent"
+    android:layout_height="match_parent"/>
diff --git a/tests/tests/widget/res/layout/textview_fallbacklinespacing_layout.xml b/tests/tests/widget/res/layout/textview_fallbacklinespacing_layout.xml
new file mode 100644
index 0000000..684d821
--- /dev/null
+++ b/tests/tests/widget/res/layout/textview_fallbacklinespacing_layout.xml
@@ -0,0 +1,52 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2018 The Android Open Source Project
+
+     Licensed under the Apache License, Version 2.0 (the "License");
+     you may not use this file except in compliance with the License.
+     You may obtain a copy of the License at
+
+         http://www.apache.org/licenses/LICENSE-2.0
+
+     Unless required by applicable law or agreed to in writing, software
+     distributed under the License is distributed on an "AS IS" BASIS,
+     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+     See the License for the specific language governing permissions and
+     limitations under the License.
+-->
+
+<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
+        android:layout_width="match_parent"
+        android:layout_height="match_parent"
+        android:id="@+id/layout_textviewtest"
+        android:orientation="vertical">
+
+    <TextView android:id="@+id/textview_true"
+              android:fallbackLineSpacing="true"
+              android:layout_width="wrap_content"
+              android:layout_height="wrap_content"/>
+
+    <TextView android:id="@+id/textview_false"
+              android:fallbackLineSpacing="false"
+              android:layout_width="wrap_content"
+              android:layout_height="wrap_content"/>
+
+    <TextView android:id="@+id/textview_default"
+              android:layout_width="wrap_content"
+              android:layout_height="wrap_content"/>
+
+    <TextView android:id="@+id/textview_style_true"
+              android:layout_width="wrap_content"
+              android:layout_height="wrap_content"
+              style="@style/TextAppearance.FallbackLineSpacingTrue"/>
+
+    <TextView android:id="@+id/textview_style_false"
+              android:layout_width="wrap_content"
+              android:layout_height="wrap_content"
+              style="@style/TextAppearance.FallbackLineSpacingFalse"/>
+
+    <TextView android:id="@+id/textview_style_default"
+              android:layout_width="wrap_content"
+              android:layout_height="wrap_content"
+              style="@style/TextAppearance"/>
+
+</LinearLayout>
diff --git a/tests/tests/widget/res/layout/textview_layout.xml b/tests/tests/widget/res/layout/textview_layout.xml
index d311d75..a616637 100644
--- a/tests/tests/widget/res/layout/textview_layout.xml
+++ b/tests/tests/widget/res/layout/textview_layout.xml
@@ -46,12 +46,14 @@
             <TextView
                 android:id="@+id/textview_password"
                 android:password="true"
+                android:minWidth="1dp"
                 android:layout_width="wrap_content"
                 android:layout_height="wrap_content"/>
 
             <TextView
                 android:id="@+id/textview_singleLine"
                 android:singleLine="true"
+                android:minWidth="1dp"
                 android:layout_width="wrap_content"
                 android:layout_height="wrap_content"/>
 
@@ -59,12 +61,14 @@
                 android:id="@+id/textview_text"
                 android:text="@string/text_view_hello"
                 android:breakStrategy="simple"
+                android:minWidth="1dp"
                 android:layout_width="wrap_content"
                 android:layout_height="wrap_content"/>
 
             <TextView
                 android:id="@+id/textview_text_two_lines"
                 android:text="@string/text_view_hello_two_lines"
+                android:minWidth="1dp"
                 android:layout_width="wrap_content"
                 android:layout_height="wrap_content"/>
 
@@ -383,6 +387,52 @@
                 android:text="@string/sample_text"
                 android:justificationMode="inter_word" />
 
+            <TextView
+                android:id="@+id/textview_textappearance_attrs1"
+                android:layout_width="wrap_content"
+                android:layout_height="wrap_content"
+                android:text="@string/sample_text"
+                style="@null"
+                android:textAppearance="@style/TextAppearance.Xml1" />
+
+            <TextView
+                android:id="@+id/textview_textappearance_attrs2"
+                android:layout_width="wrap_content"
+                android:layout_height="wrap_content"
+                android:text="@string/sample_text"
+                style="@null"
+                android:textAppearance="@style/TextAppearance.Xml2" />
+
+            <TextView
+                android:id="@+id/textview_textappearance_attrs3"
+                android:layout_width="wrap_content"
+                android:layout_height="wrap_content"
+                android:text="@string/sample_text"
+                android:textAppearance="@style/TextAppearance.Xml2"
+                style="@style/TextAppearance.Xml3" />
+
+            <TextView
+                android:id="@+id/textview_textappearance_attrs4"
+                android:layout_width="wrap_content"
+                android:layout_height="wrap_content"
+                android:text="@string/sample_text"
+                style="@style/TextAppearance.Xml3" />
+
+            <TextView
+                android:id="@+id/textview_textappearance_attrs_allcaps_password"
+                android:layout_width="wrap_content"
+                android:layout_height="wrap_content"
+                android:text="@string/sample_text"
+                style="@style/AllCapsPassword" />
+
+            <TextView
+                android:id="@+id/textview_textappearance_attrs_serif_fontfamily"
+                android:layout_width="wrap_content"
+                android:layout_height="wrap_content"
+                android:text="@string/sample_text"
+                android:typeface="serif"
+                android:textAppearance="@style/TextAppearance.Xml2" />
+
         </LinearLayout>
 
 </ScrollView>
diff --git a/tests/tests/widget/res/menu/popup_menu.xml b/tests/tests/widget/res/menu/popup_menu.xml
index 29daad0..a2653b4 100644
--- a/tests/tests/widget/res/menu/popup_menu.xml
+++ b/tests/tests/widget/res/menu/popup_menu.xml
@@ -18,12 +18,14 @@
           android:title="@string/popup_menu_highlight"
           android:contentDescription="@string/popup_menu_highlight_description"
           android:tooltipText="@string/popup_menu_highlight_tooltip" />
-    <item android:id="@+id/action_edit"
-          android:title="@string/popup_menu_edit"
-          android:contentDescription="@string/popup_menu_edit_description"
-          android:tooltipText="@string/popup_menu_edit_tooltip" />
-    <item android:id="@+id/action_delete"
-          android:title="@string/popup_menu_delete" />
+    <group android:id="@+id/group_test2">
+        <item android:id="@+id/action_edit"
+              android:title="@string/popup_menu_edit"
+              android:contentDescription="@string/popup_menu_edit_description"
+              android:tooltipText="@string/popup_menu_edit_tooltip" />
+        <item android:id="@+id/action_delete"
+              android:title="@string/popup_menu_delete" />
+    </group>
     <item android:id="@+id/action_ignore"
           android:title="@string/popup_menu_ignore" />
     <item android:id="@+id/action_share"
diff --git a/tests/tests/widget/res/values/styles.xml b/tests/tests/widget/res/values/styles.xml
index a47f169..abb85ad 100644
--- a/tests/tests/widget/res/values/styles.xml
+++ b/tests/tests/widget/res/values/styles.xml
@@ -109,6 +109,52 @@
         <item name="android:textStyle">normal</item>
     </style>
 
+    <style name="TextAppearance.Xml1">
+        <item name="android:textSize">22px</item>
+        <item name="android:typeface">sans</item>
+        <item name="android:textStyle">italic</item>
+        <item name="android:textAllCaps">true</item>
+        <item name="android:letterSpacing">2.4</item>
+        <item name="android:fontFeatureSettings">smcp</item>
+    </style>
+
+    <style name="TextAppearance.Xml2">
+        <item name="android:fontFamily">monospace</item>
+        <item name="android:textStyle">normal</item>
+        <item name="android:shadowColor">@drawable/red</item>
+        <item name="android:shadowDx">10.3</item>
+        <item name="android:shadowDy">0.5</item>
+        <item name="android:shadowRadius">3.3</item>
+        <item name="android:elegantTextHeight">true</item>
+    </style>
+
+    <style name="TextAppearance.Xml3">
+        <item name="android:textSize">32px</item>
+        <item name="android:fontFamily">serif</item>
+        <item name="android:textStyle">bold</item>
+        <item name="android:textAllCaps">false</item>
+        <item name="android:letterSpacing">2.6</item>
+        <item name="android:shadowColor">@drawable/blue</item>
+        <item name="android:shadowDx">1.3</item>
+        <item name="android:shadowDy">10.5</item>
+        <item name="android:shadowRadius">5.3</item>
+        <item name="android:elegantTextHeight">false</item>
+    </style>
+
+
+    <style name="TextAppearance.FallbackLineSpacingFalse">
+        <item name="android:fallbackLineSpacing">false</item>
+    </style>
+
+    <style name="TextAppearance.FallbackLineSpacingTrue">
+        <item name="android:fallbackLineSpacing">true</item>
+    </style>
+
+    <style name="AllCapsPassword">
+        <item name="android:textAllCaps">true</item>
+        <item name="android:password">true</item>
+    </style>
+
     <style name="TestEnum1">
         <item name="testEnum">val1</item>
     </style>
diff --git a/tests/tests/widget/src/android/widget/cts/EditTextTest.java b/tests/tests/widget/src/android/widget/cts/EditTextTest.java
index 9cb9903..e6cf9cc 100644
--- a/tests/tests/widget/src/android/widget/cts/EditTextTest.java
+++ b/tests/tests/widget/src/android/widget/cts/EditTextTest.java
@@ -30,6 +30,7 @@
 import android.support.test.filters.SmallTest;
 import android.support.test.rule.ActivityTestRule;
 import android.support.test.runner.AndroidJUnit4;
+import android.text.Editable;
 import android.text.TextUtils;
 import android.text.method.ArrowKeyMovementMethod;
 import android.text.method.MovementMethod;
@@ -360,4 +361,24 @@
             return super.getDefaultMovementMethod();
         }
     }
+
+    @Test
+    public void testGetTextNonEditable() {
+        // This subclass calls getText before the object is fully constructed. This should not cause
+        // a null pointer exception.
+        GetTextEditText editText = new GetTextEditText(mActivity);
+    }
+
+    private class GetTextEditText extends EditText {
+
+        GetTextEditText(Context context) {
+            super(context);
+        }
+
+        @Override
+        public void setText(CharSequence text, BufferType type) {
+            Editable currentText = getText();
+            super.setText(text, type);
+        }
+    }
 }
diff --git a/tests/tests/widget/src/android/widget/cts/GridViewTest.java b/tests/tests/widget/src/android/widget/cts/GridViewTest.java
index 95e3581..189a0c9 100644
--- a/tests/tests/widget/src/android/widget/cts/GridViewTest.java
+++ b/tests/tests/widget/src/android/widget/cts/GridViewTest.java
@@ -608,6 +608,7 @@
         final AttachDetachAwareView theView = new AttachDetachAwareView(mActivity);
         WidgetTestUtils.runOnMainAndDrawSync(mActivityRule, mGridView, () -> {
             mGridView.setAdapter(new DummyAdapter(1000, theView));
+            mGridView.requestFocus();
         });
         assertEquals("test sanity", 1, theView.mOnAttachCount);
         assertEquals("test sanity", 0, theView.mOnDetachCount);
diff --git a/tests/tests/widget/src/android/widget/cts/MagnifierCtsActivity.java b/tests/tests/widget/src/android/widget/cts/MagnifierCtsActivity.java
new file mode 100644
index 0000000..a828c3d
--- /dev/null
+++ b/tests/tests/widget/src/android/widget/cts/MagnifierCtsActivity.java
@@ -0,0 +1,31 @@
+/*
+ * 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.widget.cts;
+
+import android.app.Activity;
+import android.os.Bundle;
+
+/**
+ * A minimal application for {@link MagnifierTest}.
+ */
+public class MagnifierCtsActivity extends Activity {
+    @Override
+    public void onCreate(Bundle savedInstanceState) {
+        super.onCreate(savedInstanceState);
+        setContentView(R.layout.magnifier_layout);
+    }
+}
diff --git a/tests/tests/widget/src/android/widget/cts/MagnifierTest.java b/tests/tests/widget/src/android/widget/cts/MagnifierTest.java
new file mode 100644
index 0000000..46d3fe5
--- /dev/null
+++ b/tests/tests/widget/src/android/widget/cts/MagnifierTest.java
@@ -0,0 +1,94 @@
+/*
+ * 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.widget.cts;
+
+import android.app.Activity;
+import android.support.test.annotation.UiThreadTest;
+import android.support.test.filters.SmallTest;
+import android.support.test.rule.ActivityTestRule;
+import android.support.test.runner.AndroidJUnit4;
+import android.view.View;
+import android.widget.LinearLayout;
+import android.widget.LinearLayout.LayoutParams;
+import android.widget.Magnifier;
+
+import com.android.compatibility.common.util.PollingCheck;
+
+import org.junit.Before;
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+/**
+ * Tests for {@link Magnifier}.
+ */
+@SmallTest
+@RunWith(AndroidJUnit4.class)
+public class MagnifierTest {
+    private Activity mActivity;
+    private LinearLayout mMagnifierLayout;
+
+    @Rule
+    public ActivityTestRule<MagnifierCtsActivity> mActivityRule =
+            new ActivityTestRule<>(MagnifierCtsActivity.class);
+
+    @Before
+    public void setup() {
+        mActivity = mActivityRule.getActivity();
+        PollingCheck.waitFor(mActivity::hasWindowFocus);
+        mMagnifierLayout = mActivity.findViewById(R.id.magnifier_layout);
+    }
+
+    @Test
+    public void testConstructor() {
+        new Magnifier(new View(mActivity));
+    }
+
+    @Test(expected = NullPointerException.class)
+    public void testConstructor_NPE() {
+        new Magnifier(null);
+    }
+
+    @Test
+    @UiThreadTest
+    public void testShow() {
+        View view = new View(mActivity);
+        mMagnifierLayout.addView(view, new LayoutParams(200, 200));
+        Magnifier magnifier = new Magnifier(view);
+        // Valid coordinates.
+        magnifier.show(0, 0);
+        // Invalid coordinates, should both be clamped to 0.
+        magnifier.show(-1, -1);
+        // Valid coordinates.
+        magnifier.show(10, 10);
+        // Same valid coordinate as above, should skip making another copy request.
+        magnifier.show(10, 10);
+    }
+
+    @Test
+    @UiThreadTest
+    public void testDismiss() {
+        View view = new View(mActivity);
+        mMagnifierLayout.addView(view, new LayoutParams(200, 200));
+        Magnifier magnifier = new Magnifier(view);
+        // Valid coordinates.
+        magnifier.show(10, 10);
+        magnifier.dismiss();
+        // Should be no-op.
+        magnifier.dismiss();
+    }
+}
diff --git a/tests/tests/widget/src/android/widget/cts/NumberPickerTest.java b/tests/tests/widget/src/android/widget/cts/NumberPickerTest.java
index 6640cc4..7392c99 100644
--- a/tests/tests/widget/src/android/widget/cts/NumberPickerTest.java
+++ b/tests/tests/widget/src/android/widget/cts/NumberPickerTest.java
@@ -32,6 +32,7 @@
 import android.content.res.Configuration;
 import android.support.test.InstrumentationRegistry;
 import android.support.test.annotation.UiThreadTest;
+import android.support.test.filters.FlakyTest;
 import android.support.test.filters.SmallTest;
 import android.support.test.rule.ActivityTestRule;
 import android.support.test.runner.AndroidJUnit4;
@@ -47,6 +48,7 @@
 import org.junit.runner.RunWith;
 import org.mockito.InOrder;
 
+@FlakyTest
 @SmallTest
 @RunWith(AndroidJUnit4.class)
 public class NumberPickerTest {
diff --git a/tests/tests/widget/src/android/widget/cts/OWNERS b/tests/tests/widget/src/android/widget/cts/OWNERS
new file mode 100644
index 0000000..84f2ef0
--- /dev/null
+++ b/tests/tests/widget/src/android/widget/cts/OWNERS
@@ -0,0 +1,8 @@
+per-file TextView*.java = siyamed@google.com
+per-file TextView*.java = nona@google.com
+per-file TextView*.java = clarabayarri@google.com
+per-file TextView*.java = toki@google.com
+per-file EditText*.java = siyamed@google.com
+per-file EditText*.java = nona@google.com
+per-file EditText*.java = clarabayarri@google.com
+per-file EditText*.java = toki@google.com
diff --git a/tests/tests/widget/src/android/widget/cts/PopupMenuTest.java b/tests/tests/widget/src/android/widget/cts/PopupMenuTest.java
index 5ce73a8..3a85f47 100644
--- a/tests/tests/widget/src/android/widget/cts/PopupMenuTest.java
+++ b/tests/tests/widget/src/android/widget/cts/PopupMenuTest.java
@@ -40,6 +40,7 @@
 import android.view.SubMenu;
 import android.view.View;
 import android.widget.EditText;
+import android.widget.ImageView;
 import android.widget.ListView;
 import android.widget.PopupMenu;
 
@@ -273,7 +274,7 @@
 
     @Test
     public void testItemViewAttributes() throws Throwable {
-        mBuilder = new Builder().withDismissListener();
+        mBuilder = new Builder().withDismissListener().withAnchorId(R.id.anchor_upper_left);
         WidgetTestUtils.runOnMainAndLayoutSync(mActivityRule, mBuilder::show, true);
 
         Menu menu = mPopupMenu.getMenu();
@@ -303,6 +304,38 @@
         }
     }
 
+    @Test
+    public void testGroupDividerEnabledAPI() throws Throwable {
+        testGroupDivider(false);
+        testGroupDivider(true);
+    }
+
+    private void testGroupDivider(boolean groupDividerEnabled) throws Throwable {
+        mBuilder = new Builder().withGroupDivider(groupDividerEnabled)
+            .withAnchorId(R.id.anchor_upper_left);
+        WidgetTestUtils.runOnMainAndLayoutSync(mActivityRule, mBuilder::show, true);
+
+        Menu menu = mPopupMenu.getMenu();
+        ListView menuItemList = mPopupMenu.getMenuListView();
+
+        for (int i = 0; i < menuItemList.getChildCount(); i++) {
+            final int currGroupId = menu.getItem(i).getGroupId();
+            final int prevGroupId =
+                    i - 1 >= 0 ? menu.getItem(i - 1).getGroupId() : currGroupId;
+            View itemView = menuItemList.getChildAt(i);
+            ImageView groupDivider = itemView.findViewById(com.android.internal.R.id.group_divider);
+
+            assertNotNull(groupDivider);
+            if (!groupDividerEnabled || currGroupId == prevGroupId) {
+                assertEquals(groupDivider.getVisibility(), View.GONE);
+            } else {
+                assertEquals(groupDivider.getVisibility(), View.VISIBLE);
+            }
+        }
+
+        teardown();
+    }
+
     /**
      * Inner helper class to configure an instance of {@link PopupMenu} for the specific test.
      * The main reason for its existence is that once a popup menu is shown with the show() method,
@@ -315,6 +348,7 @@
         private boolean mHasMenuItemClickListener;
         private boolean mInflateWithInflater;
 
+        private int mAnchorId = R.id.anchor_middle_left;
         private int mPopupMenuContent = R.menu.popup_menu;
 
         private boolean mUseCustomPopupResource;
@@ -328,6 +362,8 @@
 
         private View mAnchor;
 
+        private boolean mGroupDividerEnabled = false;
+
         public Builder withMenuItemClickListener() {
             mHasMenuItemClickListener = true;
             return this;
@@ -360,8 +396,18 @@
             return this;
         }
 
+        public Builder withGroupDivider(boolean groupDividerEnabled) {
+            mGroupDividerEnabled = groupDividerEnabled;
+            return this;
+        }
+
+        public Builder withAnchorId(int anchorId) {
+            mAnchorId = anchorId;
+            return this;
+        }
+
         private void configure() {
-            mAnchor = mActivity.findViewById(R.id.anchor_middle_left);
+            mAnchor = mActivity.findViewById(mAnchorId);
             if (!mUseCustomGravity && !mUseCustomPopupResource) {
                 mPopupMenu = new PopupMenu(mActivity, mAnchor);
             } else if (!mUseCustomPopupResource) {
@@ -390,6 +436,10 @@
                 mOnDismissListener = mock(PopupMenu.OnDismissListener.class);
                 mPopupMenu.setOnDismissListener(mOnDismissListener);
             }
+
+            if (mGroupDividerEnabled) {
+                mPopupMenu.getMenu().setGroupDividerEnabled(true);
+            }
         }
 
         public void show() {
diff --git a/tests/tests/widget/src/android/widget/cts/PopupWindowTest.java b/tests/tests/widget/src/android/widget/cts/PopupWindowTest.java
index 330a92a..01fcccd 100644
--- a/tests/tests/widget/src/android/widget/cts/PopupWindowTest.java
+++ b/tests/tests/widget/src/android/widget/cts/PopupWindowTest.java
@@ -40,6 +40,7 @@
 import android.os.SystemClock;
 import android.support.test.InstrumentationRegistry;
 import android.support.test.annotation.UiThreadTest;
+import android.support.test.filters.FlakyTest;
 import android.support.test.filters.SmallTest;
 import android.support.test.rule.ActivityTestRule;
 import android.support.test.runner.AndroidJUnit4;
@@ -68,6 +69,7 @@
 import org.junit.Test;
 import org.junit.runner.RunWith;
 
+@FlakyTest
 @SmallTest
 @RunWith(AndroidJUnit4.class)
 public class PopupWindowTest {
@@ -1330,6 +1332,9 @@
             mActivity.runOnUiThread(() ->
                     mActivity.setRequestedOrientation(orientation));
             mActivity.waitForConfigurationChanged();
+            // Wait for main thread to be idle to make sure layout and draw have been performed
+            // before continuing.
+            mInstrumentation.waitForIdleSync();
 
             View parentWindowView = mActivity.getWindow().getDecorView();
             int parentWidth = parentWindowView.getMeasuredWidth();
@@ -1398,6 +1403,9 @@
                 mPopupWindow.getContentView().getRootView(),
                 () -> container.scrollBy(deltaX, deltaY),
                 false  /* force layout */);
+        // Since the first layout might have been caused by the original scroll event (and not by
+        // the anchor change), we need to wait until all traversals are done.
+        mInstrumentation.waitForIdleSync();
         assertPopupLocation(originalLocation, deltaX, deltaY);
 
         // Detach the anchor, the popup should stay in the same location.
@@ -1414,6 +1422,7 @@
                 mActivity.getWindow().getDecorView(),
                 () -> container.scrollBy(deltaX, deltaY),
                 true  /* force layout */);
+        mInstrumentation.waitForIdleSync();
         assertPopupLocation(originalLocation, deltaX, deltaY);
 
         // Re-attach the anchor, the popup should snap back to the new anchor location.
@@ -1530,6 +1539,10 @@
                 () -> container.scrollBy(deltaX, deltaY),
                 false  /* force layout */);
 
+        // Since the first layout might have been caused by the original scroll event (and not by
+        // the anchor change), we need to wait until all traversals are done.
+        mInstrumentation.waitForIdleSync();
+
         final int[] newPopupLocation = mPopupWindow.getContentView().getLocationOnScreen();
         assertEquals(popupLocation[0] - deltaX, newPopupLocation[0]);
         assertEquals(popupLocation[1] - deltaY, newPopupLocation[1]);
diff --git a/tests/tests/widget/src/android/widget/cts/TextClockCtsActivity.java b/tests/tests/widget/src/android/widget/cts/TextClockCtsActivity.java
new file mode 100644
index 0000000..43e3582
--- /dev/null
+++ b/tests/tests/widget/src/android/widget/cts/TextClockCtsActivity.java
@@ -0,0 +1,36 @@
+/*
+ * 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.widget.cts;
+
+import android.app.Activity;
+import android.os.Bundle;
+import android.widget.TextClock;
+
+/**
+ * A minimal application for {@link TextClock} test.
+ */
+public class TextClockCtsActivity extends Activity {
+    /**
+     * Called when the activity is first created.
+     */
+    @Override
+    public void onCreate(Bundle savedInstanceState) {
+        super.onCreate(savedInstanceState);
+        setContentView(R.layout.textclock_layout);
+    }
+}
+
diff --git a/tests/tests/widget/src/android/widget/cts/TextClockTest.java b/tests/tests/widget/src/android/widget/cts/TextClockTest.java
new file mode 100644
index 0000000..acb6796
--- /dev/null
+++ b/tests/tests/widget/src/android/widget/cts/TextClockTest.java
@@ -0,0 +1,214 @@
+/*
+ * 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.widget.cts;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertTrue;
+
+import android.app.Activity;
+import android.app.AppOpsManager;
+import android.content.ContentResolver;
+import android.database.ContentObserver;
+import android.net.Uri;
+import android.os.Handler;
+import android.provider.Settings;
+import android.support.test.InstrumentationRegistry;
+import android.support.test.filters.MediumTest;
+import android.support.test.rule.ActivityTestRule;
+import android.support.test.runner.AndroidJUnit4;
+import android.text.format.DateFormat;
+import android.util.MutableBoolean;
+import android.widget.TextClock;
+
+import com.android.compatibility.common.util.PollingCheck;
+import com.android.compatibility.common.util.SystemUtil;
+
+import org.junit.Before;
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+import java.io.IOException;
+import java.util.Calendar;
+import java.util.TimeZone;
+import java.util.concurrent.CountDownLatch;
+import java.util.concurrent.TimeUnit;
+
+/**
+ * Test {@link TextClock}.
+ */
+@MediumTest
+@RunWith(AndroidJUnit4.class)
+public class TextClockTest {
+    private Activity mActivity;
+    private TextClock mTextClock;
+    private boolean mStartedAs24;
+
+    @Rule
+    public ActivityTestRule<TextClockCtsActivity> mActivityRule =
+            new ActivityTestRule<>(TextClockCtsActivity.class);
+
+    @Before
+    public void setup() throws Throwable {
+        mActivity = mActivityRule.getActivity();
+        mTextClock = mActivity.findViewById(R.id.textclock);
+        mStartedAs24 = DateFormat.is24HourFormat(mActivity);
+    }
+
+    public void teardown() throws Throwable {
+        int base = mStartedAs24 ? 24 : 12;
+        Settings.System.putInt(mActivity.getContentResolver(), Settings.System.TIME_12_24, base);
+    }
+
+    @Test
+    public void testUpdate12_24() throws Throwable {
+        grantWriteSettingsPermission();
+
+        mActivityRule.runOnUiThread(() -> {
+            mTextClock.setFormat12Hour("h");
+            mTextClock.setFormat24Hour("H");
+            mTextClock.disableClockTick();
+        });
+
+        final ContentResolver resolver = mActivity.getContentResolver();
+        Calendar mNow = Calendar.getInstance();
+        mNow.setTimeInMillis(System.currentTimeMillis()); // just like TextClock uses
+
+        // make sure the clock is showing some time > 12pm and not near midnight
+        for (String id : TimeZone.getAvailableIDs()) {
+            final TimeZone timeZone = TimeZone.getTimeZone(id);
+            mNow.setTimeZone(timeZone);
+            int hour = mNow.get(Calendar.HOUR_OF_DAY);
+            if (hour < 22 && hour > 12) {
+                mActivityRule.runOnUiThread(() -> {
+                    mTextClock.setTimeZone(id);
+                });
+                break;
+            }
+        }
+
+        final CountDownLatch change12 = registerForChanges(Settings.System.TIME_12_24);
+        mActivityRule.runOnUiThread(() -> {
+            Settings.System.putInt(resolver, Settings.System.TIME_12_24, 12);
+        });
+        assertTrue(change12.await(1, TimeUnit.SECONDS));
+
+        // Must poll here because there are no timing guarantees for ContentObserver notification
+        PollingCheck.waitFor(() -> {
+            final MutableBoolean ok = new MutableBoolean(false);
+            try {
+                mActivityRule.runOnUiThread(() -> {
+                    int hour = Integer.parseInt(mTextClock.getText().toString());
+                    ok.value = hour >= 1 && hour < 12;
+                });
+            } catch (Throwable t) {
+                throw new RuntimeException(t.getMessage());
+            }
+            return ok.value;
+        });
+
+        final CountDownLatch change24 = registerForChanges(Settings.System.TIME_12_24);
+        mActivityRule.runOnUiThread(() -> {
+            Settings.System.putInt(resolver, Settings.System.TIME_12_24, 24);
+        });
+        assertTrue(change24.await(1, TimeUnit.SECONDS));
+
+        // Must poll here because there are no timing guarantees for ContentObserver notification
+        PollingCheck.waitFor(() -> {
+            final MutableBoolean ok = new MutableBoolean(false);
+            try {
+                mActivityRule.runOnUiThread(() -> {
+                    int hour = Integer.parseInt(mTextClock.getText().toString());
+                    ok.value = hour > 12 && hour < 24;
+                });
+                return ok.value;
+            } catch (Throwable t) {
+                throw new RuntimeException(t.getMessage());
+            }
+        });
+    }
+
+    @Test
+    public void testNoChange() throws Throwable {
+        grantWriteSettingsPermission();
+        mActivityRule.runOnUiThread(() -> {
+            mTextClock.disableClockTick();
+        });
+        final ContentResolver resolver = mActivity.getContentResolver();
+
+        // Now test that it isn't updated when a non-12/24 hour setting is set
+        mActivityRule.runOnUiThread(() -> {
+            mTextClock.setText("Nothing");
+        });
+
+        mActivityRule.runOnUiThread(() -> {
+            assertEquals("Nothing", mTextClock.getText().toString());
+        });
+
+        final CountDownLatch otherChange = registerForChanges(Settings.System.TEXT_AUTO_CAPS);
+        mActivityRule.runOnUiThread(() -> {
+            int oldAutoCaps = Settings.System.getInt(resolver, Settings.System.TEXT_AUTO_CAPS,
+                    1);
+            try {
+                int newAutoCaps = oldAutoCaps == 0 ? 1 : 0;
+                Settings.System.putInt(resolver, Settings.System.TEXT_AUTO_CAPS, newAutoCaps);
+            } finally {
+                Settings.System.putInt(resolver, Settings.System.TEXT_AUTO_CAPS, oldAutoCaps);
+            }
+        });
+
+        assertTrue(otherChange.await(1, TimeUnit.SECONDS));
+        InstrumentationRegistry.getInstrumentation().waitForIdleSync();
+
+        mActivityRule.runOnUiThread(() -> {
+            assertEquals("Nothing", mTextClock.getText().toString());
+        });
+    }
+
+    private CountDownLatch registerForChanges(String setting) throws Throwable {
+        final CountDownLatch latch = new CountDownLatch(1);
+
+        mActivityRule.runOnUiThread(() -> {
+            final ContentResolver resolver = mActivity.getContentResolver();
+            Uri uri = Settings.System.getUriFor(setting);
+            resolver.registerContentObserver(uri, true,
+                    new ContentObserver(new Handler()) {
+                        @Override
+                        public void onChange(boolean selfChange) {
+                            countDownAndRemove();
+                        }
+
+                        @Override
+                        public void onChange(boolean selfChange, Uri uri, int userId) {
+                            countDownAndRemove();
+                        }
+
+                        private void countDownAndRemove() {
+                            latch.countDown();
+                            resolver.unregisterContentObserver(this);
+                        }
+                    });
+        });
+        return latch;
+    }
+
+    private void grantWriteSettingsPermission() throws IOException {
+        SystemUtil.runShellCommand(InstrumentationRegistry.getInstrumentation(),
+                "appops set " + mActivity.getPackageName() + " "
+                        + AppOpsManager.OPSTR_WRITE_SETTINGS + " allow");
+    }
+}
diff --git a/tests/tests/widget/src/android/widget/cts/TextViewTest.java b/tests/tests/widget/src/android/widget/cts/TextViewTest.java
index ac90cb7..cc4d023 100644
--- a/tests/tests/widget/src/android/widget/cts/TextViewTest.java
+++ b/tests/tests/widget/src/android/widget/cts/TextViewTest.java
@@ -85,10 +85,12 @@
 import android.text.InputFilter;
 import android.text.InputType;
 import android.text.Layout;
+import android.text.PremeasuredText;
 import android.text.Selection;
 import android.text.Spannable;
 import android.text.SpannableString;
 import android.text.Spanned;
+import android.text.TextDirectionHeuristics;
 import android.text.TextPaint;
 import android.text.TextUtils;
 import android.text.TextUtils.TruncateAt;
@@ -139,7 +141,6 @@
 import android.view.inputmethod.ExtractedTextRequest;
 import android.view.inputmethod.InputConnection;
 import android.view.inputmethod.InputMethodManager;
-import android.view.textclassifier.TextClassification;
 import android.view.textclassifier.TextClassifier;
 import android.view.textclassifier.TextSelection;
 import android.widget.EditText;
@@ -190,15 +191,9 @@
     private static final TextClassifier FAKE_TEXT_CLASSIFIER = new TextClassifier() {
         @Override
         public TextSelection suggestSelection(
-                CharSequence text, int start, int end, LocaleList locales) {
+                CharSequence text, int start, int end, TextSelection.Options options) {
             return new TextSelection.Builder(SMARTSELECT_START, SMARTSELECT_END).build();
         }
-
-        @Override
-        public TextClassification classifyText(
-                CharSequence text, int start, int end, LocaleList locales) {
-            return new TextClassification.Builder().build();
-        }
     };
     private static final int CLICK_TIMEOUT = ViewConfiguration.getDoubleTapTimeout() + 50;
 
@@ -1640,6 +1635,16 @@
         }
     }
 
+    @UiThreadTest
+    @Test
+    public void testSetText_PremeasuredText() {
+        final TextView tv = findTextView(R.id.textview_text);
+        final PremeasuredText premeasured = PremeasuredText.build(
+                "This is an example text.", new TextPaint(), TextDirectionHeuristics.LTR);
+        tv.setText(premeasured);
+        assertEquals(premeasured.toString(), tv.getText().toString());
+    }
+
     @Test
     public void testSetTextUpdatesHeightAfterRemovingImageSpan() throws Throwable {
         // Height calculation had problems when TextView had width: match_parent
@@ -4445,6 +4450,75 @@
         assertEquals(null, mTextView.getTypeface());
     }
 
+    @Test
+    public void testXmlTextAppearance() {
+        mTextView = findTextView(R.id.textview_textappearance_attrs1);
+        assertEquals(22f, mTextView.getTextSize(), 0.01f);
+        Typeface italicSans = Typeface.create(Typeface.SANS_SERIF, Typeface.ITALIC);
+        assertEquals(italicSans, mTextView.getTypeface());
+        assertEquals(Typeface.ITALIC, mTextView.getTypeface().getStyle());
+        assertTrue(mTextView.isAllCaps());
+        assertEquals(2.4f, mTextView.getLetterSpacing(), 0.01f);
+        assertEquals("smcp", mTextView.getFontFeatureSettings());
+
+        mTextView = findTextView(R.id.textview_textappearance_attrs2);
+        assertEquals(Typeface.MONOSPACE, mTextView.getTypeface());
+        assertEquals(mActivity.getResources().getColor(R.drawable.red),
+                mTextView.getShadowColor());
+        assertEquals(10.3f, mTextView.getShadowDx(), 0.01f);
+        assertEquals(0.5f, mTextView.getShadowDy(), 0.01f);
+        assertEquals(3.3f, mTextView.getShadowRadius(), 0.01f);
+        assertTrue(mTextView.isElegantTextHeight());
+
+        // This TextView has both a TextAppearance and a style, so the style should override.
+        mTextView = findTextView(R.id.textview_textappearance_attrs3);
+        assertEquals(32f, mTextView.getTextSize(), 0.01f);
+        Typeface boldSerif = Typeface.create(Typeface.SERIF, Typeface.BOLD);
+        assertEquals(boldSerif, mTextView.getTypeface());
+        assertEquals(Typeface.BOLD, mTextView.getTypeface().getStyle());
+        assertFalse(mTextView.isAllCaps());
+        assertEquals(2.6f, mTextView.getLetterSpacing(), 0.01f);
+        assertEquals(mActivity.getResources().getColor(R.drawable.blue),
+                mTextView.getShadowColor());
+        assertEquals(1.3f, mTextView.getShadowDx(), 0.01f);
+        assertEquals(10.5f, mTextView.getShadowDy(), 0.01f);
+        assertEquals(5.3f, mTextView.getShadowRadius(), 0.01f);
+        assertFalse(mTextView.isElegantTextHeight());
+
+        // This TextView has no TextAppearance and has a style, so the style should be applied.
+        mTextView = findTextView(R.id.textview_textappearance_attrs4);
+        assertEquals(32f, mTextView.getTextSize(), 0.01f);
+        assertEquals(boldSerif, mTextView.getTypeface());
+        assertEquals(Typeface.BOLD, mTextView.getTypeface().getStyle());
+        assertFalse(mTextView.isAllCaps());
+        assertEquals(2.6f, mTextView.getLetterSpacing(), 0.01f);
+        assertEquals(mActivity.getResources().getColor(R.drawable.blue),
+                mTextView.getShadowColor());
+        assertEquals(1.3f, mTextView.getShadowDx(), 0.01f);
+        assertEquals(10.5f, mTextView.getShadowDy(), 0.01f);
+        assertEquals(5.3f, mTextView.getShadowRadius(), 0.01f);
+        assertFalse(mTextView.isElegantTextHeight());
+
+        // Note: text, link and hint colors can't be tested due to the default style overriding
+        // values b/63923542
+    }
+
+    @Test
+    public void testXmlTypefaceFontFamilyHierarchy() {
+        // This view has typeface=serif set on the view directly and a fontFamily on the appearance.
+        // In this case, the attr set directly on the view should take precedence.
+        mTextView = findTextView(R.id.textview_textappearance_attrs_serif_fontfamily);
+
+        assertEquals(Typeface.SERIF, mTextView.getTypeface());
+    }
+
+    @Test
+    public void testAttributeReading_allCapsAndPassword() {
+        // This TextView has all caps & password, therefore all caps should be ignored.
+        mTextView = findTextView(R.id.textview_textappearance_attrs_allcaps_password);
+        assertFalse(mTextView.isAllCaps());
+    }
+
     @UiThreadTest
     @Test
     public void testAccessCompoundDrawableTint() {
@@ -7743,6 +7817,70 @@
         assertEquals(Typeface.DEFAULT, mTextView.getTypeface());
     }
 
+    @UiThreadTest
+    @Test
+    public void testFallbackLineSpacing_readsFromLayoutXml() {
+        mActivity.setContentView(R.layout.textview_fallbacklinespacing_layout);
+        mTextView = findTextView(R.id.textview_true);
+        assertTrue(mTextView.isFallbackLineSpacing());
+
+        mTextView = findTextView(R.id.textview_default);
+        assertTrue(mTextView.isFallbackLineSpacing());
+
+        mTextView = findTextView(R.id.textview_false);
+        assertFalse(mTextView.isFallbackLineSpacing());
+    }
+
+    @UiThreadTest
+    @Test
+    public void testFallbackLineSpacing_set_get() {
+        mActivity.setContentView(R.layout.textview_fallbacklinespacing_layout);
+        mTextView = findTextView(R.id.textview_true);
+        assertTrue(mTextView.isFallbackLineSpacing());
+
+        mTextView.setFallbackLineSpacing(false);
+        assertFalse(mTextView.isFallbackLineSpacing());
+
+        mTextView.setFallbackLineSpacing(true);
+        assertTrue(mTextView.isFallbackLineSpacing());
+    }
+
+    @UiThreadTest
+    @Test
+    public void testFallbackLineSpacing_readsFromStyleXml() {
+        mActivity.setContentView(R.layout.textview_fallbacklinespacing_layout);
+        mTextView = findTextView(R.id.textview_style_true);
+        assertTrue(mTextView.isFallbackLineSpacing());
+
+        mTextView = findTextView(R.id.textview_style_default);
+        assertTrue(mTextView.isFallbackLineSpacing());
+
+        mTextView = findTextView(R.id.textview_style_false);
+        assertFalse(mTextView.isFallbackLineSpacing());
+    }
+
+    @UiThreadTest
+    @Test
+    public void testFallbackLineSpacing_textAppearance_set_get() {
+        mActivity.setContentView(R.layout.textview_fallbacklinespacing_layout);
+        mTextView = findTextView(R.id.textview_default);
+        assertTrue(mTextView.isFallbackLineSpacing());
+
+        mTextView.setTextAppearance(R.style.TextAppearance_FallbackLineSpacingFalse);
+        assertFalse(mTextView.isFallbackLineSpacing());
+
+        mTextView.setTextAppearance(R.style.TextAppearance_FallbackLineSpacingTrue);
+        assertTrue(mTextView.isFallbackLineSpacing());
+
+        mTextView.setFallbackLineSpacing(false);
+        mTextView.setTextAppearance(R.style.TextAppearance);
+        assertFalse(mTextView.isFallbackLineSpacing());
+
+        mTextView.setFallbackLineSpacing(true);
+        mTextView.setTextAppearance(R.style.TextAppearance);
+        assertTrue(mTextView.isFallbackLineSpacing());
+    }
+
     private void initializeTextForSmartSelection(CharSequence text) throws Throwable {
         assertTrue(text.length() >= SMARTSELECT_END);
         mActivityRule.runOnUiThread(() -> {
diff --git a/tests/tests/widget/src/android/widget/cts/util/ListScenario.java b/tests/tests/widget/src/android/widget/cts/util/ListScenario.java
index c4d4199..1d63c40 100644
--- a/tests/tests/widget/src/android/widget/cts/util/ListScenario.java
+++ b/tests/tests/widget/src/android/widget/cts/util/ListScenario.java
@@ -16,13 +16,6 @@
 
 package android.widget.cts.util;
 
-import java.util.ArrayList;
-import java.util.HashMap;
-import java.util.HashSet;
-import java.util.List;
-import java.util.Map;
-import java.util.Set;
-
 import android.app.Activity;
 import android.graphics.Rect;
 import android.os.Bundle;
@@ -36,6 +29,13 @@
 import android.widget.ListView;
 import android.widget.TextView;
 
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+
 /**
  * Utility base class for creating various List scenarios.  Configurable by the number
  * of items, how tall each item should be (in relation to the screen height), and
@@ -390,6 +390,7 @@
             mLinearLayout.addView(mListView);
             setContentView(mLinearLayout);
         }
+        mLinearLayout.restoreDefaultFocus();
     }
 
     /**
diff --git a/tests/tests/wrap/nowrap/Android.mk b/tests/tests/wrap/nowrap/Android.mk
index 35c0448..8fd1015 100644
--- a/tests/tests/wrap/nowrap/Android.mk
+++ b/tests/tests/wrap/nowrap/Android.mk
@@ -23,8 +23,8 @@
 LOCAL_PROGUARD_ENABLED := disabled
 LOCAL_STATIC_JAVA_LIBRARIES := \
 	compatibility-device-util \
-	android-support-test \
-	legacy-android-test
+	android-support-test
+LOCAL_JAVA_LIBRARIES := android.test.runner.stubs android.test.base.stubs
 LOCAL_SRC_FILES := $(call all-java-files-under, ../src)
 LOCAL_COMPATIBILITY_SUITE := cts vts general-tests
 LOCAL_SDK_VERSION := current
diff --git a/tests/tests/wrap/nowrap/AndroidTest.xml b/tests/tests/wrap/nowrap/AndroidTest.xml
index 1ca2369..b80d4ee 100644
--- a/tests/tests/wrap/nowrap/AndroidTest.xml
+++ b/tests/tests/wrap/nowrap/AndroidTest.xml
@@ -14,6 +14,7 @@
      limitations under the License.
 -->
 <configuration description="Config for CTS No Wrap test cases">
+    <option name="test-suite-tag" value="cts" />
     <option name="config-descriptor:metadata" key="component" value="art" />
     <option name="not-shardable" value="true" />
     <target_preparer class="com.android.tradefed.targetprep.suite.SuiteApkInstaller">
diff --git a/tests/tests/wrap/wrap_debug/Android.mk b/tests/tests/wrap/wrap_debug/Android.mk
index 77b6d27..c67e191 100644
--- a/tests/tests/wrap/wrap_debug/Android.mk
+++ b/tests/tests/wrap/wrap_debug/Android.mk
@@ -23,8 +23,8 @@
 LOCAL_PROGUARD_ENABLED := disabled
 LOCAL_STATIC_JAVA_LIBRARIES := \
 	compatibility-device-util \
-	android-support-test \
-	legacy-android-test
+	android-support-test
+LOCAL_JAVA_LIBRARIES := android.test.runner.stubs android.test.base.stubs
 LOCAL_SRC_FILES := $(call all-java-files-under, ../src)
 LOCAL_COMPATIBILITY_SUITE := cts vts general-tests
 LOCAL_SDK_VERSION := current
diff --git a/tests/tests/wrap/wrap_debug/AndroidTest.xml b/tests/tests/wrap/wrap_debug/AndroidTest.xml
index b3468bf..2b9cbe1 100644
--- a/tests/tests/wrap/wrap_debug/AndroidTest.xml
+++ b/tests/tests/wrap/wrap_debug/AndroidTest.xml
@@ -14,6 +14,7 @@
      limitations under the License.
 -->
 <configuration description="Config for CTS Debug Wrap test cases">
+    <option name="test-suite-tag" value="cts" />
     <option name="config-descriptor:metadata" key="component" value="art" />
     <option name="not-shardable" value="true" />
     <target_preparer class="com.android.tradefed.targetprep.suite.SuiteApkInstaller">
diff --git a/tests/tests/wrap/wrap_debug_malloc_debug/Android.mk b/tests/tests/wrap/wrap_debug_malloc_debug/Android.mk
index bc6240d..b768dcf 100644
--- a/tests/tests/wrap/wrap_debug_malloc_debug/Android.mk
+++ b/tests/tests/wrap/wrap_debug_malloc_debug/Android.mk
@@ -23,10 +23,10 @@
 LOCAL_PROGUARD_ENABLED := disabled
 LOCAL_STATIC_JAVA_LIBRARIES := \
 	compatibility-device-util \
-	android-support-test \
-	legacy-android-test
+	android-support-test
+LOCAL_JAVA_LIBRARIES := android.test.runner.stubs android.test.base.stubs
 LOCAL_SRC_FILES := $(call all-java-files-under, ../src)
-LOCAL_COMPATIBILITY_SUITE := cts
+LOCAL_COMPATIBILITY_SUITE := cts vts general-tests
 LOCAL_SDK_VERSION := current
 
 LOCAL_PREBUILT_JNI_LIBS_arm := wrap.sh
diff --git a/tests/tests/wrap/wrap_debug_malloc_debug/AndroidTest.xml b/tests/tests/wrap/wrap_debug_malloc_debug/AndroidTest.xml
index 0c740e4..2faff68 100644
--- a/tests/tests/wrap/wrap_debug_malloc_debug/AndroidTest.xml
+++ b/tests/tests/wrap/wrap_debug_malloc_debug/AndroidTest.xml
@@ -14,7 +14,9 @@
      limitations under the License.
 -->
 <configuration description="Config for CTS Debug Wrap (Malloc Debug) test cases">
+    <option name="test-suite-tag" value="cts" />
     <option name="config-descriptor:metadata" key="component" value="art" />
+    <option name="not-shardable" value="true" />
     <target_preparer class="com.android.tradefed.targetprep.suite.SuiteApkInstaller">
         <option name="cleanup-apks" value="true" />
         <option name="test-file-name" value="CtsWrapWrapDebugMallocDebugTestCases.apk" />
diff --git a/tests/tests/wrap/wrap_nodebug/Android.mk b/tests/tests/wrap/wrap_nodebug/Android.mk
index 1d6e9c0..3317f3c 100644
--- a/tests/tests/wrap/wrap_nodebug/Android.mk
+++ b/tests/tests/wrap/wrap_nodebug/Android.mk
@@ -23,8 +23,8 @@
 LOCAL_PROGUARD_ENABLED := disabled
 LOCAL_STATIC_JAVA_LIBRARIES := \
 	compatibility-device-util \
-	android-support-test \
-	legacy-android-test
+	android-support-test
+LOCAL_JAVA_LIBRARIES := android.test.runner.stubs android.test.base.stubs
 LOCAL_SRC_FILES := $(call all-java-files-under, ../src)
 LOCAL_COMPATIBILITY_SUITE := cts vts general-tests
 LOCAL_SDK_VERSION := current
diff --git a/tests/tests/wrap/wrap_nodebug/AndroidTest.xml b/tests/tests/wrap/wrap_nodebug/AndroidTest.xml
index b7a6a11..dc547cf 100644
--- a/tests/tests/wrap/wrap_nodebug/AndroidTest.xml
+++ b/tests/tests/wrap/wrap_nodebug/AndroidTest.xml
@@ -14,6 +14,7 @@
      limitations under the License.
 -->
 <configuration description="Config for CTS Wrap test cases">
+    <option name="test-suite-tag" value="cts" />
     <option name="config-descriptor:metadata" key="component" value="art" />
     <option name="not-shardable" value="true" />
     <target_preparer class="com.android.tradefed.targetprep.suite.SuiteApkInstaller">
diff --git a/tests/tvprovider/Android.mk b/tests/tvprovider/Android.mk
index 4a13909..5595519 100644
--- a/tests/tvprovider/Android.mk
+++ b/tests/tvprovider/Android.mk
@@ -20,6 +20,8 @@
 
 LOCAL_STATIC_JAVA_LIBRARIES := compatibility-device-util ctstestrunner
 
+LOCAL_JAVA_LIBRARIES := android.test.runner.stubs android.test.base.stubs
+
 LOCAL_SRC_FILES := $(call all-java-files-under, src)
 
 LOCAL_PACKAGE_NAME := CtsTvProviderTestCases
diff --git a/tests/tvprovider/AndroidTest.xml b/tests/tvprovider/AndroidTest.xml
index 9365cbd..c62cec8 100644
--- a/tests/tvprovider/AndroidTest.xml
+++ b/tests/tvprovider/AndroidTest.xml
@@ -14,6 +14,7 @@
      limitations under the License.
 -->
 <configuration description="Config for CTS TV Provider test cases">
+    <option name="test-suite-tag" value="cts" />
     <option name="config-descriptor:metadata" key="component" value="tv" />
     <option name="not-shardable" value="true" />
     <target_preparer class="com.android.tradefed.targetprep.suite.SuiteApkInstaller">
diff --git a/tests/ui/Android.mk b/tests/ui/Android.mk
index 53ae77e..abdc148 100644
--- a/tests/ui/Android.mk
+++ b/tests/ui/Android.mk
@@ -22,6 +22,8 @@
 
 LOCAL_STATIC_JAVA_LIBRARIES := compatibility-device-util ctstestrunner
 
+LOCAL_JAVA_LIBRARIES := android.test.runner.stubs android.test.base.stubs
+
 LOCAL_SRC_FILES := $(call all-java-files-under, src)
 
 LOCAL_PACKAGE_NAME := CtsUiDeviceTestCases
diff --git a/tests/video/Android.mk b/tests/video/Android.mk
index c70cfaf..b077e63 100644
--- a/tests/video/Android.mk
+++ b/tests/video/Android.mk
@@ -25,6 +25,8 @@
 
 LOCAL_STATIC_JAVA_LIBRARIES := ctsmediautil compatibility-device-util ctstestrunner
 
+LOCAL_JAVA_LIBRARIES := android.test.runner.stubs android.test.base.stubs
+
 LOCAL_JNI_SHARED_LIBRARIES := libctsmediacodec_jni libnativehelper_compat_libc++
 
 LOCAL_SRC_FILES := $(call all-java-files-under, src)
diff --git a/tests/video/AndroidTest.xml b/tests/video/AndroidTest.xml
index e30a394..3836f98 100644
--- a/tests/video/AndroidTest.xml
+++ b/tests/video/AndroidTest.xml
@@ -14,6 +14,7 @@
      limitations under the License.
 -->
 <configuration description="Config for CTS Video test cases">
+    <option name="test-suite-tag" value="cts" />
     <option name="config-descriptor:metadata" key="component" value="media" />
     <target_preparer class="com.android.tradefed.targetprep.suite.SuiteApkInstaller">
         <option name="cleanup-apks" value="true" />
diff --git a/tests/vm/AndroidTest.xml b/tests/vm/AndroidTest.xml
index c6f2058..4035349 100644
--- a/tests/vm/AndroidTest.xml
+++ b/tests/vm/AndroidTest.xml
@@ -14,6 +14,7 @@
      limitations under the License.
 -->
 <configuration description="Config for CTS VM test cases">
+    <option name="test-suite-tag" value="cts" />
     <option name="config-descriptor:metadata" key="component" value="art" />
     <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 74aebf1..56a7310 100644
--- a/tests/vr/Android.mk
+++ b/tests/vr/Android.mk
@@ -27,7 +27,9 @@
 # When built, explicitly put it in the data partition.
 LOCAL_MODULE_PATH := $(TARGET_OUT_DATA_APPS)
 
-LOCAL_STATIC_JAVA_LIBRARIES := ctstestrunner legacy-android-test
+LOCAL_STATIC_JAVA_LIBRARIES := ctstestrunner
+
+LOCAL_JAVA_LIBRARIES := android.test.runner.stubs android.test.base.stubs
 
 LOCAL_JNI_SHARED_LIBRARIES := libctsvrextensions_jni libnativehelper_compat_libc++
 
diff --git a/tools/cts-api-coverage/Android.mk b/tools/cts-api-coverage/Android.mk
index c66f7d4..7b56e21 100644
--- a/tools/cts-api-coverage/Android.mk
+++ b/tools/cts-api-coverage/Android.mk
@@ -14,23 +14,51 @@
 
 LOCAL_PATH := $(call my-dir)
 
-# the hat script
+# the cts-api-coverage script
 # ============================================================
 include $(CLEAR_VARS)
 LOCAL_IS_HOST_MODULE := true
 LOCAL_MODULE_CLASS := EXECUTABLES
 LOCAL_MODULE := cts-api-coverage
-LOCAL_STATIC_JAVA_LIBRARIES := \
-    compatibility-common-util-devicesidelib
 LOCAL_SRC_FILES := etc/$(LOCAL_MODULE)
 LOCAL_ADDITIONAL_DEPENDENCIES := $(HOST_OUT_JAVA_LIBRARIES)/$(LOCAL_MODULE)$(COMMON_JAVA_PACKAGE_SUFFIX)
 
 include $(BUILD_PREBUILT)
 
-# the other stuff
+# the ndk-api-report script
 # ============================================================
-subdirs := $(addprefix $(LOCAL_PATH)/,$(addsuffix /Android.mk, \
-		src \
-	))
+include $(CLEAR_VARS)
+LOCAL_IS_HOST_MODULE := true
+LOCAL_MODULE_CLASS := EXECUTABLES
+LOCAL_MODULE := ndk-api-report
+LOCAL_SRC_FILES := etc/$(LOCAL_MODULE)
 
-include $(subdirs)
+include $(BUILD_PREBUILT)
+
+# cts-api-coverage 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_JAVA_RESOURCE_DIRS := res
+LOCAL_JAR_MANIFEST := MANIFEST.mf
+
+LOCAL_STATIC_JAVA_LIBRARIES := \
+  compatibility-host-util \
+  dexlib2
+
+LOCAL_MODULE := cts-api-coverage
+
+# 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 += \
+        platformprotos
+
+include $(BUILD_HOST_JAVA_LIBRARY)
diff --git a/tools/cts-api-coverage/src/MANIFEST.mf b/tools/cts-api-coverage/MANIFEST.mf
similarity index 100%
rename from tools/cts-api-coverage/src/MANIFEST.mf
rename to tools/cts-api-coverage/MANIFEST.mf
diff --git a/tools/cts-api-coverage/proto/testsuite.proto b/tools/cts-api-coverage/proto/testsuite.proto
new file mode 100644
index 0000000..a0adb6f
--- /dev/null
+++ b/tools/cts-api-coverage/proto/testsuite.proto
@@ -0,0 +1,103 @@
+// 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_apicoverage;
+// [END declaration]
+
+// [START java_declaration]
+option java_package = "com.android.cts.apicoverage";
+option java_outer_classname = "TestSuiteProto";
+// [END java_declaration]
+
+// [START messages]
+message Option {
+    string name = 1;
+    string key = 2;
+    string value =3;
+}
+
+message ConfigMetadata {
+    string module_name = 1;
+    string component = 2;
+    repeated Option options = 3;
+
+    message TargetPreparer {
+        string test_class = 1;
+        repeated Option options = 2;
+    }
+    repeated TargetPreparer target_preparers = 4;
+
+    message TestClass {
+        string test_class = 1;
+        string package = 2;
+        repeated Option options = 3;
+    }
+    repeated TestClass test_classes = 5;
+}
+
+// target File Metadata for e.g. config, apk, jar, exe, so
+message FileMetadata {
+    string description = 1;
+    ConfigMetadata config_metadata = 2;
+}
+
+// An entry in a Test Suire Release messages: cts, etc.
+message Entry {
+    // Entry ID
+    string id = 1;
+    // Name
+    string name = 2;
+
+    enum EntryType {
+        FOLDER = 0;
+        FILE = 1;
+        CONFIG = 2;
+        JAR = 3;
+        APK = 4;
+        EXE = 5;
+        SO = 6;
+    }
+
+    // Type
+    EntryType type = 3;
+    // Size
+    int64 size = 4;
+    // Content ID
+    string content_id = 5;
+    // Parent entry ID
+    string parent_id = 6;
+    // Relative path
+    string relative_path = 7;
+
+    FileMetadata file_metadata = 8;
+}
+
+// Test Suite Release: cts, etc.
+message TestSuiteContent {
+    // Entry ID
+    string id = 1;
+    // Name
+    string name = 2;
+    // Version
+    string version = 3;
+    // Build ID
+    string bid = 4;
+    // Content ID
+    string content_id = 5;
+    // File Entries
+    repeated Entry file_entries = 6;
+}
+// [END messages]
diff --git a/tools/cts-api-coverage/src/res/api-coverage.xsl b/tools/cts-api-coverage/res/api-coverage.xsl
similarity index 100%
rename from tools/cts-api-coverage/src/res/api-coverage.xsl
rename to tools/cts-api-coverage/res/api-coverage.xsl
diff --git a/tools/cts-api-coverage/src/Android.mk b/tools/cts-api-coverage/src/Android.mk
deleted file mode 100644
index fcf7ad4..0000000
--- a/tools/cts-api-coverage/src/Android.mk
+++ /dev/null
@@ -1,39 +0,0 @@
-# Copyright (C) 2010 The Android Open Source Project
-#
-# Licensed under the Apache License, Version 2.0 (the "License");
-# you may not use this file except in compliance with the License.
-# You may obtain a copy of the License at
-#
-#      http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing, software
-# distributed under the License is distributed on an "AS IS" BASIS,
-# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-# See the License for the specific language governing permissions and
-# limitations under the License.
-
-LOCAL_PATH := $(call my-dir)
-
-
-# cts-api-coverage java library
-# ============================================================
-include $(CLEAR_VARS)
-
-LOCAL_SRC_FILES := $(call all-subdir-java-files)
-LOCAL_PROTOC_OPTIMIZE_TYPE := full
-LOCAL_JAVA_RESOURCE_DIRS := res 
-LOCAL_JAR_MANIFEST := MANIFEST.mf
-
-LOCAL_STATIC_JAVA_LIBRARIES := \
-  compatibility-host-util \
-  dexlib2
-
-LOCAL_MODULE := cts-api-coverage
-
-# 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 += \
-        platformprotos
-
-include $(BUILD_HOST_JAVA_LIBRARY)
diff --git a/tools/cts-api-coverage/src/com/android/cts/apicoverage/GTestApiReport.java b/tools/cts-api-coverage/src/com/android/cts/apicoverage/GTestApiReport.java
index a1a6923..3c88938 100644
--- a/tools/cts-api-coverage/src/com/android/cts/apicoverage/GTestApiReport.java
+++ b/tools/cts-api-coverage/src/com/android/cts/apicoverage/GTestApiReport.java
@@ -128,7 +128,7 @@
 
             for (File testConfigFile : testConfigFiles) {
                 XMLReader xmlReader = XMLReaderFactory.createXMLReader();
-                TestModuleConfigHandler testModuleXmlHandler = new TestModuleConfigHandler();
+                TestModuleConfigHandler testModuleXmlHandler = new TestModuleConfigHandler(file.getName());
                 xmlReader.setContentHandler(testModuleXmlHandler);
                 FileReader fileReader = null;
 
diff --git a/tools/cts-api-coverage/src/com/android/cts/apicoverage/TestModuleConfigHandler.java b/tools/cts-api-coverage/src/com/android/cts/apicoverage/TestModuleConfigHandler.java
index 85423b7..8a17153 100644
--- a/tools/cts-api-coverage/src/com/android/cts/apicoverage/TestModuleConfigHandler.java
+++ b/tools/cts-api-coverage/src/com/android/cts/apicoverage/TestModuleConfigHandler.java
@@ -16,6 +16,8 @@
 
 package com.android.cts.apicoverage;
 
+import com.android.cts.apicoverage.TestSuiteProto.*;
+
 import org.xml.sax.Attributes;
 import org.xml.sax.SAXException;
 import org.xml.sax.helpers.DefaultHandler;
@@ -25,25 +27,65 @@
  * TestModule.xml.
  */
 class TestModuleConfigHandler extends DefaultHandler {
-    private String mTestClassName;
-    private String mModuleName;
-    private Boolean inTestEle = false;
+    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";
+
+    private FileMetadata.Builder mFileMetadata;
+    private ConfigMetadata.Builder mConfigMetadata;
+    private ConfigMetadata.TestClass.Builder mTestCase;
+    private ConfigMetadata.TargetPreparer.Builder mTargetPreparer;
+    private String mModuleName = null;
+
+    TestModuleConfigHandler(String configFileName) {
+        mFileMetadata = FileMetadata.newBuilder();
+        mConfigMetadata = ConfigMetadata.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);
 
-        if ("test".equalsIgnoreCase(localName)) {
-            mTestClassName = attributes.getValue("class");
-            inTestEle = true;
-        } else if ("option".equalsIgnoreCase(localName)) {
-            if (inTestEle) {
-                String optName = attributes.getValue("name");
-                if ("module-name".equalsIgnoreCase(optName)) {
-                    mModuleName = attributes.getValue("value");
+        if (CONFIGURATION_TAG.equalsIgnoreCase(localName)) {
+            if (null != attributes.getValue(DESCRIPTION_TAG)) {
+                mFileMetadata.setDescription(attributes.getValue(DESCRIPTION_TAG));
+            } else {
+                mFileMetadata.setDescription("WARNING: no description.");
+            }
+        } else if (TEST_TAG.equalsIgnoreCase(localName)) {
+            mTestCase = ConfigMetadata.TestClass.newBuilder();
+            mTestCase.setTestClass(attributes.getValue(CLASS_TAG));
+        } else if (TARGET_PREPARER_TAG.equalsIgnoreCase(localName)) {
+            mTargetPreparer = ConfigMetadata.TargetPreparer.newBuilder();
+            mTargetPreparer.setTestClass(attributes.getValue(CLASS_TAG));
+        } else if (OPTION_TAG.equalsIgnoreCase(localName)) {
+            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);
+                if (GTEST_CLASS_TAG.equalsIgnoreCase(option.getName())) {
+                    mModuleName = option.getValue();
                 }
-                //System.out.println(String.format("%s: %s, %s, %s", localName, name, optName, attributes.getValue("value")));
+            } else if (null != mTargetPreparer) {
+                mTargetPreparer.addOptions(option);
             }
         }
     }
@@ -51,8 +93,14 @@
     @Override
     public void endElement(String uri, String localName, String name) throws SAXException {
         super.endElement(uri, localName, name);
-        if ("test".equalsIgnoreCase(localName)) {
-            inTestEle = false;
+        if (TEST_TAG.equalsIgnoreCase(localName)) {
+            mConfigMetadata.addTestClasses(mTestCase);
+            mTestCase = null;
+        } else if (TARGET_PREPARER_TAG.equalsIgnoreCase(localName)) {
+            mConfigMetadata.addTargetPreparers(mTargetPreparer);
+            mTargetPreparer = null;
+        } else if (CONFIGURATION_TAG.equalsIgnoreCase(localName)) {
+            mFileMetadata.setConfigMetadata(mConfigMetadata);
         }
     }
 
@@ -61,6 +109,11 @@
     }
 
     public String getTestClassName() {
-        return mTestClassName;
+        //return the 1st Test Class
+        return mFileMetadata.getConfigMetadata().getTestClassesList().get(0).getTestClass();
+    }
+
+    public FileMetadata getFileMetadata() {
+        return mFileMetadata.build();
     }
 }
diff --git a/tools/cts-api-coverage/src/com/android/cts/apicoverage/TestSuiteContentReport.java b/tools/cts-api-coverage/src/com/android/cts/apicoverage/TestSuiteContentReport.java
new file mode 100644
index 0000000..044acdc
--- /dev/null
+++ b/tools/cts-api-coverage/src/com/android/cts/apicoverage/TestSuiteContentReport.java
@@ -0,0 +1,353 @@
+/*
+ * 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.apicoverage;
+
+
+import com.android.cts.apicoverage.TestSuiteProto.*;
+
+import org.xml.sax.InputSource;
+import org.xml.sax.XMLReader;
+import org.xml.sax.helpers.XMLReaderFactory;
+
+import java.io.BufferedReader;
+import java.io.File;
+import java.io.FileReader;
+import java.io.FileInputStream;
+import java.io.FileNotFoundException;
+import java.io.FileOutputStream;
+import java.io.InputStreamReader;
+import java.io.IOException;
+import java.io.PrintStream;
+import java.nio.charset.StandardCharsets;
+import java.nio.file.Path;
+import java.nio.file.Paths;
+import java.security.NoSuchAlgorithmException;
+import java.security.MessageDigest;
+import java.util.ArrayList;
+import java.util.List;
+
+class TestSuiteContentReport {
+    // 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";
+
+    // Target File Extensions
+    private static final String CONFIG_EXT_TAG = ".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";
+
+    private static void printUsage() {
+        System.out.println("Usage: test-suite-content-report [OPTION]...");
+        System.out.println();
+        System.out.println("Generates test suite content protocal buffer message.");
+        System.out.println();
+        System.out.println(
+                "$ANDROID_HOST_OUT/bin/test-suite-content-report "
+                        + "-i out/host/linux-x86/cts/android-cts "
+                        + "-o ./cts-content.pb");
+        System.out.println();
+        System.out.println("Options:");
+        System.out.println("  -o FILE                output file or standard out if not given");
+        System.out.println("  -i PATH                path to the Test Suite Folder");
+        System.out.println();
+        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 TestSuiteContent parseTestSuiteFolder(String testSuitePath)
+            throws IOException, NoSuchAlgorithmException {
+
+        TestSuiteContent.Builder testSuiteContent = TestSuiteContent.newBuilder();
+        testSuiteContent.addFileEntries(parseFolder(testSuiteContent, testSuitePath, testSuitePath));
+        return testSuiteContent.build();
+    }
+
+    // Parse a file
+    private static FileMetadata parseFileMetadata(Entry.Builder fEntry, File file)
+            throws Exception {
+        if (file.getName().endsWith(CONFIG_EXT_TAG)) {
+            fEntry.setType(Entry.EntryType.CONFIG);
+            return parseConfigFile(file);
+        } else if (file.getName().endsWith(APK_EXT_TAG)) {
+            fEntry.setType(Entry.EntryType.APK);
+        } else if (file.getName().endsWith(JAR_EXT_TAG)) {
+            fEntry.setType(Entry.EntryType.JAR);
+        } else if (file.getName().endsWith(SO_EXT_TAG)) {
+            fEntry.setType(Entry.EntryType.SO);
+        } else {
+            // Just file in general
+            fEntry.setType(Entry.EntryType.FILE);
+        }
+        return null;
+    }
+
+    private static FileMetadata parseConfigFile(File file)
+            throws Exception {
+        XMLReader xmlReader = XMLReaderFactory.createXMLReader();
+        TestModuleConfigHandler testModuleXmlHandler = new TestModuleConfigHandler(file.getName());
+        xmlReader.setContentHandler(testModuleXmlHandler);
+        FileReader fileReader = null;
+        try {
+            fileReader = new FileReader(file);
+            xmlReader.parse(new InputSource(fileReader));
+            return testModuleXmlHandler.getFileMetadata();
+        } finally {
+            if (null != fileReader) {
+                fileReader.close();
+            }
+        }
+    }
+
+    // Parse a folder to add all entries
+    private static Entry.Builder parseFolder(TestSuiteContent.Builder testSuiteContent, String fPath, String rPath)
+            throws IOException, NoSuchAlgorithmException {
+        Entry.Builder folderEntry = Entry.newBuilder();
+
+        File folder = new File(fPath);
+        File rFolder = new File(rPath);
+        Path folderPath = Paths.get(folder.getAbsolutePath());
+        Path rootPath = Paths.get(rFolder.getAbsolutePath());
+        String folderRelativePath = rootPath.relativize(folderPath).toString();
+        String folderId = getId(folderRelativePath);
+        File[] fileList = folder.listFiles();
+        Long folderSize = 0L;
+        List <Entry> entryList = new ArrayList<Entry> ();
+        for (File file : fileList){
+            if (file.isFile()){
+                String fileRelativePath = rootPath.relativize(Paths.get(file.getAbsolutePath())).toString();
+                Entry.Builder fileEntry = Entry.newBuilder();
+                fileEntry.setId(getId(fileRelativePath));
+                fileEntry.setName(file.getName());
+                fileEntry.setSize(file.length());
+                fileEntry.setContentId(getFileContentId(file));
+                fileEntry.setRelativePath(fileRelativePath);
+                fileEntry.setParentId(folderId);
+                try {
+                    FileMetadata fMetadata = parseFileMetadata(fileEntry, file);
+                    if (null != fMetadata) {
+                        fileEntry.setFileMetadata(fMetadata);
+                    }
+                } catch (Exception ex) {
+                    System.err.println(
+                            String.format("Cannot parse %s",
+                                    file.getAbsolutePath()));
+                    ex.printStackTrace();
+                }
+                testSuiteContent.addFileEntries(fileEntry);
+                entryList.add(fileEntry.build());
+                folderSize += file.length();
+            } else if (file.isDirectory()){
+                Entry.Builder subFolderEntry = parseFolder(testSuiteContent, file.getAbsolutePath(), rPath);
+                subFolderEntry.setParentId(folderId);
+                testSuiteContent.addFileEntries(subFolderEntry);
+                folderSize += subFolderEntry.getSize();
+                entryList.add(subFolderEntry.build());
+            }
+        }
+
+        folderEntry.setId(folderId);
+        folderEntry.setName(folderRelativePath);
+        folderEntry.setSize(folderSize);
+        folderEntry.setType(Entry.EntryType.FOLDER);
+        folderEntry.setContentId(getFolderContentId(folderEntry, entryList));
+        folderEntry.setRelativePath(folderRelativePath);
+        return folderEntry;
+    }
+
+    private static String getFileContentId(File file)
+            throws IOException, NoSuchAlgorithmException {
+        MessageDigest md = MessageDigest.getInstance("SHA-256");
+        FileInputStream fis = new FileInputStream(file);
+
+        byte[] dataBytes = new byte[10240];
+
+        int nread = 0;
+        while ((nread = fis.read(dataBytes)) != -1) {
+          md.update(dataBytes, 0, nread);
+        };
+        byte[] mdbytes = md.digest();
+
+        // Converts to Hex String
+        StringBuffer hexString = new StringBuffer();
+        for (int i=0;i<mdbytes.length;i++) {
+            hexString.append(Integer.toHexString(0xFF & mdbytes[i]));
+        }
+        return hexString.toString();
+    }
+
+    private static String getFolderContentId(Entry.Builder folderEntry, List<Entry> entryList)
+            throws IOException, NoSuchAlgorithmException {
+        MessageDigest md = MessageDigest.getInstance("SHA-256");
+
+        for (Entry entry: entryList) {
+            md.update(entry.getContentId().getBytes(StandardCharsets.UTF_8));
+        }
+        byte[] mdbytes = md.digest();
+
+        // Converts to Hex String
+        StringBuffer hexString = new StringBuffer();
+        for (int i=0;i<mdbytes.length;i++) {
+            hexString.append(Integer.toHexString(0xFF & mdbytes[i]));
+        }
+        return hexString.toString();
+    }
+
+    private static String getId(String name)
+            throws IOException, NoSuchAlgorithmException {
+        MessageDigest md = MessageDigest.getInstance("SHA-256");
+        md.update(name.getBytes(StandardCharsets.UTF_8));
+        byte[] mdbytes = md.digest();
+
+        // Converts to Hex String
+        StringBuffer hexString = new StringBuffer();
+        for (int i=0;i<mdbytes.length;i++) {
+            hexString.append(Integer.toHexString(0xFF & mdbytes[i]));
+        }
+        return hexString.toString();
+    }
+
+    // Iterates though all test suite content and prints them.
+    static void Print(TestSuiteContent tsContent) {
+        //Header
+        System.out.printf("no,type,name,size,relative path,id,content id,parent id,description,test class");
+        // test class header
+        System.out.printf(",%s,%s,%s,%s,%s",
+                RUNTIME_HIT_TAG, PACKAGE_TAG, JAR_NAME_TAG, NATIVE_TEST_DEVICE_PATH_TAG, MODULE_TAG);
+        // target preparer header
+        System.out.printf(",%s,%s\n",
+                TEST_FILE_NAME_TAG, PUSH_TAG);
+
+        int i = 1;
+        for (Entry entry: tsContent.getFileEntriesList()) {
+            System.out.printf("%d,%s,%s,%d,%s,%s,%s,%s",
+                i++, entry.getType(), entry.getName(), entry.getSize(),
+                entry.getRelativePath(), entry.getId(), entry.getContentId(),
+                entry.getParentId());
+            if (Entry.EntryType.CONFIG == entry.getType()) {
+                ConfigMetadata config = entry.getFileMetadata().getConfigMetadata();
+                System.out.printf(",%s", entry.getFileMetadata().getDescription());
+                List<Option> optList;
+                List<ConfigMetadata.TestClass> testClassesList = config.getTestClassesList();
+                String rtHit = "";
+                String pkg = "";
+                String jar = "";
+                String ntdPath = "";
+                String module = "";
+
+                for (ConfigMetadata.TestClass tClass : testClassesList) {
+                    System.out.printf(",%s", tClass.getTestClass());
+                    optList = tClass.getOptionsList();
+                    for (Option opt : optList) {
+                        if (RUNTIME_HIT_TAG.equalsIgnoreCase(opt.getName())) {
+                            rtHit = rtHit + opt.getValue() + " ";
+                        } else if (PACKAGE_TAG.equalsIgnoreCase(opt.getName())) {
+                            pkg = pkg + opt.getValue() + " ";
+                        } else if (JAR_NAME_TAG.equalsIgnoreCase(opt.getName())) {
+                            jar = jar + opt.getValue() + " ";
+                        } else if (NATIVE_TEST_DEVICE_PATH_TAG.equalsIgnoreCase(opt.getName())) {
+                            ntdPath = ntdPath + opt.getValue() + " ";
+                        } else if (MODULE_TAG.equalsIgnoreCase(opt.getName())) {
+                            module = module + opt.getValue() + " ";
+                        }
+                    }
+                }
+                System.out.printf(",%s,%s,%s,%s,%s", rtHit.trim(), pkg.trim(),
+                        jar.trim(), module.trim(), ntdPath.trim());
+
+                List<ConfigMetadata.TargetPreparer> tPrepList = config.getTargetPreparersList();
+                String testFile = "";
+                String pushList = "";
+                for (ConfigMetadata.TargetPreparer tPrep : tPrepList) {
+                    optList = tPrep.getOptionsList();
+                    for (Option opt : optList) {
+                        if (TEST_FILE_NAME_TAG.equalsIgnoreCase(opt.getName())) {
+                            testFile = testFile + opt.getValue() + " ";
+                        } else if (PUSH_TAG.equalsIgnoreCase(opt.getName())) {
+                            pushList = pushList + opt.getValue() + " ";
+                        }
+                    }
+                }
+                System.out.printf(",%s,%s", testFile.trim(), pushList.trim());
+            }
+            System.out.printf("\n");
+        }
+    }
+
+    public static void main(String[] args)
+            throws IOException, NoSuchAlgorithmException {
+        String outputFilePath = "./tsContentMessage.pb";
+        String tsPath = "";
+
+        for (int i = 0; i < args.length; i++) {
+            if (args[i].startsWith("-")) {
+                if ("-o".equals(args[i])) {
+                    outputFilePath = getExpectedArg(args, ++i);
+                } else if ("-i".equals(args[i])) {
+                    tsPath = getExpectedArg(args, ++i);
+                    File file = new File(tsPath);
+                    if (file.isDirectory()) {
+                        //
+                    } else {
+                        printUsage();
+                    }
+                } else {
+                    printUsage();
+                }
+            }
+        }
+
+        TestSuiteContent tsContent = parseTestSuiteFolder(tsPath);
+
+        // Write test suite content message to disk.
+        FileOutputStream output = new FileOutputStream(outputFilePath);
+        try {
+          tsContent.writeTo(output);
+        } finally {
+          output.close();
+        }
+
+        // Read message from the file and print them out
+        TestSuiteContent tsContent1 =
+          TestSuiteContent.parseFrom(new FileInputStream(outputFilePath));
+        Print(tsContent1);
+    }
+}
diff --git a/tools/cts-device-info/Android.mk b/tools/cts-device-info/Android.mk
index 2f11db9..328b0a3 100644
--- a/tools/cts-device-info/Android.mk
+++ b/tools/cts-device-info/Android.mk
@@ -18,6 +18,8 @@
 
 LOCAL_SRC_FILES := $(call all-java-files-under, src)
 
+LOCAL_JAVA_LIBRARIES := android.test.base
+
 LOCAL_JNI_SHARED_LIBRARIES := libctsdeviceinfo
 
 LOCAL_MULTILIB := both
diff --git a/tools/cts-device-info/src/com/android/cts/deviceinfo/CameraDeviceInfo.java b/tools/cts-device-info/src/com/android/cts/deviceinfo/CameraDeviceInfo.java
index 41afdb9..782e075 100644
--- a/tools/cts-device-info/src/com/android/cts/deviceinfo/CameraDeviceInfo.java
+++ b/tools/cts-device-info/src/com/android/cts/deviceinfo/CameraDeviceInfo.java
@@ -518,6 +518,7 @@
         charsKeyNames.add(CameraCharacteristics.TONEMAP_MAX_CURVE_POINTS.getName());
         charsKeyNames.add(CameraCharacteristics.TONEMAP_AVAILABLE_TONE_MAP_MODES.getName());
         charsKeyNames.add(CameraCharacteristics.INFO_SUPPORTED_HARDWARE_LEVEL.getName());
+        charsKeyNames.add(CameraCharacteristics.INFO_VERSION.getName());
         charsKeyNames.add(CameraCharacteristics.SYNC_MAX_LATENCY.getName());
         charsKeyNames.add(CameraCharacteristics.REPROCESS_MAX_CAPTURE_STALL.getName());
         charsKeyNames.add(CameraCharacteristics.DEPTH_DEPTH_IS_EXCLUSIVE.getName());
diff --git a/tools/cts-holo-generation/Android.mk b/tools/cts-holo-generation/Android.mk
index 43b346d..195f5a5 100644
--- a/tools/cts-holo-generation/Android.mk
+++ b/tools/cts-holo-generation/Android.mk
@@ -23,7 +23,9 @@
 LOCAL_DEX_PREOPT := false
 LOCAL_PROGUARD_ENABLED := disabled
 
-LOCAL_STATIC_JAVA_LIBRARIES := android-support-test legacy-android-test
+LOCAL_STATIC_JAVA_LIBRARIES := android-support-test
+
+LOCAL_JAVA_LIBRARIES := android.test.runner.stubs android.test.base.stubs
 
 LOCAL_SRC_FILES := $(call all-java-files-under, src)
 
diff --git a/tools/cts-preconditions/Android.mk b/tools/cts-preconditions/Android.mk
index 76bfaa8..5f00c36 100644
--- a/tools/cts-preconditions/Android.mk
+++ b/tools/cts-preconditions/Android.mk
@@ -27,8 +27,9 @@
 
 LOCAL_STATIC_JAVA_LIBRARIES := \
     android-support-test \
-    compatibility-device-preconditions \
-    legacy-android-test
+    compatibility-device-preconditions
+
+LOCAL_JAVA_LIBRARIES := android.test.base.stubs
 
 LOCAL_SRC_FILES := $(call all-java-files-under, src)
 
diff --git a/tools/cts-reference-app-lib/Android.mk b/tools/cts-reference-app-lib/Android.mk
index 8f6039d..f006bb0 100644
--- a/tools/cts-reference-app-lib/Android.mk
+++ b/tools/cts-reference-app-lib/Android.mk
@@ -24,7 +24,9 @@
 # and when built explicitly put it in the data partition
 LOCAL_MODULE_PATH := $(TARGET_OUT_DATA_APPS)
 
-LOCAL_STATIC_JAVA_LIBRARIES := legacy-android-test junit
+LOCAL_STATIC_JAVA_LIBRARIES := junit
+
+LOCAL_JAVA_LIBRARIES := android.test.runner.stubs android.test.base.stubs
 
 LOCAL_SDK_VERSION := current
 
diff --git a/tools/cts-tradefed/res/config/cts-known-failures.xml b/tools/cts-tradefed/res/config/cts-known-failures.xml
index 9f0256c..ffbd6b1 100644
--- a/tools/cts-tradefed/res/config/cts-known-failures.xml
+++ b/tools/cts-tradefed/res/config/cts-known-failures.xml
@@ -23,8 +23,8 @@
     <option name="compatibility:exclude-filter" value="CtsLocationTestCases android.location.cts.GnssTtffTests#testTtffWithNetwork" />
 
     <!-- b/35314835 -->
-    <option name="compatibility:exclude-filter" value="CtsServicesHostTestCases android.server.cts.ActivityManagerPinnedStackTests#testAlwaysFocusablePipActivity" />
-    <option name="compatibility:exclude-filter" value="CtsServicesHostTestCases android.server.cts.ActivityManagerPinnedStackTests#testLaunchIntoPinnedStack" />
+    <option name="compatibility:exclude-filter" value="CtsActivityManagerDeviceTestCases android.server.am.ActivityManagerPinnedStackTests#testAlwaysFocusablePipActivity" />
+    <option name="compatibility:exclude-filter" value="CtsActivityManagerDeviceTestCases android.server.am.ActivityManagerPinnedStackTests#testLaunchIntoPinnedStack" />
 
     <!-- b/17595050 -->
     <option name="compatibility:exclude-filter" value="CtsAccessibilityServiceTestCases android.accessibilityservice.cts.AccessibilityTextTraversalTest#testActionNextAndPreviousAtGranularityPageOverText" />
@@ -126,6 +126,12 @@
     <!-- b/36686383 -->
     <option name="compatibility:exclude-filter" value="CtsIncidentHostTestCases com.android.server.cts.ErrorsTest#testANR" />
 
+    <!-- b/33090965 -->
+    <option name="compatibility:exclude-filter" value="CtsVideoTestCases android.video.cts.VideoEncoderDecoderTest#testVp9Goog0Perf0320x0180" />
+    <option name="compatibility:exclude-filter" value="CtsVideoTestCases android.video.cts.VideoEncoderDecoderTest#testVp9Goog0Perf0640x0360" />
+    <option name="compatibility:exclude-filter" value="CtsVideoTestCases android.video.cts.VideoEncoderDecoderTest#testVp9Goog0Perf1280x0720" />
+    <option name="compatibility:exclude-filter" value="CtsVideoTestCases android.video.cts.VideoEncoderDecoderTest#testVp9Goog0Perf1920x1080" />
+
     <!-- b/37482372 -->
     <option name="compatibility:exclude-filter" value="CtsPreference2TestCases android.preference2.cts.PreferenceActivityFlowPortraitTest#multiWindowHistoryPreservePortraitTest" />
     <option name="compatibility:exclude-filter" value="CtsPreference2TestCases android.preference2.cts.PreferenceActivityFlowPortraitTest#multiWindowInOutPortraitTest" />
@@ -175,6 +181,9 @@
     <option name="compatibility:exclude-filter" value="x86_64 CtsMediaBitstreamsTestCases" />
     <option name="compatibility:exclude-filter" value="mips64 CtsMediaBitstreamsTestCases" />
 
+    <!-- b/38280830 -->
+    <option name="compatibility:exclude-filter" value="CtsMediaTestCases android.media.cts.VideoDecoderPerfTest#testVp8Goog0Perf1280x0720" />
+
     <!-- b/38420898 -->
     <option name="compatibility:exclude-filter" value="CtsSensorTestCases android.hardware.cts.SensorDirectReportTest#testRateIndependencyAccelMultiChannel" />
     <option name="compatibility:exclude-filter" value="CtsSensorTestCases android.hardware.cts.SensorDirectReportTest#testRateIndependencyGyroMultiChannel" />
diff --git a/tools/cts-tradefed/res/config/retry.xml b/tools/cts-tradefed/res/config/retry.xml
index cbecada..e488410 100644
--- a/tools/cts-tradefed/res/config/retry.xml
+++ b/tools/cts-tradefed/res/config/retry.xml
@@ -18,6 +18,10 @@
     <device_recovery class="com.android.tradefed.device.WaitDeviceRecovery" />
     <build_provider class="com.android.compatibility.common.tradefed.build.CompatibilityBuildProvider" />
     <test class="com.android.compatibility.common.tradefed.testtype.retry.RetryFactoryTest" />
+
+    <option name="compatibility:test-arg" value="com.android.tradefed.testtype.AndroidJUnitTest:rerun-from-file:true" />
+    <option name="compatibility:test-arg" value="com.android.tradefed.testtype.AndroidJUnitTest:fallback-to-serial-rerun:false" />
+
     <logger class="com.android.tradefed.log.FileLogger">
         <option name="log-level-display" value="WARN" />
     </logger>
diff --git a/tools/vm-tests-tf/Android.mk b/tools/vm-tests-tf/Android.mk
index 70c74e1..ea4f09b 100644
--- a/tools/vm-tests-tf/Android.mk
+++ b/tools/vm-tests-tf/Android.mk
@@ -25,7 +25,6 @@
 LOCAL_MODULE_CLASS := JAVA_LIBRARIES
 LOCAL_MODULE_TAGS := optional
 LOCAL_JAVA_LIBRARIES := junit
--include cts/error_prone_rules_tests.mk
 include $(BUILD_JAVA_LIBRARY)
 
 cts-tf-dalvik-lib.jar := $(full_classes_jar)
diff --git a/tools/vm-tests-tf/AndroidTest.xml b/tools/vm-tests-tf/AndroidTest.xml
index 72b3914..fc0f8af 100644
--- a/tools/vm-tests-tf/AndroidTest.xml
+++ b/tools/vm-tests-tf/AndroidTest.xml
@@ -14,6 +14,7 @@
      limitations under the License.
 -->
 <configuration description="Config for CTS VM test cases">
+    <option name="test-suite-tag" value="cts" />
     <option name="config-descriptor:metadata" key="component" value="art" />
     <option name="not-strict-shardable" value= "true" />
     <target_preparer class="android.core.vm.targetprep.VmTestPreparer" />