Merge "Fix the SingleSource foreach test with LaunchOptions." into nougat-cts-dev am: fb41072d1a am: a6a8b9ce90
am: 06c9aacdc6

Change-Id: I6496ab88e2607ed993e7ce9f0538b6f9d7cc5774
diff --git a/apps/CameraITS/build/envsetup.sh b/apps/CameraITS/build/envsetup.sh
index bcf294a..03b45d1 100644
--- a/apps/CameraITS/build/envsetup.sh
+++ b/apps/CameraITS/build/envsetup.sh
@@ -17,7 +17,7 @@
 # and that the unit tests for the modules passed (indicating that the setup
 # is correct).
 
-CAMERA_ITS_TOP=$PWD
+export CAMERA_ITS_TOP=$PWD
 
 [[ "${BASH_SOURCE[0]}" != "${0}" ]] || \
     { echo ">> Script must be sourced with 'source $0'" >&2; exit 1; }
@@ -31,12 +31,23 @@
 python -V 2>&1 | grep -q "Python 2.7" || \
     echo ">> Require python 2.7" >&2
 
-for M in numpy PIL Image matplotlib pylab cv2 scipy.stats scipy.spatial
+for M in numpy PIL Image matplotlib pylab scipy.stats scipy.spatial
 do
     python -c "import $M" >/dev/null 2>&1 || \
         echo ">> Require Python $M module" >&2
 done
 
+CV2_VER=$(python -c "\
+try:
+    import cv2
+    print cv2.__version__
+except:
+    print \"N/A\"
+")
+
+echo $CV2_VER | grep -q "^2.4" || \
+    echo ">> Require python opencv 2.4. Got $CV2_VER" >&2
+
 export PYTHONPATH="$PWD/pymodules:$PYTHONPATH"
 
 for M in device objects image caps dng target error
diff --git a/apps/CameraITS/pymodules/its/caps.py b/apps/CameraITS/pymodules/its/caps.py
index a33757d..d9270f7 100644
--- a/apps/CameraITS/pymodules/its/caps.py
+++ b/apps/CameraITS/pymodules/its/caps.py
@@ -394,6 +394,59 @@
             "android.edge.availableEdgeModes") and mode \
             in props["android.edge.availableEdgeModes"];
 
+
+def lens_calibrated(props):
+    """Returns whether lens position is calibrated or not.
+
+    android.lens.info.focusDistanceCalibration has 3 modes.
+    0: Uncalibrated
+    1: Approximate
+    2: Calibrated
+
+    Args:
+        props: Camera properties objects.
+
+    Returns:
+        Boolean.
+    """
+    return props.has_key("android.lens.info.focusDistanceCalibration") and \
+         props["android.lens.info.focusDistanceCalibration"] == 2
+
+
+def lens_approx_calibrated(props):
+    """Returns whether lens position is calibrated or not.
+
+    android.lens.info.focusDistanceCalibration has 3 modes.
+    0: Uncalibrated
+    1: Approximate
+    2: Calibrated
+
+    Args:
+        props: Camera properties objects.
+
+    Returns:
+        Boolean.
+    """
+    return props.has_key("android.lens.info.focusDistanceCalibration") and \
+        (props["android.lens.info.focusDistanceCalibration"] == 1 or
+         props["android.lens.info.focusDistanceCalibration"] == 2)
+
+
+def fixed_focus(props):
+    """Returns whether a device is fixed focus.
+
+    props[android.lens.info.minimumFocusDistance] == 0 is fixed focus
+
+    Args:
+        props: Camera properties objects.
+
+    Returns:
+        Boolean.
+    """
+    return props.has_key("android.lens.info.minimumFocusDistance") and \
+        props["android.lens.info.minimumFocusDistance"] == 0
+
+
 class __UnitTest(unittest.TestCase):
     """Run a suite of unit tests on this module.
     """
diff --git a/apps/CameraITS/pymodules/its/device.py b/apps/CameraITS/pymodules/its/device.py
index 351b03c..3b378cc 100644
--- a/apps/CameraITS/pymodules/its/device.py
+++ b/apps/CameraITS/pymodules/its/device.py
@@ -67,9 +67,17 @@
     PACKAGE = 'com.android.cts.verifier.camera.its'
     INTENT_START = 'com.android.cts.verifier.camera.its.START'
     ACTION_ITS_RESULT = 'com.android.cts.verifier.camera.its.ACTION_ITS_RESULT'
+    EXTRA_VERSION = 'camera.its.extra.VERSION'
+    CURRENT_ITS_VERSION = '1.0' # version number to sync with CtsVerifier
     EXTRA_CAMERA_ID = 'camera.its.extra.CAMERA_ID'
-    EXTRA_SUCCESS = 'camera.its.extra.SUCCESS'
-    EXTRA_SUMMARY = 'camera.its.extra.SUMMARY'
+    EXTRA_RESULTS = 'camera.its.extra.RESULTS'
+
+    RESULT_PASS = 'PASS'
+    RESULT_FAIL = 'FAIL'
+    RESULT_NOT_EXECUTED = 'NOT_EXECUTED'
+    RESULT_VALUES = {RESULT_PASS, RESULT_FAIL, RESULT_NOT_EXECUTED}
+    RESULT_KEY = 'result'
+    SUMMARY_KEY = 'summary'
 
     adb = "adb -d"
     device_id = ""
@@ -746,15 +754,24 @@
     Return the device ID provided in the command line if it's connected. If no
     device ID is provided in the command line and there is only one device
     connected, return the device ID by parsing the result of "adb devices".
+    Also, if the environment variable ANDROID_SERIAL is set, use it as device
+    id. When both ANDROID_SERIAL and device argument present, device argument
+    takes priority.
 
     Raise an exception if no device is connected; or the device ID provided in
     the command line is not connected; or no device ID is provided in the
-    command line and there are more than 1 device connected.
+    command line or environment variable and there are more than 1 device
+    connected.
 
     Returns:
         Device ID string.
     """
     device_id = None
+
+    # Check if device id is set in env
+    if "ANDROID_SERIAL" in os.environ:
+        device_id = os.environ["ANDROID_SERIAL"]
+
     for s in sys.argv[1:]:
         if s[:7] == "device=" and len(s) > 7:
             device_id = str(s[7:])
@@ -781,34 +798,43 @@
 
     return device_id
 
-def report_result(device_id, camera_id, success, summary_path=None):
+def report_result(device_id, camera_id, results):
     """Send a pass/fail result to the device, via an intent.
 
     Args:
         device_id: The ID string of the device to report the results to.
         camera_id: The ID string of the camera for which to report pass/fail.
-        success: Boolean, indicating if the result was pass or fail.
-        summary_path: (Optional) path to ITS summary file on host PC
-
+        results: a dictionary contains all ITS scenes as key and result/summary
+                 of current ITS run. See test_report_result unit test for
+                 an example.
     Returns:
         Nothing.
     """
     adb = "adb -s " + device_id
-    device_summary_path = "/sdcard/camera_" + camera_id + "_its_summary.txt"
-    if summary_path is not None:
-        _run("%s push %s %s" % (
-                adb, summary_path, device_summary_path))
-        _run("%s shell am broadcast -a %s --es %s %s --es %s %s --es %s %s" % (
-                adb, ItsSession.ACTION_ITS_RESULT,
-                ItsSession.EXTRA_CAMERA_ID, camera_id,
-                ItsSession.EXTRA_SUCCESS, 'True' if success else 'False',
-                ItsSession.EXTRA_SUMMARY, device_summary_path))
-    else:
-        _run("%s shell am broadcast -a %s --es %s %s --es %s %s --es %s %s" % (
-                adb, ItsSession.ACTION_ITS_RESULT,
-                ItsSession.EXTRA_CAMERA_ID, camera_id,
-                ItsSession.EXTRA_SUCCESS, 'True' if success else 'False',
-                ItsSession.EXTRA_SUMMARY, "null"))
+    # Validate/process results argument
+    for scene in results:
+        result_key = ItsSession.RESULT_KEY
+        summary_key = ItsSession.SUMMARY_KEY
+        if result_key not in results[scene]:
+            raise its.error.Error('ITS result not found for ' + scene)
+        if results[scene][result_key] not in ItsSession.RESULT_VALUES:
+            raise its.error.Error('Unknown ITS result for %s: %s' % (
+                    scene, results[result_key]))
+        if summary_key in results[scene]:
+            device_summary_path = "/sdcard/its_camera%s_%s.txt" % (
+                    camera_id, scene)
+            _run("%s push %s %s" % (
+                    adb, results[scene][summary_key], device_summary_path))
+            results[scene][summary_key] = device_summary_path
+    json_results = json.dumps(results)
+    cmd = "%s shell am broadcast -a %s --es %s %s --es %s %s --es %s \'%s\'" % (
+            adb, ItsSession.ACTION_ITS_RESULT,
+            ItsSession.EXTRA_VERSION, ItsSession.CURRENT_ITS_VERSION,
+            ItsSession.EXTRA_CAMERA_ID, camera_id,
+            ItsSession.EXTRA_RESULTS, json_results)
+    if len(cmd) > 4095:
+        print "ITS command string might be too long! len:", len(cmd)
+    _run(cmd)
 
 def _run(cmd):
     """Replacement for os.system, with hiding of stdout+stderr messages.
@@ -821,8 +847,20 @@
     """Run a suite of unit tests on this module.
     """
 
-    # TODO: Add some unit tests.
-    None
+    """
+    # TODO: this test currently needs connected device to pass
+    #       Need to remove that dependency before enabling the test
+    def test_report_result(self):
+        device_id = get_device_id()
+        camera_id = "1"
+        result_key = ItsSession.RESULT_KEY
+        results = {"scene0":{result_key:"PASS"},
+                   "scene1":{result_key:"PASS"},
+                   "scene2":{result_key:"PASS"},
+                   "scene3":{result_key:"PASS"},
+                   "sceneNotExist":{result_key:"FAIL"}}
+        report_result(device_id, camera_id, results)
+    """
 
 if __name__ == '__main__':
     unittest.main()
diff --git a/apps/CameraITS/tests/scene0/test_metadata.py b/apps/CameraITS/tests/scene0/test_metadata.py
index ed1426b..e5fbba5 100644
--- a/apps/CameraITS/tests/scene0/test_metadata.py
+++ b/apps/CameraITS/tests/scene0/test_metadata.py
@@ -97,9 +97,12 @@
         print "Assert field of view: %.1f degrees" % fov
         assert 30 <= fov <= 130
 
-        hyperfocal = 1.0 / props["android.lens.info.hyperfocalDistance"]
-        print "Assert hyperfocal distance: %.2f m" % hyperfocal
-        assert 0.02 <= hyperfocal
+        if its.caps.lens_approx_calibrated(props):
+            diopter_hyperfocal = props["android.lens.info.hyperfocalDistance"]
+            if diopter_hyperfocal != 0.0:
+                hyperfocal = 1.0 / diopter_hyperfocal
+                print "Assert hyperfocal distance: %.2f m" % hyperfocal
+                assert 0.02 <= hyperfocal
 
 
 def getval(expr, default=None):
diff --git a/apps/CameraITS/tests/scene1/scene1.pdf b/apps/CameraITS/tests/scene1/scene1.pdf
new file mode 100644
index 0000000..7e47bcf
--- /dev/null
+++ b/apps/CameraITS/tests/scene1/scene1.pdf
Binary files differ
diff --git a/apps/CameraITS/tests/scene1/test_ev_compensation_basic.py b/apps/CameraITS/tests/scene1/test_ev_compensation_basic.py
index 49ccbcf..2e5bf63 100644
--- a/apps/CameraITS/tests/scene1/test_ev_compensation_basic.py
+++ b/apps/CameraITS/tests/scene1/test_ev_compensation_basic.py
@@ -20,10 +20,13 @@
 import pylab
 import matplotlib
 import matplotlib.pyplot
-import numpy
+import numpy as np
 
 #AE must converge within this number of auto requests for EV
-THREASH_CONVERGE_FOR_EV = 8
+THRESH_CONVERGE_FOR_EV = 8
+YUV_FULL_SCALE = 255.0
+YUV_SATURATION_MIN = 253.0
+
 
 def main():
     """Tests that EV compensation is applied.
@@ -42,6 +45,9 @@
         steps_per_ev = int(1.0 / ev_per_step)
         evs = range(-2 * steps_per_ev, 2 * steps_per_ev + 1, steps_per_ev)
         lumas = []
+        reds = []
+        greens = []
+        blues = []
 
         # Converge 3A, and lock AE once converged. skip AF trigger as
         # dark/bright scene could make AF convergence fail and this test
@@ -54,24 +60,37 @@
             req = its.objects.auto_capture_request()
             req['android.control.aeExposureCompensation'] = ev
             req["android.control.aeLock"] = True
-            caps = cam.do_capture([req]*THREASH_CONVERGE_FOR_EV)
+            caps = cam.do_capture([req]*THRESH_CONVERGE_FOR_EV)
             for cap in caps:
                 if (cap['metadata']['android.control.aeState'] == LOCKED):
                     y = its.image.convert_capture_to_planes(cap)[0]
                     tile = its.image.get_image_patch(y, 0.45,0.45,0.1,0.1)
                     lumas.append(its.image.compute_image_means(tile)[0])
+                    rgb = its.image.convert_capture_to_rgb_image(cap)
+                    rgb_tile = its.image.get_image_patch(rgb,
+                                                       0.45, 0.45, 0.1, 0.1)
+                    rgb_means = its.image.compute_image_means(rgb_tile)
+                    reds.append(rgb_means[0])
+                    greens.append(rgb_means[1])
+                    blues.append(rgb_means[2])
                     break
             assert(cap['metadata']['android.control.aeState'] == LOCKED)
 
-        pylab.plot(evs, lumas, 'r')
+        pylab.plot(evs, lumas, '-ro')
+        pylab.xlabel('EV Compensation')
+        pylab.ylabel('Mean Luma (Normalized)')
         matplotlib.pyplot.savefig("%s_plot_means.png" % (NAME))
 
-        # trim trailing 1.0s (for saturated image)
-        while lumas and lumas[-1] == 1.0:
-            lumas.pop(-1)
+        # Trim extra saturated images
+        while lumas and lumas[-1] >= YUV_SATURATION_MIN/YUV_FULL_SCALE:
+            if reds[-1] == greens[-1] == blues[-1]:
+                lumas.pop(-1)
+                reds.pop(-1)
+                greens.pop(-1)
+                blues.pop(-1)
         # Only allow positive EVs to give saturated image
         assert(len(lumas) > 2)
-        luma_diffs = numpy.diff(lumas)
+        luma_diffs = np.diff(lumas)
         min_luma_diffs = min(luma_diffs)
         print "Min of the luma value difference between adjacent ev comp: ", \
                 min_luma_diffs
diff --git a/apps/CameraITS/tests/scene2/scene2.pdf b/apps/CameraITS/tests/scene2/scene2.pdf
new file mode 100644
index 0000000..ccde9d98
--- /dev/null
+++ b/apps/CameraITS/tests/scene2/scene2.pdf
Binary files differ
diff --git a/apps/CameraITS/tests/scene2/test_faces.py b/apps/CameraITS/tests/scene2/test_faces.py
index cce74e7..ba4f317 100644
--- a/apps/CameraITS/tests/scene2/test_faces.py
+++ b/apps/CameraITS/tests/scene2/test_faces.py
@@ -31,7 +31,13 @@
         fd_modes = props['android.statistics.info.availableFaceDetectModes']
         a = props['android.sensor.info.activeArraySize']
         aw, ah = a['right'] - a['left'], a['bottom'] - a['top']
-        cam.do_3a()
+        gain, exp, _, _, focus = cam.do_3a(get_results=True)
+        print 'iso = %d' % gain
+        print 'exp = %.2fms' % (exp*1.0E-6)
+        if focus == 0.0:
+            print 'fd = infinity'
+        else:
+            print 'fd = %.2fcm' % (1.0E2/focus)
         for fd_mode in fd_modes:
             assert(FD_MODE_OFF <= fd_mode <= FD_MODE_FULL)
             req = its.objects.auto_capture_request()
@@ -41,6 +47,9 @@
                 md = cap['metadata']
                 assert(md['android.statistics.faceDetectMode'] == fd_mode)
                 faces = md['android.statistics.faces']
+                img = its.image.convert_capture_to_rgb_image(cap, props=props)
+                img_name = "%s_fd_mode_%s.jpg" % (NAME, fd_mode)
+                its.image.write_image(img, img_name)
 
                 # 0 faces should be returned for OFF mode
                 if fd_mode == FD_MODE_OFF:
diff --git a/apps/CameraITS/tests/scene3/scene3.pdf b/apps/CameraITS/tests/scene3/scene3.pdf
new file mode 100644
index 0000000..4c787b1
--- /dev/null
+++ b/apps/CameraITS/tests/scene3/scene3.pdf
Binary files differ
diff --git a/apps/CameraITS/tests/scene4/scene4.pdf b/apps/CameraITS/tests/scene4/scene4.pdf
new file mode 100644
index 0000000..7dcc4b9
--- /dev/null
+++ b/apps/CameraITS/tests/scene4/scene4.pdf
Binary files differ
diff --git a/apps/CameraITS/tests/sensor_fusion/test_sensor_fusion.py b/apps/CameraITS/tests/sensor_fusion/test_sensor_fusion.py
index c4f9b84..288d6e4 100644
--- a/apps/CameraITS/tests/sensor_fusion/test_sensor_fusion.py
+++ b/apps/CameraITS/tests/sensor_fusion/test_sensor_fusion.py
@@ -35,8 +35,20 @@
 # Capture 210 VGA frames (which is 7s at 30fps)
 N = 210
 W,H = 640,480
+FEATURE_MARGIN = H * 0.20 / 2 # Only take feature points from the center 20%
+                              # so that the rotation measured have much less
+                              # of rolling shutter effect
 
-FEATURE_PARAMS = dict( maxCorners = 80,
+MIN_FEATURE_PTS = 30          # Minimum number of feature points required to
+                              # perform rotation analysis
+
+MAX_CAM_FRM_RANGE_SEC = 9.0   # Maximum allowed camera frame range. When this
+                              # number is significantly larger than 7 seconds,
+                              # usually system is in some busy/bad states.
+
+MIN_GYRO_SMP_RATE = 100.0     # Minimum gyro sample rate
+
+FEATURE_PARAMS = dict( maxCorners = 240,
                        qualityLevel = 0.3,
                        minDistance = 7,
                        blockSize = 7 )
@@ -102,6 +114,14 @@
             min_cam_time, max_cam_time, min_gyro_time, max_gyro_time)
         assert(0)
 
+    cam_frame_range = max_cam_time - min_cam_time
+    gyro_time_range = max_gyro_time - min_gyro_time
+    gyro_smp_per_sec = len(gyro_times) / gyro_time_range
+    print "Camera frame range", max_cam_time - min_cam_time
+    print "Gyro samples per second", gyro_smp_per_sec
+    assert(cam_frame_range < MAX_CAM_FRM_RANGE_SEC)
+    assert(gyro_smp_per_sec > MIN_GYRO_SMP_RATE)
+
     # Compute the camera rotation displacements (rad) between each pair of
     # adjacent frames.
     cam_rots = get_cam_rotations(frames, events["facing"])
@@ -140,9 +160,9 @@
     Returns:
         Offset (seconds) of the best alignment.
     """
-    # Measure the corr. dist. over a shift of up to +/- 100ms (1ms step size).
+    # Measure the corr. dist. over a shift of up to +/- 50ms (0.5ms step size).
     # Get the shift corresponding to the best (lowest) score.
-    candidates = range(-100,101)
+    candidates = numpy.arange(-50,50.5,0.5).tolist()
     dists = []
     for shift in candidates:
         times = cam_times + shift*MSEC_TO_NSEC
@@ -151,22 +171,26 @@
     best_corr_dist = min(dists)
     best_shift = candidates[dists.index(best_corr_dist)]
 
+    print "Best shift without fitting is ", best_shift, "ms"
+
     # Fit a curve to the corr. dist. data to measure the minima more
     # accurately, by looking at the correlation distances within a range of
-    # +/- 20ms from the measured best score; note that this will use fewer
-    # than the full +/- 20 range for the curve fit if the measured score
-    # (which is used as the center of the fit) is within 20ms of the edge of
-    # the +/- 100ms candidate range.
-    i = len(dists)/2 + best_shift
+    # +/- 10ms from the measured best score; note that this will use fewer
+    # than the full +/- 10 range for the curve fit if the measured score
+    # (which is used as the center of the fit) is within 10ms of the edge of
+    # the +/- 50ms candidate range.
+    i = dists.index(best_corr_dist)
     candidates = candidates[i-20:i+21]
     dists = dists[i-20:i+21]
     a,b,c = numpy.polyfit(candidates, dists, 2)
     exact_best_shift = -b/(2*a)
     if abs(best_shift - exact_best_shift) > 2.0 or a <= 0 or c <= 0:
         print "Test failed; bad fit to time-shift curve"
+        print "best_shift %f, exact_best_shift %f, a %f, c %f" % (best_shift,
+                exact_best_shift, a, c)
         assert(0)
 
-    xfit = [x/10.0 for x in xrange(candidates[0]*10,candidates[-1]*10)]
+    xfit = numpy.arange(candidates[0], candidates[-1], 0.05).tolist()
     yfit = [a*x*x+b*x+c for x in xfit]
     fig = matplotlib.pyplot.figure()
     pylab.plot(candidates, dists, 'r', label="data")
@@ -263,13 +287,23 @@
         frame = (frame * 255.0).astype(numpy.uint8)
         gframes.append(cv2.cvtColor(frame, cv2.COLOR_RGB2GRAY))
     rots = []
+    ymin = H/2 - FEATURE_MARGIN
+    ymax = H/2 + FEATURE_MARGIN
     for i in range(1,len(gframes)):
         gframe0 = gframes[i-1]
         gframe1 = gframes[i]
         p0 = cv2.goodFeaturesToTrack(gframe0, mask=None, **FEATURE_PARAMS)
-        p1,st,_ = cv2.calcOpticalFlowPyrLK(gframe0, gframe1, p0, None,
+        # p0's shape is N * 1 * 2
+        mask = (p0[:,0,1] >= ymin) & (p0[:,0,1] <= ymax)
+        p0_filtered = p0[mask]
+        if len(p0_filtered) < MIN_FEATURE_PTS:
+            print "Not enough feature points in frame", i
+            print "Need at least %d features, got %d" % (
+                    MIN_FEATURE_PTS, len(p0_filtered))
+            assert(0)
+        p1,st,_ = cv2.calcOpticalFlowPyrLK(gframe0, gframe1, p0_filtered, None,
                 **LK_PARAMS)
-        tform = procrustes_rotation(p0[st==1], p1[st==1])
+        tform = procrustes_rotation(p0_filtered[st==1], p1[st==1])
         if facing == FACING_BACK:
             rot = -math.atan2(tform[0, 1], tform[0, 0])
         elif facing == FACING_FRONT:
@@ -282,7 +316,7 @@
             # Save a debug visualization of the features that are being
             # tracked in the first frame.
             frame = frames[i]
-            for x,y in p0[st==1]:
+            for x,y in p0_filtered[st==1]:
                 cv2.circle(frame, (x,y), 3, (100,100,255), -1)
             its.image.write_image(frame, "%s_features.png"%(NAME))
     return numpy.array(rots)
@@ -342,8 +376,8 @@
         print "Starting sensor event collection"
         cam.start_sensor_events()
 
-        # Sleep a few seconds for gyro events to stabilize.
-        time.sleep(2)
+        # Sleep a while for gyro events to stabilize.
+        time.sleep(0.5)
 
         # TODO: Ensure that OIS is disabled; set to DISABLE and wait some time.
 
@@ -354,7 +388,7 @@
             assert(0)
 
         fmt = {"format":"yuv", "width":W, "height":H}
-        s,e,_,_,_ = cam.do_3a(get_results=True)
+        s,e,_,_,_ = cam.do_3a(get_results=True, do_af=False)
         req = its.objects.manual_capture_request(s, e)
         print "Capturing %dx%d with sens. %d, exp. time %.1fms" % (
                 W, H, s, e*NSEC_TO_MSEC)
@@ -363,6 +397,7 @@
         # Get the gyro events.
         print "Reading out sensor events"
         gyro = cam.get_sensor_events()["gyro"]
+        print "Number of gyro samples", len(gyro)
 
         # Combine the events into a single structure.
         print "Dumping event data"
diff --git a/apps/CameraITS/tools/load_scene.py b/apps/CameraITS/tools/load_scene.py
new file mode 100644
index 0000000..4e245f4
--- /dev/null
+++ b/apps/CameraITS/tools/load_scene.py
@@ -0,0 +1,61 @@
+# 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 re
+import subprocess
+import sys
+import time
+
+
+def main():
+    """Load charts on device and display."""
+    camera_id = -1
+    scene = None
+    for s in sys.argv[1:]:
+        if s[:6] == 'scene=' and len(s) > 6:
+            scene = s[6:]
+        elif s[:7] == 'screen=' and len(s) > 7:
+            screen_id = s[7:]
+
+    cmd = ('adb -s %s shell am force-stop com.google.android.apps.docs' %
+           screen_id)
+    subprocess.Popen(cmd.split())
+
+    if not scene:
+        print 'Error: need to specify which scene to load'
+        assert False
+
+    if not screen_id:
+        print 'Error: need to specify screen serial'
+        assert False
+
+    remote_scene_file = '/sdcard/Download/%s.pdf' % scene
+    local_scene_file = os.path.join(os.environ['CAMERA_ITS_TOP'], 'tests',
+                                    scene, scene+'.pdf')
+    print 'Loading %s on %s' % (remote_scene_file, screen_id)
+    cmd = 'adb -s %s push %s /mnt%s' % (screen_id, local_scene_file,
+                                        remote_scene_file)
+    subprocess.Popen(cmd.split())
+    time.sleep(1)  # wait-for-device doesn't always seem to work...
+    # The intent require PDF viewing app be installed on device.
+    # Also the first time such app is opened it might request some permission,
+    # so it's  better to grant those permissions before using this script
+    cmd = ("adb -s %s wait-for-device shell am start -d 'file://%s'"
+           " -a android.intent.action.VIEW" % (screen_id,
+                                               remote_scene_file))
+    subprocess.Popen(cmd.split())
+
+if __name__ == '__main__':
+    main()
diff --git a/apps/CameraITS/tools/run_all_tests.py b/apps/CameraITS/tools/run_all_tests.py
index 52780eb..c6ff548 100644
--- a/apps/CameraITS/tools/run_all_tests.py
+++ b/apps/CameraITS/tools/run_all_tests.py
@@ -20,12 +20,25 @@
 import sys
 import textwrap
 import its.device
+from its.device import ItsSession
 
 def main():
     """Run all the automated tests, saving intermediate files, and producing
     a summary/report of the results.
 
     Script should be run from the top-level CameraITS directory.
+
+    Command line Arguments:
+        camera: the camera(s) to be tested. Use comma to separate multiple
+                camera Ids. Ex: "camera=0,1" or "camera=1"
+        scenes: the test scene(s) to be executed. Use comma to separate multiple
+                scenes. Ex: "scenes=scene0,scene1" or "scenes=0,1,sensor_fusion"
+                (sceneX can be abbreviated by X where X is a integer)
+        chart: [Experimental] another android device served as test chart
+               display. When this argument presents, change of test scene will
+               be handled automatically. Note that this argument requires
+               special physical/hardware setup to work and may not work on
+               all android devices.
     """
 
     SKIP_RET_CODE = 101
@@ -49,10 +62,9 @@
         "sensor_fusion":[]
     }
 
-    # Get all the scene0 and scene1 tests, which can be run using the same
-    # physical setup.
-    scenes = ["scene0", "scene1", "scene2", "scene3", "scene4", "scene5",
-              "sensor_fusion"]
+    all_scenes = ["scene0", "scene1", "scene2", "scene3", "scene4", "scene5"]
+
+    auto_scenes = ["scene0", "scene1", "scene2", "scene3", "scene4"]
 
     scene_req = {
         "scene0" : None,
@@ -75,12 +87,54 @@
     scene_extra_args = {
         "scene5" : ["doAF=False"]
     }
-    tests = []
-    for d in scenes:
-        tests += [(d,s[:-3],os.path.join("tests", d, s))
-                  for s in os.listdir(os.path.join("tests",d))
-                  if s[-3:] == ".py"]
-    tests.sort()
+
+    camera_ids = []
+    scenes = []
+    chart_host_id = None
+    for s in sys.argv[1:]:
+        if s[:7] == "camera=" and len(s) > 7:
+            camera_ids = s[7:].split(',')
+        elif s[:7] == "scenes=" and len(s) > 7:
+            scenes = s[7:].split(',')
+        elif s[:6] == 'chart=' and len(s) > 6:
+            chart_host_id = s[6:]
+
+    auto_scene_switch = chart_host_id is not None
+
+    # Run through all scenes if user does not supply one
+    possible_scenes = auto_scenes if auto_scene_switch else all_scenes
+    if not scenes:
+        scenes = possible_scenes
+    else:
+        # Validate user input scene names
+        valid_scenes = True
+        temp_scenes = []
+        for s in scenes:
+            if s in possible_scenes:
+                temp_scenes.append(s)
+            else:
+                try:
+                    # Try replace "X" to "sceneX"
+                    scene_num = int(s)
+                    scene_str = "scene" + s
+                    if scene_str not in possible_scenes:
+                        valid_scenes = False
+                        break
+                    temp_scenes.append(scene_str)
+                except ValueError:
+                    valid_scenes = False
+                    break
+
+        if not valid_scenes:
+            print "Unknown scene specifiied:", s
+            assert(False)
+        scenes = temp_scenes
+
+    # Initialize test results
+    results = {}
+    result_key = ItsSession.RESULT_KEY
+    for s in all_scenes:
+        results[s] = {result_key: ItsSession.RESULT_NOT_EXECUTED}
 
     # Make output directories to hold the generated files.
     topdir = tempfile.mkdtemp()
@@ -90,11 +144,6 @@
     device_id_arg = "device=" + device_id
     print "Testing device " + device_id
 
-    camera_ids = []
-    for s in sys.argv[1:]:
-        if s[:7] == "camera=" and len(s) > 7:
-            camera_ids.append(s[7:])
-
     # user doesn't specify camera id, run through all cameras
     if not camera_ids:
         camera_ids_path = os.path.join(topdir, "camera_ids.txt")
@@ -108,7 +157,15 @@
             for line in f:
                 camera_ids.append(line.replace('\n', ''))
 
-    print "Running ITS on the following cameras:", camera_ids
+    print "Running ITS on camera: %s, scene %s" % (camera_ids, scenes)
+
+    if auto_scene_switch:
+        print 'Waking up chart screen: ', chart_host_id
+        screen_id_arg = ('screen=%s' % chart_host_id)
+        cmd = ['python', os.path.join(os.environ['CAMERA_ITS_TOP'], 'tools',
+                                      'wake_up_screen.py'), screen_id_arg]
+        retcode = subprocess.call(cmd)
+        assert retcode == 0
 
     for camera_id in camera_ids:
         # Loop capturing images until user confirm test scene is correct
@@ -119,85 +176,117 @@
         for d in scenes:
             os.mkdir(os.path.join(topdir, camera_id, d))
 
-        print "Start running ITS on camera: ", camera_id
-        # Run each test, capturing stdout and stderr.
-        summary = "ITS test result summary for camera " + camera_id + "\n"
-        numpass = 0
-        numskip = 0
-        numnotmandatedfail = 0
-        numfail = 0
+        for scene in scenes:
+            tests = [(s[:-3],os.path.join("tests", scene, s))
+                     for s in os.listdir(os.path.join("tests",scene))
+                     if s[-3:] == ".py" and s[:4] == "test"]
+            tests.sort()
 
-        prev_scene = ""
-        for (scene,testname,testpath) in tests:
-            if scene != prev_scene and scene_req[scene] != None:
+            summary = "Cam" + camera_id + " " + scene + "\n"
+            numpass = 0
+            numskip = 0
+            num_not_mandated_fail = 0
+            numfail = 0
+            if scene_req[scene] != None:
                 out_path = os.path.join(topdir, camera_id, scene+".jpg")
                 out_arg = "out=" + out_path
-                scene_arg = "scene=" + scene_req[scene]
-                extra_args = scene_extra_args.get(scene, [])
-                cmd = ['python',
-                        os.path.join(os.getcwd(),"tools/validate_scene.py"),
-                        camera_id_arg, out_arg, scene_arg, device_id_arg] + \
-                        extra_args
+                if auto_scene_switch:
+                    scene_arg = "scene=" + scene
+                    cmd = ['python',
+                           os.path.join(os.getcwd(), 'tools/load_scene.py'),
+                           scene_arg, screen_id_arg]
+                else:
+                    scene_arg = "scene=" + scene_req[scene]
+                    extra_args = scene_extra_args.get(scene, [])
+                    cmd = ['python',
+                            os.path.join(os.getcwd(),"tools/validate_scene.py"),
+                            camera_id_arg, out_arg,
+                            scene_arg, device_id_arg] + extra_args
                 retcode = subprocess.call(cmd,cwd=topdir)
                 assert(retcode == 0)
-                print "Start running tests for", scene
-            prev_scene = scene
-            cmd = ['python', os.path.join(os.getcwd(),testpath)] + \
-                  sys.argv[1:] + [camera_id_arg]
-            outdir = os.path.join(topdir,camera_id,scene)
-            outpath = os.path.join(outdir,testname+"_stdout.txt")
-            errpath = os.path.join(outdir,testname+"_stderr.txt")
-            t0 = time.time()
-            with open(outpath,"w") as fout, open(errpath,"w") as ferr:
-                retcode = subprocess.call(cmd,stderr=ferr,stdout=fout,cwd=outdir)
-            t1 = time.time()
+            print "Start running ITS on camera %s, %s" % (camera_id, scene)
 
-            if retcode == 0:
-                retstr = "PASS "
-                numpass += 1
-            elif retcode == SKIP_RET_CODE:
-                retstr = "SKIP "
-                numskip += 1
-            elif retcode != 0 and testname in NOT_YET_MANDATED[scene]:
-                retstr = "FAIL*"
-                numnotmandatedfail += 1
+            # Run each test, capturing stdout and stderr.
+            for (testname,testpath) in tests:
+                if auto_scene_switch:
+                    # Send an input event to keep the screen not dimmed.
+                    # Since we are not using camera of chart screen, FOCUS event
+                    # should does nothing but keep the screen from dimming.
+                    # The "sleep after x minutes of inactivity" display setting
+                    # determines how long this command can keep screen bright.
+                    # Setting it to something like 30 minutes should be enough.
+                    cmd = ('adb -s %s shell input keyevent FOCUS'
+                           % chart_host_id)
+                    subprocess.call(cmd.split())
+                cmd = ['python', os.path.join(os.getcwd(),testpath)] + \
+                      sys.argv[1:] + [camera_id_arg]
+                outdir = os.path.join(topdir,camera_id,scene)
+                outpath = os.path.join(outdir,testname+"_stdout.txt")
+                errpath = os.path.join(outdir,testname+"_stderr.txt")
+                t0 = time.time()
+                with open(outpath,"w") as fout, open(errpath,"w") as ferr:
+                    retcode = subprocess.call(
+                            cmd,stderr=ferr,stdout=fout,cwd=outdir)
+                t1 = time.time()
+
+                test_failed = False
+                if retcode == 0:
+                    retstr = "PASS "
+                    numpass += 1
+                elif retcode == SKIP_RET_CODE:
+                    retstr = "SKIP "
+                    numskip += 1
+                elif retcode != 0 and testname in NOT_YET_MANDATED[scene]:
+                    retstr = "FAIL*"
+                    num_not_mandated_fail += 1
+                else:
+                    retstr = "FAIL "
+                    numfail += 1
+                    test_failed = True
+
+                msg = "%s %s/%s [%.1fs]" % (retstr, scene, testname, t1-t0)
+                print msg
+                msg_short = "%s %s [%.1fs]" % (retstr, testname, t1-t0)
+                if test_failed:
+                    summary += msg_short + "\n"
+
+            if numskip > 0:
+                skipstr = ", %d test%s skipped" % (
+                        numskip, "s" if numskip > 1 else "")
             else:
-                retstr = "FAIL "
-                numfail += 1
+                skipstr = ""
 
-            msg = "%s %s/%s [%.1fs]" % (retstr, scene, testname, t1-t0)
-            print msg
-            summary += msg + "\n"
-            if retcode != 0 and retcode != SKIP_RET_CODE:
-                # Dump the stderr if the test fails
-                with open (errpath, "r") as error_file:
-                    errors = error_file.read()
-                    summary += errors + "\n"
+            test_result = "\n%d / %d tests passed (%.1f%%)%s" % (
+                    numpass + num_not_mandated_fail, len(tests) - numskip,
+                    100.0 * float(numpass + num_not_mandated_fail) /
+                            (len(tests) - numskip)
+                            if len(tests) != numskip else 100.0,
+                    skipstr)
+            print test_result
 
-        if numskip > 0:
-            skipstr = ", %d test%s skipped" % (numskip, "s" if numskip > 1 else "")
-        else:
-            skipstr = ""
+            if num_not_mandated_fail > 0:
+                msg = "(*) tests are not yet mandated"
+                print msg
 
-        test_result = "\n%d / %d tests passed (%.1f%%)%s" % (
-                numpass + numnotmandatedfail, len(tests) - numskip,
-                100.0 * float(numpass + numnotmandatedfail) / (len(tests) - numskip)
-                    if len(tests) != numskip else 100.0,
-                skipstr)
-        print test_result
-        summary += test_result + "\n"
+            summary_path = os.path.join(topdir, camera_id, scene, "summary.txt")
+            with open(summary_path, "w") as f:
+                f.write(summary)
 
-        if numnotmandatedfail > 0:
-            msg = "(*) tests are not yet mandated"
-            print msg
-            summary += msg + "\n"
+            passed = numfail == 0
+            results[scene][result_key] = (ItsSession.RESULT_PASS if passed
+                    else ItsSession.RESULT_FAIL)
+            results[scene][ItsSession.SUMMARY_KEY] = summary_path
 
-        result = numfail == 0
         print "Reporting ITS result to CtsVerifier"
-        summary_path = os.path.join(topdir, camera_id, "summary.txt")
-        with open(summary_path, "w") as f:
-            f.write(summary)
-        its.device.report_result(device_id, camera_id, result, summary_path)
+        its.device.report_result(device_id, camera_id, results)
+
+    if auto_scene_switch:
+        print 'Shutting down chart screen: ', chart_host_id
+        screen_id_arg = ('screen=%s' % chart_host_id)
+        cmd = ['python', os.path.join(os.environ['CAMERA_ITS_TOP'], 'tools',
+                                      'turn_off_screen.py'), screen_id_arg]
+        retcode = subprocess.call(cmd)
+        assert retcode == 0
 
     print "ITS tests finished. Please go back to CtsVerifier and proceed"
 
diff --git a/apps/CameraITS/tools/turn_off_screen.py b/apps/CameraITS/tools/turn_off_screen.py
new file mode 100644
index 0000000..4163ab4
--- /dev/null
+++ b/apps/CameraITS/tools/turn_off_screen.py
@@ -0,0 +1,42 @@
+# 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 re
+import subprocess
+import sys
+
+
+def main():
+    """Put screen to sleep."""
+    screen_id = ''
+    for s in sys.argv[1:]:
+        if s[:7] == 'screen=' and len(s) > 7:
+            screen_id = s[7:]
+
+    if not screen_id:
+        print 'Error: need to specify screen serial'
+        assert False
+
+    cmd = ('adb -s %s shell dumpsys power | egrep "Display Power"'
+           % screen_id)
+    process = subprocess.Popen(cmd.split(), stdout=subprocess.PIPE)
+    cmd_ret = process.stdout.read()
+    screen_state = re.split(r'[s|=]', cmd_ret)[-1]
+    if screen_state == 'OFF\n':
+        print 'Screen OFF. Turning ON.'
+    else:
+        wakeup = ('adb -s %s shell input keyevent POWER' % screen_id)
+        subprocess.Popen(wakeup.split())
+if __name__ == '__main__':
+    main()
diff --git a/apps/CameraITS/tools/wake_up_screen.py b/apps/CameraITS/tools/wake_up_screen.py
new file mode 100644
index 0000000..68a974a
--- /dev/null
+++ b/apps/CameraITS/tools/wake_up_screen.py
@@ -0,0 +1,60 @@
+# Copyright 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.
+
+import re
+import subprocess
+import sys
+import time
+
+DISPLAY_LEVEL = 96  # [0:255] Depends on tablet model. Adjust for best result.
+DISPLAY_WAIT = 0.5  # seconds. Screen commands take time to have effect
+
+
+def main():
+    """Power up and unlock screen as needed."""
+    screen_id = None
+    for s in sys.argv[1:]:
+        if s[:7] == 'screen=' and len(s) > 7:
+            screen_id = s[7:]
+
+    if not screen_id:
+        print 'Error: need to specify screen serial'
+        assert False
+
+    # turn on screen if necessary and unlock
+    cmd = ('adb -s %s shell dumpsys display | egrep "mScreenState"'
+           % screen_id)
+    process = subprocess.Popen(cmd.split(), stdout=subprocess.PIPE)
+    cmd_ret = process.stdout.read()
+    screen_state = re.split(r'[s|=]', cmd_ret)[-1]
+    if 'OFF' in screen_state:
+        print 'Screen OFF. Turning ON.'
+        wakeup = ('adb -s %s shell input keyevent POWER' % screen_id)
+        subprocess.Popen(wakeup.split())
+        time.sleep(DISPLAY_WAIT)
+    unlock = ('adb -s %s wait-for-device shell wm dismiss-keyguard'
+              % screen_id)
+    subprocess.Popen(unlock.split())
+    time.sleep(DISPLAY_WAIT)
+
+    # set brightness
+    print 'Tablet display brightness set to %d' % DISPLAY_LEVEL
+    bright = ('adb -s %s shell settings put system screen_brightness %d'
+              % (screen_id, DISPLAY_LEVEL))
+    subprocess.Popen(bright.split())
+    time.sleep(DISPLAY_WAIT)
+
+
+if __name__ == '__main__':
+    main()
diff --git a/apps/CtsVerifier/AndroidManifest.xml b/apps/CtsVerifier/AndroidManifest.xml
index 824ab67..6f29335 100644
--- a/apps/CtsVerifier/AndroidManifest.xml
+++ b/apps/CtsVerifier/AndroidManifest.xml
@@ -1246,6 +1246,18 @@
             </intent-filter>
         </service>
 
+        <activity android:name=".notifications.ShortcutThrottlingResetActivity"
+            android:label="@string/shortcut_reset_test"
+            android:configChanges="mcc|mnc|locale|touchscreen|keyboard|keyboardHidden|navigation|screenLayout|fontScale|uiMode|orientation|screenSize|smallestScreenSize|layoutDirection">
+            <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_notifications" />
+            <meta-data android:name="test_excluded_features"
+                android:value="android.hardware.type.watch:android.software.leanback" />
+        </activity>
+
         <activity android:name=".vr.VrListenerVerifierActivity"
             android:label="@string/vr_tests">
             <intent-filter>
diff --git a/apps/CtsVerifier/res/layout/its_main.xml b/apps/CtsVerifier/res/layout/its_main.xml
index 2f5eade..26f15bb 100644
--- a/apps/CtsVerifier/res/layout/its_main.xml
+++ b/apps/CtsVerifier/res/layout/its_main.xml
@@ -21,4 +21,14 @@
 
     <include layout="@layout/pass_fail_buttons" />
 
+    <TextView
+        android:id="@+id/its_progress"
+        android:layout_width="match_parent"
+        android:layout_height="wrap_content"
+        android:gravity="bottom"
+        android:padding="2dp"
+        android:scrollbars = "vertical"
+        android:text="@string/its_test_progress"
+        android:textSize="16sp" />
+
 </LinearLayout>
diff --git a/apps/CtsVerifier/res/values/strings.xml b/apps/CtsVerifier/res/values/strings.xml
index 4ef177e..d6b0f47 100644
--- a/apps/CtsVerifier/res/values/strings.xml
+++ b/apps/CtsVerifier/res/values/strings.xml
@@ -991,6 +991,10 @@
     </string>
     <string name="its_test_passed">All Camera ITS tests passed.  Pass button enabled!</string>
     <string name="its_test_failed">Some Camera ITS tests failed.</string>
+    <string name="its_version_mismatch">
+        CtsVerifier and ITS script version mismatch. Please update CtsVerifier and ITS script.
+    </string>
+    <string name="its_test_progress">ITS test progress will be shown here.</string>
 
     <!-- Strings for the Camera Flashlight test activity -->
     <string name="camera_flashlight_test">Camera Flashlight</string>
@@ -1200,6 +1204,16 @@
     <string name="package_priority_default">Find \"%s\" in the \"Notifications\" settings panel, and disallow it to Override Do Not Disturb.</string>
     <string name="package_priority_user_order">Check that ranker respects user priorities.</string>
 
+    <string name="shortcut_reset_test">Shortcut Reset Rate-limiting Test</string>
+    <string name="shortcut_reset_info">This test checks that when an inline-reply happens, ShortcutManager\'s rate-limiting
+        is reset.</string>
+    <string name="shortcut_reset_bot">Verifying that the CTS Robot helper package is installed.</string>
+    <string name="shortcut_reset_start">Showing the notification.</string>
+    <string name="shortcut_reset_prompt_inline_reply">Open the notification shade,
+        find the \"Shortcut Reset Rate-limiting Test\" notification, type something in the inline-reply field and
+        press the send button.</string>
+    <string name="shortcut_reset_check_result">Check ShortcutManager rate-limit has been reset.</string>
+
     <string name="attention_test">Notification Attention Management Test</string>
     <string name="attention_info">This test checks that the NotificationManagerService is
         respecting user preferences about notification ranking and filtering.
diff --git a/apps/CtsVerifier/src/com/android/cts/verifier/audio/HifiUltrasoundSpeakerTestActivity.java b/apps/CtsVerifier/src/com/android/cts/verifier/audio/HifiUltrasoundSpeakerTestActivity.java
index dc81e19..f9f5823 100644
--- a/apps/CtsVerifier/src/com/android/cts/verifier/audio/HifiUltrasoundSpeakerTestActivity.java
+++ b/apps/CtsVerifier/src/com/android/cts/verifier/audio/HifiUltrasoundSpeakerTestActivity.java
@@ -41,6 +41,9 @@
 import com.androidplot.xy.XYSeries;
 import com.androidplot.xy.*;
 
+import com.android.compatibility.common.util.CddTest;
+
+@CddTest(requirement="7.8.3")
 public class HifiUltrasoundSpeakerTestActivity extends PassFailButtons.Activity {
 
   public enum Status {
diff --git a/apps/CtsVerifier/src/com/android/cts/verifier/camera/its/ItsService.java b/apps/CtsVerifier/src/com/android/cts/verifier/camera/its/ItsService.java
index 6f54821..8710096 100644
--- a/apps/CtsVerifier/src/com/android/cts/verifier/camera/its/ItsService.java
+++ b/apps/CtsVerifier/src/com/android/cts/verifier/camera/its/ItsService.java
@@ -197,6 +197,8 @@
     private volatile LinkedList<MySensorEvent> mEvents = null;
     private volatile Object mEventLock = new Object();
     private volatile boolean mEventsEnabled = false;
+    private HandlerThread mSensorThread = null;
+    private Handler mSensorHandler = null;
 
     public interface CaptureCallback {
         void onCaptureAvailable(Image capture);
@@ -228,9 +230,15 @@
             mAccelSensor = mSensorManager.getDefaultSensor(Sensor.TYPE_ACCELEROMETER);
             mMagSensor = mSensorManager.getDefaultSensor(Sensor.TYPE_MAGNETIC_FIELD);
             mGyroSensor = mSensorManager.getDefaultSensor(Sensor.TYPE_GYROSCOPE);
-            mSensorManager.registerListener(this, mAccelSensor, SensorManager.SENSOR_DELAY_FASTEST);
-            mSensorManager.registerListener(this, mMagSensor, SensorManager.SENSOR_DELAY_FASTEST);
-            mSensorManager.registerListener(this, mGyroSensor, SensorManager.SENSOR_DELAY_FASTEST);
+            mSensorThread = new HandlerThread("SensorThread");
+            mSensorThread.start();
+            mSensorHandler = new Handler(mSensorThread.getLooper());
+            mSensorManager.registerListener(this, mAccelSensor,
+                    SensorManager.SENSOR_DELAY_NORMAL, mSensorHandler);
+            mSensorManager.registerListener(this, mMagSensor,
+                    SensorManager.SENSOR_DELAY_NORMAL, mSensorHandler);
+            mSensorManager.registerListener(this, mGyroSensor,
+                    /*200hz*/5000, mSensorHandler);
 
             // Get a handle to the system vibrator.
             mVibrator = (Vibrator)getSystemService(Context.VIBRATOR_SERVICE);
@@ -291,6 +299,10 @@
                 mSaveThreads[i] = null;
             }
         }
+        if (mSensorThread != null) {
+            mSensorThread.quitSafely();
+            mSensorThread = null;
+        }
         if (mResultThread != null) {
             mResultThread.quitSafely();
             mResultThread = null;
@@ -404,6 +416,7 @@
         // (called on different threads) will need to send data back to the host script.
 
         public Socket mOpenSocket = null;
+        private Thread mThread = null;
 
         public SocketWriteRunnable(Socket openSocket) {
             mOpenSocket = openSocket;
@@ -421,6 +434,7 @@
                     ByteBuffer b = mSocketWriteQueue.take();
                     synchronized(mSocketWriteDrainLock) {
                         if (mOpenSocket == null) {
+                            Logt.e(TAG, "No open socket connection!");
                             continue;
                         }
                         if (b.hasArray()) {
@@ -442,14 +456,26 @@
                     }
                 } catch (IOException e) {
                     Logt.e(TAG, "Error writing to socket", e);
+                    mOpenSocket = null;
                     break;
                 } catch (java.lang.InterruptedException e) {
                     Logt.e(TAG, "Error writing to socket (interrupted)", e);
+                    mOpenSocket = null;
                     break;
                 }
             }
             Logt.i(TAG, "Socket writer thread terminated");
         }
+
+        public synchronized void checkAndStartThread() {
+            if (mThread == null || mThread.getState() == Thread.State.TERMINATED) {
+                mThread = new Thread(this);
+            }
+            if (mThread.getState() == Thread.State.NEW) {
+                mThread.start();
+            }
+        }
+
     }
 
     class SocketRunnable implements Runnable {
@@ -475,7 +501,6 @@
 
             // Create a new thread to handle writes to this socket.
             mSocketWriteRunnable = new SocketWriteRunnable(null);
-            (new Thread(mSocketWriteRunnable)).start();
 
             while (!mThreadExitFlag) {
                 // Receive the socket-open request from the host.
@@ -489,6 +514,7 @@
                     mSocketWriteQueue.clear();
                     mInflightImageSizes.clear();
                     mSocketWriteRunnable.setOpenSocket(mOpenSocket);
+                    mSocketWriteRunnable.checkAndStartThread();
                     Logt.i(TAG, "Socket connected");
                 } catch (IOException e) {
                     Logt.e(TAG, "Socket open error: ", e);
@@ -1277,6 +1303,8 @@
 
             // Initiate the captures.
             long maxExpTimeNs = -1;
+            List<CaptureRequest> requestList =
+                    new ArrayList<>(requests.size());
             for (int i = 0; i < requests.size(); i++) {
                 CaptureRequest.Builder req = requests.get(i);
                 // For DNG captures, need the LSC map to be available.
@@ -1291,8 +1319,9 @@
                 for (int j = 0; j < numCaptureSurfaces; j++) {
                     req.addTarget(mOutputImageReaders[j].getSurface());
                 }
-                mSession.capture(req.build(), mCaptureResultListener, mResultHandler);
+                requestList.add(req.build());
             }
+            mSession.captureBurst(requestList, mCaptureResultListener, mResultHandler);
 
             long timeout = TIMEOUT_CALLBACK * 1000;
             if (maxExpTimeNs > 0) {
@@ -1478,6 +1507,11 @@
     }
 
     @Override
+    public final void onAccuracyChanged(Sensor sensor, int accuracy) {
+        Logt.i(TAG, "Sensor " + sensor.getName() + " accuracy changed to " + accuracy);
+    }
+
+    @Override
     public final void onSensorChanged(SensorEvent event) {
         synchronized(mEventLock) {
             if (mEventsEnabled) {
@@ -1492,10 +1526,6 @@
         }
     }
 
-    @Override
-    public final void onAccuracyChanged(Sensor sensor, int accuracy) {
-    }
-
     private final CaptureCallback mCaptureCallback = new CaptureCallback() {
         @Override
         public void onCaptureAvailable(Image capture) {
diff --git a/apps/CtsVerifier/src/com/android/cts/verifier/camera/its/ItsTestActivity.java b/apps/CtsVerifier/src/com/android/cts/verifier/camera/its/ItsTestActivity.java
index 0c39a9e..a8affcd 100644
--- a/apps/CtsVerifier/src/com/android/cts/verifier/camera/its/ItsTestActivity.java
+++ b/apps/CtsVerifier/src/com/android/cts/verifier/camera/its/ItsTestActivity.java
@@ -25,15 +25,21 @@
 import android.hardware.camera2.CameraCharacteristics;
 import android.hardware.camera2.CameraManager;
 import android.os.Bundle;
+import android.text.method.ScrollingMovementMethod;
 import android.util.Log;
 import android.view.WindowManager;
+import android.widget.TextView;
 import android.widget.Toast;
 
 import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Comparator;
 import java.util.HashSet;
 import java.util.HashMap;
-import java.util.Arrays;
+import java.util.Iterator;
 import java.util.List;
+import java.util.Set;
+import java.util.TreeSet;
 import java.io.BufferedReader;
 import java.io.FileReader;
 import java.io.FileNotFoundException;
@@ -44,6 +50,8 @@
 import com.android.cts.verifier.PassFailButtons;
 import com.android.cts.verifier.R;
 
+import org.json.JSONArray;
+import org.json.JSONObject;
 
 /**
  * Test for Camera features that require that the camera be aimed at a specific test scene.
@@ -53,48 +61,216 @@
 public class ItsTestActivity extends PassFailButtons.Activity {
     private static final String TAG = "ItsTestActivity";
     private static final String EXTRA_CAMERA_ID = "camera.its.extra.CAMERA_ID";
-    private static final String EXTRA_SUCCESS = "camera.its.extra.SUCCESS";
-    private static final String EXTRA_SUMMARY = "camera.its.extra.SUMMARY";
+    private static final String EXTRA_RESULTS = "camera.its.extra.RESULTS";
+    private static final String EXTRA_VERSION = "camera.its.extra.VERSION";
+    private static final String CURRENT_VERSION = "1.0";
     private static final String ACTION_ITS_RESULT =
             "com.android.cts.verifier.camera.its.ACTION_ITS_RESULT";
 
-    class SuccessReceiver extends BroadcastReceiver {
+    private static final String RESULT_PASS = "PASS";
+    private static final String RESULT_FAIL = "FAIL";
+    private static final String RESULT_NOT_EXECUTED = "NOT_EXECUTED";
+    private static final Set<String> RESULT_VALUES = new HashSet<String>(
+            Arrays.asList(new String[] {RESULT_PASS, RESULT_FAIL, RESULT_NOT_EXECUTED}));
+    private static final int MAX_SUMMARY_LEN = 200;
+
+    private final ResultReceiver mResultsReceiver = new ResultReceiver();
+
+    // Initialized in onCreate
+    ArrayList<String> mNonLegacyCameraIds = null;
+
+    // TODO: cache the following in saved bundle
+    private Set<ResultKey> mAllScenes = null;
+    // (camera, scene) -> (pass, fail)
+    private final HashMap<ResultKey, Boolean> mExecutedScenes = new HashMap<>();
+    // map camera id to ITS summary report path
+    private final HashMap<ResultKey, String> mSummaryMap = new HashMap<>();
+
+    final class ResultKey {
+        public final String cameraId;
+        public final String sceneId;
+
+        public ResultKey(String cameraId, String sceneId) {
+            this.cameraId = cameraId;
+            this.sceneId = sceneId;
+        }
+
+        @Override
+        public boolean equals(final Object o) {
+            if (o == null) return false;
+            if (this == o) return true;
+            if (o instanceof ResultKey) {
+                final ResultKey other = (ResultKey) o;
+                return cameraId.equals(other.cameraId) && sceneId.equals(other.sceneId);
+            }
+            return false;
+        }
+
+        @Override
+        public int hashCode() {
+            int h = cameraId.hashCode();
+            h = ((h << 5) - h) ^ sceneId.hashCode();
+            return h;
+        }
+    }
+
+    private final Comparator<ResultKey> mComparator = new Comparator<ResultKey>() {
+        @Override
+        public int compare(ResultKey k1, ResultKey k2) {
+            if (k1.cameraId.equals(k2.cameraId))
+                return k1.sceneId.compareTo(k2.sceneId);
+            return k1.cameraId.compareTo(k2.cameraId);
+        }
+    };
+
+    class ResultReceiver extends BroadcastReceiver {
         @Override
         public void onReceive(Context context, Intent intent) {
             Log.i(TAG, "Received result for Camera ITS tests");
             if (ACTION_ITS_RESULT.equals(intent.getAction())) {
+                String version = intent.getStringExtra(EXTRA_VERSION);
+                if (version == null || !version.equals(CURRENT_VERSION)) {
+                    Log.e(TAG, "Its result version mismatch: expect " + CURRENT_VERSION +
+                            ", got " + ((version == null) ? "null" : version));
+                    ItsTestActivity.this.showToast(R.string.its_version_mismatch);
+                    return;
+                }
+
                 String cameraId = intent.getStringExtra(EXTRA_CAMERA_ID);
-                String result = intent.getStringExtra(EXTRA_SUCCESS);
-                String summaryPath = intent.getStringExtra(EXTRA_SUMMARY);
+                String results = intent.getStringExtra(EXTRA_RESULTS);
+                if (cameraId == null || results == null) {
+                    Log.e(TAG, "cameraId = " + ((cameraId == null) ? "null" : cameraId) +
+                            ", results = " + ((results == null) ? "null" : results));
+                    return;
+                }
+
                 if (!mNonLegacyCameraIds.contains(cameraId)) {
                     Log.e(TAG, "Unknown camera id " + cameraId + " reported to ITS");
                     return;
                 }
 
-                Log.i(TAG, "ITS summary path is: " + summaryPath);
-                mSummaryMap.put(cameraId, summaryPath);
-                // Create summary report
-                if (mSummaryMap.keySet().containsAll(mNonLegacyCameraIds)) {
+                try {
+                    /* Sample JSON results string
+                    {
+                       "scene0":{
+                          "result":"PASS",
+                          "summary":"/sdcard/cam0_scene0.txt"
+                       },
+                       "scene1":{
+                          "result":"NOT_EXECUTED"
+                       },
+                       "scene2":{
+                          "result":"FAIL",
+                          "summary":"/sdcard/cam0_scene2.txt"
+                       }
+                    }
+                    */
+                    JSONObject jsonResults = new JSONObject(results);
+                    Set<String> scenes = new HashSet<>();
+                    Iterator<String> keys = jsonResults.keys();
+                    while (keys.hasNext()) {
+                        scenes.add(keys.next());
+                    }
+                    boolean newScenes = false;
+                    if (mAllScenes == null) {
+                        mAllScenes = new TreeSet<>(mComparator);
+                        newScenes = true;
+                    } else { // See if scene lists changed
+                        for (String scene : scenes) {
+                            if (!mAllScenes.contains(new ResultKey(cameraId, scene))) {
+                                // Scene list changed. Cleanup previous test results
+                                newScenes = true;
+                                break;
+                            }
+                        }
+                        for (ResultKey k : mAllScenes) {
+                            if (!scenes.contains(k.sceneId)) {
+                                newScenes = true;
+                                break;
+                            }
+                        }
+                    }
+                    if (newScenes) {
+                        mExecutedScenes.clear();
+                        mAllScenes.clear();
+                        for (String scene : scenes) {
+                            for (String c : mNonLegacyCameraIds) {
+                                mAllScenes.add(new ResultKey(c, scene));
+                            }
+                        }
+                    }
+
+                    // Update test execution results
+                    for (String scene : scenes) {
+                        JSONObject sceneResult = jsonResults.getJSONObject(scene);
+                        String result = sceneResult.getString("result");
+                        if (result == null) {
+                            Log.e(TAG, "Result for " + scene + " is null");
+                            return;
+                        }
+                        Log.i(TAG, "ITS camera" + cameraId + " " + scene + ": result:" + result);
+                        if (!RESULT_VALUES.contains(result)) {
+                            Log.e(TAG, "Unknown result for " + scene + ": " + result);
+                            return;
+                        }
+                        ResultKey key = new ResultKey(cameraId, scene);
+                        if (result.equals(RESULT_PASS) || result.equals(RESULT_FAIL)) {
+                            boolean pass = result.equals(RESULT_PASS);
+                            mExecutedScenes.put(key, pass);
+                            String summary = sceneResult.optString("summary");
+                            if (!summary.equals("")) {
+                                mSummaryMap.put(key, summary);
+                            }
+                        } // do nothing for NOT_EXECUTED scenes
+                    }
+                } catch (org.json.JSONException e) {
+                    Log.e(TAG, "Error reading json result string:" + results , e);
+                    return;
+                }
+
+                // Set summary if all scenes reported
+                if (mSummaryMap.keySet().containsAll(mAllScenes)) {
                     StringBuilder summary = new StringBuilder();
-                    for (String id : mNonLegacyCameraIds) {
-                        String path = mSummaryMap.get(id);
+                    for (String path : mSummaryMap.values()) {
                         appendFileContentToSummary(summary, path);
                     }
+                    if (summary.length() > MAX_SUMMARY_LEN) {
+                        Log.w(TAG, "ITS summary report too long: len: " + summary.length());
+                    }
                     ItsTestActivity.this.getReportLog().setSummary(
                             summary.toString(), 1.0, ResultType.NEUTRAL, ResultUnit.NONE);
                 }
-                boolean pass = result.equals("True");
-                if(pass) {
-                    Log.i(TAG, "Received Camera " + cameraId + " ITS SUCCESS from host.");
-                    mITSPassedCameraIds.add(cameraId);
-                    if (mNonLegacyCameraIds != null && mNonLegacyCameraIds.size() != 0 &&
-                            mITSPassedCameraIds.containsAll(mNonLegacyCameraIds)) {
-                        ItsTestActivity.this.showToast(R.string.its_test_passed);
-                        ItsTestActivity.this.getPassButton().setEnabled(true);
+
+                // Display current progress
+                StringBuilder progress = new StringBuilder();
+                for (ResultKey k : mAllScenes) {
+                    String status = RESULT_NOT_EXECUTED;
+                    if (mExecutedScenes.containsKey(k)) {
+                        status = mExecutedScenes.get(k) ? RESULT_PASS : RESULT_FAIL;
                     }
+                    progress.append(String.format("Cam %s, %s: %s\n",
+                            k.cameraId, k.sceneId, status));
+                }
+                TextView progressView = (TextView) findViewById(R.id.its_progress);
+                progressView.setMovementMethod(new ScrollingMovementMethod());
+                progressView.setText(progress.toString());
+
+
+                // Enable pass button if all scenes pass
+                boolean allScenesPassed = true;
+                for (ResultKey k : mAllScenes) {
+                    Boolean pass = mExecutedScenes.get(k);
+                    if (pass == null || pass == false) {
+                        allScenesPassed = false;
+                        break;
+                    }
+                }
+                if (allScenesPassed) {
+                    // Enable pass button
+                    ItsTestActivity.this.showToast(R.string.its_test_passed);
+                    ItsTestActivity.this.getPassButton().setEnabled(true);
                 } else {
-                    Log.i(TAG, "Received Camera " + cameraId + " ITS FAILURE from host.");
-                    ItsTestActivity.this.showToast(R.string.its_test_failed);
+                    ItsTestActivity.this.getPassButton().setEnabled(false);
                 }
             }
         }
@@ -127,12 +303,6 @@
         }
     }
 
-    private final SuccessReceiver mSuccessReceiver = new SuccessReceiver();
-    private final HashSet<String> mITSPassedCameraIds = new HashSet<>();
-    // map camera id to ITS summary report path
-    private final HashMap<String, String> mSummaryMap = new HashMap<>();
-    ArrayList<String> mNonLegacyCameraIds = null;
-
     @Override
     protected void onCreate(Bundle savedInstanceState) {
         super.onCreate(savedInstanceState);
@@ -168,6 +338,7 @@
                     "Received error from camera service while checking device capabilities: "
                             + e, Toast.LENGTH_SHORT).show();
         }
+
         getPassButton().setEnabled(false);
     }
 
@@ -180,7 +351,7 @@
         } else {
             Log.d(TAG, "register ITS result receiver");
             IntentFilter filter = new IntentFilter(ACTION_ITS_RESULT);
-            registerReceiver(mSuccessReceiver, filter);
+            registerReceiver(mResultsReceiver, filter);
         }
     }
 
@@ -188,7 +359,7 @@
     protected void onPause() {
         super.onPause();
         Log.d(TAG, "unregister ITS result receiver");
-        unregisterReceiver(mSuccessReceiver);
+        unregisterReceiver(mResultsReceiver);
     }
 
     @Override
diff --git a/apps/CtsVerifier/src/com/android/cts/verifier/notifications/PackagePriorityVerifierActivity.java b/apps/CtsVerifier/src/com/android/cts/verifier/notifications/PackagePriorityVerifierActivity.java
index 5870981..b40ecc6 100644
--- a/apps/CtsVerifier/src/com/android/cts/verifier/notifications/PackagePriorityVerifierActivity.java
+++ b/apps/CtsVerifier/src/com/android/cts/verifier/notifications/PackagePriorityVerifierActivity.java
@@ -39,7 +39,7 @@
     private static final String ACTION_CANCEL = "com.android.cts.robot.ACTION_CANCEL";
     private static final String EXTRA_ID = "ID";
     private static final String EXTRA_NOTIFICATION = "NOTIFICATION";
-    private static final String NOTIFICATION_BOT_PACKAGE = "com.android.cts.robot";
+    static final String NOTIFICATION_BOT_PACKAGE = "com.android.cts.robot";
     private CharSequence mAppLabel;
 
     @Override
diff --git a/apps/CtsVerifier/src/com/android/cts/verifier/notifications/ShortcutThrottlingResetActivity.java b/apps/CtsVerifier/src/com/android/cts/verifier/notifications/ShortcutThrottlingResetActivity.java
new file mode 100644
index 0000000..313ebfa
--- /dev/null
+++ b/apps/CtsVerifier/src/com/android/cts/verifier/notifications/ShortcutThrottlingResetActivity.java
@@ -0,0 +1,207 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.cts.verifier.notifications;
+
+import android.content.BroadcastReceiver;
+import android.content.Context;
+import android.content.Intent;
+import android.content.IntentFilter;
+import android.content.pm.PackageManager;
+import android.os.Bundle;
+import android.util.Log;
+import android.view.View;
+import android.view.ViewGroup;
+
+import com.android.cts.verifier.R;
+
+import java.security.SecureRandom;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.concurrent.atomic.AtomicReference;
+
+/**
+ * Test to make sure, when an inline reply happens, the shortcut manager rate-limiting must
+ * be reset.
+ *
+ * We use the "BOT" apk here, because rate-limiting will be reset when an app shows an activity
+ * too -- so as long as this (or any) test activity is shown, CTS verifier won't be rate-limited.
+ */
+public class ShortcutThrottlingResetActivity extends InteractiveVerifierActivity {
+    private static final String TAG = "ShortcutThrottlingReset";
+
+    private static final String NOTIFICATION_BOT_PACKAGE
+            = PackagePriorityVerifierActivity.NOTIFICATION_BOT_PACKAGE;
+
+    private static final String ACTION_RESET_SETUP_NOTIFICATION =
+            "com.android.cts.robot.ACTION_RESET_SETUP_NOTIFICATION";
+
+    private static final String EXTRA_NOTIFICATION_TITLE = "EXTRA_NOTIFICATION_TITLE";
+    private static final String EXTRA_RESET_REPLY_PACKAGE = "EXTRA_RESET_REPLY_PACKAGE";
+    private static final String EXTRA_RESET_REPLY_ACTION = "EXTRA_RESET_REPLY_ACTION";
+    private static final String EXTRA_RESET_REPLY_ERROR = "EXTRA_RESET_REPLY_ERROR";
+
+    private static final String SUCCESS = "**SUCCESS**";
+
+    private String mReplyAction;
+
+    private final AtomicReference<Intent> mReplyIntent = new AtomicReference<>(null);
+
+    @Override
+    int getTitleResource() {
+        return R.string.shortcut_reset_test;
+    }
+
+    @Override
+    int getInstructionsResource() {
+        return R.string.shortcut_reset_info;
+    }
+
+    @Override
+    protected void onCreate(Bundle savedState) {
+        super.onCreate(savedState);
+
+        // Generate an unique reply action and register the reply receiver.
+        mReplyAction = "reply_" + new SecureRandom().nextLong();
+        final IntentFilter replyFilter = new IntentFilter(mReplyAction);
+        registerReceiver(mReplyReceiver, replyFilter);
+    }
+
+    @Override
+    protected void onDestroy() {
+        unregisterReceiver(mReplyReceiver);
+        super.onDestroy();
+    }
+
+    @Override
+    protected List<InteractiveTestCase> createTestItems() {
+        List<InteractiveTestCase> tests = new ArrayList<>();
+        tests.add(new CheckForBot());
+        tests.add(new SetupNotification());
+        tests.add(new WaitForTestReply());
+        tests.add(new CheckResult());
+        return tests;
+    }
+
+
+    private final BroadcastReceiver mReplyReceiver = new BroadcastReceiver() {
+        @Override
+        public void onReceive(Context context, Intent intent) {
+            Log.i(TAG, "Received reply from robot helper: " + intent);
+            mReplyIntent.set(intent);
+        }
+    };
+
+
+    /** Make sure the helper package is installed. */
+    protected class CheckForBot extends InteractiveTestCase {
+        @Override
+        View inflate(ViewGroup parent) {
+            return createAutoItem(parent, R.string.shortcut_reset_bot);
+        }
+
+        @Override
+        void test() {
+            PackageManager pm = mContext.getPackageManager();
+            try {
+                pm.getPackageInfo(NOTIFICATION_BOT_PACKAGE, 0);
+                status = PASS;
+            } catch (PackageManager.NameNotFoundException e) {
+                status = FAIL;
+                logFail("You must install the CTS Robot helper, aka " + NOTIFICATION_BOT_PACKAGE);
+            }
+            next();
+        }
+    }
+
+    /**
+     * Request the bot apk to show the notification.
+     */
+    protected class SetupNotification extends InteractiveTestCase {
+        @Override
+        View inflate(ViewGroup parent) {
+            return createAutoItem(parent, R.string.shortcut_reset_start);
+        }
+
+        @Override
+        void test() {
+            final Intent intent = new Intent(ACTION_RESET_SETUP_NOTIFICATION);
+            intent.setPackage(NOTIFICATION_BOT_PACKAGE);
+
+            intent.putExtra(EXTRA_NOTIFICATION_TITLE, getResources().getString(getTitleResource()));
+
+            intent.putExtra(EXTRA_RESET_REPLY_PACKAGE, getPackageName());
+            intent.putExtra(EXTRA_RESET_REPLY_ACTION, mReplyAction);
+
+            intent.addFlags(Intent.FLAG_INCLUDE_STOPPED_PACKAGES);
+            sendBroadcast(intent);
+            status = PASS;
+            next();
+        }
+    }
+
+    /**
+     * Let the human tester do an inline reply, and wait for the reply broadcast from the bot apk.
+     */
+    protected class WaitForTestReply extends InteractiveTestCase {
+        @Override
+        View inflate(ViewGroup parent) {
+            return createAutoItem(parent, R.string.shortcut_reset_prompt_inline_reply);
+        }
+
+        @Override
+        void test() {
+            final Intent replyIntent = mReplyIntent.get();
+            if (replyIntent == null) {
+                // Reply not received yet.
+                status = RETEST;
+                delay();
+                return;
+            }
+            status = PASS;
+            next();
+        }
+    }
+
+    /**
+     * Check the reply from the bot apk.
+     */
+    protected class CheckResult extends InteractiveTestCase {
+        @Override
+        View inflate(ViewGroup parent) {
+            return createAutoItem(parent, R.string.shortcut_reset_check_result);
+        }
+
+        @Override
+        void test() {
+            final Intent replyIntent = mReplyIntent.get();
+            if (replyIntent == null) {
+                logFail("Internal error, replyIntent shouldn't be null here.");
+                status = FAIL;
+                return;
+            }
+            final String error = replyIntent.getStringExtra(EXTRA_RESET_REPLY_ERROR);
+            if (SUCCESS.equals(error)) {
+                status = PASS;
+                next();
+                return;
+            }
+            logFail("Test failed. Error message=" + error);
+            status = FAIL;
+            next();
+        }
+    }
+}
diff --git a/apps/NotificationBot/AndroidManifest.xml b/apps/NotificationBot/AndroidManifest.xml
index b63791f..0388cbc 100644
--- a/apps/NotificationBot/AndroidManifest.xml
+++ b/apps/NotificationBot/AndroidManifest.xml
@@ -39,10 +39,10 @@
             <intent-filter>
                 <action android:name="com.android.cts.robot.ACTION_POST" />
                 <action android:name="com.android.cts.robot.ACTION_CANCEL" />
+                <action android:name="com.android.cts.robot.ACTION_RESET_SETUP_NOTIFICATION" />
+                <action android:name="com.android.cts.robot.ACTION_INLINE_REPLY" />
             </intent-filter>
         </receiver>
-
-
     </application>
 
 </manifest>
diff --git a/apps/NotificationBot/src/com/android/cts/robot/NotificationBot.java b/apps/NotificationBot/src/com/android/cts/robot/NotificationBot.java
index 2aa5f41..746b840 100644
--- a/apps/NotificationBot/src/com/android/cts/robot/NotificationBot.java
+++ b/apps/NotificationBot/src/com/android/cts/robot/NotificationBot.java
@@ -15,14 +15,23 @@
  */
 package com.android.cts.robot;
 
-import android.app.Activity;
 import android.app.Notification;
+import android.app.Notification.Action;
 import android.app.NotificationManager;
+import android.app.PendingIntent;
+import android.app.RemoteInput;
 import android.content.BroadcastReceiver;
+import android.content.ComponentName;
 import android.content.Context;
 import android.content.Intent;
+import android.content.pm.ShortcutInfo;
+import android.content.pm.ShortcutManager;
+import android.os.SystemClock;
 import android.util.Log;
 
+import java.util.ArrayList;
+import java.util.List;
+
 
 public class NotificationBot extends BroadcastReceiver {
     private static final String TAG = "NotificationBot";
@@ -30,6 +39,21 @@
     private static final String EXTRA_NOTIFICATION = "NOTIFICATION";
     private static final String ACTION_POST = "com.android.cts.robot.ACTION_POST";
     private static final String ACTION_CANCEL = "com.android.cts.robot.ACTION_CANCEL";
+    private static final String ACTION_RESET_SETUP_NOTIFICATION =
+            "com.android.cts.robot.ACTION_RESET_SETUP_NOTIFICATION";
+
+    private static final String ACTION_INLINE_REPLY =
+            "com.android.cts.robot.ACTION_INLINE_REPLY";
+
+    private static final String EXTRA_RESET_REPLY_PACKAGE = "EXTRA_RESET_REPLY_PACKAGE";
+    private static final String EXTRA_RESET_REPLY_ACTION = "EXTRA_RESET_REPLY_ACTION";
+    private static final String EXTRA_NOTIFICATION_TITLE = "EXTRA_NOTIFICATION_TITLE";
+
+    private static final String EXTRA_RESET_REPLY_ERROR = "EXTRA_RESET_REPLY_ERROR";
+
+    private static final String EXTRA_RESET_REQUEST_INTENT = "EXTRA_RESET_REQUEST_INTENT";
+
+    private static final String SUCCESS = "**SUCCESS**";
 
     @Override
     public void onReceive(Context context, Intent intent) {
@@ -60,8 +84,112 @@
                     (NotificationManager) context.getSystemService(Context.NOTIFICATION_SERVICE);
             noMa.cancel(id);
 
+        } else if (ACTION_RESET_SETUP_NOTIFICATION.equals(intent.getAction())) {
+            testShortcutResetSetupNotification(context, intent);
+
+        } else if (ACTION_INLINE_REPLY.equals(intent.getAction())) {
+            testShortcutResetInlineReplyReceived(context, intent);
+
         } else {
             Log.i(TAG, "received unexpected action: " + intent.getAction());
         }
     }
+
+    /**
+     * Test start request from CTS verifier.  Show a notification with inline reply, which will
+     * trigger {@link #testShortcutResetInlineReplyReceived}.
+     */
+    private static void testShortcutResetSetupNotification(Context context, Intent intent) {
+        final NotificationManager nm = context.getSystemService(NotificationManager.class);
+        nm.cancelAll();
+
+        final ShortcutManager sm = context.getSystemService(ShortcutManager.class);
+
+        final List<ShortcutInfo> EMPTY_LIST = new ArrayList<>();
+
+        long timeout = SystemClock.elapsedRealtime() + 10 * 1000;
+
+        // First, make sure this package is throttled.
+        while (!sm.isRateLimitingActive()) {
+            sm.setDynamicShortcuts(EMPTY_LIST);
+            try {
+                Thread.sleep(0);
+            } catch (InterruptedException e) {
+            }
+            if (SystemClock.elapsedRealtime() >= timeout) {
+                sendShortcutResetReply(context, intent,
+                        "ShortcutMager rate-limiting not activated.");
+                return;
+            }
+        }
+
+        // Show a notification with inline reply.
+        final PendingIntent receiverIntent =
+                PendingIntent.getBroadcast(context, 0,
+                        new Intent(ACTION_INLINE_REPLY)
+                                .setComponent(new ComponentName(context, NotificationBot.class))
+                                .putExtra(EXTRA_RESET_REQUEST_INTENT, intent),
+                        PendingIntent.FLAG_UPDATE_CURRENT);
+        final RemoteInput ri = new RemoteInput.Builder("result")
+                .setLabel("Type something here and press send button").build();
+
+        final Notification.Builder nb = new Notification.Builder(context)
+                .setContentTitle(intent.getStringExtra(EXTRA_NOTIFICATION_TITLE))
+                .setSmallIcon(android.R.drawable.ic_popup_sync)
+                .addAction(new Action.Builder(0,
+                        "Type something here and press send button", receiverIntent)
+                        .addRemoteInput(ri)
+                        .build());
+        context.getSystemService(NotificationManager.class).notify(0, nb.build());
+    }
+
+    /**
+     * Invoked when the inline reply from {@link #testShortcutResetSetupNotification} is performed.
+     *
+     * Check the shortcut manager rate-limiting state, and post the reply to CTS verifier.
+     */
+    private static void testShortcutResetInlineReplyReceived(Context context, Intent intent) {
+        Log.i(TAG, "Inline reply received");
+
+        final NotificationManager nm = context.getSystemService(NotificationManager.class);
+        nm.cancelAll();
+
+        // Close notification shade.
+        context.sendBroadcast(new Intent(Intent.ACTION_CLOSE_SYSTEM_DIALOGS));
+
+        // Check if rate-limiting has been reset.
+        final ShortcutManager sm = context.getSystemService(ShortcutManager.class);
+
+        String error;
+        final boolean success = !sm.isRateLimitingActive();
+        if (success) {
+            error = SUCCESS;
+        } else {
+            error = "Inline reply received, but ShortcutManager rate-limiting is still active.";
+        }
+
+        // Send back the result.
+        sendShortcutResetReply(context,
+                intent.getParcelableExtra(EXTRA_RESET_REQUEST_INTENT), error);
+    }
+
+    /**
+     * Reply an error message, or {@link #SUCCESS} for success, to CTS verifier for shortcut manager
+     * reset rate-limiting test.
+
+     * @param requestIntent original intent sent from the verifier to
+     *     {@link #testShortcutResetSetupNotification}.
+     * @param error error message, or {@link #SUCCESS} if success.
+     */
+    private static void sendShortcutResetReply(Context context, Intent requestIntent, String error) {
+        final Intent replyIntent = new Intent();
+        replyIntent.setAction(requestIntent.getStringExtra(EXTRA_RESET_REPLY_ACTION));
+        replyIntent.putExtra(EXTRA_RESET_REPLY_ERROR, error);
+
+        if (error != null) {
+            Log.e(TAG, error);
+        }
+
+        context.sendBroadcast(replyIntent);
+    }
 }
diff --git a/common/device-side/device-info/src/com/android/compatibility/common/deviceinfo/DeviceInfo.java b/common/device-side/device-info/src/com/android/compatibility/common/deviceinfo/DeviceInfo.java
index 8aa3380..750e45f 100644
--- a/common/device-side/device-info/src/com/android/compatibility/common/deviceinfo/DeviceInfo.java
+++ b/common/device-side/device-info/src/com/android/compatibility/common/deviceinfo/DeviceInfo.java
@@ -87,7 +87,7 @@
                     mResultCode = ResultCode.COMPLETED;
                 }
             } catch (Exception e) {
-                failed("Could not collect device info: " + e.getMessage());
+                failed("Could not collect device info", e);
             }
         }
 
@@ -136,6 +136,12 @@
         Log.e(LOG_TAG, message, exception);
     }
 
+    private void failed(String message, Throwable exception) {
+        mResultCode = ResultCode.FAILED;
+        mErrorMessage = message;
+        Log.e(LOG_TAG, message, exception);
+    }
+
     private void failed(String message) {
         mResultCode = ResultCode.FAILED;
         mErrorMessage = message;
diff --git a/common/device-side/util/src/com/android/compatibility/common/util/DeviceInfoStore.java b/common/device-side/util/src/com/android/compatibility/common/util/DeviceInfoStore.java
index 9f1bda5..735b955 100644
--- a/common/device-side/util/src/com/android/compatibility/common/util/DeviceInfoStore.java
+++ b/common/device-side/util/src/com/android/compatibility/common/util/DeviceInfoStore.java
@@ -135,9 +135,7 @@
      */
     @Override
     public void addResult(String name, float value) throws IOException {
-        checkName(name);
-        mJsonWriter.name(name);
-        mJsonWriter.value(value);
+        addResult(name, (double) value);
     }
 
     /**
@@ -146,8 +144,12 @@
     @Override
     public void addResult(String name, double value) throws IOException {
         checkName(name);
-        mJsonWriter.name(name);
-        mJsonWriter.value(value);
+        if (isDoubleNaNOrInfinite(value)) {
+            return;
+        } else {
+            mJsonWriter.name(name);
+            mJsonWriter.value(value);
+        }
     }
 
     /**
@@ -203,13 +205,11 @@
      */
     @Override
     public void addArrayResult(String name, float[] array) throws IOException {
-        checkName(name);
-        mJsonWriter.name(name);
-        mJsonWriter.beginArray();
-        for (float value : checkArray(array)) {
-            mJsonWriter.value(value);
+        double[] doubleArray = new double[array.length];
+        for (int i = 0; i < array.length; i++) {
+            doubleArray[i] = array[i];
         }
-        mJsonWriter.endArray();
+        addArrayResult(name, doubleArray);
     }
 
     /**
@@ -221,6 +221,9 @@
         mJsonWriter.name(name);
         mJsonWriter.beginArray();
         for (double value : checkArray(array)) {
+            if (isDoubleNaNOrInfinite(value)) {
+                continue;
+            }
             mJsonWriter.value(value);
         }
         mJsonWriter.endArray();
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 8c8bc8a5..b8ab089 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
@@ -132,6 +132,13 @@
                 addSubPlan(flatArgs);
             }
         }, "a(?:dd)?", "s(?:ubplan)?", null);
+        trie.put(new Runnable() {
+            @Override
+            public void run() {
+                printLine(String.format("Android %s %s (%s)", SuiteInfo.FULLNAME,
+                        SuiteInfo.VERSION, SuiteInfo.BUILD_NUMBER));
+            }
+        }, "version"); // override tradefed 'version' command to print test suite name and version
 
         // find existing help for 'LIST_PATTERN' commands, and append these commands help
         String listHelp = commandHelp.get(LIST_PATTERN);
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 804c13f..9bd8647 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
@@ -370,13 +370,23 @@
             }
 
             // Set values and run preconditions
+            boolean isPrepared = true; // whether the device has been successfully prepared
             for (int i = 0; i < moduleCount; i++) {
                 IModuleDef module = modules.get(i);
                 module.setBuild(mBuildHelper.getBuildInfo());
                 module.setDevice(mDevice);
                 module.setPreparerWhitelist(mPreparerWhitelist);
-                module.prepare(mSkipPreconditions);
+                isPrepared &= (module.prepare(mSkipPreconditions));
             }
+            mModuleRepo.setPrepared(isPrepared);
+
+            if (!mModuleRepo.isPrepared()) {
+                CLog.logAndDisplay(LogLevel.ERROR,
+                        "Incorrect preparation detected, exiting test run from %s",
+                        mDevice.getSerialNumber());
+                return;
+            }
+
             // Run the tests
             for (int i = 0; i < moduleCount; i++) {
                 IModuleDef module = modules.get(i);
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 45ef438..14427ca 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
@@ -66,7 +66,8 @@
 
     /**
      * Runs the module's precondition checks and setup tasks.
+     * @return whether preparation succeeded.
      */
-    void prepare(boolean skipPrep) throws DeviceNotAvailableException;
+    boolean prepare(boolean skipPrep) throws DeviceNotAvailableException;
 
 }
diff --git a/common/host-side/tradefed/src/com/android/compatibility/common/tradefed/testtype/IModuleRepo.java b/common/host-side/tradefed/src/com/android/compatibility/common/tradefed/testtype/IModuleRepo.java
index 7985230..540373b 100644
--- a/common/host-side/tradefed/src/com/android/compatibility/common/tradefed/testtype/IModuleRepo.java
+++ b/common/host-side/tradefed/src/com/android/compatibility/common/tradefed/testtype/IModuleRepo.java
@@ -29,6 +29,16 @@
 public interface IModuleRepo {
 
     /**
+     * @return true after each shard has prepared successfully.
+     */
+    boolean isPrepared();
+
+    /**
+     * Indicates to the repo whether a shard is prepared to run.
+     */
+    void setPrepared(boolean isPrepared);
+
+    /**
      * @return true if this repository has been initialized.
      */
     boolean isInitialized();
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 0844a6e..2c3f96b 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
@@ -268,7 +268,7 @@
      * {@inheritDoc}
      */
     @Override
-    public void prepare(boolean skipPrep) throws DeviceNotAvailableException {
+    public boolean prepare(boolean skipPrep) throws DeviceNotAvailableException {
         for (ITargetPreparer preparer : mPreconditions) {
             CLog.d("Preparer: %s", preparer.getClass().getSimpleName());
             if (preparer instanceof IAbiReceiver) {
@@ -282,13 +282,16 @@
                 // This should only happen for flashing new build
                 CLog.e("Unexpected BuildError from precondition: %s",
                         preparer.getClass().getCanonicalName());
+                return false;
             } catch (TargetSetupError e) {
                 // log precondition class then rethrow & let caller handle
                 CLog.e("TargetSetupError in precondition: %s",
                         preparer.getClass().getCanonicalName());
-                throw new RuntimeException(e);
+                e.printStackTrace();
+                return false;
             }
         }
+        return true;
     }
 
     private void setOption(Object target, String option, String value) {
diff --git a/common/host-side/tradefed/src/com/android/compatibility/common/tradefed/testtype/ModuleRepo.java b/common/host-side/tradefed/src/com/android/compatibility/common/tradefed/testtype/ModuleRepo.java
index a1a1925..3d1b234 100644
--- a/common/host-side/tradefed/src/com/android/compatibility/common/tradefed/testtype/ModuleRepo.java
+++ b/common/host-side/tradefed/src/com/android/compatibility/common/tradefed/testtype/ModuleRepo.java
@@ -47,6 +47,7 @@
 import java.util.Map;
 import java.util.Map.Entry;
 import java.util.Set;
+import java.util.concurrent.CountDownLatch;
 import java.util.concurrent.TimeUnit;
 
 /**
@@ -78,6 +79,10 @@
     private IConfigurationFactory mConfigFactory = ConfigurationFactory.getInstance();
 
     private volatile boolean mInitialized = false;
+    // Whether the modules in this repo are ready to run on their assigned devices.
+    // True until explicitly set false in setPrepared().
+    private volatile boolean mPrepared = true;
+    private CountDownLatch mPreparedLatch;
 
     // Holds all the small tests waiting to be run.
     private List<IModuleDef> mSmallModules = new ArrayList<>();
@@ -197,6 +202,28 @@
      * {@inheritDoc}
      */
     @Override
+    public boolean isPrepared() {
+        try {
+            mPreparedLatch.await();
+        } catch (InterruptedException e) {
+            return false;
+        }
+        return mPrepared;
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    @Override
+    public void setPrepared(boolean isPrepared) {
+        mPrepared &= isPrepared;
+        mPreparedLatch.countDown();
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    @Override
     public boolean isInitialized() {
         return mInitialized;
     }
@@ -214,6 +241,7 @@
                 includeFilters, excludeFilters);
         mInitialized = true;
         mShards = shards;
+        mPreparedLatch = new CountDownLatch(shards);
         for (String line : deviceTokens) {
             String[] parts = line.split(":");
             if (parts.length == 2) {
diff --git a/common/host-side/tradefed/tests/src/com/android/compatibility/common/tradefed/testtype/ModuleRepoTest.java b/common/host-side/tradefed/tests/src/com/android/compatibility/common/tradefed/testtype/ModuleRepoTest.java
index 078557d..5a0ebed 100644
--- a/common/host-side/tradefed/tests/src/com/android/compatibility/common/tradefed/testtype/ModuleRepoTest.java
+++ b/common/host-side/tradefed/tests/src/com/android/compatibility/common/tradefed/testtype/ModuleRepoTest.java
@@ -262,6 +262,26 @@
         assertArrayEquals(EXPECTED_MODULE_IDS, mRepo.getModuleIds());
     }
 
+    public void testIsPrepared() {
+        mRepo.initialize(3, mTestsDir, ABIS, DEVICE_TOKENS, TEST_ARGS, MODULE_ARGS, INCLUDES,
+                EXCLUDES, mBuild);
+        assertTrue("Should be initialized", mRepo.isInitialized());
+        mRepo.setPrepared(true);
+        mRepo.setPrepared(true);
+        mRepo.setPrepared(true); // each shard should call setPrepared() once
+        assertTrue(mRepo.isPrepared());
+    }
+
+    public void testIsNotPrepared() {
+        mRepo.initialize(3, mTestsDir, ABIS, DEVICE_TOKENS, TEST_ARGS, MODULE_ARGS, INCLUDES,
+                EXCLUDES, mBuild);
+        assertTrue("Should be initialized", mRepo.isInitialized());
+        mRepo.setPrepared(true);
+        mRepo.setPrepared(false); // mRepo should return false for setPrepared() after third call
+        mRepo.setPrepared(true);
+        assertFalse(mRepo.isPrepared());
+    }
+
     private void assertArrayEquals(Object[] expected, Object[] actual) {
         assertEquals(Arrays.asList(expected), Arrays.asList(actual));
     }
diff --git a/common/util/src/com/android/compatibility/common/util/CddTest.java b/common/util/src/com/android/compatibility/common/util/CddTest.java
new file mode 100644
index 0000000..34ee663
--- /dev/null
+++ b/common/util/src/com/android/compatibility/common/util/CddTest.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 com.android.compatibility.common.util;
+
+import java.lang.annotation.ElementType;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.lang.annotation.Target;
+
+/**
+ * Marks the type of test with purpose of asserting CDD requirements.
+ */
+@Retention(RetentionPolicy.RUNTIME)
+@Target({ElementType.METHOD, ElementType.TYPE})
+public @interface CddTest {
+    String requirement();
+}
diff --git a/common/util/src/com/android/compatibility/common/util/InfoStore.java b/common/util/src/com/android/compatibility/common/util/InfoStore.java
index 6c5cc0d..b8014f7 100644
--- a/common/util/src/com/android/compatibility/common/util/InfoStore.java
+++ b/common/util/src/com/android/compatibility/common/util/InfoStore.java
@@ -189,4 +189,8 @@
         }
         return value;
     }
+
+    protected static boolean isDoubleNaNOrInfinite(Double value) {
+        return Double.isNaN(value) || Double.isInfinite(value);
+    }
 }
diff --git a/hostsidetests/appsecurity/src/android/appsecurity/cts/DirectBootHostTest.java b/hostsidetests/appsecurity/src/android/appsecurity/cts/DirectBootHostTest.java
index 4983119..ed673ba 100644
--- a/hostsidetests/appsecurity/src/android/appsecurity/cts/DirectBootHostTest.java
+++ b/hostsidetests/appsecurity/src/android/appsecurity/cts/DirectBootHostTest.java
@@ -173,7 +173,7 @@
                     getDevice().executeShellCommand("sm set-emulate-fbe false");
                     getDevice().waitForDeviceOnline();
                 } else {
-                    getDevice().nonBlockingReboot();
+                    getDevice().rebootUntilOnline();
                 }
                 waitForBootCompleted();
             }
diff --git a/hostsidetests/appsecurity/src/android/appsecurity/cts/DocumentsTestCase.java b/hostsidetests/appsecurity/src/android/appsecurity/cts/DocumentsTestCase.java
index 7bd42b5..009d81b 100644
--- a/hostsidetests/appsecurity/src/android/appsecurity/cts/DocumentsTestCase.java
+++ b/hostsidetests/appsecurity/src/android/appsecurity/cts/DocumentsTestCase.java
@@ -51,10 +51,7 @@
         assertNotNull(mAbi);
         assertNotNull(mCtsBuild);
 
-        getDevice().uninstallPackage(CLIENT_PKG);
-
-        assertNull(getDevice().installPackage(
-                MigrationHelper.getTestFile(mCtsBuild, CLIENT_APK), false));
+        reinstallClientPackage();
     }
 
     @Override
@@ -68,4 +65,11 @@
             throws DeviceNotAvailableException {
         Utils.runDeviceTests(getDevice(), packageName, testClassName, testMethodName);
     }
+
+    protected void reinstallClientPackage() throws Exception {
+        getDevice().uninstallPackage(CLIENT_PKG);
+
+        assertNull(getDevice().installPackage(
+                MigrationHelper.getTestFile(mCtsBuild, CLIENT_APK), false));
+    }
 }
diff --git a/hostsidetests/appsecurity/src/android/appsecurity/cts/ScopedDirectoryAccessTest.java b/hostsidetests/appsecurity/src/android/appsecurity/cts/ScopedDirectoryAccessTest.java
index ffc7597..889b20b 100644
--- a/hostsidetests/appsecurity/src/android/appsecurity/cts/ScopedDirectoryAccessTest.java
+++ b/hostsidetests/appsecurity/src/android/appsecurity/cts/ScopedDirectoryAccessTest.java
@@ -42,6 +42,14 @@
         runDeviceTests(CLIENT_PKG, ".ScopedDirectoryAccessClientTest", "testNotAskedAgain");
     }
 
+    public void testDeniesOnceForAllClearedWhenPackageRemoved() throws Exception {
+        runDeviceTests(CLIENT_PKG, ".ScopedDirectoryAccessClientTest",
+                "testRemovePackageStep1UserDenies");
+        reinstallClientPackage();
+        runDeviceTests(CLIENT_PKG, ".ScopedDirectoryAccessClientTest",
+                "testRemovePackageStep2UserAcceptsDoNotClear");
+    }
+
     public void testDeniesOnceButAllowsAskingAgain() throws Exception {
         runDeviceTests(CLIENT_PKG, ".ScopedDirectoryAccessClientTest",
                 "testDeniesOnceButAllowsAskingAgain");
diff --git a/hostsidetests/appsecurity/test-apps/DocumentClient/src/com/android/cts/documentclient/DocumentsClientTestCase.java b/hostsidetests/appsecurity/test-apps/DocumentClient/src/com/android/cts/documentclient/DocumentsClientTestCase.java
index e52af73..a26ec2d 100644
--- a/hostsidetests/appsecurity/test-apps/DocumentClient/src/com/android/cts/documentclient/DocumentsClientTestCase.java
+++ b/hostsidetests/appsecurity/test-apps/DocumentClient/src/com/android/cts/documentclient/DocumentsClientTestCase.java
@@ -150,7 +150,15 @@
         return result;
     }
 
+    /**
+     * Clears the DocumentsUI package data, unless test name ends on {@code DoNotClear}.
+     */
     protected void clearDocumentsUi() throws Exception {
+        final String testName = getName();
+        if (testName.endsWith("DoNotClear")) {
+            Log.d(TAG, "Not clearing DocumentsUI due to test name: " + testName);
+            return;
+        }
         executeShellCommand("pm clear com.android.documentsui");
     }
 }
diff --git a/hostsidetests/appsecurity/test-apps/DocumentClient/src/com/android/cts/documentclient/ScopedDirectoryAccessClientTest.java b/hostsidetests/appsecurity/test-apps/DocumentClient/src/com/android/cts/documentclient/ScopedDirectoryAccessClientTest.java
index a4d35fb..dec8769 100644
--- a/hostsidetests/appsecurity/test-apps/DocumentClient/src/com/android/cts/documentclient/ScopedDirectoryAccessClientTest.java
+++ b/hostsidetests/appsecurity/test-apps/DocumentClient/src/com/android/cts/documentclient/ScopedDirectoryAccessClientTest.java
@@ -117,9 +117,9 @@
         if (!supportedHardware()) return;
 
         for (StorageVolume volume : getVolumes()) {
-            userAcceptsOpenExternalDirectoryTest(volume, DIRECTORY_PICTURES);
+            userAcceptsTest(volume, DIRECTORY_PICTURES);
             if (!volume.isPrimary()) {
-                userAcceptsOpenExternalDirectoryTest(volume, DIRECTORY_ROOT);
+                userAcceptsTest(volume, DIRECTORY_ROOT);
             }
         }
     }
@@ -133,7 +133,7 @@
         if (!output.isEmpty()) {
             fail("Command '" + command + "' failed: '" + output + "'");
         }
-        userAcceptsOpenExternalDirectoryTest(getPrimaryVolume(), DIRECTORY_PICTURES);
+        userAcceptsTest(getPrimaryVolume(), DIRECTORY_PICTURES);
     }
 
     public void testNotAskedAgain() throws Exception {
@@ -141,7 +141,7 @@
 
         for (StorageVolume volume : getVolumes()) {
             final String volumeDesc = volume.getDescription(getInstrumentation().getContext());
-            final Uri grantedUri = userAcceptsOpenExternalDirectoryTest(volume, DIRECTORY_PICTURES);
+            final Uri grantedUri = userAcceptsTest(volume, DIRECTORY_PICTURES);
 
             // Calls it again - since the permission has been granted, it should return right
             // away, without popping up the permissions dialog.
@@ -151,7 +151,7 @@
             assertEquals(grantedUri, newData.getData());
 
             // Make sure other directories still require user permission.
-            final Uri grantedUri2 = userAcceptsOpenExternalDirectoryTest(volume, DIRECTORY_ALARMS);
+            final Uri grantedUri2 = userAcceptsTest(volume, DIRECTORY_ALARMS);
             assertNotEqual(grantedUri, grantedUri2);
         }
     }
@@ -162,7 +162,7 @@
         for (StorageVolume volume : getVolumes()) {
             if (volume.isPrimary()) continue;
             final String volumeDesc = volume.getDescription(getInstrumentation().getContext());
-            final Uri grantedRootUri = userAcceptsOpenExternalDirectoryTest(volume, DIRECTORY_ROOT);
+            final Uri grantedRootUri = userAcceptsTest(volume, DIRECTORY_ROOT);
 
             // Calls it again - since the permission has been granted, it should return right
             // away, without popping up the permissions dialog.
@@ -204,7 +204,7 @@
                 assertActivityFailed();
 
                 // Third time is a charm...
-                userAcceptsOpenExternalDirectoryTest(volume, dir);
+                userAcceptsTest(volume, dir);
             }
         }
     }
@@ -216,33 +216,48 @@
         for (StorageVolume volume : getVolumes()) {
             for (String dir : dirs) {
                 if (volume.isPrimary() && dir == DIRECTORY_ROOT) continue;
-                // Rejects the first attempt...
-                UiAlertDialog dialog = openExternalDirectoryValidPath(volume, dir);
-                dialog.assertDoNotAskAgainVisibility(false);
-                dialog.noButton.click();
-                assertActivityFailed();
-
-                // ...and the second, checking the box
-                dialog = openExternalDirectoryValidPath(volume, dir);
-                UiObject checkbox = dialog.assertDoNotAskAgainVisibility(true);
-                assertTrue("checkbox should not be checkable", checkbox.isCheckable());
-                assertFalse("checkbox should not be checked", checkbox.isChecked());
-                checkbox.click();
-                assertTrue("checkbox should be checked", checkbox.isChecked()); // Sanity check
-                assertFalse("allow button should be disabled", dialog.yesButton.isEnabled());
-
-                dialog.noButton.click();
-                assertActivityFailed();
-
-                // Third strike out...
-                sendOpenExternalDirectoryIntent(volume, dir);
-                assertActivityFailed();
+                deniesOnceForAllTest(volume, dir);
             }
         }
     }
 
-    private Uri userAcceptsOpenExternalDirectoryTest(StorageVolume volume, String directoryName)
-            throws Exception {
+    private void deniesOnceForAllTest(StorageVolume volume, String dir) throws Exception {
+        // Rejects the first attempt...
+        UiAlertDialog dialog = openExternalDirectoryValidPath(volume, dir);
+        dialog.assertDoNotAskAgainVisibility(false);
+        dialog.noButton.click();
+        assertActivityFailed();
+
+        // ...and the second, checking the box
+        dialog = openExternalDirectoryValidPath(volume, dir);
+        UiObject checkbox = dialog.assertDoNotAskAgainVisibility(true);
+        assertTrue("checkbox should not be checkable", checkbox.isCheckable());
+        assertFalse("checkbox should not be checked", checkbox.isChecked());
+        checkbox.click();
+        assertTrue("checkbox should be checked", checkbox.isChecked()); // Sanity check
+        assertFalse("allow button should be disabled", dialog.yesButton.isEnabled());
+
+        dialog.noButton.click();
+        assertActivityFailed();
+
+        // Third strike out...
+        sendOpenExternalDirectoryIntent(volume, dir);
+        assertActivityFailed();
+    }
+
+    public void testRemovePackageStep1UserDenies() throws Exception {
+        if (!supportedHardware()) return;
+
+        deniesOnceForAllTest(getPrimaryVolume(), DIRECTORY_NOTIFICATIONS);
+    }
+
+    public void testRemovePackageStep2UserAcceptsDoNotClear() throws Exception {
+        if (!supportedHardware()) return;
+
+        userAcceptsTest(getPrimaryVolume(), DIRECTORY_NOTIFICATIONS);
+    }
+
+    private Uri userAcceptsTest(StorageVolume volume, String directoryName) throws Exception {
         // Asserts dialog contain the proper message.
         final UiAlertDialog dialog = openExternalDirectoryValidPath(volume, directoryName);
         final String message = dialog.messageText.getText();
diff --git a/hostsidetests/appsecurity/test-apps/EncryptionApp/src/com/android/cts/encryptionapp/EncryptionAppTest.java b/hostsidetests/appsecurity/test-apps/EncryptionApp/src/com/android/cts/encryptionapp/EncryptionAppTest.java
index 531511c..f00592f 100644
--- a/hostsidetests/appsecurity/test-apps/EncryptionApp/src/com/android/cts/encryptionapp/EncryptionAppTest.java
+++ b/hostsidetests/appsecurity/test-apps/EncryptionApp/src/com/android/cts/encryptionapp/EncryptionAppTest.java
@@ -161,6 +161,14 @@
         view.click();
         mDevice.waitForIdle();
 
+        // Yes, we really want "none" if prompted again
+        view = new UiObject(new UiSelector()
+                .resourceId("com.android.settings:id/lock_none"));
+        if (view.waitForExists(TIMEOUT)) {
+            view.click();
+            mDevice.waitForIdle();
+        }
+
         // Yes, we really want to
         view = new UiObject(new UiSelector()
                 .resourceId("android:id/button1"));
diff --git a/hostsidetests/appsecurity/test-apps/ExternalStorageApp/src/com/android/cts/externalstorageapp/ExternalStorageTest.java b/hostsidetests/appsecurity/test-apps/ExternalStorageApp/src/com/android/cts/externalstorageapp/ExternalStorageTest.java
index 6030f1c..13b7bcb 100644
--- a/hostsidetests/appsecurity/test-apps/ExternalStorageApp/src/com/android/cts/externalstorageapp/ExternalStorageTest.java
+++ b/hostsidetests/appsecurity/test-apps/ExternalStorageApp/src/com/android/cts/externalstorageapp/ExternalStorageTest.java
@@ -128,8 +128,12 @@
                     getContext().getExternalFilesDir(Environment.DIRECTORY_DOWNLOADS), "meow");
 
             final long id = dm.enqueue(new Request(source).setDestinationUri(Uri.fromFile(target)));
-            receiver.waitForDownloadComplete(30 * DateUtils.SECOND_IN_MILLIS, id);
-            assertSuccessfulDownload(id, target);
+            try {
+                receiver.waitForDownloadComplete(30 * DateUtils.SECOND_IN_MILLIS, id);
+                assertSuccessfulDownload(id, target);
+            } finally {
+                dm.remove(id);
+            }
         } finally {
             mContext.unregisterReceiver(receiver);
         }
diff --git a/hostsidetests/devicepolicy/app/AccountCheck/Android.mk b/hostsidetests/devicepolicy/app/AccountCheck/Android.mk
new file mode 100644
index 0000000..41a41d0
--- /dev/null
+++ b/hostsidetests/devicepolicy/app/AccountCheck/Android.mk
@@ -0,0 +1,17 @@
+#
+# Copyright (C) 2016 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#      http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT 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/devicepolicy/app/AccountCheck/Auth/Android.mk b/hostsidetests/devicepolicy/app/AccountCheck/Auth/Android.mk
new file mode 100644
index 0000000..207b875
--- /dev/null
+++ b/hostsidetests/devicepolicy/app/AccountCheck/Auth/Android.mk
@@ -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.
+
+LOCAL_PATH:= $(call my-dir)
+
+include $(CLEAR_VARS)
+
+# Tag this module as a cts test artifact
+LOCAL_COMPATIBILITY_SUITE := cts
+
+LOCAL_PACKAGE_NAME := CtsAccountCheckAuthApp
+
+LOCAL_MODULE_TAGS := optional
+
+LOCAL_MODULE_PATH := $(TARGET_OUT_DATA_APPS)
+
+LOCAL_SRC_FILES := $(call all-java-files-under, src)
+
+LOCAL_STATIC_JAVA_LIBRARIES := android-support-v4 ctstestrunner ub-uiautomator android-support-test
+
+LOCAL_SDK_VERSION := current
+
+include $(BUILD_CTS_PACKAGE)
diff --git a/hostsidetests/devicepolicy/app/AccountCheck/Auth/AndroidManifest.xml b/hostsidetests/devicepolicy/app/AccountCheck/Auth/AndroidManifest.xml
new file mode 100644
index 0000000..1f488e5
--- /dev/null
+++ b/hostsidetests/devicepolicy/app/AccountCheck/Auth/AndroidManifest.xml
@@ -0,0 +1,45 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ -->
+
+<manifest xmlns:android="http://schemas.android.com/apk/res/android"
+    package="com.android.cts.devicepolicy.accountcheck.auth"
+    android:sharedUserId="com.android.cts.devicepolicy.accountcheck.uid">
+
+    <!-- GET_ACCOUNTS may stop working.  Targeting at 25 may prevent it. -->
+    <uses-sdk android:minSdkVersion="25" android:targetSdkVersion="25" />
+
+    <uses-permission android:name="android.permission.GET_ACCOUNTS" />
+    <uses-permission android:name="android.permission.AUTHENTICATE_ACCOUNTS" />
+    <uses-permission android:name="android.permission.MANAGE_ACCOUNTS" />
+
+    <application>
+        <service android:name="com.android.cts.devicepolicy.accountcheck.TestAuthenticator"
+            android:exported="true">
+            <intent-filter>
+                <action android:name="android.accounts.AccountAuthenticator" />
+            </intent-filter>
+
+            <meta-data android:name="android.accounts.AccountAuthenticator"
+                       android:resource="@xml/authenticator" />
+        </service>
+    </application>
+
+    <instrumentation
+        android:name="android.support.test.runner.AndroidJUnitRunner"
+        android:targetPackage="com.android.cts.devicepolicy.accountcheck.auth" />
+</manifest>
+
diff --git a/hostsidetests/devicepolicy/app/AccountCheck/Auth/res/xml/authenticator.xml b/hostsidetests/devicepolicy/app/AccountCheck/Auth/res/xml/authenticator.xml
new file mode 100644
index 0000000..ad963d7
--- /dev/null
+++ b/hostsidetests/devicepolicy/app/AccountCheck/Auth/res/xml/authenticator.xml
@@ -0,0 +1,20 @@
+<!-- Copyright (C) 2016 The Android Open Source Project
+
+     Licensed under the Apache License, Version 2.0 (the "License");
+     you may not use this file except in compliance with the License.
+     You may obtain a copy of the License at
+
+          http://www.apache.org/licenses/LICENSE-2.0
+
+     Unless required by applicable law or agreed to in writing, software
+     distributed under the License is distributed on an "AS IS" BASIS,
+     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+     See the License for the specific language governing permissions and
+     limitations under the License.
+-->
+
+<!-- The attributes in this XML file provide configuration information -->
+<!-- for the Account Manager. -->
+
+<account-authenticator xmlns:android="http://schemas.android.com/apk/res/android"
+    android:accountType="com.android.cts.devicepolicy.accountcheck" />
diff --git a/hostsidetests/devicepolicy/app/AccountCheck/Auth/src/com/android/cts/devicepolicy/accountcheck/AccountCheckTest.java b/hostsidetests/devicepolicy/app/AccountCheck/Auth/src/com/android/cts/devicepolicy/accountcheck/AccountCheckTest.java
new file mode 100644
index 0000000..73c18af
--- /dev/null
+++ b/hostsidetests/devicepolicy/app/AccountCheck/Auth/src/com/android/cts/devicepolicy/accountcheck/AccountCheckTest.java
@@ -0,0 +1,171 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.cts.devicepolicy.accountcheck;
+
+import android.accounts.Account;
+import android.accounts.AccountManager;
+import android.app.admin.DevicePolicyManager;
+import android.content.ComponentName;
+import android.os.Bundle;
+import android.os.SystemClock;
+import android.test.AndroidTestCase;
+import android.util.Log;
+
+public class AccountCheckTest extends AndroidTestCase {
+    private static final String TAG = "AccountCheckTest";
+
+    private static final String ACCOUNT_TYPE = "com.android.cts.devicepolicy.accountcheck";
+    private static final String ACCOUNT_FEATURE_ALLOWED =
+            "android.account.DEVICE_OR_PROFILE_OWNER_ALLOWED";
+    private static final String ACCOUNT_FEATURE_DISALLOWED =
+            "android.account.DEVICE_OR_PROFILE_OWNER_DISALLOWED";
+
+    private DevicePolicyManager mDevicePolicyManager;
+    private AccountManager mAccountManager;
+
+    @Override
+    protected void setUp() throws Exception {
+        super.setUp();
+
+        mDevicePolicyManager = getContext().getSystemService(DevicePolicyManager.class);
+        mAccountManager = getContext().getSystemService(AccountManager.class);
+    }
+
+    /**
+     * Remove all test accounts.
+     */
+    public void testRemoveAllAccounts() throws Exception {
+        for (Account account : mAccountManager.getAccountsByType(ACCOUNT_TYPE)) {
+            Log.i(TAG, "Removing account: " + account);
+            mAccountManager.removeAccountExplicitly(account);
+        }
+    }
+
+    private void addAccount(String... features) throws Exception {
+        final Bundle result = mAccountManager.addAccount(
+                ACCOUNT_TYPE,
+                null, // tokentype
+                features,
+                null, // options
+                null, // activity
+                null, // callback
+                null // handler
+        ).getResult();
+        assertEquals(ACCOUNT_TYPE, result.getString(AccountManager.KEY_ACCOUNT_TYPE));
+    }
+
+    /**
+     * Add an incompatible account, type A.
+     */
+    public void testAddIncompatibleA() throws Exception {
+        addAccount();
+    }
+
+    /**
+     * Add an incompatible account, type B.
+     */
+    public void testAddIncompatibleB() throws Exception {
+        addAccount(ACCOUNT_FEATURE_DISALLOWED);
+    }
+
+    /**
+     * Add an incompatible account, type C.
+     */
+    public void testAddIncompatibleC() throws Exception {
+        addAccount(ACCOUNT_FEATURE_ALLOWED, ACCOUNT_FEATURE_DISALLOWED);
+    }
+
+    /**
+     * Add a compatible account.
+     */
+    public void testAddCompatible() throws Exception {
+        addAccount(ACCOUNT_FEATURE_ALLOWED);
+    }
+
+    /**
+     * Remove the non-test-only (profile|device) owner.  Note this package and the test-only owner
+     * have the same UID, so we can call clearXxX() from this package.
+     */
+    public void testCleanUpNonTestOwner() throws Exception {
+        final ComponentName admin = new ComponentName(
+                "com.android.cts.devicepolicy.accountcheck.nontestonly",
+                "com.android.cts.devicepolicy.accountcheck.owner.AdminReceiver");
+
+        if (mDevicePolicyManager.isDeviceOwnerApp(admin.getPackageName())) {
+            Log.i(TAG, "testCleanUpNonTestOwner: Removing as DO");
+            mDevicePolicyManager.clearDeviceOwnerApp(admin.getPackageName());
+        }
+
+        if (mDevicePolicyManager.isProfileOwnerApp(admin.getPackageName())) {
+            Log.i(TAG, "testCleanUpNonTestOwner: Removing as PO");
+            mDevicePolicyManager.clearProfileOwner(admin);
+        }
+
+        if (mDevicePolicyManager.isAdminActive(admin)) {
+            Log.i(TAG, "testCleanUpNonTestOwner: Removing as DA");
+            mDevicePolicyManager.removeActiveAdmin(admin);
+
+            final long timeout = SystemClock.elapsedRealtime() + 60 * 1000;
+            while (SystemClock.elapsedRealtime() < timeout
+                    && mDevicePolicyManager.isAdminActive(admin)) {
+                Thread.sleep(100);
+            }
+        }
+        // Give the system a breath.
+        Thread.sleep(5000);
+    }
+
+    /**
+     * Test there are no preconfigured accounts that don't accept DO/PO.
+     */
+    public void testCheckPreconfiguredAccountFeatures() {
+        final AccountManager am = AccountManager.get(mContext);
+        final Account accounts[] = am.getAccounts();
+        if (accounts.length == 0) {
+            Log.v(TAG, "No preconfigured accounts found.");
+            return; // pass.
+        }
+        final String[] feature_allow =
+                {"android.account.DEVICE_OR_PROFILE_OWNER_ALLOWED"};
+        final String[] feature_disallow =
+                {"android.account.DEVICE_OR_PROFILE_OWNER_DISALLOWED"};
+
+        // Even if we find incompatible accounts along the way, we still check all accounts
+        // for logging.
+        final StringBuilder error = new StringBuilder();
+        for (Account account : accounts) {
+            Log.v(TAG, "Checking " + account);
+            if (hasAccountFeatures(am, account, feature_disallow)) {
+                error.append(account + " has " + feature_disallow[0] + "\n");
+            }
+            if (!hasAccountFeatures(am, account, feature_allow)) {
+                error.append(account + " doesn't have " + feature_allow[0] + "\n");
+            }
+        }
+        if (error.length() > 0) {
+            fail(error.toString());
+        }
+    }
+
+    private boolean hasAccountFeatures(AccountManager am, Account account, String[] features) {
+        try {
+            return am.hasFeatures(account, features, null, null).getResult();
+        } catch (Exception e) {
+            Log.w(TAG, "Failed to get account feature", e);
+            return false;
+        }
+    }
+}
diff --git a/hostsidetests/devicepolicy/app/AccountCheck/Auth/src/com/android/cts/devicepolicy/accountcheck/TestAuthenticator.java b/hostsidetests/devicepolicy/app/AccountCheck/Auth/src/com/android/cts/devicepolicy/accountcheck/TestAuthenticator.java
new file mode 100644
index 0000000..31c79d7
--- /dev/null
+++ b/hostsidetests/devicepolicy/app/AccountCheck/Auth/src/com/android/cts/devicepolicy/accountcheck/TestAuthenticator.java
@@ -0,0 +1,135 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.cts.devicepolicy.accountcheck;
+
+import android.accounts.AbstractAccountAuthenticator;
+import android.accounts.Account;
+import android.accounts.AccountAuthenticatorResponse;
+import android.accounts.AccountManager;
+import android.accounts.NetworkErrorException;
+import android.app.Service;
+import android.content.Context;
+import android.content.Intent;
+import android.os.Bundle;
+import android.os.IBinder;
+import android.os.SystemClock;
+import android.text.TextUtils;
+import android.util.Log;
+
+import java.util.Arrays;
+import java.util.List;
+
+public class TestAuthenticator extends Service {
+    private static final String TAG = "TestAuthenticator";
+
+    private static Authenticator sInstance;
+
+    @Override
+    public IBinder onBind(Intent intent) {
+        if (sInstance == null) {
+            sInstance = new Authenticator(getApplicationContext());
+
+        }
+        return sInstance.getIBinder();
+    }
+
+    public static class Authenticator extends AbstractAccountAuthenticator {
+
+        private final Context mContxet;
+
+        public Authenticator(Context context) {
+            super(context);
+            mContxet = context;
+        }
+
+        @Override
+        public Bundle addAccount(AccountAuthenticatorResponse response, String accountType,
+                String authTokenType, String[] requiredFeatures, Bundle options)
+                throws NetworkErrorException {
+
+            // Create an account whose name is:
+            //   [current time] + ":" + [all requested features concatenated with , ]
+
+            if (requiredFeatures == null) {
+                requiredFeatures = new String[0];
+            }
+
+            final String name = SystemClock.elapsedRealtimeNanos()
+                    + ":" + TextUtils.join(",", requiredFeatures);
+
+            Log.v(TAG, "Adding account '" + name + "' for " + accountType
+                    + "... " + Arrays.asList(requiredFeatures));
+
+            Bundle result = new Bundle();
+            result.putString(AccountManager.KEY_ACCOUNT_TYPE, accountType);
+            result.putString(AccountManager.KEY_ACCOUNT_NAME, name);
+
+            mContxet.getSystemService(AccountManager.class).addAccountExplicitly(
+                    new Account(name, accountType), "password", new Bundle());
+
+            return result;
+        }
+
+        @Override
+        public Bundle editProperties(AccountAuthenticatorResponse response, String accountType) {
+            return new Bundle();
+        }
+
+        @Override
+        public Bundle updateCredentials(AccountAuthenticatorResponse response, Account account,
+                String authTokenType, Bundle options) throws NetworkErrorException {
+            return new Bundle();
+        }
+
+        @Override
+        public Bundle confirmCredentials(AccountAuthenticatorResponse response, Account account,
+                Bundle options) throws NetworkErrorException {
+            return new Bundle();
+        }
+
+        @Override
+        public Bundle getAuthToken(AccountAuthenticatorResponse response, Account account,
+                String authTokenType, Bundle options) throws NetworkErrorException {
+            return new Bundle();
+        }
+
+        @Override
+        public String getAuthTokenLabel(String authTokenType) {
+            return "token_label";
+        }
+
+        @Override
+        public Bundle hasFeatures(AccountAuthenticatorResponse response, Account account,
+                String[] features) throws NetworkErrorException {
+
+            final int p = account.name.indexOf(':');
+
+            boolean hasAll = true;
+            final List<String> hasFeatures =
+                    Arrays.asList(TextUtils.split(account.name.substring(p + 1), ","));
+            for (String requested : features) {
+                if (!hasFeatures.contains(requested)) {
+                    hasAll = false;
+                    break;
+                }
+            }
+
+            Bundle result = new Bundle();
+            result.putBoolean(AccountManager.KEY_BOOLEAN_RESULT, hasAll);
+            return result;
+        }
+    }
+}
diff --git a/hostsidetests/devicepolicy/app/AccountCheck/NonTestOnlyOwner/Android.mk b/hostsidetests/devicepolicy/app/AccountCheck/NonTestOnlyOwner/Android.mk
new file mode 100644
index 0000000..26e6dca
--- /dev/null
+++ b/hostsidetests/devicepolicy/app/AccountCheck/NonTestOnlyOwner/Android.mk
@@ -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.
+
+LOCAL_PATH:= $(call my-dir)
+
+include $(CLEAR_VARS)
+
+# Tag this module as a cts test artifact
+LOCAL_COMPATIBILITY_SUITE := cts
+
+LOCAL_PACKAGE_NAME := CtsAccountCheckNonTestOnlyOwnerApp
+
+LOCAL_MODULE_TAGS := optional
+
+LOCAL_MODULE_PATH := $(TARGET_OUT_DATA_APPS)
+
+LOCAL_SRC_FILES := $(call all-java-files-under, ../src-owner)
+
+LOCAL_STATIC_JAVA_LIBRARIES := android-support-v4 ctstestrunner ub-uiautomator android-support-test
+
+LOCAL_SDK_VERSION := current
+
+include $(BUILD_CTS_PACKAGE)
diff --git a/hostsidetests/devicepolicy/app/AccountCheck/NonTestOnlyOwner/AndroidManifest.xml b/hostsidetests/devicepolicy/app/AccountCheck/NonTestOnlyOwner/AndroidManifest.xml
new file mode 100644
index 0000000..6b130b5
--- /dev/null
+++ b/hostsidetests/devicepolicy/app/AccountCheck/NonTestOnlyOwner/AndroidManifest.xml
@@ -0,0 +1,37 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ -->
+
+<manifest xmlns:android="http://schemas.android.com/apk/res/android"
+    package="com.android.cts.devicepolicy.accountcheck.nontestonly"
+    android:sharedUserId="com.android.cts.devicepolicy.accountcheck.uid">
+
+    <application android:testOnly="false">
+        <receiver
+            android:name="com.android.cts.devicepolicy.accountcheck.owner.AdminReceiver"
+            android:permission="android.permission.BIND_DEVICE_ADMIN">
+            <meta-data android:name="android.app.device_admin"
+                android:resource="@xml/device_admin" />
+            <intent-filter>
+                <action android:name="android.app.action.DEVICE_ADMIN_ENABLED" />
+            </intent-filter>
+        </receiver>
+    </application>
+    <!--
+      Don't need instrumentation. All the three device side apps have the same UID, so we're able
+      to run all tests from the Auth package.
+    -->
+</manifest>
diff --git a/hostsidetests/devicepolicy/app/AccountCheck/NonTestOnlyOwner/res/xml/device_admin.xml b/hostsidetests/devicepolicy/app/AccountCheck/NonTestOnlyOwner/res/xml/device_admin.xml
new file mode 100644
index 0000000..98e7028
--- /dev/null
+++ b/hostsidetests/devicepolicy/app/AccountCheck/NonTestOnlyOwner/res/xml/device_admin.xml
@@ -0,0 +1,16 @@
+<!-- Copyright (C) 2016 The Android Open Source Project
+
+     Licensed under the Apache License, Version 2.0 (the "License");
+     you may not use this file except in compliance with the License.
+     You may obtain a copy of the License at
+
+          http://www.apache.org/licenses/LICENSE-2.0
+
+     Unless required by applicable law or agreed to in writing, software
+     distributed under the License is distributed on an "AS IS" BASIS,
+     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+     See the License for the specific language governing permissions and
+     limitations under the License.
+-->
+<device-admin xmlns:android="http://schemas.android.com/apk/res/android" android:visible="false">
+</device-admin>
diff --git a/hostsidetests/devicepolicy/app/AccountCheck/TestOnlyOwner/Android.mk b/hostsidetests/devicepolicy/app/AccountCheck/TestOnlyOwner/Android.mk
new file mode 100644
index 0000000..eeba939
--- /dev/null
+++ b/hostsidetests/devicepolicy/app/AccountCheck/TestOnlyOwner/Android.mk
@@ -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.
+
+LOCAL_PATH:= $(call my-dir)
+
+include $(CLEAR_VARS)
+
+# Tag this module as a cts test artifact
+LOCAL_COMPATIBILITY_SUITE := cts
+
+LOCAL_PACKAGE_NAME := CtsAccountCheckTestOnlyOwnerApp
+
+LOCAL_MODULE_TAGS := optional
+
+LOCAL_MODULE_PATH := $(TARGET_OUT_DATA_APPS)
+
+LOCAL_SRC_FILES := $(call all-java-files-under, ../src-owner)
+
+LOCAL_STATIC_JAVA_LIBRARIES := android-support-v4 ctstestrunner ub-uiautomator android-support-test
+
+LOCAL_SDK_VERSION := current
+
+include $(BUILD_CTS_PACKAGE)
diff --git a/hostsidetests/devicepolicy/app/AccountCheck/TestOnlyOwner/AndroidManifest.xml b/hostsidetests/devicepolicy/app/AccountCheck/TestOnlyOwner/AndroidManifest.xml
new file mode 100644
index 0000000..a9673e9
--- /dev/null
+++ b/hostsidetests/devicepolicy/app/AccountCheck/TestOnlyOwner/AndroidManifest.xml
@@ -0,0 +1,37 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ -->
+
+<manifest xmlns:android="http://schemas.android.com/apk/res/android"
+    package="com.android.cts.devicepolicy.accountcheck.testonly"
+    android:sharedUserId="com.android.cts.devicepolicy.accountcheck.uid">
+
+    <application android:testOnly="true">
+        <receiver
+            android:name="com.android.cts.devicepolicy.accountcheck.owner.AdminReceiver"
+            android:permission="android.permission.BIND_DEVICE_ADMIN">
+            <meta-data android:name="android.app.device_admin"
+                android:resource="@xml/device_admin" />
+            <intent-filter>
+                <action android:name="android.app.action.DEVICE_ADMIN_ENABLED" />
+            </intent-filter>
+        </receiver>
+    </application>
+    <!--
+      Don't need instrumentation. All the three device side apps have the same UID, so we're able
+      to run all tests from the Auth package.
+    -->
+</manifest>
diff --git a/hostsidetests/devicepolicy/app/AccountCheck/TestOnlyOwner/res/xml/device_admin.xml b/hostsidetests/devicepolicy/app/AccountCheck/TestOnlyOwner/res/xml/device_admin.xml
new file mode 100644
index 0000000..98e7028
--- /dev/null
+++ b/hostsidetests/devicepolicy/app/AccountCheck/TestOnlyOwner/res/xml/device_admin.xml
@@ -0,0 +1,16 @@
+<!-- Copyright (C) 2016 The Android Open Source Project
+
+     Licensed under the Apache License, Version 2.0 (the "License");
+     you may not use this file except in compliance with the License.
+     You may obtain a copy of the License at
+
+          http://www.apache.org/licenses/LICENSE-2.0
+
+     Unless required by applicable law or agreed to in writing, software
+     distributed under the License is distributed on an "AS IS" BASIS,
+     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+     See the License for the specific language governing permissions and
+     limitations under the License.
+-->
+<device-admin xmlns:android="http://schemas.android.com/apk/res/android" android:visible="false">
+</device-admin>
diff --git a/hostsidetests/devicepolicy/app/AccountCheck/TestOnlyOwnerUpdate/Android.mk b/hostsidetests/devicepolicy/app/AccountCheck/TestOnlyOwnerUpdate/Android.mk
new file mode 100644
index 0000000..a86a98b
--- /dev/null
+++ b/hostsidetests/devicepolicy/app/AccountCheck/TestOnlyOwnerUpdate/Android.mk
@@ -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.
+
+LOCAL_PATH:= $(call my-dir)
+
+include $(CLEAR_VARS)
+
+# Tag this module as a cts test artifact
+LOCAL_COMPATIBILITY_SUITE := cts
+
+LOCAL_PACKAGE_NAME := CtsAccountCheckTestOnlyOwnerUpdateApp
+
+LOCAL_MODULE_TAGS := optional
+
+LOCAL_MODULE_PATH := $(TARGET_OUT_DATA_APPS)
+
+LOCAL_SRC_FILES := $(call all-java-files-under, ../src-owner)
+
+LOCAL_STATIC_JAVA_LIBRARIES := android-support-v4 ctstestrunner ub-uiautomator android-support-test
+
+LOCAL_SDK_VERSION := current
+
+include $(BUILD_CTS_PACKAGE)
diff --git a/hostsidetests/devicepolicy/app/AccountCheck/TestOnlyOwnerUpdate/AndroidManifest.xml b/hostsidetests/devicepolicy/app/AccountCheck/TestOnlyOwnerUpdate/AndroidManifest.xml
new file mode 100644
index 0000000..cd186e9
--- /dev/null
+++ b/hostsidetests/devicepolicy/app/AccountCheck/TestOnlyOwnerUpdate/AndroidManifest.xml
@@ -0,0 +1,39 @@
+<?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.
+ -->
+
+<!-- This package is exactly same as TestOnlyOwner, except for testOnly=false -->
+
+<manifest xmlns:android="http://schemas.android.com/apk/res/android"
+    package="com.android.cts.devicepolicy.accountcheck.testonly"
+    android:sharedUserId="com.android.cts.devicepolicy.accountcheck.uid">
+
+    <application android:testOnly="false">
+        <receiver
+            android:name="com.android.cts.devicepolicy.accountcheck.owner.AdminReceiver"
+            android:permission="android.permission.BIND_DEVICE_ADMIN">
+            <meta-data android:name="android.app.device_admin"
+                android:resource="@xml/device_admin" />
+            <intent-filter>
+                <action android:name="android.app.action.DEVICE_ADMIN_ENABLED" />
+            </intent-filter>
+        </receiver>
+    </application>
+    <!--
+      Don't need instrumentation. All the three device side apps have the same UID, so we're able
+      to run all tests from the Auth package.
+    -->
+</manifest>
diff --git a/hostsidetests/devicepolicy/app/AccountCheck/TestOnlyOwnerUpdate/res/xml/device_admin.xml b/hostsidetests/devicepolicy/app/AccountCheck/TestOnlyOwnerUpdate/res/xml/device_admin.xml
new file mode 100644
index 0000000..98e7028
--- /dev/null
+++ b/hostsidetests/devicepolicy/app/AccountCheck/TestOnlyOwnerUpdate/res/xml/device_admin.xml
@@ -0,0 +1,16 @@
+<!-- Copyright (C) 2016 The Android Open Source Project
+
+     Licensed under the Apache License, Version 2.0 (the "License");
+     you may not use this file except in compliance with the License.
+     You may obtain a copy of the License at
+
+          http://www.apache.org/licenses/LICENSE-2.0
+
+     Unless required by applicable law or agreed to in writing, software
+     distributed under the License is distributed on an "AS IS" BASIS,
+     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+     See the License for the specific language governing permissions and
+     limitations under the License.
+-->
+<device-admin xmlns:android="http://schemas.android.com/apk/res/android" android:visible="false">
+</device-admin>
diff --git a/hostsidetests/devicepolicy/app/AccountCheck/src-owner/com/android/cts/devicepolicy/accountcheck/owner/AdminReceiver.java b/hostsidetests/devicepolicy/app/AccountCheck/src-owner/com/android/cts/devicepolicy/accountcheck/owner/AdminReceiver.java
new file mode 100644
index 0000000..bf2ec17
--- /dev/null
+++ b/hostsidetests/devicepolicy/app/AccountCheck/src-owner/com/android/cts/devicepolicy/accountcheck/owner/AdminReceiver.java
@@ -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.devicepolicy.accountcheck.owner;
+
+import android.app.admin.DeviceAdminReceiver;
+
+public class AdminReceiver extends DeviceAdminReceiver {
+}
diff --git a/hostsidetests/devicepolicy/app/DeviceAdmin/src/com.android.cts.deviceadmin/ClearDeviceAdminWithNoProtectionTest.java b/hostsidetests/devicepolicy/app/DeviceAdmin/src/com.android.cts.deviceadmin/ClearDeviceAdminWithNoProtectionTest.java
index 3bfa905..7dd303f 100644
--- a/hostsidetests/devicepolicy/app/DeviceAdmin/src/com.android.cts.deviceadmin/ClearDeviceAdminWithNoProtectionTest.java
+++ b/hostsidetests/devicepolicy/app/DeviceAdmin/src/com.android.cts.deviceadmin/ClearDeviceAdminWithNoProtectionTest.java
@@ -33,7 +33,7 @@
 
         if (dpm.isAdminActive(cn)) {
             dpm.removeActiveAdmin(cn);
-            for (int i = 0; i < 1000 && dpm.isAdminActive(cn); i++) {
+            for (int i = 0; i < 6000 && dpm.isAdminActive(cn); i++) {
                 Thread.sleep(10);
             }
         }
diff --git a/hostsidetests/devicepolicy/app/DeviceAdmin/src/com.android.cts.deviceadmin/DeviceAdminPasswordTest.java b/hostsidetests/devicepolicy/app/DeviceAdmin/src/com.android.cts.deviceadmin/DeviceAdminPasswordTest.java
index 2d1197b..26d89c0 100644
--- a/hostsidetests/devicepolicy/app/DeviceAdmin/src/com.android.cts.deviceadmin/DeviceAdminPasswordTest.java
+++ b/hostsidetests/devicepolicy/app/DeviceAdmin/src/com.android.cts.deviceadmin/DeviceAdminPasswordTest.java
@@ -68,20 +68,22 @@
     }
 
     private void assertHasPassword() {
-        dpm.setPasswordMinimumLength(mAdminComponent, 1);
+        final int currentQuality = dpm.getPasswordQuality(mAdminComponent);
+        dpm.setPasswordQuality(mAdminComponent, DevicePolicyManager.PASSWORD_QUALITY_SOMETHING);
         try {
             assertTrue("No password set", dpm.isActivePasswordSufficient());
         } finally {
-            dpm.setPasswordMinimumLength(mAdminComponent, 0);
+            dpm.setPasswordQuality(mAdminComponent, currentQuality);
         }
     }
 
     private void assertNoPassword() {
-        dpm.setPasswordMinimumLength(mAdminComponent, 1);
+        final int currentQuality = dpm.getPasswordQuality(mAdminComponent);
+        dpm.setPasswordQuality(mAdminComponent, DevicePolicyManager.PASSWORD_QUALITY_SOMETHING);
         try {
             assertFalse("Password is set", dpm.isActivePasswordSufficient());
         } finally {
-            dpm.setPasswordMinimumLength(mAdminComponent, 0);
+            dpm.setPasswordQuality(mAdminComponent, currentQuality);
         }
     }
 
diff --git a/hostsidetests/devicepolicy/app/DeviceAdmin/src/com.android.cts.deviceadmin/DeviceOwnerPasswordTest.java b/hostsidetests/devicepolicy/app/DeviceAdmin/src/com.android.cts.deviceadmin/DeviceOwnerPasswordTest.java
index a00b4eb..d7c3bcf 100644
--- a/hostsidetests/devicepolicy/app/DeviceAdmin/src/com.android.cts.deviceadmin/DeviceOwnerPasswordTest.java
+++ b/hostsidetests/devicepolicy/app/DeviceAdmin/src/com.android.cts.deviceadmin/DeviceOwnerPasswordTest.java
@@ -56,8 +56,9 @@
         dpm.setPasswordMinimumLength(mAdminComponent, 10);
         caseDescription = "minimum password length = 10";
         assertEquals(10, dpm.getPasswordMinimumLength(mAdminComponent));
-        assertFalse(dpm.isActivePasswordSufficient());
+        assertTrue(dpm.isActivePasswordSufficient()); // length not checked for this quality
 
+        // TODO(ascull): fix resetPassword() logic so these succeed
         assertPasswordFails("1234", caseDescription);
         assertPasswordFails("abcd", caseDescription);
         assertPasswordFails("abcd1234", caseDescription);
diff --git a/hostsidetests/devicepolicy/app/DeviceAndProfileOwner/latest/AndroidManifest.xml b/hostsidetests/devicepolicy/app/DeviceAndProfileOwner/latest/AndroidManifest.xml
index 744db8f..632899d 100644
--- a/hostsidetests/devicepolicy/app/DeviceAndProfileOwner/latest/AndroidManifest.xml
+++ b/hostsidetests/devicepolicy/app/DeviceAndProfileOwner/latest/AndroidManifest.xml
@@ -39,8 +39,6 @@
             </intent-filter>
         </receiver>
         <activity
-            android:name="com.android.cts.deviceandprofileowner.ScreenCaptureDisabledActivity" />
-        <activity
             android:name="com.android.cts.deviceandprofileowner.ExampleIntentReceivingActivity1">
             <intent-filter>
                 <action android:name="com.android.cts.deviceandprofileowner.EXAMPLE_ACTION" />
diff --git a/hostsidetests/devicepolicy/app/DeviceAndProfileOwner/src/com/android/cts/deviceandprofileowner/ScreenCaptureDisabledActivity.java b/hostsidetests/devicepolicy/app/DeviceAndProfileOwner/src/com/android/cts/deviceandprofileowner/ScreenCaptureDisabledActivity.java
deleted file mode 100644
index b5b4fdc..0000000
--- a/hostsidetests/devicepolicy/app/DeviceAndProfileOwner/src/com/android/cts/deviceandprofileowner/ScreenCaptureDisabledActivity.java
+++ /dev/null
@@ -1,34 +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.deviceandprofileowner;
-
-import android.app.Activity;
-import android.content.Intent;
-
-/**
- * Test activity for setScreenCaptureDisabled().
- */
-public class ScreenCaptureDisabledActivity extends Activity {
-
-    static final String ACTIVITY_RESUMED =
-            "com.android.cts.deviceandprofileowner.ACTIVITY_RESUMED";
-
-    @Override
-    protected void onResume() {
-        super.onResume();
-        sendBroadcast(new Intent(ACTIVITY_RESUMED));
-    }
-}
diff --git a/hostsidetests/devicepolicy/app/DeviceAndProfileOwner/src/com/android/cts/deviceandprofileowner/ScreenCaptureDisabledTest.java b/hostsidetests/devicepolicy/app/DeviceAndProfileOwner/src/com/android/cts/deviceandprofileowner/ScreenCaptureDisabledTest.java
index e2deaa4..b7f9066 100644
--- a/hostsidetests/devicepolicy/app/DeviceAndProfileOwner/src/com/android/cts/deviceandprofileowner/ScreenCaptureDisabledTest.java
+++ b/hostsidetests/devicepolicy/app/DeviceAndProfileOwner/src/com/android/cts/deviceandprofileowner/ScreenCaptureDisabledTest.java
@@ -16,16 +16,8 @@
 package com.android.cts.deviceandprofileowner;
 
 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.util.Log;
 
-import java.util.concurrent.Semaphore;
-import java.util.concurrent.TimeUnit;
-
 /**
  * Tests for {@link DevicePolicyManager#setScreenCaptureDisabled} and
  * {@link DevicePolicyManager#getScreenCaptureDisabled} APIs.
@@ -34,66 +26,23 @@
 
     private static final String TAG = "ScreenCaptureDisabledTest";
 
-    private ScreenCaptureBroadcastReceiver mReceiver = new ScreenCaptureBroadcastReceiver();
-
-    protected void setUp() throws Exception {
-        super.setUp();
-        mContext.registerReceiver(mReceiver, new IntentFilter(
-                ScreenCaptureDisabledActivity.ACTIVITY_RESUMED));
-    }
-
-    protected void tearDown() throws Exception {
-        mContext.unregisterReceiver(mReceiver);
-        super.tearDown();
-    }
-
     public void testSetScreenCaptureDisabled_false() throws Exception {
         mDevicePolicyManager.setScreenCaptureDisabled(ADMIN_RECEIVER_COMPONENT, false);
         assertFalse(mDevicePolicyManager.getScreenCaptureDisabled(ADMIN_RECEIVER_COMPONENT));
         assertFalse(mDevicePolicyManager.getScreenCaptureDisabled(null /* any admin */));
-        startTestActivity();
-        assertNotNull(getInstrumentation().getUiAutomation().takeScreenshot());
     }
 
     public void testSetScreenCaptureDisabled_true() throws Exception {
         mDevicePolicyManager.setScreenCaptureDisabled(ADMIN_RECEIVER_COMPONENT, true);
         assertTrue(mDevicePolicyManager.getScreenCaptureDisabled(ADMIN_RECEIVER_COMPONENT));
         assertTrue(mDevicePolicyManager.getScreenCaptureDisabled(null /* any admin */));
-        startTestActivity();
+    }
+
+    public void testScreenCaptureImpossible() throws Exception {
         assertNull(getInstrumentation().getUiAutomation().takeScreenshot());
     }
 
     public void testScreenCapturePossible() throws Exception {
         assertNotNull(getInstrumentation().getUiAutomation().takeScreenshot());
     }
-
-    // We need to launch an activity before trying to take a screen shot, because screenshots are
-    // only blocked on a per-user basis in the profile owner case depending on the owner of the
-    // foreground activity.
-    private void startTestActivity() throws Exception {
-        Intent launchIntent = new Intent();
-        launchIntent.setComponent(new ComponentName(PACKAGE_NAME,
-                ScreenCaptureDisabledActivity.class.getName()));
-        launchIntent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
-        mContext.startActivity(launchIntent);
-        assertTrue(mReceiver.waitForBroadcast());
-        Thread.sleep(1000);
-    }
-
-    private class ScreenCaptureBroadcastReceiver extends BroadcastReceiver {
-        private final Semaphore mSemaphore = new Semaphore(0);
-
-        @Override
-        public void onReceive(Context context, Intent intent) {
-            Log.d(TAG, "Broadcast received");
-            mSemaphore.release();
-        }
-
-        public boolean waitForBroadcast() throws Exception {
-            if (mSemaphore.tryAcquire(5, TimeUnit.SECONDS)) {
-                return true;
-            }
-            return false;
-        }
-    }
 }
diff --git a/hostsidetests/devicepolicy/src/com/android/cts/devicepolicy/AccountCheckHostSideTest.java b/hostsidetests/devicepolicy/src/com/android/cts/devicepolicy/AccountCheckHostSideTest.java
new file mode 100644
index 0000000..93e8503
--- /dev/null
+++ b/hostsidetests/devicepolicy/src/com/android/cts/devicepolicy/AccountCheckHostSideTest.java
@@ -0,0 +1,271 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.cts.devicepolicy;
+
+import com.android.tradefed.log.LogUtil.CLog;
+
+import junit.framework.AssertionFailedError;
+
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
+
+public class AccountCheckHostSideTest extends BaseDevicePolicyTest {
+    private static final String APK_NON_TEST_ONLY = "CtsAccountCheckNonTestOnlyOwnerApp.apk";
+    private static final String APK_TEST_ONLY = "CtsAccountCheckTestOnlyOwnerApp.apk";
+    private static final String APK_TEST_ONLY_UPDATE = "CtsAccountCheckTestOnlyOwnerUpdateApp.apk";
+    private static final String APK_AUTH = "CtsAccountCheckAuthApp.apk";
+
+    private static final String PACKAGE_NON_TEST_ONLY =
+            "com.android.cts.devicepolicy.accountcheck.nontestonly";
+    private static final String PACKAGE_TEST_ONLY =
+            "com.android.cts.devicepolicy.accountcheck.testonly";
+    private static final String PACKAGE_AUTH = "com.android.cts.devicepolicy.accountcheck.auth";
+
+    private static final String OWNER_TEST_ONLY = PACKAGE_TEST_ONLY
+            + "/com.android.cts.devicepolicy.accountcheck.owner.AdminReceiver";
+    private static final String OWNER_NON_TEST_ONLY = PACKAGE_NON_TEST_ONLY
+            + "/com.android.cts.devicepolicy.accountcheck.owner.AdminReceiver";
+
+    private static final String TEST_CLASS =
+            "com.android.cts.devicepolicy.accountcheck.AccountCheckTest";
+
+    @Override
+    protected void tearDown() throws Exception {
+        if (mHasFeature) {
+            if (getDevice().getInstalledPackageNames().contains(PACKAGE_AUTH)) {
+                runCleanupTestOnlyOwnerAllowingFailure();
+                runCleanupNonTestOnlyOwnerAllowingFailure();
+
+                // This shouldn't be needed since we're uninstalling the authenticator,
+                // but sometimes the account manager fails to clean up?
+                removeAllAccountsAllowingFailure();
+            }
+
+            getDevice().uninstallPackage(PACKAGE_AUTH);
+            getDevice().uninstallPackage(PACKAGE_TEST_ONLY);
+            getDevice().uninstallPackage(PACKAGE_NON_TEST_ONLY);
+        }
+        super.tearDown();
+    }
+
+    private void runTest(String method) throws Exception {
+        assertTrue(runDeviceTests(PACKAGE_AUTH, TEST_CLASS, method));
+    }
+
+    private void runCleanupTestOnlyOwner() throws Exception {
+        assertTrue(removeAdmin(OWNER_TEST_ONLY, mPrimaryUserId));
+    }
+
+    private void runCleanupTestOnlyOwnerAllowingFailure() throws Exception {
+        try {
+            runCleanupTestOnlyOwner();
+        } catch (AssertionFailedError ignore) {
+        }
+    }
+
+    private void runCleanupNonTestOnlyOwner() throws Exception {
+        runTest("testCleanUpNonTestOwner");
+    }
+
+    private void runCleanupNonTestOnlyOwnerAllowingFailure() throws Exception {
+        try {
+            runCleanupNonTestOnlyOwner();
+        } catch (AssertionFailedError ignore) {
+        }
+    }
+
+    private void removeAllAccounts() throws Exception {
+        runTest("testRemoveAllAccounts");
+    }
+
+    private void removeAllAccountsAllowingFailure() throws Exception {
+        try {
+            removeAllAccounts();
+        } catch (AssertionFailedError ignore) {
+        }
+    }
+
+    private void assertTestOnlyInstallable() throws Exception {
+        setDeviceOwnerOrFail(OWNER_TEST_ONLY, mPrimaryUserId);
+        runCleanupTestOnlyOwner();
+
+        setProfileOwnerOrFail(OWNER_TEST_ONLY, mPrimaryUserId);
+        runCleanupTestOnlyOwner();
+    }
+
+    private void assertNonTestOnlyInstallable() throws Exception {
+        setDeviceOwnerOrFail(OWNER_NON_TEST_ONLY, mPrimaryUserId);
+        runCleanupNonTestOnlyOwner();
+
+        setProfileOwnerOrFail(OWNER_NON_TEST_ONLY, mPrimaryUserId);
+        runCleanupNonTestOnlyOwner();
+    }
+
+    private void assertTestOnlyNotInstallable() throws Exception {
+        setDeviceOwnerExpectingFailure(OWNER_TEST_ONLY, mPrimaryUserId);
+        runCleanupTestOnlyOwnerAllowingFailure();
+
+        setProfileOwnerExpectingFailure(OWNER_TEST_ONLY, mPrimaryUserId);
+        runCleanupTestOnlyOwnerAllowingFailure();
+    }
+
+    private void assertNonTestOnlyNotInstallable() throws Exception {
+        setDeviceOwnerExpectingFailure(OWNER_NON_TEST_ONLY, mPrimaryUserId);
+        runCleanupNonTestOnlyOwnerAllowingFailure();
+
+        setProfileOwnerExpectingFailure(OWNER_NON_TEST_ONLY, mPrimaryUserId);
+        runCleanupNonTestOnlyOwnerAllowingFailure();
+    }
+
+    private boolean hasAccounts() throws Exception {
+        final String accountDump = getDevice().executeShellCommand("dumpsys account");
+
+        final Pattern p = Pattern.compile("^\\s*Accounts\\:\\s*(\\d+)", Pattern.MULTILINE);
+        final Matcher m = p.matcher(accountDump);
+        if (!m.find()) {
+            fail("Unable to obtain # of accounts");
+            return true;
+        }
+        final String count = m.group(1);
+
+        CLog.i("# of preconfigured accounts=" + count);
+
+        return Integer.parseInt(count) > 0;
+    }
+
+    public void testAccountCheck() throws Exception {
+        if (!mHasFeature) {
+            return;
+        }
+        installAppAsUser(APK_AUTH, mPrimaryUserId);
+        installAppAsUser(APK_NON_TEST_ONLY, mPrimaryUserId);
+        installAppAsUser(APK_TEST_ONLY, mPrimaryUserId);
+
+        runCleanupTestOnlyOwnerAllowingFailure();
+        runCleanupNonTestOnlyOwnerAllowingFailure();
+        removeAllAccountsAllowingFailure();
+        try {
+            runTest("testCheckPreconfiguredAccountFeatures");
+
+            final boolean hasPreconfiguredAccounts = hasAccounts();
+
+            // All pre-configured accounts must be "compatible", so the test-only owner can be
+            // installed.
+            assertTestOnlyInstallable();
+
+            if (hasPreconfiguredAccounts) {
+                assertNonTestOnlyNotInstallable();
+            } else {
+                assertNonTestOnlyInstallable();
+            }
+
+            // Incompatible, type A.
+            runTest("testAddIncompatibleA");
+
+            assertTestOnlyNotInstallable();
+            assertNonTestOnlyNotInstallable();
+
+            // Incompatible, type B.
+            removeAllAccounts();
+            runTest("testAddIncompatibleB");
+
+            assertTestOnlyNotInstallable();
+            assertNonTestOnlyNotInstallable();
+
+            // Incompatible, type C.
+            removeAllAccounts();
+            runTest("testAddIncompatibleC");
+
+            assertTestOnlyNotInstallable();
+            assertNonTestOnlyNotInstallable();
+
+            // Compatible.
+            removeAllAccounts();
+            runTest("testAddCompatible");
+
+            assertTestOnlyInstallable(); // Now test-only owner can be accepted.
+            assertNonTestOnlyNotInstallable();
+
+            // 2 compatible accounts.
+            removeAllAccounts();
+            runTest("testAddCompatible");
+            runTest("testAddCompatible");
+
+            assertTestOnlyInstallable(); // Now test-only owner can be accepted.
+
+            assertNonTestOnlyNotInstallable();
+
+            // 2 compatible accounts + 1 incompatible.
+            removeAllAccounts();
+            runTest("testAddIncompatibleA");
+            runTest("testAddCompatible");
+            runTest("testAddCompatible");
+
+            assertTestOnlyNotInstallable();
+            assertNonTestOnlyNotInstallable();
+
+            // 2 compatible accounts + 1 incompatible, different order.
+            removeAllAccounts();
+            runTest("testAddCompatible");
+            runTest("testAddCompatible");
+            runTest("testAddIncompatibleB");
+
+            assertTestOnlyNotInstallable();
+            assertNonTestOnlyNotInstallable();
+        } catch (Throwable th) {
+            CLog.w("Tests failed; current accounts are:");
+            CLog.w(getDevice().executeShellCommand("dumpsys account"));
+
+            // Dump accounts
+            throw th;
+        }
+    }
+
+    /**
+     * Make sure even if the "test-only" flag changes when an app is updated, we still respect
+     * the original value.
+     */
+    public void testInheritTestOnly() throws Exception {
+        if (!mHasFeature) {
+            return;
+        }
+        try {
+            installAppAsUser(APK_TEST_ONLY, mPrimaryUserId);
+
+            // Set as DO.
+            setDeviceOwnerOrFail(OWNER_TEST_ONLY, mPrimaryUserId);
+
+            // Override with a package that's not test-only.
+            installAppAsUser(APK_TEST_ONLY_UPDATE, mPrimaryUserId);
+
+            // But DPMS keeps the original test-only flag, so it's still removable.
+            runCleanupTestOnlyOwner();
+
+            return;
+        } catch (Throwable e) {
+            // If failed, re-install the APK with test-only=true.
+            try {
+                installAppAsUser(APK_TEST_ONLY, mPrimaryUserId);
+                runCleanupTestOnlyOwner();
+            } catch (Exception inner) {
+                CLog.e("Unable to clean up after a failure: " + e.getMessage());
+            }
+
+            throw e;
+        }
+    }
+}
diff --git a/hostsidetests/devicepolicy/src/com/android/cts/devicepolicy/BaseDevicePolicyTest.java b/hostsidetests/devicepolicy/src/com/android/cts/devicepolicy/BaseDevicePolicyTest.java
index a5008e5..aef1ec0 100644
--- a/hostsidetests/devicepolicy/src/com/android/cts/devicepolicy/BaseDevicePolicyTest.java
+++ b/hostsidetests/devicepolicy/src/com/android/cts/devicepolicy/BaseDevicePolicyTest.java
@@ -17,8 +17,6 @@
 package com.android.cts.devicepolicy;
 
 import com.android.cts.migration.MigrationHelper;
-import com.android.ddmlib.Log.LogLevel;
-import com.android.ddmlib.testrunner.InstrumentationResultParser;
 import com.android.ddmlib.testrunner.RemoteAndroidTestRunner;
 import com.android.ddmlib.testrunner.TestIdentifier;
 import com.android.ddmlib.testrunner.TestResult;
@@ -26,21 +24,17 @@
 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.DeviceTestCase;
 import com.android.tradefed.testtype.IBuildReceiver;
 
-import java.io.File;
 import java.io.FileNotFoundException;
 import java.util.ArrayList;
 import java.util.Arrays;
 import java.util.Collections;
 import java.util.HashSet;
 import java.util.Map;
-import java.util.regex.Matcher;
-import java.util.regex.Pattern;
 
 import javax.annotation.Nullable;
 
@@ -229,6 +223,12 @@
         return runDeviceTestsAsUser(pkgName, testClassName, testMethodName, userId, params);
     }
 
+    protected boolean runDeviceTests(
+            String pkgName, @Nullable String testClassName, String testMethodName)
+            throws DeviceNotAvailableException {
+        return runDeviceTestsAsUser(pkgName, testClassName, testMethodName, mPrimaryUserId);
+    }
+
     protected boolean runDeviceTestsAsUser(String pkgName, @Nullable String testClassName,
             @Nullable String testMethodName, int userId,
             Map<String, String> params) throws DeviceNotAvailableException {
@@ -404,11 +404,23 @@
     protected void setProfileOwnerOrFail(String componentName, int userId)
             throws Exception {
         if (!setProfileOwner(componentName, userId, /*expectFailure*/ false)) {
-            removeUser(userId);
+            if (userId != 0) { // don't remove system user.
+                removeUser(userId);
+            }
             fail("Failed to set profile owner");
         }
     }
 
+    protected void setProfileOwnerExpectingFailure(String componentName, int userId)
+            throws Exception {
+        if (setProfileOwner(componentName, userId, /* expectFailure =*/ true)) {
+            if (userId != 0) { // don't remove system user.
+                removeUser(userId);
+            }
+            fail("Setting profile owner should have failed.");
+        }
+    }
+
     private String setDeviceAdminInner(String componentName, int userId)
             throws DeviceNotAvailableException {
         String command = "dpm set-active-admin --user " + userId + " '" + componentName + "'";
@@ -448,6 +460,16 @@
         return success;
     }
 
+    protected void setDeviceOwnerOrFail(String componentName, int userId)
+            throws Exception {
+        assertTrue(setDeviceOwner(componentName, userId, /* expectFailure =*/ false));
+    }
+
+    protected void setDeviceOwnerExpectingFailure(String componentName, int userId)
+            throws Exception {
+        assertFalse(setDeviceOwner(componentName, userId, /* expectFailure =*/ true));
+    }
+
     protected String getSettings(String namespace, String name, int userId)
             throws DeviceNotAvailableException {
         String command = "settings --user " + userId + " get " + namespace + " " + name;
diff --git a/hostsidetests/devicepolicy/src/com/android/cts/devicepolicy/DeviceAndProfileOwnerTest.java b/hostsidetests/devicepolicy/src/com/android/cts/devicepolicy/DeviceAndProfileOwnerTest.java
index fe301f1..ca6fb62 100644
--- a/hostsidetests/devicepolicy/src/com/android/cts/devicepolicy/DeviceAndProfileOwnerTest.java
+++ b/hostsidetests/devicepolicy/src/com/android/cts/devicepolicy/DeviceAndProfileOwnerTest.java
@@ -106,6 +106,7 @@
             getDevice().uninstallPackage(INTENT_RECEIVER_PKG);
             getDevice().uninstallPackage(INTENT_SENDER_PKG);
             getDevice().uninstallPackage(CUSTOMIZATION_APP_PKG);
+            getDevice().uninstallPackage(TEST_APP_PKG);
 
             // Press the HOME key to close any alart dialog that may be shown.
             getDevice().executeShellCommand("input keyevent 3");
@@ -288,11 +289,9 @@
         // We need to ensure that the policy is deactivated for the device owner case, so making
         // sure the second test is run even if the first one fails
         try {
-            executeDeviceTestMethod(".ScreenCaptureDisabledTest",
-                    "testSetScreenCaptureDisabled_true");
+            setScreenCaptureDisabled(mUserId, true);
         } finally {
-            executeDeviceTestMethod(".ScreenCaptureDisabledTest",
-                    "testSetScreenCaptureDisabled_false");
+            setScreenCaptureDisabled(mUserId, false);
         }
     }
 
@@ -496,12 +495,7 @@
             putSettings(SECURE_SETTING_CATEGORY, PACKAGE_VERIFIER_USER_CONSENT_SETTING, "-1",
                     mUserId);
             putSettings(GLOBAL_SETTING_CATEGORY, PACKAGE_VERIFIER_ENABLE_SETTING, "0", mUserId);
-            assertEquals("1",
-                    getSettings(SECURE_SETTING_CATEGORY, UNKNOWN_SOURCES_SETTING, mUserId));
-            assertEquals("-1", getSettings(SECURE_SETTING_CATEGORY,
-                    PACKAGE_VERIFIER_USER_CONSENT_SETTING, mUserId));
-            assertEquals("0", getSettings(GLOBAL_SETTING_CATEGORY,
-                    PACKAGE_VERIFIER_ENABLE_SETTING, mUserId));
+            // Skip verifying above setting values as some of them may be overrided.
             assertTrue(runDeviceTestsAsUser(PACKAGE_INSTALLER_PKG, ".ManualPackageInstallTest",
                     "testManualInstallSucceeded", mUserId));
         } finally {
@@ -617,4 +611,38 @@
         assertTrue("Command was expected to succeed " + commandOutput,
                 commandOutput.contains("Status: ok"));
     }
+
+    /**
+     * Start SimpleActivity synchronously in a particular user.
+     */
+    protected void startScreenCaptureDisabledActivity(int userId) throws Exception {
+        installAppAsUser(TEST_APP_APK, userId);
+        String command = "am start -W --user " + userId + " " + TEST_APP_PKG + "/"
+                + TEST_APP_PKG + ".SimpleActivity";
+        getDevice().executeShellCommand(command);
+    }
+
+    // TODO: Remove this after investigation in b/28995242 is done
+    // So we can check which one is the top window / activity.
+    private void runDumpsysWindow() throws Exception {
+        String command = "dumpsys window displays";
+        CLog.d("Output for command " + command + ": " + getDevice().executeShellCommand(command));
+        command = "dumpsys activity a";
+        CLog.d("Output for command " + command + ": " + getDevice().executeShellCommand(command));
+    }
+
+    protected void setScreenCaptureDisabled(int userId, boolean disabled) throws Exception {
+        String testMethodName = disabled
+                ? "testSetScreenCaptureDisabled_true"
+                : "testSetScreenCaptureDisabled_false";
+        executeDeviceTestMethod(".ScreenCaptureDisabledTest", testMethodName);
+        startScreenCaptureDisabledActivity(userId);
+        // [b/28995242], dump windows to make sure the top window is
+        // ScreenCaptureDisabledActivity.
+        runDumpsysWindow();
+        testMethodName = disabled
+                ? "testScreenCaptureImpossible"
+                : "testScreenCapturePossible";
+        executeDeviceTestMethod(".ScreenCaptureDisabledTest", testMethodName);
+    }
 }
diff --git a/hostsidetests/devicepolicy/src/com/android/cts/devicepolicy/DeviceOwnerTest.java b/hostsidetests/devicepolicy/src/com/android/cts/devicepolicy/DeviceOwnerTest.java
index 4748b35..256b03c 100644
--- a/hostsidetests/devicepolicy/src/com/android/cts/devicepolicy/DeviceOwnerTest.java
+++ b/hostsidetests/devicepolicy/src/com/android/cts/devicepolicy/DeviceOwnerTest.java
@@ -101,7 +101,7 @@
     }
 
     public void testWifi() throws Exception {
-        if (hasDeviceFeature("android.hardware.wifi")) {
+        if (!hasDeviceFeature("android.hardware.wifi")) {
             return;
         }
         executeDeviceOwnerTest("WifiTest");
diff --git a/hostsidetests/devicepolicy/src/com/android/cts/devicepolicy/MixedManagedProfileOwnerTest.java b/hostsidetests/devicepolicy/src/com/android/cts/devicepolicy/MixedManagedProfileOwnerTest.java
index b467aed..afc4e34 100644
--- a/hostsidetests/devicepolicy/src/com/android/cts/devicepolicy/MixedManagedProfileOwnerTest.java
+++ b/hostsidetests/devicepolicy/src/com/android/cts/devicepolicy/MixedManagedProfileOwnerTest.java
@@ -73,29 +73,13 @@
         if (!mHasFeature) {
             return;
         }
-        runDumpsysWindow();
-        try {
-            executeDeviceTestMethod(".ScreenCaptureDisabledTest",
-                    "testSetScreenCaptureDisabled_true");
-            // start the ScreenCaptureDisabledActivity in the parent
-            installAppAsUser(DEVICE_ADMIN_APK, mParentUserId);
-            String command = "am start -W --user " + mParentUserId + " " + DEVICE_ADMIN_PKG + "/"
-                    + DEVICE_ADMIN_PKG + ".ScreenCaptureDisabledActivity";
-            getDevice().executeShellCommand(command);
-            executeDeviceTestMethod(".ScreenCaptureDisabledTest", "testScreenCapturePossible");
-        } catch (AssertionError e) {
-            runDumpsysWindow();
-            CLog.e("testScreenCaptureDisabled_allowedPrimaryUser failed", e);
-            fail("testScreenCaptureDisabled_allowedPrimaryUser failed");
-        }
-    }
+        // disable screen capture in profile
+        setScreenCaptureDisabled(mUserId, true);
 
-    // TODO: Remove this after investigation in b/28995242 is done
-    private void runDumpsysWindow() throws Exception {
-        String command = "dumpsys window displays";
-        CLog.d("Output for command " + command + ": " + getDevice().executeShellCommand(command));
-        command = "dumpsys window policy";
-        CLog.d("Output for command " + command + ": " + getDevice().executeShellCommand(command));
+        // start the ScreenCaptureDisabledActivity in the parent
+        installAppAsUser(DEVICE_ADMIN_APK, mParentUserId);
+        startScreenCaptureDisabledActivity(mParentUserId);
+        executeDeviceTestMethod(".ScreenCaptureDisabledTest", "testScreenCapturePossible");
     }
 
     @Override
diff --git a/hostsidetests/devicepolicy/src/com/android/cts/devicepolicy/ProfileOwnerTest.java b/hostsidetests/devicepolicy/src/com/android/cts/devicepolicy/ProfileOwnerTest.java
index 4105012..41f7010 100644
--- a/hostsidetests/devicepolicy/src/com/android/cts/devicepolicy/ProfileOwnerTest.java
+++ b/hostsidetests/devicepolicy/src/com/android/cts/devicepolicy/ProfileOwnerTest.java
@@ -53,13 +53,6 @@
         super.tearDown();
     }
 
-    public void testWifi() throws Exception {
-        if (hasDeviceFeature("android.hardware.wifi")) {
-            return;
-        }
-        executeProfileOwnerTest("WifiTest");
-    }
-
     private void executeProfileOwnerTest(String testClassName) throws Exception {
         if (!mHasFeature) {
             return;
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 ba56665..78ba4b9 100644
--- a/hostsidetests/net/app/src/com/android/cts/net/hostside/AbstractAppIdleTestCase.java
+++ b/hostsidetests/net/app/src/com/android/cts/net/hostside/AbstractAppIdleTestCase.java
@@ -16,6 +16,7 @@
 
 package com.android.cts.net.hostside;
 
+import android.os.SystemClock;
 import android.util.Log;
 
 /**
@@ -138,4 +139,40 @@
         assertsForegroundAlwaysHasNetworkAccess();
         assertBackgroundNetworkAccess(true);
     }
+
+    public void testAppIdleNetworkAccess_whenCharging() throws Exception {
+        if (!isSupported()) return;
+
+        // Check that app is paroled when charging
+        setAppIdle(true);
+        assertBackgroundNetworkAccess(false);
+        turnBatteryOn();
+        assertBackgroundNetworkAccess(true);
+        turnBatteryOff();
+        assertBackgroundNetworkAccess(false);
+
+        // Check that app is restricted when not idle but power-save is on
+        setAppIdle(false);
+        assertBackgroundNetworkAccess(true);
+        setBatterySaverMode(true);
+        assertBackgroundNetworkAccess(false);
+        turnBatteryOn();
+        assertBackgroundNetworkAccess(true);
+
+        // And when no longer charging, it still has network access, since it's not idle
+        turnBatteryOff();
+        assertBackgroundNetworkAccess(true);
+    }
+
+    public void testAppIdle_toast() throws Exception {
+        if (!isSupported()) return;
+
+        setAppIdle(true);
+        assertAppIdle(true);
+        assertEquals("Shown", showToast());
+        assertAppIdle(true);
+        // Wait for a couple of seconds for the toast to actually be shown
+        SystemClock.sleep(2000);
+        assertAppIdle(true);
+    }
 }
diff --git a/hostsidetests/net/app/src/com/android/cts/net/hostside/AbstractBatterySaverModeTestCase.java b/hostsidetests/net/app/src/com/android/cts/net/hostside/AbstractBatterySaverModeTestCase.java
index c1c91da..50bcc60 100644
--- a/hostsidetests/net/app/src/com/android/cts/net/hostside/AbstractBatterySaverModeTestCase.java
+++ b/hostsidetests/net/app/src/com/android/cts/net/hostside/AbstractBatterySaverModeTestCase.java
@@ -85,16 +85,24 @@
         assertsForegroundAlwaysHasNetworkAccess();
         assertBackgroundNetworkAccess(false);
 
-        // Make sure foreground app doesn't lose access upon enabling it.
+        // Make sure foreground app doesn't lose access upon Battery Saver.
         setBatterySaverMode(false);
         launchActivity();
         assertForegroundNetworkAccess();
         setBatterySaverMode(true);
         assertForegroundNetworkAccess();
+
+        // Although it should not have access while the screen is off.
+        turnScreenOff();
+        assertBackgroundNetworkAccess(false);
+        turnScreenOn();
+        assertForegroundNetworkAccess();
+
+        // Goes back to background state.
         finishActivity();
         assertBackgroundNetworkAccess(false);
 
-        // Same for foreground service.
+        // Make sure foreground service doesn't lose access upon enabling Battery Saver.
         setBatterySaverMode(false);
         startForegroundService();
         assertForegroundNetworkAccess();
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 4bfd50d..b4d7d9d 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
@@ -64,6 +64,8 @@
             "com.android.cts.net.hostside.app2.action.RECEIVER_READY";
     static final String ACTION_SEND_NOTIFICATION =
             "com.android.cts.net.hostside.app2.action.SEND_NOTIFICATION";
+    static final String ACTION_SHOW_TOAST =
+            "com.android.cts.net.hostside.app2.action.SHOW_TOAST";
     private static final String EXTRA_ACTION = "com.android.cts.net.hostside.app2.extra.ACTION";
     private static final String EXTRA_RECEIVER_NAME =
             "com.android.cts.net.hostside.app2.extra.RECEIVER_NAME";
@@ -244,7 +246,7 @@
             if (isBackground(state.state)) {
                 return;
             }
-            Log.d(TAG, "App not on background state on attempt #" + i
+            Log.d(TAG, "App not on background state (" + state + ") on attempt #" + i
                     + "; sleeping 1s before trying again");
             SystemClock.sleep(SECOND_IN_MS);
         }
@@ -783,6 +785,17 @@
         mContext.sendBroadcast(intent);
     }
 
+    protected String showToast() {
+        final Intent intent = new Intent(ACTION_SHOW_TOAST);
+        intent.setPackage(TEST_APP2_PKG);
+        Log.d(TAG, "Sending request to show toast");
+        try {
+            return sendOrderedBroadcast(intent, 3 * SECOND_IN_MS);
+        } catch (Exception e) {
+            return "";
+        }
+    }
+
     private String toString(int status) {
         switch (status) {
             case RESTRICT_BACKGROUND_STATUS_DISABLED:
diff --git a/hostsidetests/net/app/src/com/android/cts/net/hostside/DataSaverModeTest.java b/hostsidetests/net/app/src/com/android/cts/net/hostside/DataSaverModeTest.java
index 3e6bd33..881b3b4 100644
--- a/hostsidetests/net/app/src/com/android/cts/net/hostside/DataSaverModeTest.java
+++ b/hostsidetests/net/app/src/com/android/cts/net/hostside/DataSaverModeTest.java
@@ -98,16 +98,24 @@
         assertsForegroundAlwaysHasNetworkAccess();
         assertDataSaverStatusOnBackground(RESTRICT_BACKGROUND_STATUS_ENABLED);
 
-        // Make sure foreground app doesn't lose access upon enabling it.
+        // Make sure foreground app doesn't lose access upon enabling Data Saver.
         setRestrictBackground(false);
         launchActivity();
         assertForegroundNetworkAccess();
         setRestrictBackground(true);
         assertForegroundNetworkAccess();
+
+        // Although it should not have access while the screen is off.
+        turnScreenOff();
+        assertBackgroundNetworkAccess(false);
+        turnScreenOn();
+        assertForegroundNetworkAccess();
+
+        // Goes back to background state.
         finishActivity();
         assertBackgroundNetworkAccess(false);
 
-        // Same for foreground service.
+        // Make sure foreground service doesn't lose access upon enabling Data Saver.
         setRestrictBackground(false);
         startForegroundService();
         assertForegroundNetworkAccess();
diff --git a/hostsidetests/net/app2/AndroidManifest.xml b/hostsidetests/net/app2/AndroidManifest.xml
index 1fa49ba..adf0045 100644
--- a/hostsidetests/net/app2/AndroidManifest.xml
+++ b/hostsidetests/net/app2/AndroidManifest.xml
@@ -46,6 +46,7 @@
                 <action android:name="com.android.cts.net.hostside.app2.action.GET_RESTRICT_BACKGROUND_STATUS" />
                 <action android:name="com.android.cts.net.hostside.app2.action.CHECK_NETWORK" />
                 <action android:name="com.android.cts.net.hostside.app2.action.SEND_NOTIFICATION" />
+                <action android:name="com.android.cts.net.hostside.app2.action.SHOW_TOAST" />
                 </intent-filter>
         </receiver>
     </application>
diff --git a/hostsidetests/net/app2/src/com/android/cts/net/hostside/app2/Common.java b/hostsidetests/net/app2/src/com/android/cts/net/hostside/app2/Common.java
index 8806e3b..e07c0f5 100644
--- a/hostsidetests/net/app2/src/com/android/cts/net/hostside/app2/Common.java
+++ b/hostsidetests/net/app2/src/com/android/cts/net/hostside/app2/Common.java
@@ -38,6 +38,8 @@
             "com.android.cts.net.hostside.app2.action.FINISH_ACTIVITY";
     static final String ACTION_SEND_NOTIFICATION =
             "com.android.cts.net.hostside.app2.action.SEND_NOTIFICATION";
+    static final String ACTION_SHOW_TOAST =
+            "com.android.cts.net.hostside.app2.action.SHOW_TOAST";
     static final String EXTRA_ACTION = "com.android.cts.net.hostside.app2.extra.ACTION";
     static final String EXTRA_RECEIVER_NAME =
             "com.android.cts.net.hostside.app2.extra.RECEIVER_NAME";
diff --git a/hostsidetests/net/app2/src/com/android/cts/net/hostside/app2/MyBroadcastReceiver.java b/hostsidetests/net/app2/src/com/android/cts/net/hostside/app2/MyBroadcastReceiver.java
index 6d01b15..733c3aa 100644
--- a/hostsidetests/net/app2/src/com/android/cts/net/hostside/app2/MyBroadcastReceiver.java
+++ b/hostsidetests/net/app2/src/com/android/cts/net/hostside/app2/MyBroadcastReceiver.java
@@ -23,6 +23,7 @@
 import static com.android.cts.net.hostside.app2.Common.ACTION_GET_RESTRICT_BACKGROUND_STATUS;
 import static com.android.cts.net.hostside.app2.Common.ACTION_RECEIVER_READY;
 import static com.android.cts.net.hostside.app2.Common.ACTION_SEND_NOTIFICATION;
+import static com.android.cts.net.hostside.app2.Common.ACTION_SHOW_TOAST;
 import static com.android.cts.net.hostside.app2.Common.EXTRA_ACTION;
 import static com.android.cts.net.hostside.app2.Common.EXTRA_NOTIFICATION_ID;
 import static com.android.cts.net.hostside.app2.Common.EXTRA_NOTIFICATION_TYPE;
@@ -51,6 +52,7 @@
 import android.net.NetworkInfo;
 import android.os.Bundle;
 import android.util.Log;
+import android.widget.Toast;
 
 import java.net.HttpURLConnection;
 import java.net.URL;
@@ -104,6 +106,9 @@
             case ACTION_SEND_NOTIFICATION:
                 sendNotification(context, intent);
                 break;
+            case ACTION_SHOW_TOAST:
+                showToast(context);
+                break;
             default:
                 Log.e(TAG, "received unexpected action: " + action);
         }
@@ -302,4 +307,9 @@
         ((NotificationManager) context.getSystemService(Context.NOTIFICATION_SERVICE))
             .notify(notificationId, notification);
     }
+
+    private void showToast(Context context) {
+        Toast.makeText(context, "Toast from CTS test", Toast.LENGTH_SHORT).show();
+        setResultData("Shown");
+    }
 }
diff --git a/hostsidetests/net/src/com/android/cts/net/HostsideRestrictBackgroundNetworkTests.java b/hostsidetests/net/src/com/android/cts/net/HostsideRestrictBackgroundNetworkTests.java
index 7d5f817..faf75d9 100644
--- a/hostsidetests/net/src/com/android/cts/net/HostsideRestrictBackgroundNetworkTests.java
+++ b/hostsidetests/net/src/com/android/cts/net/HostsideRestrictBackgroundNetworkTests.java
@@ -171,6 +171,22 @@
                 "testBackgroundNetworkAccess_enabled");
     }
 
+    public void testAppIdleNonMetered_whenCharging() throws Exception {
+        runDeviceTests(TEST_PKG, TEST_PKG + ".AppIdleNonMeteredTest",
+                "testAppIdleNetworkAccess_whenCharging");
+    }
+
+    public void testAppIdleMetered_whenCharging() throws Exception {
+        runDeviceTests(TEST_PKG, TEST_PKG + ".AppIdleMeteredTest",
+                "testAppIdleNetworkAccess_whenCharging");
+    }
+
+    public void testAppIdle_toast() throws Exception {
+        // Check that showing a toast doesn't bring an app out of standby
+        runDeviceTests(TEST_PKG, TEST_PKG + ".AppIdleNonMeteredTest",
+                "testAppIdle_toast");
+    }
+
     /********************
      * Doze Mode tests. *
      ********************/
diff --git a/hostsidetests/retaildemo/Android.mk b/hostsidetests/retaildemo/Android.mk
new file mode 100644
index 0000000..0aa5ee1
--- /dev/null
+++ b/hostsidetests/retaildemo/Android.mk
@@ -0,0 +1,35 @@
+# Copyright (C) 2016 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#      http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT 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 := CtsRetailDemoHostTestCases
+
+LOCAL_MODULE_TAGS := optional
+
+LOCAL_SRC_FILES := $(call all-java-files-under, src)
+
+LOCAL_JAVA_LIBRARIES := tools-common-prebuilt cts-tradefed tradefed-prebuilt
+
+LOCAL_CTS_TEST_PACKAGE := android.host.retaildemo
+
+# tag this module as a cts test artifact
+LOCAL_COMPATIBILITY_SUITE := cts
+
+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/retaildemo/AndroidTest.xml b/hostsidetests/retaildemo/AndroidTest.xml
new file mode 100644
index 0000000..c174489
--- /dev/null
+++ b/hostsidetests/retaildemo/AndroidTest.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.
+-->
+<configuration description="Config for the CTS retaildemo host tests">
+
+    <target_preparer class="com.android.compatibility.common.tradefed.targetprep.ApkInstaller">
+        <option name="test-file-name" value="CtsRetailDemoApp.apk" />
+        <option name="cleanup-apks" value="true" />
+    </target_preparer>
+
+    <test class="com.android.compatibility.common.tradefed.testtype.JarHostTest" >
+        <option name="jar" value="CtsRetailDemoHostTestCases.jar" />
+    </test>
+
+</configuration>
\ No newline at end of file
diff --git a/hostsidetests/retaildemo/app/Android.mk b/hostsidetests/retaildemo/app/Android.mk
new file mode 100644
index 0000000..e91e1da
--- /dev/null
+++ b/hostsidetests/retaildemo/app/Android.mk
@@ -0,0 +1,35 @@
+# Copyright (C) 2016 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#      http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+LOCAL_PATH:= $(call my-dir)
+
+include $(CLEAR_VARS)
+
+# Don't include this package in any target
+LOCAL_MODULE_TAGS := tests
+# When built, explicitly put it in the data partition.
+LOCAL_MODULE_PATH := $(TARGET_OUT_DATA_APPS)
+
+LOCAL_STATIC_JAVA_LIBRARIES := android-support-test
+
+LOCAL_SRC_FILES := $(call all-java-files-under, src)
+
+# tag this module as a cts test artifact
+LOCAL_COMPATIBILITY_SUITE := cts
+
+LOCAL_PACKAGE_NAME := CtsRetailDemoApp
+
+LOCAL_SDK_VERSION := current
+
+include $(BUILD_CTS_PACKAGE)
diff --git a/hostsidetests/retaildemo/app/AndroidManifest.xml b/hostsidetests/retaildemo/app/AndroidManifest.xml
new file mode 100644
index 0000000..b36ee1b
--- /dev/null
+++ b/hostsidetests/retaildemo/app/AndroidManifest.xml
@@ -0,0 +1,28 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ -->
+
+<manifest xmlns:android="http://schemas.android.com/apk/res/android"
+        package="com.android.cts.retaildemo">
+
+    <application>
+        <uses-library android:name="android.test.runner" />
+    </application>
+
+    <instrumentation android:name="android.support.test.runner.AndroidJUnitRunner"
+            android:targetPackage="com.android.cts.retaildemo"
+            android:label="RetailDemo device side tests" />
+</manifest>
diff --git a/hostsidetests/retaildemo/app/src/com/android/cts/retaildemo/DemoUserTest.java b/hostsidetests/retaildemo/app/src/com/android/cts/retaildemo/DemoUserTest.java
new file mode 100644
index 0000000..bb20b1a
--- /dev/null
+++ b/hostsidetests/retaildemo/app/src/com/android/cts/retaildemo/DemoUserTest.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 com.android.cts.retaildemo;
+
+import static junit.framework.Assert.assertFalse;
+import static junit.framework.Assert.assertTrue;
+
+import android.content.Context;
+import android.os.UserManager;
+import android.support.test.InstrumentationRegistry;
+import android.support.test.filters.SmallTest;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.JUnit4;
+
+@SmallTest
+@RunWith(JUnit4.class)
+public class DemoUserTest {
+    private UserManager mUm;
+
+    @Before
+    public void setUp() {
+        mUm = InstrumentationRegistry.getContext().getSystemService(UserManager.class);
+    }
+
+    @Test
+    public void testIsDemoUser_success() {
+        assertTrue(mUm.isDemoUser());
+    }
+
+    @Test
+    public void testIsDemoUser_failure() {
+        assertFalse(mUm.isDemoUser());
+    }
+}
diff --git a/hostsidetests/retaildemo/src/android/host/retaildemo/BaseTestCase.java b/hostsidetests/retaildemo/src/android/host/retaildemo/BaseTestCase.java
new file mode 100644
index 0000000..5033b6d
--- /dev/null
+++ b/hostsidetests/retaildemo/src/android/host/retaildemo/BaseTestCase.java
@@ -0,0 +1,129 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package android.host.retaildemo;
+
+import com.android.compatibility.common.tradefed.build.CompatibilityBuildHelper;
+import com.android.ddmlib.testrunner.RemoteAndroidTestRunner;
+import com.android.ddmlib.testrunner.TestIdentifier;
+import com.android.ddmlib.testrunner.TestResult;
+import com.android.ddmlib.testrunner.TestResult.TestStatus;
+import com.android.ddmlib.testrunner.TestRunResult;
+import com.android.tradefed.build.IBuildInfo;
+import com.android.tradefed.device.DeviceNotAvailableException;
+import com.android.tradefed.log.LogUtil.CLog;
+import com.android.tradefed.result.CollectingTestListener;
+import com.android.tradefed.testtype.DeviceTestCase;
+import com.android.tradefed.testtype.IBuildReceiver;
+
+import java.io.File;
+import java.io.FileNotFoundException;
+import java.util.ArrayList;
+import java.util.Map;
+
+public class BaseTestCase extends DeviceTestCase implements IBuildReceiver {
+    private static final String RETAIL_DEMO_TEST_PKG = "com.android.cts.retaildemo";
+    private static final String RUNNER = "android.support.test.runner.AndroidJUnitRunner";
+
+    private IBuildInfo mBuildInfo;
+    private CompatibilityBuildHelper mBuildHelper;
+
+    private ArrayList<Integer> mTestUsers;
+
+    @Override
+    public void setBuild(IBuildInfo buildInfo) {
+        mBuildInfo = buildInfo;
+        mBuildHelper = new CompatibilityBuildHelper(mBuildInfo);
+    }
+
+    @Override
+    protected void setUp() throws Exception {
+        super.setUp();
+        assertNotNull(mBuildInfo); // ensure build has been set before test is run.
+        mTestUsers = new ArrayList<>();
+    }
+
+    @Override
+    protected void tearDown() throws Exception {
+        for (int userId : mTestUsers) {
+            getDevice().removeUser(userId);
+        }
+        super.tearDown();
+    }
+
+    protected int createDemoUser() throws DeviceNotAvailableException, IllegalStateException {
+        final String command = "pm create-user --ephemeral --demo "
+                + "TestUser_" + System.currentTimeMillis();
+        CLog.d("Starting command: " + command);
+        final String output = getDevice().executeShellCommand(command);
+        CLog.d("Output for command " + command + ": " + output);
+
+        if (output.startsWith("Success")) {
+            try {
+                int userId = Integer.parseInt(output.substring(output.lastIndexOf(" ")).trim());
+                mTestUsers.add(userId);
+                return userId;
+            } catch (NumberFormatException e) {
+                CLog.e("Failed to parse result: %s", output);
+            }
+        } else {
+            CLog.e("Failed to create demo user: %s", output);
+        }
+        throw new IllegalStateException();
+    }
+
+    protected void installAppAsUser(String appFileName, int userId)
+            throws FileNotFoundException, DeviceNotAvailableException {
+        CLog.d("Installing app " + appFileName + " for user " + userId);
+        File apkFile = new File(mBuildHelper.getTestsDir(), appFileName);
+        final String result = getDevice().installPackageForUser(
+                apkFile, true, true, userId, "-t");
+        assertNull("Failed to install " + appFileName + " for user " + userId + ": " + result,
+                result);
+    }
+
+    protected boolean runDeviceTestsAsUser(String testClassName, String testMethodName, int userId)
+            throws Exception {
+        if (testClassName != null && testClassName.startsWith(".")) {
+            testClassName = RETAIL_DEMO_TEST_PKG + testClassName;
+        }
+
+        RemoteAndroidTestRunner testRunner = new RemoteAndroidTestRunner(
+                RETAIL_DEMO_TEST_PKG, RUNNER, getDevice().getIDevice());
+        if (testClassName != null && testMethodName != null) {
+            testRunner.setMethodName(testClassName, testMethodName);
+        } else if (testClassName != null) {
+            testRunner.setClassName(testClassName);
+        }
+
+        CollectingTestListener listener = new CollectingTestListener();
+        assertTrue(getDevice().runInstrumentationTestsAsUser(testRunner, userId, listener));
+
+        TestRunResult runResult = listener.getCurrentRunResults();
+        printTestResult(runResult);
+        return !runResult.hasFailedTests() && runResult.getNumTestsInState(TestStatus.PASSED) > 0;
+    }
+
+    private void printTestResult(TestRunResult runResult) {
+        for (Map.Entry<TestIdentifier, TestResult> testEntry :
+                runResult.getTestResults().entrySet()) {
+            TestResult testResult = testEntry.getValue();
+            CLog.d("Test " + testEntry.getKey() + ": " + testResult.getStatus());
+            if (testResult.getStatus() != TestStatus.PASSED) {
+                CLog.d(testResult.getStackTrace());
+            }
+        }
+    }
+}
diff --git a/hostsidetests/retaildemo/src/android/host/retaildemo/DemoModeTest.java b/hostsidetests/retaildemo/src/android/host/retaildemo/DemoModeTest.java
new file mode 100644
index 0000000..8252022
--- /dev/null
+++ b/hostsidetests/retaildemo/src/android/host/retaildemo/DemoModeTest.java
@@ -0,0 +1,35 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package android.host.retaildemo;
+
+import static junit.framework.Assert.assertTrue;
+
+public class DemoModeTest extends BaseTestCase {
+    private static final String RETAIL_DEMO_TEST_APK = "CtsRetailDemoApp.apk";
+
+    public void testIsDemoUser_inPrimaryUser() throws Exception {
+        assertTrue(runDeviceTestsAsUser(
+                ".DemoUserTest", "testIsDemoUser_failure", getDevice().getPrimaryUserId()));
+    }
+
+    public void testIsDemoUser_inDemoUser() throws Exception {
+        final int demoUserId = createDemoUser();
+        getDevice().startUser(demoUserId);
+        installAppAsUser(RETAIL_DEMO_TEST_APK, demoUserId);
+        assertTrue(runDeviceTestsAsUser(
+                ".DemoUserTest", "testIsDemoUser_success", demoUserId));
+    }
+}
diff --git a/hostsidetests/security/src/android/security/cts/SELinuxHostTest.java b/hostsidetests/security/src/android/security/cts/SELinuxHostTest.java
index b74eba7..a8c35d2 100644
--- a/hostsidetests/security/src/android/security/cts/SELinuxHostTest.java
+++ b/hostsidetests/security/src/android/security/cts/SELinuxHostTest.java
@@ -27,6 +27,8 @@
 import com.android.tradefed.testtype.IBuildReceiver;
 import com.android.tradefed.testtype.IDeviceTest;
 
+import com.android.compatibility.common.util.CddTest;
+
 import java.io.BufferedReader;
 import java.io.File;
 import java.io.FileReader;
@@ -123,6 +125,7 @@
      *
      * @throws Exception
      */
+    @CddTest(requirement="9.7")
     public void testGlobalEnforcing() throws Exception {
         CollectingOutputReceiver out = new CollectingOutputReceiver();
         mDevice.executeShellCommand("cat /sys/fs/selinux/enforce", out);
@@ -134,6 +137,7 @@
      *
      * @throws Exception
      */
+    @CddTest(requirement="9.7")
     @RestrictedBuildTest
     public void testAllDomainsEnforcing() throws Exception {
 
@@ -188,6 +192,7 @@
      *
      * @throws Exception
      */
+    @CddTest(requirement="9.7")
     public void testMLSAttributes() throws Exception {
         assertNotInAttribute("mlstrustedsubject", "untrusted_app");
         assertNotInAttribute("mlstrustedobject", "app_data_file");
@@ -198,6 +203,7 @@
      *
      * @throws Exception
      */
+    @CddTest(requirement="9.7")
     public void testValidSeappContexts() throws Exception {
 
         /* obtain seapp_contexts file from running device */
@@ -257,6 +263,7 @@
      *
      * @throws Exception
      */
+    @CddTest(requirement="9.7")
     public void testAospSeappContexts() throws Exception {
 
         /* obtain seapp_contexts file from running device */
@@ -276,6 +283,7 @@
      *
      * @throws Exception
      */
+    @CddTest(requirement="9.7")
     public void testAospFileContexts() throws Exception {
 
         /* retrieve the checkfc executable from jar */
@@ -311,6 +319,7 @@
      *
      * @throws Exception
      */
+    @CddTest(requirement="9.7")
     public void testAospPropertyContexts() throws Exception {
 
         /* obtain property_contexts file from running device */
@@ -330,6 +339,7 @@
      *
      * @throws Exception
      */
+    @CddTest(requirement="9.7")
     public void testAospServiceContexts() throws Exception {
 
         /* obtain service_contexts file from running device */
@@ -348,6 +358,7 @@
      *
      * @throws Exception
      */
+    @CddTest(requirement="9.7")
     public void testValidFileContexts() throws Exception {
 
         /* retrieve the checkfc executable from jar */
@@ -383,6 +394,7 @@
      *
      * @throws Exception
      */
+    @CddTest(requirement="9.7")
     public void testValidPropertyContexts() throws Exception {
 
         /* retrieve the checkfc executable from jar */
@@ -418,6 +430,7 @@
      *
      * @throws Exception
      */
+    @CddTest(requirement="9.7")
     public void testValidServiceContexts() throws Exception {
 
         /* retrieve the checkfc executable from jar */
@@ -453,6 +466,7 @@
      *
      * @throws Exception
      */
+    @CddTest(requirement="9.7")
     public void testNoBooleans() throws Exception {
 
         /* run sepolicy-analyze booleans check on policy file */
@@ -629,67 +643,80 @@
     }
 
     /* Init is always there */
+    @CddTest(requirement="9.7")
     public void testInitDomain() throws DeviceNotAvailableException {
         assertDomainOne("u:r:init:s0", "/init");
     }
 
     /* Ueventd is always there */
+    @CddTest(requirement="9.7")
     public void testUeventdDomain() throws DeviceNotAvailableException {
         assertDomainOne("u:r:ueventd:s0", "/sbin/ueventd");
     }
 
     /* Devices always have healthd */
+    @CddTest(requirement="9.7")
     public void testHealthdDomain() throws DeviceNotAvailableException {
         assertDomainOne("u:r:healthd:s0", "/sbin/healthd");
     }
 
     /* Servicemanager is always there */
+    @CddTest(requirement="9.7")
     public void testServicemanagerDomain() throws DeviceNotAvailableException {
         assertDomainOne("u:r:servicemanager:s0", "/system/bin/servicemanager");
     }
 
     /* Vold is always there */
+    @CddTest(requirement="9.7")
     public void testVoldDomain() throws DeviceNotAvailableException {
         assertDomainOne("u:r:vold:s0", "/system/bin/vold");
     }
 
     /* netd is always there */
+    @CddTest(requirement="9.7")
     public void testNetdDomain() throws DeviceNotAvailableException {
         assertDomainOne("u:r:netd:s0", "/system/bin/netd");
     }
 
     /* Debuggerd is always there */
+    @CddTest(requirement="9.7")
     public void testDebuggerdDomain() throws DeviceNotAvailableException {
         assertDomainN("u:r:debuggerd:s0", "/system/bin/debuggerd", "/system/bin/debuggerd64",
                 "debuggerd:signaller", "debuggerd64:signaller");
     }
 
     /* Surface flinger is always there */
+    @CddTest(requirement="9.7")
     public void testSurfaceflingerDomain() throws DeviceNotAvailableException {
         assertDomainOne("u:r:surfaceflinger:s0", "/system/bin/surfaceflinger");
     }
 
     /* Zygote is always running */
+    @CddTest(requirement="9.7")
     public void testZygoteDomain() throws DeviceNotAvailableException {
         assertDomainN("u:r:zygote:s0", "zygote", "zygote64");
     }
 
     /* Checks drmserver for devices that require it */
+    @CddTest(requirement="9.7")
     public void testDrmServerDomain() throws DeviceNotAvailableException {
         assertDomainZeroOrOne("u:r:drmserver:s0", "/system/bin/drmserver");
     }
 
     /* Installd is always running */
+    @CddTest(requirement="9.7")
     public void testInstalldDomain() throws DeviceNotAvailableException {
         assertDomainOne("u:r:installd:s0", "/system/bin/installd");
     }
 
     /* keystore is always running */
+    @CddTest(requirement="9.7")
     public void testKeystoreDomain() throws DeviceNotAvailableException {
         assertDomainOne("u:r:keystore:s0", "/system/bin/keystore");
     }
 
     /* System server better be running :-P */
+    @CddTest(requirement="9.7")
     public void testSystemServerDomain() throws DeviceNotAvailableException {
         assertDomainOne("u:r:system_server:s0", "system_server");
     }
@@ -698,26 +725,31 @@
      * Some OEMs do not use sdcardd so transient. Other OEMs have multiple sdcards
      * so they run the daemon multiple times.
      */
+    @CddTest(requirement="9.7")
     public void testSdcarddDomain() throws DeviceNotAvailableException {
         assertDomainHasExecutable("u:r:sdcardd:s0", "/system/bin/sdcard");
     }
 
     /* Watchdogd may or may not be there */
+    @CddTest(requirement="9.7")
     public void testWatchdogdDomain() throws DeviceNotAvailableException {
         assertDomainZeroOrOne("u:r:watchdogd:s0", "/sbin/watchdogd");
     }
 
     /* logd may or may not be there */
+    @CddTest(requirement="9.7")
     public void testLogdDomain() throws DeviceNotAvailableException {
         assertDomainZeroOrOne("u:r:logd:s0", "/system/bin/logd");
     }
 
     /* lmkd may or may not be there */
+    @CddTest(requirement="9.7")
     public void testLmkdDomain() throws DeviceNotAvailableException {
         assertDomainZeroOrOne("u:r:lmkd:s0", "/system/bin/lmkd");
     }
 
     /* Wifi may be off so cardinality of 0 or 1 is ok */
+    @CddTest(requirement="9.7")
     public void testWpaDomain() throws DeviceNotAvailableException {
         assertDomainZeroOrOne("u:r:wpa:s0", "/system/bin/wpa_supplicant");
     }
@@ -726,6 +758,7 @@
      * Nothing should be running in this domain, cardinality test is all thats
      * needed
      */
+    @CddTest(requirement="9.7")
     public void testInitShellDomain() throws DeviceNotAvailableException {
         assertDomainEmpty("u:r:init_shell:s0");
     }
@@ -734,6 +767,7 @@
      * Nothing should be running in this domain, cardinality test is all thats
      * needed
      */
+    @CddTest(requirement="9.7")
     public void testRecoveryDomain() throws DeviceNotAvailableException {
         assertDomainEmpty("u:r:recovery:s0");
     }
@@ -742,6 +776,7 @@
      * Nothing should be running in this domain, cardinality test is all thats
      * needed
      */
+    @CddTest(requirement="9.7")
     @RestrictedBuildTest
     public void testSuDomain() throws DeviceNotAvailableException {
         assertDomainEmpty("u:r:su:s0");
@@ -750,6 +785,7 @@
     /*
      * All kthreads should be in kernel context.
      */
+    @CddTest(requirement="9.7")
     public void testKernelDomain() throws DeviceNotAvailableException {
         String domain = "u:r:kernel:s0";
         List<ProcessDetails> procs = ProcessDetails.getProcMap(mDevice).get(domain);
diff --git a/hostsidetests/services/activitymanager/app/src/android/server/app/AbstractLifecycleLogActivity.java b/hostsidetests/services/activitymanager/app/src/android/server/app/AbstractLifecycleLogActivity.java
index bb54bc4..8abb1c5 100644
--- a/hostsidetests/services/activitymanager/app/src/android/server/app/AbstractLifecycleLogActivity.java
+++ b/hostsidetests/services/activitymanager/app/src/android/server/app/AbstractLifecycleLogActivity.java
@@ -57,7 +57,8 @@
         final String line = "config" +
                 " size=" + buildCoordString(config.screenWidthDp, config.screenHeightDp) +
                 " displaySize=" + buildCoordString(point.x, point.y) +
-                " metricsSize=" + buildCoordString(metrics.widthPixels, metrics.heightPixels);
+                " metricsSize=" + buildCoordString(metrics.widthPixels, metrics.heightPixels) +
+                " smallestScreenWidth=" + config.smallestScreenWidthDp;
 
         Log.i(getTag(), line);
     }
diff --git a/hostsidetests/services/activitymanager/src/android/server/cts/ActivityManagerAppConfigurationTests.java b/hostsidetests/services/activitymanager/src/android/server/cts/ActivityManagerAppConfigurationTests.java
index 5b21ee9..a224211 100644
--- a/hostsidetests/services/activitymanager/src/android/server/cts/ActivityManagerAppConfigurationTests.java
+++ b/hostsidetests/services/activitymanager/src/android/server/cts/ActivityManagerAppConfigurationTests.java
@@ -66,13 +66,10 @@
     public void testConfigurationUpdatesWhenRotatingWhileFullscreen() throws Exception {
         setDeviceRotation(0);
         launchActivityInStack(RESIZEABLE_ACTIVITY_NAME, FULLSCREEN_WORKSPACE_STACK_ID);
-        final ReportedSizes orientationASizes = getActivityDisplaySize(RESIZEABLE_ACTIVITY_NAME,
+        final ReportedSizes initialSizes = getActivityDisplaySize(RESIZEABLE_ACTIVITY_NAME,
                 FULLSCREEN_WORKSPACE_STACK_ID);
 
-        setDeviceRotation(1);
-        final ReportedSizes orientationBSizes = getActivityDisplaySize(RESIZEABLE_ACTIVITY_NAME,
-                FULLSCREEN_WORKSPACE_STACK_ID);
-        assertSizesRotate(orientationASizes, orientationBSizes);
+        rotateAndCheckSizes(initialSizes, FULLSCREEN_WORKSPACE_STACK_ID);
     }
 
     /**
@@ -82,13 +79,37 @@
     public void testConfigurationUpdatesWhenRotatingWhileDocked() throws Exception {
         setDeviceRotation(0);
         launchActivityInStack(RESIZEABLE_ACTIVITY_NAME, DOCKED_STACK_ID);
-        final ReportedSizes orientationASizes = getActivityDisplaySize(RESIZEABLE_ACTIVITY_NAME,
+        final ReportedSizes initialSizes = getActivityDisplaySize(RESIZEABLE_ACTIVITY_NAME,
                 DOCKED_STACK_ID);
 
-        setDeviceRotation(1);
-        final ReportedSizes orientationBSizes = getActivityDisplaySize(RESIZEABLE_ACTIVITY_NAME,
-                DOCKED_STACK_ID);
-        assertSizesRotate(orientationASizes, orientationBSizes);
+        rotateAndCheckSizes(initialSizes, DOCKED_STACK_ID);
+    }
+
+    /**
+     * Same as {@link #testConfigurationUpdatesWhenRotatingWhileDocked()} but when the Activity
+     * is launched to side from docked stack.
+     */
+    public void testConfigurationUpdatesWhenRotatingToSideFromDocked() throws Exception {
+        setDeviceRotation(0);
+
+        launchActivityInDockStack(LAUNCHING_ACTIVITY);
+        launchActivityToSide(false /* randomData */, false /* multipleTaskFlag */,
+                RESIZEABLE_ACTIVITY_NAME);
+        final ReportedSizes initialSizes = getActivityDisplaySize(RESIZEABLE_ACTIVITY_NAME,
+                FULLSCREEN_WORKSPACE_STACK_ID);
+
+        rotateAndCheckSizes(initialSizes, FULLSCREEN_WORKSPACE_STACK_ID);
+    }
+
+    private void rotateAndCheckSizes(ReportedSizes prevSizes, int stackId) throws Exception {
+        for (int rotation = 3; rotation >= 0; --rotation) {
+            clearLogcat();
+            setDeviceRotation(rotation);
+            final ReportedSizes rotatedSizes = getActivityDisplaySize(RESIZEABLE_ACTIVITY_NAME,
+                    stackId);
+            assertSizesRotate(prevSizes, rotatedSizes);
+            prevSizes = rotatedSizes;
+        }
     }
 
     /**
@@ -207,6 +228,8 @@
         final boolean afterConfigPortrait = rotationB.widthDp < rotationB.heightDp;
         assertEquals(beforePortrait, beforeConfigPortrait);
         assertEquals(afterPortrait, afterConfigPortrait);
+
+        assertEquals(rotationA.smallestWidthDp, rotationB.smallestWidthDp);
     }
 
     /**
@@ -238,6 +261,7 @@
         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, int stackId)
diff --git a/hostsidetests/services/activitymanager/src/android/server/cts/ActivityManagerDockedStackTests.java b/hostsidetests/services/activitymanager/src/android/server/cts/ActivityManagerDockedStackTests.java
index 034c208..7facf7d 100644
--- a/hostsidetests/services/activitymanager/src/android/server/cts/ActivityManagerDockedStackTests.java
+++ b/hostsidetests/services/activitymanager/src/android/server/cts/ActivityManagerDockedStackTests.java
@@ -351,9 +351,4 @@
             throws Exception {
         launchActivityToSide(randomData, multipleTaskFlag, null);
     }
-
-    private void launchActivityToSide(boolean randomData, boolean multipleTaskFlag,
-            String targetActivity) throws Exception {
-        launchActivity(true /* toSide */, randomData, multipleTaskFlag, targetActivity);
-    }
 }
diff --git a/hostsidetests/services/activitymanager/src/android/server/cts/ActivityManagerTestBase.java b/hostsidetests/services/activitymanager/src/android/server/cts/ActivityManagerTestBase.java
index f4d84c7..c3d34f9 100644
--- a/hostsidetests/services/activitymanager/src/android/server/cts/ActivityManagerTestBase.java
+++ b/hostsidetests/services/activitymanager/src/android/server/cts/ActivityManagerTestBase.java
@@ -190,6 +190,11 @@
         moveActivityToDockStack(activityName);
     }
 
+    protected void launchActivityToSide(boolean randomData, boolean multipleTaskFlag,
+            String targetActivity) throws Exception {
+        launchActivity(true /* toSide */, randomData, multipleTaskFlag, targetActivity);
+    }
+
     protected void moveActivityToDockStack(String activityName) throws Exception {
         moveActivityToStack(activityName, DOCKED_STACK_ID);
     }
@@ -461,7 +466,7 @@
     private static final Pattern sDestroyPattern = Pattern.compile("(.+): onDestroy");
     private static final Pattern sNewConfigPattern = Pattern.compile(
             "(.+): config size=\\((\\d+),(\\d+)\\) displaySize=\\((\\d+),(\\d+)\\)" +
-            " metricsSize=\\((\\d+),(\\d+)\\)");
+            " metricsSize=\\((\\d+),(\\d+)\\) smallestScreenWidth=(\\d+)");
     private static final Pattern sDisplayStatePattern =
             Pattern.compile("Display Power: state=(.+)");
 
@@ -472,6 +477,15 @@
         int displayHeight;
         int metricsWidth;
         int metricsHeight;
+        int smallestWidthDp;
+
+        @Override
+        public String toString() {
+            return "ReportedSizes: {widthDp=" + widthDp + " heightDp=" + heightDp +
+                    " displayWidth=" + displayWidth + " displayHeight=" + displayHeight +
+                    " metricsWidth=" + metricsWidth + " metricsHeight=" + metricsHeight +
+                    " smallestWidthDp=" + smallestWidthDp + "}";
+        }
     }
 
     protected ReportedSizes getLastReportedSizesForActivity(String activityName)
@@ -488,6 +502,7 @@
                 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));
                 return details;
             }
         }
diff --git a/hostsidetests/sustainedperf/src/android/SustainedPerformance/cts/SustainedPerformanceHostTest.java b/hostsidetests/sustainedperf/src/android/SustainedPerformance/cts/SustainedPerformanceHostTest.java
index 7b3a30c..7fe64fb 100644
--- a/hostsidetests/sustainedperf/src/android/SustainedPerformance/cts/SustainedPerformanceHostTest.java
+++ b/hostsidetests/sustainedperf/src/android/SustainedPerformance/cts/SustainedPerformanceHostTest.java
@@ -194,27 +194,7 @@
         appResultsWithMode.clear();
         dhrystoneResultsWithoutMode.clear();
         dhrystoneResultsWithMode.clear();
-        /*
-         * Run the test without the mode.
-         * Start the application and collect stats.
-         * Run two threads of dhrystone and collect stats.
-         */
-        setUpEnvironment();
-        device.executeShellCommand(START_COMMAND);
-        Thread dhrystone = new Thread(new Dhrystone(false, 1));
-        Thread dhrystone1 = new Thread(new Dhrystone(false, 2));
-        dhrystone.start();
-        dhrystone1.start();
-        Thread.sleep(testDuration);
-        device.executeShellCommand(STOP_COMMAND);
-        dhrystone.join();
-        dhrystone1.join();
-        logs = device.executeAdbCommand("logcat", "-v", "brief", "-d", CLASS + ":I", "*:S");
-        analyzeResults(logs, false);
-        double diff = (dhryMax - dhryMin)*100/dhryMax;
-        dhrystoneResultsWithoutMode.add(0, dhryMin);
-        dhrystoneResultsWithoutMode.add(1, dhryMax);
-        dhrystoneResultsWithoutMode.add(2, diff);
+
         /*
          * Run the test with the mode.
          * Start the application and collect stats.
@@ -222,8 +202,8 @@
          */
         setUpEnvironment();
         device.executeShellCommand(START_COMMAND_MODE);
-        dhrystone = new Thread(new Dhrystone(true, 1));
-        dhrystone1 = new Thread(new Dhrystone(true, 2));
+        Thread dhrystone = new Thread(new Dhrystone(true, 1));
+        Thread dhrystone1 = new Thread(new Dhrystone(true, 2));
         dhrystone.start();
         dhrystone1.start();
         Thread.sleep(testDuration);
@@ -232,7 +212,7 @@
         dhrystone1.join();
         logs = device.executeAdbCommand("logcat", "-v", "brief", "-d", CLASS + ":I", "*:S");
         analyzeResults(logs, true);
-        diff = (dhryMax - dhryMin)*100/dhryMax;
+        double diff = (dhryMax - dhryMin)*100/dhryMax;
         dhrystoneResultsWithMode.add(0, dhryMin);
         dhrystoneResultsWithMode.add(1, dhryMax);
         dhrystoneResultsWithMode.add(2, diff);
@@ -240,12 +220,9 @@
         device.executeShellCommand("settings put global airplane_mode_on 0");
         device.executeShellCommand("am broadcast -a android.intent.action.AIRPLANE_MODE --ez state false");
 
-        double perfdegradapp = (appResultsWithMode.get(1) - appResultsWithoutMode.get(1))*100/appResultsWithMode.get(1);
-        double perfdegraddhry = (dhrystoneResultsWithoutMode.get(0) - dhrystoneResultsWithMode.get(0))*100/dhrystoneResultsWithoutMode.get(0);
-
         /*
          * Checks if the performance in the mode is consistent with
-         * 5% error margin.
+         * 5% error margin for shader and 10% error margin for dhrystone.
          */
         assertFalse("Results in the mode are not sustainable",
                 (dhrystoneResultsWithMode.get(2) > 10) ||
diff --git a/hostsidetests/theme/README b/hostsidetests/theme/README
index b2ae8de..76902e4 100644
--- a/hostsidetests/theme/README
+++ b/hostsidetests/theme/README
@@ -39,7 +39,7 @@
      adb devices
 
   2. Image generation occurs on all devices in parallel. Resulting sets of
-     reference images are saved in assets/<api>/<dpi>.zip and will overwrite
+     reference images are saved in assets/<dpi>.zip and will overwrite
      any existing sets. Image generation may be started using:
 
      ./cts/hostsidetests/theme/generate_images.sh
diff --git a/hostsidetests/theme/app/AndroidManifest.xml b/hostsidetests/theme/app/AndroidManifest.xml
index d39f1ae..d9a89c6 100755
--- a/hostsidetests/theme/app/AndroidManifest.xml
+++ b/hostsidetests/theme/app/AndroidManifest.xml
@@ -31,7 +31,7 @@
                 <category android:name="android.intent.category.LAUNCHER" />
             </intent-filter>
         </activity>
-        <activity android:name=".GenerateImagesActivity"
+        <activity android:name=".GenerateImagesActivity" android:screenOrientation="portrait"
                   android:exported="true" />
     </application>
 
diff --git a/hostsidetests/theme/assets/420dpi.zip b/hostsidetests/theme/assets/420dpi.zip
index 1c380ff..bf70d35 100644
--- a/hostsidetests/theme/assets/420dpi.zip
+++ b/hostsidetests/theme/assets/420dpi.zip
Binary files differ
diff --git a/hostsidetests/theme/assets/560dpi.zip b/hostsidetests/theme/assets/560dpi.zip
index a171f7c..74e2228 100644
--- a/hostsidetests/theme/assets/560dpi.zip
+++ b/hostsidetests/theme/assets/560dpi.zip
Binary files differ
diff --git a/hostsidetests/theme/assets/xhdpi.zip b/hostsidetests/theme/assets/xhdpi.zip
index 96d4ae0..cceab80 100644
--- a/hostsidetests/theme/assets/xhdpi.zip
+++ b/hostsidetests/theme/assets/xhdpi.zip
Binary files differ
diff --git a/hostsidetests/theme/assets/xxhdpi.zip b/hostsidetests/theme/assets/xxhdpi.zip
index 60529ca..05f2b8d 100644
--- a/hostsidetests/theme/assets/xxhdpi.zip
+++ b/hostsidetests/theme/assets/xxhdpi.zip
Binary files differ
diff --git a/hostsidetests/theme/assets/24/xxxhdpi.zip b/hostsidetests/theme/assets/xxxhdpi.zip
similarity index 83%
rename from hostsidetests/theme/assets/24/xxxhdpi.zip
rename to hostsidetests/theme/assets/xxxhdpi.zip
index c058118..f4e0713 100644
--- a/hostsidetests/theme/assets/24/xxxhdpi.zip
+++ b/hostsidetests/theme/assets/xxxhdpi.zip
Binary files differ
diff --git a/hostsidetests/theme/run_theme_capture_device.py b/hostsidetests/theme/run_theme_capture_device.py
index 8d4c72f..a516717 100755
--- a/hostsidetests/theme/run_theme_capture_device.py
+++ b/hostsidetests/theme/run_theme_capture_device.py
@@ -97,7 +97,6 @@
     print "Found device: " + deviceSerial
     device = androidDevice(deviceSerial)
 
-    outPath = outPath + "/%d" % (device.getSdkLevel())
     density = device.getDensity()
     if CTS_THEME_dict.has_key(density):
         resName = CTS_THEME_dict[density]
diff --git a/hostsidetests/theme/src/android/theme/cts/ComparisonTask.java b/hostsidetests/theme/src/android/theme/cts/ComparisonTask.java
index 5f4a741..d9ed8a1 100755
--- a/hostsidetests/theme/src/android/theme/cts/ComparisonTask.java
+++ b/hostsidetests/theme/src/android/theme/cts/ComparisonTask.java
@@ -36,6 +36,9 @@
 
     private static final int IMAGE_THRESHOLD = 2;
 
+    /** Maximum allowable number of consecutive failed pixels. */
+    private static final int MAX_CONSECUTIVE_FAILURES = 1;
+
     private final File mExpected;
     private final File mActual;
 
@@ -95,6 +98,8 @@
         }
 
         for (int i = 0; i < w; i++) {
+            int consecutive = 0;
+
             for (int j = 0; j < h; j++) {
                 final int p1 = reference.getRGB(i, j);
                 final int p2 = generated.getRGB(i, j);
@@ -106,7 +111,13 @@
                 if (Math.abs(db) > threshold ||
                         Math.abs(dg) > threshold ||
                         Math.abs(dr) > threshold) {
-                    return false;
+                    consecutive++;
+
+                    if (consecutive > MAX_CONSECUTIVE_FAILURES) {
+                        return false;
+                    }
+                } else {
+                    consecutive = 0;
                 }
             }
         }
diff --git a/tests/app/app/AndroidManifest.xml b/tests/app/app/AndroidManifest.xml
index 10a6792..b5460a6 100644
--- a/tests/app/app/AndroidManifest.xml
+++ b/tests/app/app/AndroidManifest.xml
@@ -329,6 +329,21 @@
 
         <activity android:name="android.app.stubs.KeyboardShortcutsActivity" />
 
+        <service
+            android:name="android.app.stubs.LiveWallpaper"
+            android:icon="@drawable/robot"
+            android:label="@string/wallpaper_title"
+            android:permission="android.permission.BIND_WALLPAPER">
+            <intent-filter>
+                <action android:name="android.service.wallpaper.WallpaperService">
+                </action>
+            </intent-filter>
+            <meta-data
+                android:name="android.service.wallpaper"
+                android:resource="@xml/wallpaper">
+            </meta-data>
+        </service>
+
     </application>
 
 </manifest>
diff --git a/tests/app/app/res/values/strings.xml b/tests/app/app/res/values/strings.xml
index 5e9e6d7..839e398 100644
--- a/tests/app/app/res/values/strings.xml
+++ b/tests/app/app/res/values/strings.xml
@@ -177,4 +177,10 @@
 I think so, so how about double this string, like copy and paste! </string>
     <string name="rectangle200">"M 0,0 l 200,0 l 0, 200 l -200, 0 z"</string>
     <string name="hello">Hello World</string>
+
+    <string name="wallpaper_description">Description</string>
+    <string name="wallpaper_collection">Collection</string>
+    <string name="wallpaper_title">Title</string>
+    <string name="wallpaper_context">Context</string>
+    <string name="wallpaper_context_uri">http://android.com</string>
 </resources>
diff --git a/tests/app/app/res/xml/wallpaper.xml b/tests/app/app/res/xml/wallpaper.xml
new file mode 100644
index 0000000..f70b20f
--- /dev/null
+++ b/tests/app/app/res/xml/wallpaper.xml
@@ -0,0 +1,25 @@
+<?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
+  -->
+<wallpaper
+    xmlns:android="http://schemas.android.com/apk/res/android"
+    android:thumbnail="@drawable/icon_red"
+    android:author="@string/wallpaper_collection"
+    android:description="@string/wallpaper_description"
+    android:showMetadataInPreview="true"
+    android:contextDescription="@string/wallpaper_context"
+    android:contextUri="@string/wallpaper_context_uri"
+/>
\ No newline at end of file
diff --git a/tests/app/app/src/android/app/stubs/LiveWallpaper.java b/tests/app/app/src/android/app/stubs/LiveWallpaper.java
new file mode 100644
index 0000000..5b8a82e
--- /dev/null
+++ b/tests/app/app/src/android/app/stubs/LiveWallpaper.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.app.stubs;
+
+import android.service.wallpaper.WallpaperService;
+
+public class LiveWallpaper extends WallpaperService {
+
+    @Override
+    public Engine onCreateEngine() {
+        return new Engine();
+    }
+}
diff --git a/tests/app/src/android/app/cts/ActivityManagerMemoryClassTest.java b/tests/app/src/android/app/cts/ActivityManagerMemoryClassTest.java
index 2b84be6..fefa546 100644
--- a/tests/app/src/android/app/cts/ActivityManagerMemoryClassTest.java
+++ b/tests/app/src/android/app/cts/ActivityManagerMemoryClassTest.java
@@ -27,6 +27,8 @@
 import android.view.Display;
 import android.view.WindowManager;
 
+import com.android.compatibility.common.util.CddTest;
+
 import java.util.HashMap;
 import java.util.Map;
 
@@ -148,6 +150,7 @@
         }
     }
 
+    @CddTest(requirement="3.7")
     public void testGetMemoryClass() throws Exception {
         int memoryClass = getMemoryClass();
         int screenDensity = getScreenDensity();
diff --git a/tests/app/src/android/app/cts/DownloadManagerTest.java b/tests/app/src/android/app/cts/DownloadManagerTest.java
index 50eb96b..1bec983 100644
--- a/tests/app/src/android/app/cts/DownloadManagerTest.java
+++ b/tests/app/src/android/app/cts/DownloadManagerTest.java
@@ -34,6 +34,8 @@
 import android.util.Log;
 import android.webkit.cts.CtsTestServer;
 
+import com.android.compatibility.common.util.CddTest;
+
 import java.io.File;
 import java.util.Arrays;
 import java.util.HashSet;
@@ -181,6 +183,7 @@
         }
     }
 
+    @CddTest(requirement="7.6.1")
     public void testMinimumDownload() throws Exception {
         final DownloadCompleteReceiver receiver = new DownloadCompleteReceiver();
         try {
diff --git a/tests/app/src/android/app/cts/WallpaperInfoTest.java b/tests/app/src/android/app/cts/WallpaperInfoTest.java
new file mode 100644
index 0000000..1b30902
--- /dev/null
+++ b/tests/app/src/android/app/cts/WallpaperInfoTest.java
@@ -0,0 +1,58 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR 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 org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNotNull;
+
+import android.app.WallpaperInfo;
+import android.content.Context;
+import android.content.Intent;
+import android.content.pm.PackageManager;
+import android.content.pm.ResolveInfo;
+import android.service.wallpaper.WallpaperService;
+import android.support.test.InstrumentationRegistry;
+import android.support.test.runner.AndroidJUnit4;
+
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+import java.util.List;
+
+@RunWith(AndroidJUnit4.class)
+public class WallpaperInfoTest {
+
+    @Test
+    public void test() throws Exception {
+        Context context = InstrumentationRegistry.getTargetContext();
+        Intent intent = new Intent(WallpaperService.SERVICE_INTERFACE);
+        intent.setPackage("android.app.stubs");
+        PackageManager pm = context.getPackageManager();
+        List<ResolveInfo> result = pm.queryIntentServices(intent, PackageManager.GET_META_DATA);
+        assertEquals(1, result.size());
+        ResolveInfo info = result.get(0);
+        WallpaperInfo wallpaperInfo = new WallpaperInfo(context, info);
+        assertEquals("Title", wallpaperInfo.loadLabel(pm));
+        assertEquals("Description", wallpaperInfo.loadDescription(pm));
+        assertEquals("Collection", wallpaperInfo.loadAuthor(pm));
+        assertEquals("Context", wallpaperInfo.loadContextDescription(pm));
+        assertEquals("http://android.com", wallpaperInfo.loadContextUri(pm).toString());
+        assertEquals(true, wallpaperInfo.getShowMetadataInPreview());
+        assertNotNull(wallpaperInfo.loadIcon(pm));
+        assertNotNull(wallpaperInfo.loadThumbnail(pm));
+    }
+}
diff --git a/tests/camera/src/android/hardware/camera2/cts/CaptureRequestTest.java b/tests/camera/src/android/hardware/camera2/cts/CaptureRequestTest.java
index ebda3dd..ae5c2ea 100644
--- a/tests/camera/src/android/hardware/camera2/cts/CaptureRequestTest.java
+++ b/tests/camera/src/android/hardware/camera2/cts/CaptureRequestTest.java
@@ -2622,7 +2622,7 @@
             }
         }
 
-        mSession.stopRepeating();
+        stopPreview();
     }
 
     /**
diff --git a/tests/camera/src/android/hardware/camera2/cts/DngCreatorTest.java b/tests/camera/src/android/hardware/camera2/cts/DngCreatorTest.java
index 6ab55b0..8f32d52 100644
--- a/tests/camera/src/android/hardware/camera2/cts/DngCreatorTest.java
+++ b/tests/camera/src/android/hardware/camera2/cts/DngCreatorTest.java
@@ -40,6 +40,7 @@
 import android.media.Image;
 import android.media.ImageReader;
 import android.os.ConditionVariable;
+import android.os.SystemClock;
 import android.util.Log;
 import android.util.Pair;
 import android.util.Size;
@@ -54,8 +55,10 @@
 import java.util.Arrays;
 import java.util.Calendar;
 import java.util.Collections;
+import java.util.Date;
 import java.util.List;
 import java.util.TimeZone;
+import java.text.SimpleDateFormat;
 
 import static android.hardware.camera2.cts.helpers.AssertHelpers.*;
 
@@ -243,10 +246,19 @@
                         previewListener));
                 captureListeners.add(previewListener);
 
+                Date beforeCaptureDate = new Date();
                 Pair<List<Image>, CaptureResult> resultPair = captureSingleRawShot(activeArraySize,
                         captureReaders, /*waitForAe*/false, captureListeners);
+                Date afterCaptureDate = new Date();
                 CameraCharacteristics characteristics = mStaticInfo.getCharacteristics();
 
+                if (VERBOSE) {
+                    Log.v(TAG, "Sensor timestamp (ms): " +
+                            resultPair.second.get(CaptureResult.SENSOR_TIMESTAMP) / 1000000);
+                    Log.v(TAG, "SystemClock.elapsedRealtimeNanos (ms): " +
+                            SystemClock.elapsedRealtimeNanos() / 1000000);
+                    Log.v(TAG, "SystemClock.uptimeMillis(): " + SystemClock.uptimeMillis());
+                }
                 // Test simple writeImage, no header checks
                 DngCreator dngCreator = new DngCreator(characteristics, resultPair.second);
                 Location l = new Location("test");
@@ -264,7 +276,7 @@
 
                 String filePath = DEBUG_FILE_NAME_BASE + "/camera_thumb_" + deviceId + "_" +
                         DEBUG_DNG_FILE;
-                // Write out captured DNG file for the first camera device if setprop is enabled
+                // Write out captured DNG file for the first camera device
                 fileStream = new FileOutputStream(filePath);
                 fileStream.write(outputStream.toByteArray());
                 fileStream.flush();
@@ -292,6 +304,30 @@
                         exifInterface.getAttributeInt(ExifInterface.TAG_ORIENTATION,
                                 ExifInterface.ORIENTATION_UNDEFINED));
 
+                // Verify the date/time
+                final SimpleDateFormat dngDateTimeStampFormat =
+                        new SimpleDateFormat("yyyy:MM:dd HH:mm:ss");
+                dngDateTimeStampFormat.setLenient(false);
+
+                String dateTimeString =
+                        exifInterface.getAttribute(ExifInterface.TAG_DATETIME);
+                assertTrue(dateTimeString != null);
+
+                Date dateTime = dngDateTimeStampFormat.parse(dateTimeString);
+                long captureTimeMs = dateTime.getTime();
+
+                Log.i(TAG, "DNG DateTime tag: " + dateTimeString);
+                Log.i(TAG, "Before capture time: " + beforeCaptureDate.getTime());
+                Log.i(TAG, "Capture time: " + captureTimeMs);
+                Log.i(TAG, "After capture time: " + afterCaptureDate.getTime());
+
+                // Offset beforeCaptureTime by 1 second to account for rounding down of
+                // DNG tag
+                long beforeCaptureTimeMs = beforeCaptureDate.getTime() - 1000;
+                long afterCaptureTimeMs = afterCaptureDate.getTime();
+                assertTrue(captureTimeMs >= beforeCaptureTimeMs);
+                assertTrue(captureTimeMs <= afterCaptureTimeMs);
+
                 if (!VERBOSE) {
                     // Delete the captured DNG file.
                     File dngFile = new File(filePath);
diff --git a/tests/camera/src/android/hardware/camera2/cts/SurfaceViewPreviewTest.java b/tests/camera/src/android/hardware/camera2/cts/SurfaceViewPreviewTest.java
index aec5685..2a49857 100644
--- a/tests/camera/src/android/hardware/camera2/cts/SurfaceViewPreviewTest.java
+++ b/tests/camera/src/android/hardware/camera2/cts/SurfaceViewPreviewTest.java
@@ -461,7 +461,13 @@
 
         for (int i = 0; i < fpsRanges.length; i += 1) {
             fpsRange = fpsRanges[i];
-            maxPreviewSz = getMaxPreviewSizeForFpsRange(fpsRange);
+            if (mStaticInfo.isHardwareLevelLegacy()) {
+                // Legacy devices don't report minimum frame duration for preview sizes. The FPS
+                // range should be valid for any supported preview size.
+                maxPreviewSz = mOrderedPreviewSizes.get(0);
+            } else {
+                maxPreviewSz = getMaxPreviewSizeForFpsRange(fpsRange);
+            }
 
             requestBuilder.set(CaptureRequest.CONTROL_AE_TARGET_FPS_RANGE, fpsRange);
             // Turn off auto antibanding to avoid exposure time and frame duration interference
diff --git a/tests/camera/src/android/hardware/camera2/cts/testcases/Camera2SurfaceViewTestCase.java b/tests/camera/src/android/hardware/camera2/cts/testcases/Camera2SurfaceViewTestCase.java
index 8d141c4..a95f4f3 100644
--- a/tests/camera/src/android/hardware/camera2/cts/testcases/Camera2SurfaceViewTestCase.java
+++ b/tests/camera/src/android/hardware/camera2/cts/testcases/Camera2SurfaceViewTestCase.java
@@ -222,9 +222,11 @@
      * Does _not_ wait for the device to go idle
      */
     protected void stopPreview() throws Exception {
-        if (VERBOSE) Log.v(TAG, "Stopping preview");
         // Stop repeat, wait for captures to complete, and disconnect from surfaces
-        mSession.close();
+        if (mSession != null) {
+            if (VERBOSE) Log.v(TAG, "Stopping preview");
+            mSession.close();
+        }
     }
 
     /**
@@ -232,11 +234,13 @@
      * resulting in an idle device.
      */
     protected void stopPreviewAndDrain() throws Exception {
-        if (VERBOSE) Log.v(TAG, "Stopping preview and waiting for idle");
         // Stop repeat, wait for captures to complete, and disconnect from surfaces
-        mSession.close();
-        mSessionListener.getStateWaiter().waitForState(BlockingSessionCallback.SESSION_CLOSED,
-                /*timeoutMs*/WAIT_FOR_RESULT_TIMEOUT_MS);
+        if (mSession != null) {
+            if (VERBOSE) Log.v(TAG, "Stopping preview and waiting for idle");
+            mSession.close();
+            mSessionListener.getStateWaiter().waitForState(BlockingSessionCallback.SESSION_CLOSED,
+                    /*timeoutMs*/WAIT_FOR_RESULT_TIMEOUT_MS);
+        }
     }
 
     /**
diff --git a/tests/filesystem/src/android/filesystem/cts/RandomRWTest.java b/tests/filesystem/src/android/filesystem/cts/RandomRWTest.java
index f30c7a3..a2df743 100644
--- a/tests/filesystem/src/android/filesystem/cts/RandomRWTest.java
+++ b/tests/filesystem/src/android/filesystem/cts/RandomRWTest.java
@@ -18,6 +18,7 @@
 
 import android.cts.util.CtsAndroidTestCase;
 
+import com.android.compatibility.common.util.CddTest;
 import com.android.compatibility.common.util.DeviceReportLog;
 
 public class RandomRWTest extends CtsAndroidTestCase {
@@ -32,6 +33,7 @@
         super.tearDown();
     }
 
+    @CddTest(requirement="8.2")
     public void testRandomRead() throws Exception {
         final int READ_BUFFER_SIZE = 4 * 1024;
         final long fileSize = FileUtil.getFileSizeExceedingMemory(getContext(), READ_BUFFER_SIZE);
@@ -46,6 +48,7 @@
     }
 
     // It is taking too long in some device, and thus cannot run multiple times
+    @CddTest(requirement="8.2")
     public void testRandomUpdate() throws Exception {
         final int WRITE_BUFFER_SIZE = 4 * 1024;
         final long fileSize = 256 * 1024 * 1024;
diff --git a/tests/filesystem/src/android/filesystem/cts/SequentialRWTest.java b/tests/filesystem/src/android/filesystem/cts/SequentialRWTest.java
index c19d03c..2db2f24 100644
--- a/tests/filesystem/src/android/filesystem/cts/SequentialRWTest.java
+++ b/tests/filesystem/src/android/filesystem/cts/SequentialRWTest.java
@@ -25,6 +25,8 @@
 import com.android.compatibility.common.util.ResultUnit;
 import com.android.compatibility.common.util.Stat;
 
+import com.android.compatibility.common.util.CddTest;
+
 import java.io.File;
 import java.io.FileInputStream;
 import java.io.IOException;
@@ -44,6 +46,7 @@
         super.tearDown();
     }
 
+    @CddTest(requirement="8.2")
     public void testSingleSequentialWrite() throws Exception {
         final long fileSize = FileUtil.getFileSizeExceedingMemory(getContext(), BUFFER_SIZE);
         if (fileSize == 0) { // not enough space, give up
@@ -85,6 +88,7 @@
                 NUMBER_REPETITION, REPORT_LOG_NAME, streamName);
     }
 
+    @CddTest(requirement="8.2")
     public void testSingleSequentialRead() throws Exception {
         final long fileSize = FileUtil.getFileSizeExceedingMemory(getContext(), BUFFER_SIZE);
         if (fileSize == 0) { // not enough space, give up
diff --git a/tests/signature/src/android/signature/cts/JDiffClassDescription.java b/tests/signature/src/android/signature/cts/JDiffClassDescription.java
index 2e13650..92f56bd 100644
--- a/tests/signature/src/android/signature/cts/JDiffClassDescription.java
+++ b/tests/signature/src/android/signature/cts/JDiffClassDescription.java
@@ -1213,7 +1213,9 @@
     private static String scrubJdiffParamType(String paramType) {
         // <? extends java.lang.Object and <?> are the same, so
         // canonicalize them to one form.
-        return paramType.replace("<? extends java.lang.Object>", "<?>");
+        return paramType
+            .replace("? extends java.lang.Object", "?")
+            .replace("? super java.lang.Object", "? super ?");
     }
 
     /**
diff --git a/tests/tests/content/src/android/content/cts/IntentTest.java b/tests/tests/content/src/android/content/cts/IntentTest.java
index 65da548..dbbbe15 100644
--- a/tests/tests/content/src/android/content/cts/IntentTest.java
+++ b/tests/tests/content/src/android/content/cts/IntentTest.java
@@ -38,6 +38,8 @@
 import android.util.AttributeSet;
 import android.util.Xml;
 
+import com.android.content.cts.DummyParcelable;
+
 import java.io.IOException;
 import java.io.Serializable;
 import java.net.URISyntaxException;
@@ -1750,6 +1752,49 @@
         assertEquals("foo/bar", Intent.normalizeMimeType("   foo/bar    "));
     }
 
+    public void testRemoveUnsafeExtras() {
+        final Intent intent = new Intent();
+        final DummyParcelable dummyParcelable = new DummyParcelable();
+        intent.removeUnsafeExtras();
+        assertNull(intent.getExtras());
+
+        // Check that removeUnsafeExtras keeps the same bundle if no changes are made.
+        Bundle origExtras = new Bundle();
+        origExtras.putString("foo", "bar");
+        intent.replaceExtras(origExtras);
+        intent.removeUnsafeExtras();
+        Bundle newExtras = intent.getExtras();
+        assertEquals(1, newExtras.size());
+        assertEquals("bar", newExtras.get("foo"));
+
+        // Check that removeUnsafeExtras will strip non-framework parcelables without modifying
+        // the original extras bundle.
+        origExtras.putParcelable("baddy", dummyParcelable);
+        intent.replaceExtras(origExtras);
+        intent.removeUnsafeExtras();
+        newExtras = intent.getExtras();
+        assertEquals(1, newExtras.size());
+        assertEquals("bar", newExtras.get("foo"));
+        assertEquals(2, origExtras.size());
+        assertEquals("bar", origExtras.get("foo"));
+        assertSame(dummyParcelable, origExtras.get("baddy"));
+
+        // Check that nested bad values will be stripped.
+        Bundle origSubExtras = new Bundle();
+        origSubExtras.putParcelable("baddy", dummyParcelable);
+        origExtras.putBundle("baddy", origSubExtras);
+        intent.replaceExtras(origExtras);
+        intent.removeUnsafeExtras();
+        newExtras = intent.getExtras();
+        assertEquals(2, newExtras.size());
+        assertEquals("bar", newExtras.get("foo"));
+        Bundle newSubExtras = newExtras.getBundle("baddy");
+        assertNotSame(origSubExtras, newSubExtras);
+        assertEquals(0, newSubExtras.size());
+        assertEquals(1, origSubExtras.size());
+        assertSame(dummyParcelable, origSubExtras.get("baddy"));
+    }
+
     private static class TestSerializable implements Serializable {
         static final long serialVersionUID = 1l;
         public String Name;
diff --git a/tests/tests/content/src/com/android/content/cts/DummyParcelable.java b/tests/tests/content/src/com/android/content/cts/DummyParcelable.java
new file mode 100644
index 0000000..5eb8418
--- /dev/null
+++ b/tests/tests/content/src/com/android/content/cts/DummyParcelable.java
@@ -0,0 +1,42 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.content.cts;
+
+import android.os.Parcel;
+import android.os.Parcelable;
+
+public class DummyParcelable implements Parcelable {
+    @Override
+    public int describeContents() {
+        return 0;
+    }
+
+    @Override
+    public void writeToParcel(Parcel dest, int flags) {
+    }
+
+    public static final Parcelable.Creator<DummyParcelable> CREATOR
+            = new Parcelable.Creator<DummyParcelable>() {
+        public DummyParcelable createFromParcel(Parcel in) {
+            return new DummyParcelable();
+        }
+
+        public DummyParcelable[] newArray(int size) {
+            return new DummyParcelable[size];
+        }
+    };
+}
diff --git a/tests/tests/graphics/Android.mk b/tests/tests/graphics/Android.mk
index 7e26e07..d4d0d33 100644
--- a/tests/tests/graphics/Android.mk
+++ b/tests/tests/graphics/Android.mk
@@ -32,8 +32,6 @@
 # Tag this module as a cts test artifact
 LOCAL_COMPATIBILITY_SUITE := cts
 
-LOCAL_SDK_VERSION := current
-
 include $(BUILD_CTS_PACKAGE)
 
 include $(call all-makefiles-under,$(LOCAL_PATH))
diff --git a/tests/tests/graphics/res/drawable-nodpi/bitmap_shader_am_density_golden_160.png b/tests/tests/graphics/res/drawable-nodpi/bitmap_shader_am_density_golden_160.png
new file mode 100644
index 0000000..2e77270
--- /dev/null
+++ b/tests/tests/graphics/res/drawable-nodpi/bitmap_shader_am_density_golden_160.png
Binary files differ
diff --git a/tests/tests/graphics/res/drawable-nodpi/bitmap_shader_am_density_golden_320.png b/tests/tests/graphics/res/drawable-nodpi/bitmap_shader_am_density_golden_320.png
new file mode 100644
index 0000000..5a5c3d2
--- /dev/null
+++ b/tests/tests/graphics/res/drawable-nodpi/bitmap_shader_am_density_golden_320.png
Binary files differ
diff --git a/tests/tests/graphics/res/drawable-nodpi/bitmap_shader_am_density_golden_80.png b/tests/tests/graphics/res/drawable-nodpi/bitmap_shader_am_density_golden_80.png
new file mode 100644
index 0000000..611b27b
--- /dev/null
+++ b/tests/tests/graphics/res/drawable-nodpi/bitmap_shader_am_density_golden_80.png
Binary files differ
diff --git a/tests/tests/graphics/res/drawable-nodpi/bitmap_shader_density_golden_160.png b/tests/tests/graphics/res/drawable-nodpi/bitmap_shader_density_golden_160.png
new file mode 100644
index 0000000..2e77270
--- /dev/null
+++ b/tests/tests/graphics/res/drawable-nodpi/bitmap_shader_density_golden_160.png
Binary files differ
diff --git a/tests/tests/graphics/res/drawable-nodpi/bitmap_shader_density_golden_320.png b/tests/tests/graphics/res/drawable-nodpi/bitmap_shader_density_golden_320.png
new file mode 100644
index 0000000..e8beaa5
--- /dev/null
+++ b/tests/tests/graphics/res/drawable-nodpi/bitmap_shader_density_golden_320.png
Binary files differ
diff --git a/tests/tests/graphics/res/drawable-nodpi/bitmap_shader_density_golden_80.png b/tests/tests/graphics/res/drawable-nodpi/bitmap_shader_density_golden_80.png
new file mode 100644
index 0000000..b869ed7
--- /dev/null
+++ b/tests/tests/graphics/res/drawable-nodpi/bitmap_shader_density_golden_80.png
Binary files differ
diff --git a/tests/tests/graphics/res/drawable/bitmap_shader_am_density.xml b/tests/tests/graphics/res/drawable/bitmap_shader_am_density.xml
new file mode 100644
index 0000000..dfecfbb
--- /dev/null
+++ b/tests/tests/graphics/res/drawable/bitmap_shader_am_density.xml
@@ -0,0 +1,22 @@
+<?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.
+-->
+
+<bitmap xmlns:android="http://schemas.android.com/apk/res/android"
+        android:src="@drawable/bitmap_shader_density_internal"
+        android:tileModeX="repeat"
+        android:tileModeY="clamp"
+        android:autoMirrored="true" />
diff --git a/tests/tests/graphics/res/drawable/bitmap_shader_am_density_internal.png b/tests/tests/graphics/res/drawable/bitmap_shader_am_density_internal.png
new file mode 100644
index 0000000..b6d4d89
--- /dev/null
+++ b/tests/tests/graphics/res/drawable/bitmap_shader_am_density_internal.png
Binary files differ
diff --git a/tests/tests/graphics/res/drawable/bitmap_shader_density.xml b/tests/tests/graphics/res/drawable/bitmap_shader_density.xml
new file mode 100644
index 0000000..435b06a
--- /dev/null
+++ b/tests/tests/graphics/res/drawable/bitmap_shader_density.xml
@@ -0,0 +1,21 @@
+<?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.
+-->
+
+<bitmap xmlns:android="http://schemas.android.com/apk/res/android"
+        android:src="@drawable/bitmap_shader_density_internal"
+        android:tileModeX="repeat"
+        android:tileModeY="clamp" />
diff --git a/tests/tests/graphics/res/drawable/bitmap_shader_density_internal.png b/tests/tests/graphics/res/drawable/bitmap_shader_density_internal.png
new file mode 100644
index 0000000..b6d4d89
--- /dev/null
+++ b/tests/tests/graphics/res/drawable/bitmap_shader_density_internal.png
Binary files differ
diff --git a/tests/tests/graphics/res/drawable/custom_animation_scale_list_drawable.xml b/tests/tests/graphics/res/drawable/custom_animation_scale_list_drawable.xml
new file mode 100644
index 0000000..1e9fbff
--- /dev/null
+++ b/tests/tests/graphics/res/drawable/custom_animation_scale_list_drawable.xml
@@ -0,0 +1,22 @@
+<?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.
+ -->
+
+<com.android.internal.graphics.drawable.AnimationScaleListDrawable
+        xmlns:android="http://schemas.android.com/apk/res/android">
+    <item android:drawable="@drawable/vector_icon_create" />
+    <item android:drawable="@drawable/animation_vector_drawable_grouping_1" />
+</com.android.internal.graphics.drawable.AnimationScaleListDrawable>
diff --git a/tests/tests/graphics/res/drawable/layerdrawable_theme.xml b/tests/tests/graphics/res/drawable/layerdrawable_theme.xml
index 2a678ff..fac42b2 100644
--- a/tests/tests/graphics/res/drawable/layerdrawable_theme.xml
+++ b/tests/tests/graphics/res/drawable/layerdrawable_theme.xml
@@ -25,5 +25,6 @@
             android:dither="?attr/themeBoolean"
             android:src="?attr/themeNinePatch" />
     </item>
+    <item android:drawable="?attr/themeDrawable" />
 
-</layer-list>
\ No newline at end of file
+</layer-list>
diff --git a/tests/tests/graphics/src/android/graphics/drawable/cts/BitmapDrawableTest.java b/tests/tests/graphics/src/android/graphics/drawable/cts/BitmapDrawableTest.java
index 755da01..f9592dc 100644
--- a/tests/tests/graphics/src/android/graphics/drawable/cts/BitmapDrawableTest.java
+++ b/tests/tests/graphics/src/android/graphics/drawable/cts/BitmapDrawableTest.java
@@ -41,6 +41,7 @@
 import android.graphics.drawable.Drawable.ConstantState;
 import android.test.InstrumentationTestCase;
 import android.util.AttributeSet;
+import android.util.LayoutDirection;
 import android.util.Xml;
 import android.view.Gravity;
 
@@ -506,29 +507,25 @@
     };
 
     private static final int[] DENSITY_IMAGES = new int[] {
-            R.drawable.bitmap_density
-    };
-
-    private static final int[][] DENSITY_GOLDEN_IMAGES = new int[][] {
-            {
-                    R.drawable.bitmap_density_golden_160,
-                    R.drawable.bitmap_density_golden_80,
-                    R.drawable.bitmap_density_golden_320,
-            }
+            R.drawable.bitmap_density,
+            R.drawable.bitmap_shader_density,
+            R.drawable.bitmap_shader_am_density,
     };
 
     public void testPreloadDensity() throws XmlPullParserException, IOException {
         final Resources res = mContext.getResources();
         final int densityDpi = res.getConfiguration().densityDpi;
         try {
-            testPreloadDensityInner(res, DENSITY_IMAGES[0], DENSITY_VALUES, DENSITY_GOLDEN_IMAGES[0]);
+            for (int i = 0; i < DENSITY_IMAGES.length; i++) {
+                testPreloadDensityInner(res, DENSITY_IMAGES[i], DENSITY_VALUES);
+            }
         } finally {
             DrawableTestUtils.setResourcesDensity(res, densityDpi);
         }
     }
 
-    private void testPreloadDensityInner(Resources res, int sourceResId, int[] densities,
-            int[] goldenResIds) throws XmlPullParserException, IOException {
+    private void testPreloadDensityInner(Resources res, int sourceResId, int[] densities)
+            throws XmlPullParserException, IOException {
         final Rect tempPadding = new Rect();
 
         // Capture initial state at preload density.
@@ -544,7 +541,7 @@
         final int origHeight = preloadedDrawable.getIntrinsicHeight();
         assertFalse(preloadedDrawable.getPadding(tempPadding));
 
-        compareOrSave(preloadedDrawable, preloadDensityDpi, sourceResId, goldenResIds[0]);
+        compareOrSave(preloadedDrawable, preloadDensityDpi, sourceResId);
 
         for (int i = 1; i < densities.length; i++) {
             final int scaledDensityDpi = densities[i];
@@ -553,6 +550,7 @@
 
             final BitmapDrawable scaledDrawable =
                     (BitmapDrawable) preloadedConstantState.newDrawable(res);
+            scaledDrawable.setLayoutDirection(LayoutDirection.RTL);
 
             // Sizes are rounded.
             assertEquals(Math.round(origWidth * scale), scaledDrawable.getIntrinsicWidth());
@@ -561,7 +559,7 @@
             // Bitmaps have no padding.
             assertFalse(scaledDrawable.getPadding(tempPadding));
 
-            compareOrSave(scaledDrawable, scaledDensityDpi, sourceResId, goldenResIds[i]);
+            compareOrSave(scaledDrawable, scaledDensityDpi, sourceResId);
 
             // Ensure theme density is applied correctly. Unlike most
             // drawables, we don't have any loss of accuracy because density
@@ -576,7 +574,7 @@
         }
     }
 
-    private void compareOrSave(Drawable dr, int densityDpi, int sourceResId, int goldenResId) {
+    private void compareOrSave(Drawable dr, int densityDpi, int sourceResId) {
         final int width = dr.getIntrinsicWidth();
         final int height = dr.getIntrinsicHeight();
         final Bitmap bitmap = Bitmap.createBitmap(width, height, Bitmap.Config.ARGB_8888);
@@ -589,6 +587,7 @@
         if (DBG_DUMP_PNG) {
             saveGoldenImage(bitmap, sourceResId, densityDpi);
         } else {
+            final int goldenResId = getGoldenImageResId(sourceResId, densityDpi);
             final Bitmap golden = BitmapFactory.decodeResource(
                     mContext.getResources(), goldenResId);
             DrawableTestUtils.compareImages(densityDpi + " dpi", golden, bitmap,
@@ -596,28 +595,32 @@
         }
     }
 
+    private int getGoldenImageResId(int sourceResId, int densityDpi) {
+        final String name = getGoldenImageName(sourceResId, densityDpi);
+        return mContext.getResources().getIdentifier(name, "drawable", mContext.getPackageName());
+    }
+
+    private String getGoldenImageName(int sourceResId, int densityDpi) {
+        return mContext.getResources().getResourceEntryName(sourceResId) + "_golden_" + densityDpi;
+    }
+
     private void saveGoldenImage(Bitmap bitmap, int sourceResId, int densityDpi) {
         // Save the image to the disk.
         FileOutputStream out = null;
 
         try {
-            final String outputFolder = "/sdcard/temp/";
-            final File folder = new File(outputFolder);
-            if (!folder.exists()) {
-                folder.mkdir();
+            final File outputFolder = new File("/sdcard/temp/");
+            if (!outputFolder.exists()) {
+                outputFolder.mkdir();
             }
 
-            final String sourceFilename = new File(
-                    mContext.getResources().getString(sourceResId)).getName();
-            final String sourceTitle = sourceFilename.substring(0, sourceFilename.lastIndexOf("."));
-            final String outputTitle = sourceTitle + "_golden_" + densityDpi;
-            final String outputFilename = outputFolder + outputTitle + ".png";
-            final File outputFile = new File(outputFilename);
-            if (!outputFile.exists()) {
-                outputFile.createNewFile();
+            final String goldenFilename = getGoldenImageName(sourceResId, densityDpi) + ".png";
+            final File goldenFile = new File(outputFolder, goldenFilename);
+            if (!goldenFile.exists()) {
+                goldenFile.createNewFile();
             }
 
-            out = new FileOutputStream(outputFile, false);
+            out = new FileOutputStream(goldenFile, false);
             bitmap.compress(Bitmap.CompressFormat.PNG, 100, out);
         } catch (Exception e) {
             e.printStackTrace();
diff --git a/tests/tests/graphics/src/android/graphics/drawable/cts/CustomAnimationScaleListDrawableTest.java b/tests/tests/graphics/src/android/graphics/drawable/cts/CustomAnimationScaleListDrawableTest.java
new file mode 100644
index 0000000..3445641
--- /dev/null
+++ b/tests/tests/graphics/src/android/graphics/drawable/cts/CustomAnimationScaleListDrawableTest.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.graphics.drawable.cts;
+
+import android.animation.ValueAnimator;
+import android.graphics.cts.R;
+import android.graphics.drawable.Animatable;
+import android.graphics.drawable.Drawable;
+import android.graphics.drawable.DrawableContainer;
+import android.test.AndroidTestCase;
+import android.test.suitebuilder.annotation.MediumTest;
+
+/**
+ * This test is used to verify that the CustomAnimationScaleListDrawable's current drawable depends
+ * on animation duration scale. When the scale is 0, it is a static drawable, otherwise, it is an
+ * animatable drawable.
+ */
+public class CustomAnimationScaleListDrawableTest extends AndroidTestCase {
+    @MediumTest
+    public void testNonZeroDurationScale() {
+        float originalScale = ValueAnimator.getDurationScale();
+        ValueAnimator.setDurationScale(2.0f);
+        Drawable dr = getContext().getDrawable(R.drawable.custom_animation_scale_list_drawable);
+        assertTrue(dr instanceof DrawableContainer);
+
+        assertTrue(dr.getCurrent() instanceof Animatable);
+        ValueAnimator.setDurationScale(originalScale);
+    }
+
+    @MediumTest
+    public void testZeroDurationScale() {
+        float originalScale = ValueAnimator.getDurationScale();
+        ValueAnimator.setDurationScale(0f);
+        Drawable dr = getContext().getDrawable(R.drawable.custom_animation_scale_list_drawable);
+        assertTrue(dr instanceof DrawableContainer);
+        assertFalse(dr.getCurrent() instanceof Animatable);
+        ValueAnimator.setDurationScale(originalScale);
+    }
+}
diff --git a/tests/tests/graphics/src/android/graphics/drawable/cts/ThemedDrawableTest.java b/tests/tests/graphics/src/android/graphics/drawable/cts/ThemedDrawableTest.java
index 52bef55..7c6fe7c 100644
--- a/tests/tests/graphics/src/android/graphics/drawable/cts/ThemedDrawableTest.java
+++ b/tests/tests/graphics/src/android/graphics/drawable/cts/ThemedDrawableTest.java
@@ -22,6 +22,7 @@
 import android.graphics.Color;
 import android.graphics.Rect;
 import android.graphics.Shader.TileMode;
+import android.graphics.drawable.Drawable;
 import android.graphics.drawable.BitmapDrawable;
 import android.graphics.drawable.ColorDrawable;
 import android.graphics.drawable.GradientDrawable;
@@ -34,7 +35,6 @@
 
 import android.graphics.cts.R;
 
-@TargetApi(21)
 public class ThemedDrawableTest extends AndroidTestCase {
 
     @Override
@@ -150,9 +150,14 @@
         assertEquals(true, d.isAutoMirrored());
 
         BitmapDrawable bitmapDrawable  = (BitmapDrawable) d.getDrawable(0);
+        assertEquals(d, bitmapDrawable.getCallback());
         internalTestBitmapDrawable(bitmapDrawable);
 
         NinePatchDrawable ninePatchDrawable = (NinePatchDrawable) d.getDrawable(1);
+        assertEquals(d, ninePatchDrawable.getCallback());
         internalTestNinePatchDrawable(ninePatchDrawable);
+
+        Drawable themeDrawable = (Drawable) d.getDrawable(2);
+        assertEquals(d, themeDrawable.getCallback());
     }
 }
diff --git a/tests/tests/graphics/src/android/opengl/cts/EglConfigTest.java b/tests/tests/graphics/src/android/opengl/cts/EglConfigTest.java
index 33e82043..1468382 100644
--- a/tests/tests/graphics/src/android/opengl/cts/EglConfigTest.java
+++ b/tests/tests/graphics/src/android/opengl/cts/EglConfigTest.java
@@ -63,6 +63,8 @@
             EglConfigCtsActivity activity = launchActivity("android.graphics.cts",
                     EglConfigCtsActivity.class, extras);
             activity.waitToFinishDrawing();
+            // TODO(b/30948621): Remove the sleep below once b/30948621 is fixed.
+            Thread.sleep(500);
             activity.finish();
             mInstrumentation.waitForIdleSync();
         }
diff --git a/tests/tests/location/src/android/location/cts/GnssMeasurementWhenNoLocationTest.java b/tests/tests/location/src/android/location/cts/GnssMeasurementWhenNoLocationTest.java
index 0f4775e..32c54c9 100644
--- a/tests/tests/location/src/android/location/cts/GnssMeasurementWhenNoLocationTest.java
+++ b/tests/tests/location/src/android/location/cts/GnssMeasurementWhenNoLocationTest.java
@@ -135,7 +135,13 @@
         List<GnssMeasurementsEvent> events = mMeasurementListener.getEvents();
         Log.i(TAG, "Number of GPS measurement events received = " + events.size());
 
-        // Ensure that after getting a few (at least 2) measurements, that we still don't have
+        if (events.isEmpty()) {
+            SoftAssert.failOrWarning(isMeasurementTestStrict(), "No measurement events received",
+                    false);
+            return;  // All of the following checks rely on there being measurements
+        }
+
+        // Ensure that after getting a few (at least 2) measurement events, that we still don't have
         // location (i.e. that we got measurements before location.)  Fail, if strict, warn, if not.
         SoftAssert.failOrWarning(isMeasurementTestStrict(),
                 "Location was received before " + events.size() +
@@ -149,9 +155,8 @@
             return; // allow a (passing) return, if not strict, otherwise continue
         }
 
-        // If device is not indoors, verify
-        // 1) that we receive GPS measurements before being able to calculate the position solution
-        // 2) that mandatory fields of GnssMeasurement are in expected ranges.
+        // If device has received measurements also verify
+        // that mandatory fields of GnssMeasurement are in expected ranges.
         GnssMeasurementsEvent firstEvent = events.get(0);
         Collection<GnssMeasurement> gpsMeasurements = firstEvent.getMeasurements();
         int satelliteCount = gpsMeasurements.size();
diff --git a/tests/tests/location/src/android/location/cts/SoftAssert.java b/tests/tests/location/src/android/location/cts/SoftAssert.java
index 3913149..4349f07 100644
--- a/tests/tests/location/src/android/location/cts/SoftAssert.java
+++ b/tests/tests/location/src/android/location/cts/SoftAssert.java
@@ -156,7 +156,9 @@
         if (testIsStrict) {
             Assert.assertTrue(message, condition);
         } else {
-            failAsWarning("", message);
+            if (!condition) {
+                failAsWarning("", message);
+            }
         }
     }
 
diff --git a/tests/tests/location/src/android/location/cts/TestMeasurementUtil.java b/tests/tests/location/src/android/location/cts/TestMeasurementUtil.java
index 0b2d875..217d8eb 100644
--- a/tests/tests/location/src/android/location/cts/TestMeasurementUtil.java
+++ b/tests/tests/location/src/android/location/cts/TestMeasurementUtil.java
@@ -28,8 +28,11 @@
 
 import junit.framework.Assert;
 
+import java.util.Arrays;
+import java.util.HashSet;
 import java.util.List;
 import java.util.concurrent.TimeUnit;
+import java.util.Set;
 
 /**
  * Helper class for GnssMeasurement Tests.
@@ -52,6 +55,21 @@
             " listener has failed, this indicates a platform bug. Please report the issue with" +
             " a full bugreport.";
 
+    // The valid Gnss navigation message type as listed in
+    // android/hardware/libhardware/include/hardware/gps.h
+    public static final Set<Integer> GNSS_NAVIGATION_MESSAGE_TYPE =
+        new HashSet<Integer>(Arrays.asList(
+            0x0101,
+            0x0102,
+            0x0103,
+            0x0104,
+            0x0301,
+            0x0501,
+            0x0502,
+            0x0601,
+            0x0602
+        ));
+
     /**
      * Check if test can be run on the current device.
      *
@@ -63,15 +81,12 @@
                                                     String testTag,
                                                     int minHardwareYear,
                                                     boolean isCtsVerifier) {
-       // TODO(sumitk): Enable this check once api 24 for N is avaiable.
-       /*
        if (Build.VERSION.SDK_INT < Build.VERSION_CODES.N) {
             Log.i(TAG, "This test is designed to work on N or newer. " +
                     "Test is being skipped because the platform version is being run in " +
                     Build.VERSION.SDK_INT);
             return false;
         }
-        */
 
         // If device does not have a GPS, skip the test.
         PackageManager pm = testLocationManager.getContext().getPackageManager();
@@ -670,9 +685,9 @@
         SoftAssert softAssert = new SoftAssert(TAG);
         for (GnssNavigationMessage message : events) {
             int type = message.getType();
-            softAssert.assertTrue("Gnss Navigation Message Type:expected [0x0101 - 0x0104]," +
-                            " actual = " + type,
-                    type >= 0x0101 && type <= 0x0104);
+            softAssert.assertTrue("Gnss Navigation Message Type:expected [" +
+                getGnssNavMessageTypes() + "] actual = " + type,
+                    GNSS_NAVIGATION_MESSAGE_TYPE.contains(type));
 
             // if message type == TYPE_L1CA, verify PRN & Data Size.
             int messageType = message.getType();
@@ -691,4 +706,14 @@
         }
         softAssert.assertAll();
     }
+
+    private static String getGnssNavMessageTypes() {
+        StringBuilder typesStr = new StringBuilder();
+        for (int type : GNSS_NAVIGATION_MESSAGE_TYPE) {
+            typesStr.append(String.format("0x%04X", type));
+            typesStr.append(", ");
+        }
+
+        return typesStr.length() > 2 ? typesStr.substring(0, typesStr.length() - 2) : "";
+    }
 }
diff --git a/tests/tests/media/res/raw/largealbumart.mp3 b/tests/tests/media/res/raw/largealbumart.mp3
new file mode 100644
index 0000000..e630f0d
--- /dev/null
+++ b/tests/tests/media/res/raw/largealbumart.mp3
Binary files differ
diff --git a/tests/tests/media/src/android/media/cts/DeviceUtils.java b/tests/tests/media/src/android/media/cts/DeviceUtils.java
new file mode 100644
index 0000000..9051c69
--- /dev/null
+++ b/tests/tests/media/src/android/media/cts/DeviceUtils.java
@@ -0,0 +1,58 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.media.cts;
+
+import android.content.BroadcastReceiver;
+import android.content.Context;
+import android.content.Intent;
+import android.content.IntentFilter;
+import android.content.pm.PackageManager;
+
+import android.media.AudioDeviceInfo;
+import android.media.AudioManager;
+
+import android.util.Log;
+
+/* package */ class DeviceUtils {
+    private static final String TAG = "DeviceUtils";
+
+    /* package */ static boolean hasOutputDevice(AudioManager audioMgr) {
+        AudioDeviceInfo[] devices = audioMgr.getDevices(AudioManager.GET_DEVICES_OUTPUTS);
+        return devices.length != 0;
+    }
+
+    /* package */ static boolean hasInputDevice(AudioManager audioMgr) {
+        AudioDeviceInfo[] devices = audioMgr.getDevices(AudioManager.GET_DEVICES_INPUTS);
+        return devices.length != 0;
+    }
+
+    /* package */ static boolean isTVDevice(Context context) {
+        return context.getPackageManager().hasSystemFeature(PackageManager.FEATURE_LEANBACK);
+    }
+
+    /*
+     * HDMI
+     */
+    /* package */ static boolean isHDMIConnected(Context context) {
+        // configure the IntentFilter
+        IntentFilter intentFilter = new IntentFilter();
+        intentFilter.addAction(AudioManager.ACTION_HDMI_AUDIO_PLUG);
+        Intent intent = context.registerReceiver(null, intentFilter);
+
+        return intent != null && intent.getIntExtra(AudioManager.EXTRA_AUDIO_PLUG_STATE, 0) != 0;
+    }
+}
diff --git a/tests/tests/media/src/android/media/cts/EnumDevicesTest.java b/tests/tests/media/src/android/media/cts/EnumDevicesTest.java
index 94af087..e3ed453 100644
--- a/tests/tests/media/src/android/media/cts/EnumDevicesTest.java
+++ b/tests/tests/media/src/android/media/cts/EnumDevicesTest.java
@@ -28,10 +28,14 @@
 
 import android.test.AndroidTestCase;
 
+import android.util.Log;
+
 /**
  * TODO: Insert description here. (generated by pmclean)
  */
 public class EnumDevicesTest extends AndroidTestCase {
+    private static final String TAG = "EnumDevicesTest";
+
     private AudioManager mAudioManager;
 
     boolean mAddCallbackCalled = false;
@@ -54,27 +58,41 @@
         assertTrue(deviceList != null);
         assertTrue(deviceList.length == 0);
 
+        PackageManager pkgMgr = mContext.getPackageManager();
+
+        boolean isTvDevice = DeviceUtils.isTVDevice(mContext);
+
         int numOutputDevices = 0;
-        if (mContext.getPackageManager().hasSystemFeature(PackageManager.FEATURE_AUDIO_OUTPUT)) {
+        if (pkgMgr.hasSystemFeature(PackageManager.FEATURE_AUDIO_OUTPUT)) {
             // test OUTPUTS
             deviceList = mAudioManager.getDevices(AudioManager.GET_DEVICES_OUTPUTS);
             assertTrue(deviceList != null);
-            numOutputDevices = deviceList.length;
-            assertTrue(numOutputDevices != 0);
 
-            // all should be "sinks"
+            numOutputDevices = deviceList.length;
+            if (numOutputDevices == 0) {
+                boolean isHDMIConnected = DeviceUtils.isHDMIConnected(mContext);
+                if (isTvDevice && !isHDMIConnected) {
+                    Log.w(TAG, "getDevices test: failure due to missing reported output " +
+                               "or the test is run on a TV device with no HDMI connected");
+                }
+                assertTrue("getDevices test: failure due to missing HDMI connection " +
+                           "or missing output", false);
+            }
+
+            // any reported output devices should be "sinks"
             for(int index = 0; index < numOutputDevices; index++) {
                 assertTrue(deviceList[index].isSink());
             }
         }
 
         int numInputDevices = 0;
-        if (mContext.getPackageManager().hasSystemFeature(PackageManager.FEATURE_MICROPHONE)) {
+        if (pkgMgr.hasSystemFeature(PackageManager.FEATURE_MICROPHONE)) {
             // test INPUTS
             deviceList = mAudioManager.getDevices(AudioManager.GET_DEVICES_INPUTS);
             assertTrue(deviceList != null);
+
             numInputDevices = deviceList.length;
-            assertTrue(numInputDevices != 0);
+            assertTrue(numOutputDevices != 0);
 
             // all should be "sources"
             for(int index = 0; index < numInputDevices; index++) {
@@ -86,6 +104,7 @@
         if (mContext.getPackageManager().hasSystemFeature(PackageManager.FEATURE_AUDIO_OUTPUT) &&
                 mContext.getPackageManager().hasSystemFeature(PackageManager.FEATURE_MICROPHONE)) {
             deviceList = mAudioManager.getDevices(AudioManager.GET_DEVICES_ALL);
+            assertTrue(deviceList != null);
             assertTrue(deviceList.length == (numOutputDevices + numInputDevices));
         }
     }
diff --git a/tests/tests/media/src/android/media/cts/MediaMetadataRetrieverTest.java b/tests/tests/media/src/android/media/cts/MediaMetadataRetrieverTest.java
index 0b38e1b..890073c 100644
--- a/tests/tests/media/src/android/media/cts/MediaMetadataRetrieverTest.java
+++ b/tests/tests/media/src/android/media/cts/MediaMetadataRetrieverTest.java
@@ -124,6 +124,12 @@
                 mRetriever.extractMetadata(MediaMetadataRetriever.METADATA_KEY_WRITER));
     }
 
+    public void testLargeAlbumArt() {
+        setDataSourceFd(R.raw.largealbumart);
+
+        assertNotNull("couldn't retrieve album art", mRetriever.getEmbeddedPicture());
+    }
+
     public void testSetDataSourceNullPath() {
         try {
             mRetriever.setDataSource((String)null);
diff --git a/tests/tests/media/src/android/media/cts/RoutingTest.java b/tests/tests/media/src/android/media/cts/RoutingTest.java
index 22aaaa3..7614568 100644
--- a/tests/tests/media/src/android/media/cts/RoutingTest.java
+++ b/tests/tests/media/src/android/media/cts/RoutingTest.java
@@ -352,6 +352,11 @@
     }
 
     public void test_audioTrack_getRoutedDevice() {
+        if (!DeviceUtils.hasOutputDevice(mAudioManager)) {
+            Log.i(TAG, "No output devices. Test skipped");
+            return; // nothing to test here
+        }
+
         int bufferSize =
                 AudioTrack.getMinBufferSize(
                     41000,
@@ -415,6 +420,11 @@
             return;
         }
 
+        if (!DeviceUtils.hasInputDevice(mAudioManager)) {
+            Log.i(TAG, "No input devices. Test skipped");
+            return; // nothing to test here
+        }
+
         int bufferSize =
                 AudioRecord.getMinBufferSize(
                     41000,
diff --git a/tests/tests/media/src/android/media/cts/VideoDecoderPerfTest.java b/tests/tests/media/src/android/media/cts/VideoDecoderPerfTest.java
index 07f00eb..c6e0578 100644
--- a/tests/tests/media/src/android/media/cts/VideoDecoderPerfTest.java
+++ b/tests/tests/media/src/android/media/cts/VideoDecoderPerfTest.java
@@ -28,6 +28,7 @@
 import android.media.MediaExtractor;
 import android.media.MediaFormat;
 import android.util.Log;
+import android.util.Pair;
 import android.view.Surface;
 
 import com.android.compatibility.common.util.DeviceReportLog;
@@ -60,7 +61,8 @@
     private static final boolean OTHER = false;
 
     private static final int MAX_SIZE_SAMPLES_IN_MEMORY_BYTES = 12 << 20;  // 12MB
-    LinkedList<ByteBuffer> mSamplesInMemory = new LinkedList<ByteBuffer>();
+    // each sample contains the buffer and the PTS offset from the frame index
+    LinkedList<Pair<ByteBuffer, Double>> mSamplesInMemory = new LinkedList<Pair<ByteBuffer, Double>>();
     private MediaFormat mDecInputFormat;
     private MediaFormat mDecOutputFormat;
     private int mBitrate;
@@ -117,6 +119,16 @@
         int trackIndex = extractor.getSampleTrackIndex();
         MediaFormat format = extractor.getTrackFormat(trackIndex);
         String mime = format.getString(MediaFormat.KEY_MIME);
+
+        // use frame rate to calculate PTS offset used for PTS scaling
+        double frameRate = 0.; // default - 0 is used for using zero PTS offset
+        if (format.containsKey(MediaFormat.KEY_FRAME_RATE)) {
+            frameRate = format.getInteger(MediaFormat.KEY_FRAME_RATE);
+        } else if (!mime.equals(MediaFormat.MIMETYPE_VIDEO_VP8)
+                && !mime.equals(MediaFormat.MIMETYPE_VIDEO_VP9)) {
+            fail("need framerate info for video file");
+        }
+
         ByteBuffer[] codecInputBuffers;
         ByteBuffer[] codecOutputBuffers;
 
@@ -125,14 +137,25 @@
             ByteBuffer tmpBuf = ByteBuffer.allocate(w * h * 3 / 2);
             int sampleSize = 0;
             int index = 0;
+            long firstPTS = 0;
+            double presentationOffset = 0.;
             while ((sampleSize = extractor.readSampleData(tmpBuf, 0 /* offset */)) > 0) {
                 if (totalMemory + sampleSize > MAX_SIZE_SAMPLES_IN_MEMORY_BYTES) {
                     break;
                 }
+                if (mSamplesInMemory.size() == 0) {
+                    firstPTS = extractor.getSampleTime();
+                }
                 ByteBuffer copied = ByteBuffer.allocate(sampleSize);
                 copied.put(tmpBuf);
-                mSamplesInMemory.addLast(copied);
+                if (frameRate > 0.) {
+                    // presentation offset is an offset from the frame index
+                    presentationOffset =
+                        (extractor.getSampleTime() - firstPTS) * frameRate / 1e6 - index;
+                }
+                mSamplesInMemory.addLast(Pair.create(copied, presentationOffset));
                 totalMemory += sampleSize;
+                ++index;
                 extractor.advance();
             }
             Log.d(TAG, mSamplesInMemory.size() + " samples in memory for " +
@@ -149,7 +172,7 @@
 
         MediaCodec codec = MediaCodec.createByCodecName(name);
         VideoCapabilities cap = codec.getCodecInfo().getCapabilitiesForType(mime).getVideoCapabilities();
-        int frameRate = cap.getSupportedFrameRatesFor(w, h).getUpper().intValue();
+        frameRate = cap.getSupportedFrameRatesFor(w, h).getUpper();
         codec.configure(format, surface, null /* crypto */, 0 /* flags */);
         codec.start();
         codecInputBuffers = codec.getInputBuffers();
@@ -174,13 +197,14 @@
 
                 if (inputBufIndex >= 0) {
                     ByteBuffer dstBuf = codecInputBuffers[inputBufIndex];
-                    ByteBuffer sample =
+                    // sample contains the buffer and the PTS offset normalized to frame index
+                    Pair<ByteBuffer, Double> sample =
                             mSamplesInMemory.get(sampleIndex++ % mSamplesInMemory.size());
-                    sample.rewind();
-                    int sampleSize = sample.remaining();
-                    dstBuf.put(sample);
-                    // use 120fps to compute pts
-                    long presentationTimeUs = inputNum * 1000000L / frameRate;
+                    sample.first.rewind();
+                    int sampleSize = sample.first.remaining();
+                    dstBuf.put(sample.first);
+                    // use max supported framerate to compute pts
+                    long presentationTimeUs = (long)((inputNum + sample.second) * 1e6 / frameRate);
 
                     long elapsed = System.currentTimeMillis() - start;
                     sawInputEOS = ((++inputNum == TOTAL_FRAMES)
diff --git a/tests/tests/net/src/android/net/cts/LocalSocketTest.java b/tests/tests/net/src/android/net/cts/LocalSocketTest.java
index 77f0a44..0ff4a30 100644
--- a/tests/tests/net/src/android/net/cts/LocalSocketTest.java
+++ b/tests/tests/net/src/android/net/cts/LocalSocketTest.java
@@ -22,12 +22,18 @@
 import android.net.LocalServerSocket;
 import android.net.LocalSocket;
 import android.net.LocalSocketAddress;
+import android.system.Os;
+import android.system.OsConstants;
 
 import java.io.FileDescriptor;
 import java.io.IOException;
 import java.io.InputStream;
 import java.io.OutputStream;
+import java.util.concurrent.Callable;
 import java.util.concurrent.CountDownLatch;
+import java.util.concurrent.ExecutorService;
+import java.util.concurrent.Executors;
+import java.util.concurrent.Future;
 import java.util.concurrent.TimeUnit;
 
 public class LocalSocketTest extends TestCase {
@@ -177,58 +183,114 @@
         socket.close();
     }
 
+    // http://b/31205169
+    public void testSetSoTimeout_readTimeout() throws Exception {
+        String address = ADDRESS_PREFIX + "_testSetSoTimeout_readTimeout";
+
+        try (LocalSocketPair socketPair = LocalSocketPair.createConnectedSocketPair(address)) {
+            final LocalSocket clientSocket = socketPair.clientSocket;
+
+            // Set the timeout in millis.
+            int timeoutMillis = 1000;
+            clientSocket.setSoTimeout(timeoutMillis);
+
+            // Avoid blocking the test run if timeout doesn't happen by using a separate thread.
+            Callable<Result> reader = () -> {
+                try {
+                    clientSocket.getInputStream().read();
+                    return Result.noException("Did not block");
+                } catch (IOException e) {
+                    return Result.exception(e);
+                }
+            };
+            // Allow the configured timeout, plus some slop.
+            int allowedTime = timeoutMillis + 2000;
+            Result result = runInSeparateThread(allowedTime, reader);
+
+            // Check the message was a timeout, it's all we have to go on.
+            String expectedMessage = Os.strerror(OsConstants.EAGAIN);
+            result.assertThrewIOException(expectedMessage);
+        }
+    }
+
+    // http://b/31205169
+    public void testSetSoTimeout_writeTimeout() throws Exception {
+        String address = ADDRESS_PREFIX + "_testSetSoTimeout_writeTimeout";
+
+        try (LocalSocketPair socketPair = LocalSocketPair.createConnectedSocketPair(address)) {
+            final LocalSocket clientSocket = socketPair.clientSocket;
+
+            // Set the timeout in millis.
+            int timeoutMillis = 1000;
+            clientSocket.setSoTimeout(timeoutMillis);
+
+            // Set a small buffer size so we know we can flood it.
+            clientSocket.setSendBufferSize(100);
+            final int bufferSize = clientSocket.getSendBufferSize();
+
+            // Avoid blocking the test run if timeout doesn't happen by using a separate thread.
+            Callable<Result> writer = () -> {
+                try {
+                    byte[] toWrite = new byte[bufferSize * 2];
+                    clientSocket.getOutputStream().write(toWrite);
+                    return Result.noException("Did not block");
+                } catch (IOException e) {
+                    return Result.exception(e);
+                }
+            };
+            // Allow the configured timeout, plus some slop.
+            int allowedTime = timeoutMillis + 2000;
+
+            Result result = runInSeparateThread(allowedTime, writer);
+
+            // Check the message was a timeout, it's all we have to go on.
+            String expectedMessage = Os.strerror(OsConstants.EAGAIN);
+            result.assertThrewIOException(expectedMessage);
+        }
+    }
+
     public void testAvailable() throws Exception {
         String address = ADDRESS_PREFIX + "_testAvailable";
-        LocalServerSocket localServerSocket = new LocalServerSocket(address);
-        LocalSocket clientSocket = new LocalSocket();
 
-        // establish connection between client and server
-        LocalSocketAddress locSockAddr = new LocalSocketAddress(address);
-        clientSocket.connect(locSockAddr);
-        assertTrue(clientSocket.isConnected());
-        LocalSocket serverSocket = localServerSocket.accept();
+        try (LocalSocketPair socketPair = LocalSocketPair.createConnectedSocketPair(address)) {
+            LocalSocket clientSocket = socketPair.clientSocket;
+            LocalSocket serverSocket = socketPair.serverSocket.accept();
 
-        OutputStream clientOutputStream = clientSocket.getOutputStream();
-        InputStream serverInputStream = serverSocket.getInputStream();
-        assertEquals(0, serverInputStream.available());
+            OutputStream clientOutputStream = clientSocket.getOutputStream();
+            InputStream serverInputStream = serverSocket.getInputStream();
+            assertEquals(0, serverInputStream.available());
 
-        byte[] buffer = new byte[50];
-        clientOutputStream.write(buffer);
-        assertEquals(50, serverInputStream.available());
+            byte[] buffer = new byte[50];
+            clientOutputStream.write(buffer);
+            assertEquals(50, serverInputStream.available());
 
-        InputStream clientInputStream = clientSocket.getInputStream();
-        OutputStream serverOutputStream = serverSocket.getOutputStream();
-        assertEquals(0, clientInputStream.available());
-        serverOutputStream.write(buffer);
-        assertEquals(50, serverInputStream.available());
+            InputStream clientInputStream = clientSocket.getInputStream();
+            OutputStream serverOutputStream = serverSocket.getOutputStream();
+            assertEquals(0, clientInputStream.available());
+            serverOutputStream.write(buffer);
+            assertEquals(50, serverInputStream.available());
 
-        clientSocket.close();
-        serverSocket.close();
-        localServerSocket.close();
+            serverSocket.close();
+        }
     }
 
     public void testFlush() throws Exception {
         String address = ADDRESS_PREFIX + "_testFlush";
-        LocalServerSocket localServerSocket = new LocalServerSocket(address);
-        LocalSocket clientSocket = new LocalSocket();
 
-        // establish connection between client and server
-        LocalSocketAddress locSockAddr = new LocalSocketAddress(address);
-        clientSocket.connect(locSockAddr);
-        assertTrue(clientSocket.isConnected());
-        LocalSocket serverSocket = localServerSocket.accept();
+        try (LocalSocketPair socketPair = LocalSocketPair.createConnectedSocketPair(address)) {
+            LocalSocket clientSocket = socketPair.clientSocket;
+            LocalSocket serverSocket = socketPair.serverSocket.accept();
 
-        OutputStream clientOutputStream = clientSocket.getOutputStream();
-        InputStream serverInputStream = serverSocket.getInputStream();
-        testFlushWorks(clientOutputStream, serverInputStream);
+            OutputStream clientOutputStream = clientSocket.getOutputStream();
+            InputStream serverInputStream = serverSocket.getInputStream();
+            testFlushWorks(clientOutputStream, serverInputStream);
 
-        OutputStream serverOutputStream = serverSocket.getOutputStream();
-        InputStream clientInputStream = clientSocket.getInputStream();
-        testFlushWorks(serverOutputStream, clientInputStream);
+            OutputStream serverOutputStream = serverSocket.getOutputStream();
+            InputStream clientInputStream = clientSocket.getInputStream();
+            testFlushWorks(serverOutputStream, clientInputStream);
 
-        clientSocket.close();
-        serverSocket.close();
-        localServerSocket.close();
+            serverSocket.close();
+        }
     }
 
     private void testFlushWorks(OutputStream outputStream, InputStream inputStream)
@@ -296,4 +358,64 @@
             assertEquals(expected, bytesRead);
         }
     }
+
+    private static class Result {
+        private final String type;
+        private final Exception e;
+
+        private Result(String type, Exception e) {
+            this.type = type;
+            this.e = e;
+        }
+
+        static Result noException(String description) {
+            return new Result(description, null);
+        }
+
+        static Result exception(Exception e) {
+            return new Result(e.getClass().getName(), e);
+        }
+
+        void assertThrewIOException(String expectedMessage) {
+            assertEquals("Unexpected result type", IOException.class.getName(), type);
+            assertEquals("Unexpected exception message", expectedMessage, e.getMessage());
+        }
+    }
+
+    private static Result runInSeparateThread(int allowedTime, final Callable<Result> callable)
+            throws Exception {
+        ExecutorService service = Executors.newSingleThreadScheduledExecutor();
+        Future<Result> future = service.submit(callable);
+        Result result = future.get(allowedTime, TimeUnit.MILLISECONDS);
+        if (!future.isDone()) {
+            fail("Worker thread appears blocked");
+        }
+        return result;
+    }
+
+    private static class LocalSocketPair implements AutoCloseable {
+        static LocalSocketPair createConnectedSocketPair(String address) throws Exception {
+            LocalServerSocket localServerSocket = new LocalServerSocket(address);
+            final LocalSocket clientSocket = new LocalSocket();
+
+            // Establish connection between client and server
+            LocalSocketAddress locSockAddr = new LocalSocketAddress(address);
+            clientSocket.connect(locSockAddr);
+            assertTrue(clientSocket.isConnected());
+            return new LocalSocketPair(localServerSocket, clientSocket);
+        }
+
+        final LocalServerSocket serverSocket;
+        final LocalSocket clientSocket;
+
+        LocalSocketPair(LocalServerSocket serverSocket, LocalSocket clientSocket) {
+            this.serverSocket = serverSocket;
+            this.clientSocket = clientSocket;
+        }
+
+        public void close() throws Exception {
+            serverSocket.close();
+            clientSocket.close();
+        }
+    }
 }
diff --git a/tests/tests/os/src/android/os/cts/AsyncTaskTest.java b/tests/tests/os/src/android/os/cts/AsyncTaskTest.java
index efd1eed..5bf1f59 100644
--- a/tests/tests/os/src/android/os/cts/AsyncTaskTest.java
+++ b/tests/tests/os/src/android/os/cts/AsyncTaskTest.java
@@ -21,6 +21,7 @@
 import android.os.AsyncTask;
 import android.test.InstrumentationTestCase;
 
+import java.util.concurrent.CountDownLatch;
 import java.util.concurrent.TimeUnit;
 
 public class AsyncTaskTest extends InstrumentationTestCase {
@@ -30,7 +31,8 @@
     private static final long DURATION = 2000;
     private static final String[] PARAM = { "Test" };
 
-    private static MyAsyncTask mAsyncTask;
+    private static AsyncTask mAsyncTask;
+    private static MyAsyncTask mMyAsyncTask;
 
     public void testAsyncTask() throws Throwable {
         doTestAsyncTask(0);
@@ -43,51 +45,51 @@
     private void doTestAsyncTask(final long timeout) throws Throwable {
         startAsyncTask();
         if (timeout > 0) {
-            assertEquals(RESULT, mAsyncTask.get(DURATION, TimeUnit.MILLISECONDS).longValue());
+            assertEquals(RESULT, mMyAsyncTask.get(DURATION, TimeUnit.MILLISECONDS).longValue());
         } else {
-            assertEquals(RESULT, mAsyncTask.get().longValue());
+            assertEquals(RESULT, mMyAsyncTask.get().longValue());
         }
 
         // wait for the task to finish completely (including onPostResult()).
         new PollingCheck(DURATION) {
             protected boolean check() {
-                return mAsyncTask.getStatus() == AsyncTask.Status.FINISHED;
+                return mMyAsyncTask.getStatus() == AsyncTask.Status.FINISHED;
             }
         }.run();
 
-        assertTrue(mAsyncTask.isOnPreExecuteCalled);
-        assert(mAsyncTask.hasRun);
-        assertEquals(PARAM.length, mAsyncTask.parameters.length);
+        assertTrue(mMyAsyncTask.isOnPreExecuteCalled);
+        assert(mMyAsyncTask.hasRun);
+        assertEquals(PARAM.length, mMyAsyncTask.parameters.length);
         for (int i = 0; i < PARAM.length; i++) {
-            assertEquals(PARAM[i], mAsyncTask.parameters[i]);
+            assertEquals(PARAM[i], mMyAsyncTask.parameters[i]);
         }
         // even though the background task has run, the onPostExecute() may not have been
         // executed yet and the progress update may not have been processed. Wait until the task
         // has completed, which guarantees that onPostExecute has been called.
 
-        assertEquals(RESULT, mAsyncTask.postResult.longValue());
-        assertEquals(AsyncTask.Status.FINISHED, mAsyncTask.getStatus());
+        assertEquals(RESULT, mMyAsyncTask.postResult.longValue());
+        assertEquals(AsyncTask.Status.FINISHED, mMyAsyncTask.getStatus());
 
-        if (mAsyncTask.exception != null) {
-            throw mAsyncTask.exception;
+        if (mMyAsyncTask.exception != null) {
+            throw mMyAsyncTask.exception;
         }
 
         // wait for progress update to be processed (happens asynchronously)
         new PollingCheck(DURATION) {
             protected boolean check() {
-                return mAsyncTask.updateValue != null;
+                return mMyAsyncTask.updateValue != null;
             }
         }.run();
-        assertEquals(UPDATE_VALUE.length, mAsyncTask.updateValue.length);
+        assertEquals(UPDATE_VALUE.length, mMyAsyncTask.updateValue.length);
         for (int i = 0; i < UPDATE_VALUE.length; i++) {
-            assertEquals(UPDATE_VALUE[i], mAsyncTask.updateValue[i]);
+            assertEquals(UPDATE_VALUE[i], mMyAsyncTask.updateValue[i]);
         }
 
         runTestOnUiThread(new Runnable() {
             public void run() {
                 try {
                     // task should not be allowed to execute twice
-                    mAsyncTask.execute(PARAM);
+                    mMyAsyncTask.execute(PARAM);
                     fail("Failed to throw exception!");
                 } catch (IllegalStateException e) {
                     // expected
@@ -99,44 +101,81 @@
     public void testCancelWithInterrupt() throws Throwable {
         startAsyncTask();
         Thread.sleep(COMPUTE_TIME / 2);
-        assertTrue(mAsyncTask.cancel(true));
+        assertTrue(mMyAsyncTask.cancel(true));
         // already cancelled
-        assertFalse(mAsyncTask.cancel(true));
+        assertFalse(mMyAsyncTask.cancel(true));
         Thread.sleep(DURATION);
-        assertTrue(mAsyncTask.isCancelled());
-        assertTrue(mAsyncTask.isOnCancelledCalled);
-        assertNotNull(mAsyncTask.exception);
-        assertTrue(mAsyncTask.exception instanceof InterruptedException);
+        assertTrue(mMyAsyncTask.isCancelled());
+        assertTrue(mMyAsyncTask.isOnCancelledCalled);
+        assertNotNull(mMyAsyncTask.exception);
+        assertTrue(mMyAsyncTask.exception instanceof InterruptedException);
     }
 
     public void testCancel() throws Throwable {
         startAsyncTask();
         Thread.sleep(COMPUTE_TIME / 2);
-        assertTrue(mAsyncTask.cancel(false));
+        assertTrue(mMyAsyncTask.cancel(false));
         // already cancelled
-        assertFalse(mAsyncTask.cancel(false));
+        assertFalse(mMyAsyncTask.cancel(false));
         Thread.sleep(DURATION);
-        assertTrue(mAsyncTask.isCancelled());
-        assertTrue(mAsyncTask.isOnCancelledCalled);
-        assertNull(mAsyncTask.exception);
+        assertTrue(mMyAsyncTask.isCancelled());
+        assertTrue(mMyAsyncTask.isOnCancelledCalled);
+        assertNull(mMyAsyncTask.exception);
     }
 
     public void testCancelTooLate() throws Throwable {
         startAsyncTask();
         Thread.sleep(DURATION);
-        assertFalse(mAsyncTask.cancel(false));
-        assertTrue(mAsyncTask.isCancelled());
-        assertFalse(mAsyncTask.isOnCancelledCalled);
-        assertNull(mAsyncTask.exception);
+        assertFalse(mMyAsyncTask.cancel(false));
+        assertTrue(mMyAsyncTask.isCancelled());
+        assertFalse(mMyAsyncTask.isOnCancelledCalled);
+        assertNull(mMyAsyncTask.exception);
+    }
+
+    public void testCancellationWithException() throws Throwable {
+        final CountDownLatch readyToCancel = new CountDownLatch(1);
+        final CountDownLatch readyToThrow = new CountDownLatch(1);
+        final CountDownLatch calledOnCancelled = new CountDownLatch(1);
+        runTestOnUiThread(new Runnable() {
+            @Override
+            public void run() {
+                mAsyncTask = new AsyncTask() {
+                    @Override
+                    protected Object doInBackground(Object... params) {
+                        readyToCancel.countDown();
+                        try {
+                            readyToThrow.await();
+                        } catch (InterruptedException e) {}
+                        // This exception is expected to be caught and ignored
+                        throw new RuntimeException();
+                    }
+
+                    @Override
+                    protected void onCancelled(Object o) {
+                        calledOnCancelled.countDown();
+                    }
+                };
+            }
+        });
+
+        mAsyncTask.execute();
+        if (!readyToCancel.await(5, TimeUnit.SECONDS)) {
+            fail("Test failure: doInBackground did not run in time.");
+        }
+        mAsyncTask.cancel(false);
+        readyToThrow.countDown();
+        if (!calledOnCancelled.await(5, TimeUnit.SECONDS)) {
+            fail("onCancelled not called!");
+        }
     }
 
     private void startAsyncTask() throws Throwable {
         runTestOnUiThread(new Runnable() {
             public void run() {
-                mAsyncTask = new MyAsyncTask();
-                assertEquals(AsyncTask.Status.PENDING, mAsyncTask.getStatus());
-                assertEquals(mAsyncTask, mAsyncTask.execute(PARAM));
-                assertEquals(AsyncTask.Status.RUNNING, mAsyncTask.getStatus());
+                mMyAsyncTask = new MyAsyncTask();
+                assertEquals(AsyncTask.Status.PENDING, mMyAsyncTask.getStatus());
+                assertEquals(mMyAsyncTask, mMyAsyncTask.execute(PARAM));
+                assertEquals(AsyncTask.Status.RUNNING, mMyAsyncTask.getStatus());
             }
         });
     }
diff --git a/tests/tests/os/src/android/os/cts/BuildVersionTest.java b/tests/tests/os/src/android/os/cts/BuildVersionTest.java
index 75c8db5..66066e9 100644
--- a/tests/tests/os/src/android/os/cts/BuildVersionTest.java
+++ b/tests/tests/os/src/android/os/cts/BuildVersionTest.java
@@ -30,7 +30,7 @@
 
     private static final String LOG_TAG = "BuildVersionTest";
     private static final Set<String> EXPECTED_RELEASES =
-            new HashSet<String>(Arrays.asList("7.1"));
+            new HashSet<String>(Arrays.asList("7.1","7.1.1"));
     private static final int EXPECTED_SDK = 25;
     private static final String EXPECTED_BUILD_VARIANT = "user";
     private static final String EXPECTED_TAG = "release-keys";
diff --git a/tests/tests/provider/res/raw/testthumbvideo.mp4 b/tests/tests/provider/res/raw/testthumbvideo.mp4
new file mode 100644
index 0000000..6599e1f
--- /dev/null
+++ b/tests/tests/provider/res/raw/testthumbvideo.mp4
Binary files differ
diff --git a/tests/tests/provider/src/android/provider/cts/MediaStore_Video_ThumbnailsTest.java b/tests/tests/provider/src/android/provider/cts/MediaStore_Video_ThumbnailsTest.java
index 9c41274..8e51cb8 100644
--- a/tests/tests/provider/src/android/provider/cts/MediaStore_Video_ThumbnailsTest.java
+++ b/tests/tests/provider/src/android/provider/cts/MediaStore_Video_ThumbnailsTest.java
@@ -45,7 +45,8 @@
     private FileCopyHelper mFileHelper;
 
     private boolean hasCodec() {
-        return MediaUtils.hasCodecForResourceAndDomain(mContext, R.raw.testvideo, "video/");
+        return MediaUtils.hasCodecForResourceAndDomain(
+                mContext, R.raw.testthumbvideo, "video/");
     }
 
     @Override
@@ -127,7 +128,7 @@
         mResolver.delete(Media.EXTERNAL_CONTENT_URI,
                 "_data=?", new String[] { file.getAbsolutePath() });
         file.delete();
-        mFileHelper.copyToExternalStorage(R.raw.testvideo, file);
+        mFileHelper.copyToExternalStorage(R.raw.testthumbvideo, file);
 
         ContentValues values = new ContentValues();
         values.put(VideoColumns.DATA, file.getAbsolutePath());
diff --git a/tests/tests/security/src/android/security/cts/DeviceIdleControllerTest.java b/tests/tests/security/src/android/security/cts/DeviceIdleControllerTest.java
new file mode 100644
index 0000000..0b183aa
--- /dev/null
+++ b/tests/tests/security/src/android/security/cts/DeviceIdleControllerTest.java
@@ -0,0 +1,68 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR 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.os.Bundle;
+import android.os.Handler;
+import android.os.IBinder;
+import android.os.PowerManager;
+import android.os.RemoteException;
+import android.os.ResultReceiver;
+import android.os.ServiceManager;
+import android.test.AndroidTestCase;
+
+import java.io.FileDescriptor;
+
+/**
+ * Check past exploits of DeviceIdleController.
+ */
+public class DeviceIdleControllerTest extends AndroidTestCase {
+    int mResult;
+
+    /**
+     * Verify that the command line interface can not be used to add an app to the whitelist.
+     */
+    public void testAddWhiteList() {
+        final IBinder service = ServiceManager.getService("deviceidle");
+        final Object mSync = new Object();
+        mResult = 0;
+        try {
+            service.shellCommand(FileDescriptor.in, FileDescriptor.out, FileDescriptor.err,
+                    new String[]{"whitelist", "+" + mContext.getPackageName()},
+                    new ResultReceiver(null) {
+                        @Override
+                        protected void onReceiveResult(int resultCode, Bundle resultData) {
+                            mResult = resultCode;
+                            synchronized (mSync) {
+                                mSync.notifyAll();
+                            }
+                        }
+                    });
+        } catch (RemoteException e) {
+        }
+        try {
+            synchronized (mSync) {
+                mSync.wait();
+            }
+        } catch (InterruptedException e) {
+        }
+        assertEquals(-1, mResult);
+        PowerManager pm = mContext.getSystemService(PowerManager.class);
+        assertFalse(pm.isIgnoringBatteryOptimizations(mContext.getPackageName()));
+    }
+}
+
diff --git a/tests/tests/security/src/android/security/cts/STKFrameworkTest.java b/tests/tests/security/src/android/security/cts/STKFrameworkTest.java
index 1fae3c8..a30387f 100644
--- a/tests/tests/security/src/android/security/cts/STKFrameworkTest.java
+++ b/tests/tests/security/src/android/security/cts/STKFrameworkTest.java
@@ -17,12 +17,17 @@
 
 import android.content.ComponentName;
 import android.content.Intent;
+import android.content.pm.PackageManager;
 import android.test.AndroidTestCase;
 
 public class STKFrameworkTest extends AndroidTestCase {
+    private boolean mHasTelephony;
+
     @Override
     protected void setUp() throws Exception {
         super.setUp();
+        mHasTelephony = getContext().getPackageManager().hasSystemFeature(
+                PackageManager.FEATURE_TELEPHONY);
     }
 
     @Override
@@ -35,6 +40,10 @@
      * zero-permission malicious application
      */
     public void testInterceptedSIMCommandsToTelephony() {
+        if (!mHasTelephony) {
+            return;
+        }
+
         Intent intent = new Intent();
         intent.setAction("android.intent.action.stk.command");
         intent.putExtra("STK CMD", "test");
diff --git a/tests/tests/systemintents/Android.mk b/tests/tests/systemintents/Android.mk
new file mode 100644
index 0000000..1af6702
--- /dev/null
+++ b/tests/tests/systemintents/Android.mk
@@ -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.
+
+LOCAL_PATH:= $(call my-dir)
+
+include $(CLEAR_VARS)
+
+LOCAL_MODULE_TAGS := optional
+
+LOCAL_MODULE_PATH := $(TARGET_OUT_DATA_APPS)
+
+# Tag this module as a cts test artifact
+LOCAL_COMPATIBILITY_SUITE := cts
+
+LOCAL_SRC_FILES := $(call all-java-files-under, src)
+
+LOCAL_PACKAGE_NAME := CtsSystemIntentTestCases
+
+LOCAL_STATIC_JAVA_LIBRARIES := ctstestrunner
+
+LOCAL_SDK_VERSION := test_current
+
+include $(BUILD_CTS_PACKAGE)
diff --git a/tests/tests/systemintents/AndroidManifest.xml b/tests/tests/systemintents/AndroidManifest.xml
new file mode 100644
index 0000000..da0cbac
--- /dev/null
+++ b/tests/tests/systemintents/AndroidManifest.xml
@@ -0,0 +1,29 @@
+<?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.systemintents.cts">
+
+    <application>
+        <uses-library android:name="android.test.runner" />
+    </application>
+
+    <instrumentation android:name="android.support.test.runner.AndroidJUnitRunner"
+                     android:targetPackage="android.systemintents.cts"
+                     android:label="System intent tests"/>
+</manifest>
\ No newline at end of file
diff --git a/tests/tests/systemintents/AndroidTest.xml b/tests/tests/systemintents/AndroidTest.xml
new file mode 100644
index 0000000..eceb909
--- /dev/null
+++ b/tests/tests/systemintents/AndroidTest.xml
@@ -0,0 +1,25 @@
+<?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
+  -->
+<configuration description="Config for CTS system intent test cases">
+    <target_preparer class="com.android.compatibility.common.tradefed.targetprep.ApkInstaller">
+        <option name="cleanup-apks" value="true" />
+        <option name="test-file-name" value="CtsSystemIntentTestCases.apk" />
+    </target_preparer>
+    <test class="com.android.tradefed.testtype.AndroidJUnitTest" >
+        <option name="package" value="android.systemintents.cts" />
+    </test>
+</configuration>
\ No newline at end of file
diff --git a/tests/tests/systemintents/src/android/systemintents/cts/TestSystemIntents.java b/tests/tests/systemintents/src/android/systemintents/cts/TestSystemIntents.java
new file mode 100644
index 0000000..c572629
--- /dev/null
+++ b/tests/tests/systemintents/src/android/systemintents/cts/TestSystemIntents.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.systemintents.cts;
+
+import android.content.Intent;
+import android.content.pm.PackageManager;
+import android.content.pm.ResolveInfo;
+import android.content.res.Configuration;
+import android.net.Uri;
+import android.provider.Settings;
+import android.support.test.filters.MediumTest;
+import android.test.AndroidTestCase;
+import android.util.Log;
+
+import org.junit.Rule;
+import org.junit.Test;
+
+@MediumTest
+public class TestSystemIntents extends AndroidTestCase {
+    /*
+     * List of activity intents defined by the system.  Activities to handle each of these
+     * intents must all exist.
+     *
+     * They are Intents here rather than simply action strings so that the test can
+     * easily accommodate data URIs or similar for correct resolution.
+     *
+     * The flags associated with each intent indicate kinds of device on which the given
+     * UI intent is *not* applicable.
+     */
+
+    private static final int EXCLUDE_TV = 1 << 0;
+    private static final int EXCLUDE_WATCH = 1 << 1;
+    private static final int EXCLUDE_NON_TELEPHONY = 1 << 2;
+
+    class IntentEntry {
+        public int flags;
+        public Intent intent;
+
+        public IntentEntry(int f, Intent i) {
+            flags = f;
+            intent = i;
+        }
+    }
+
+    @Rule
+    private final IntentEntry[] mTestIntents = {
+            /* Settings-namespace intent actions */
+            new IntentEntry(0, new Intent(Settings.ACTION_SETTINGS)),
+            new IntentEntry(0, new Intent(Settings.ACTION_WEBVIEW_SETTINGS)),
+            new IntentEntry(0, new Intent(Settings.ACTION_APPLICATION_DEVELOPMENT_SETTINGS)),
+            new IntentEntry(0, new Intent(Settings.ACTION_IGNORE_BATTERY_OPTIMIZATION_SETTINGS)),
+            new IntentEntry(0, new Intent(Settings.ACTION_REQUEST_IGNORE_BATTERY_OPTIMIZATIONS)
+                    .setData(Uri.parse("package:android.systemintents.cts"))),
+            new IntentEntry(0, new Intent(Settings.ACTION_IGNORE_BACKGROUND_DATA_RESTRICTIONS_SETTINGS)
+                    .setData(Uri.parse("package:android.systemintents.cts"))),
+            new IntentEntry(0, new Intent(Settings.ACTION_HOME_SETTINGS)),
+            new IntentEntry(EXCLUDE_NON_TELEPHONY,
+                    new Intent(Settings.ACTION_APN_SETTINGS)),
+            new IntentEntry(EXCLUDE_TV|EXCLUDE_WATCH,
+                    new Intent(Settings.ACTION_NOTIFICATION_POLICY_ACCESS_SETTINGS))
+    };
+
+    @Test
+    public void testSystemIntents() {
+        final PackageManager pm = getContext().getPackageManager();
+        int productFlags = 0;
+
+        if (pm.hasSystemFeature(PackageManager.FEATURE_LEANBACK)) {
+            productFlags |= EXCLUDE_TV;
+        }
+
+        if (!pm.hasSystemFeature(PackageManager.FEATURE_TELEPHONY)) {
+            productFlags |= EXCLUDE_NON_TELEPHONY;
+        }
+
+        final Configuration config = getContext().getResources().getConfiguration();
+        if ((config.uiMode & Configuration.UI_MODE_TYPE_WATCH) != 0) {
+            productFlags |= EXCLUDE_WATCH;
+        }
+
+        for (IntentEntry e : mTestIntents) {
+            if ((productFlags & e.flags) == 0) {
+                final ResolveInfo ri = pm.resolveActivity(e.intent, PackageManager.MATCH_DEFAULT_ONLY);
+                assertTrue("API intent " + e.intent + " not implemented by any activity", ri != null);
+            }
+        }
+    }
+}
\ No newline at end of file
diff --git a/tests/tests/telecom/src/android/telecom/cts/CallDetailsTest.java b/tests/tests/telecom/src/android/telecom/cts/CallDetailsTest.java
index ff84655..d167249 100644
--- a/tests/tests/telecom/src/android/telecom/cts/CallDetailsTest.java
+++ b/tests/tests/telecom/src/android/telecom/cts/CallDetailsTest.java
@@ -445,6 +445,10 @@
      * {@link android.telecom.Call.Callback#onDetailsChanged(Call, Call.Details)}.
      */
     public void testConnectionRemoveExtras() {
+        if (!mShouldTestTelecom) {
+            return;
+        }
+
         testConnectionPutExtras();
 
         mConnection.removeExtras(Arrays.asList(TEST_EXTRA_KEY));
@@ -458,6 +462,10 @@
      * {@link android.telecom.Call.Callback#onDetailsChanged(Call, Call.Details)}.
      */
     public void testConnectionRemoveExtras2() {
+        if (!mShouldTestTelecom) {
+            return;
+        }
+
         testConnectionPutExtras();
 
         mConnection.removeExtras(TEST_EXTRA_KEY);
diff --git a/tests/tests/telephony/src/android/telephony/cts/SmsMessageTest.java b/tests/tests/telephony/src/android/telephony/cts/SmsMessageTest.java
index 2406ad8..e247775 100644
--- a/tests/tests/telephony/src/android/telephony/cts/SmsMessageTest.java
+++ b/tests/tests/telephony/src/android/telephony/cts/SmsMessageTest.java
@@ -114,7 +114,7 @@
 
         // Test create from null Pdu
         sms = SmsMessage.createFromPdu(null, SmsMessage.FORMAT_3GPP);
-        assertNotNull(sms);
+        assertNull(sms);
 
         // Test create from long Pdu
         pdu = "07912160130310F2040B915121927786F300036060924180008A0DA"
diff --git a/tests/tests/toast/Android.mk b/tests/tests/toast/Android.mk
new file mode 100644
index 0000000..d9f15eb
--- /dev/null
+++ b/tests/tests/toast/Android.mk
@@ -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.
+#
+LOCAL_PATH:= $(call my-dir)
+
+include $(CLEAR_VARS)
+
+LOCAL_MODULE_TAGS := tests
+
+LOCAL_MODULE_PATH := $(TARGET_OUT_DATA_APPS)
+
+LOCAL_COMPATIBILITY_SUITE := cts
+
+LOCAL_STATIC_JAVA_LIBRARIES := ctstestrunner android-support-test
+
+LOCAL_SRC_FILES := $(call all-java-files-under, src)
+
+LOCAL_PACKAGE_NAME := CtsToastTestCases
+
+include $(BUILD_CTS_PACKAGE)
+
diff --git a/tests/tests/toast/AndroidManifest.xml b/tests/tests/toast/AndroidManifest.xml
new file mode 100644
index 0000000..1fa71c4
--- /dev/null
+++ b/tests/tests/toast/AndroidManifest.xml
@@ -0,0 +1,33 @@
+<?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.widget.toast.cts">
+
+    <uses-sdk android:minSdkVersion="25" android:targetSdkVersion="26" />
+
+    <application>
+    </application>
+
+    <instrumentation android:name="android.support.test.runner.AndroidJUnitRunner"
+        android:targetPackage="android.widget.toast.cts"
+        android:label="CTS tests for toast windows">
+        <meta-data android:name="listener"
+            android:value="com.android.cts.runner.CtsTestRunListener" />
+    </instrumentation>
+
+</manifest>
+
diff --git a/tests/tests/toast/AndroidTest.xml b/tests/tests/toast/AndroidTest.xml
new file mode 100644
index 0000000..d0a5eed
--- /dev/null
+++ b/tests/tests/toast/AndroidTest.xml
@@ -0,0 +1,24 @@
+<?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.
+-->
+<configuration description="Config for Toast test cases">
+    <target_preparer class="com.android.compatibility.common.tradefed.targetprep.ApkInstaller">
+        <option name="cleanup-apks" value="true" />
+        <option name="test-file-name" value="CtsToastTestCases.apk" />
+    </target_preparer>
+    <test class="com.android.tradefed.testtype.AndroidJUnitTest" >
+        <option name="package" value="android.widget.toast.cts" />
+    </test>
+</configuration>
diff --git a/tests/tests/toast/src/android/widget/toast/cts/BaseToastTest.java b/tests/tests/toast/src/android/widget/toast/cts/BaseToastTest.java
new file mode 100644
index 0000000..fd75309
--- /dev/null
+++ b/tests/tests/toast/src/android/widget/toast/cts/BaseToastTest.java
@@ -0,0 +1,101 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR 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.toast.cts;
+
+import android.app.Instrumentation;
+import android.app.UiAutomation;
+import android.content.Context;
+import android.graphics.PixelFormat;
+import android.os.SystemClock;
+import android.support.test.InstrumentationRegistry;
+import android.view.WindowManager;
+import android.widget.TextView;
+import android.widget.Toast;
+import org.junit.Before;
+
+/**
+ * Base class for toast tests.
+ */
+public abstract class BaseToastTest {
+    protected static final long TOAST_TIMEOUT_MILLIS = 5000; // 5 sec
+    protected static final long EVENT_TIMEOUT_MILLIS = 5000; // 5 sec
+
+    protected Context mContext;
+    protected Instrumentation mInstrumentation;
+    protected UiAutomation mUiAutomation;
+
+    @Before
+    public void setUp() {
+        mContext = InstrumentationRegistry.getContext();
+        mInstrumentation = InstrumentationRegistry.getInstrumentation();
+        mUiAutomation = mInstrumentation.getUiAutomation();
+        waitForToastTimeout();
+    }
+
+    protected void waitForToastTimeout() {
+        SystemClock.sleep(TOAST_TIMEOUT_MILLIS);
+    }
+
+    protected void showToastsViaToastApis(int count) throws Exception {
+        Exception[] exceptions = new Exception[1];
+        mInstrumentation.runOnMainSync(
+                () -> {
+                    try {
+                        for (int i = 0; i < count; i++) {
+                            Toast.makeText(mContext, getClass().getName(),
+                                    Toast.LENGTH_LONG).show();
+                        }
+                    } catch (Exception e) {
+                        exceptions[0] = e;
+                    }
+                });
+        if (exceptions[0] != null) {
+            throw exceptions[0];
+        }
+    }
+
+    protected void showToastsViaAddingWindow(int count, boolean focusable) throws Exception {
+        Exception[] exceptions = new Exception[1];
+        mInstrumentation.runOnMainSync(() -> {
+            try {
+                for (int i = 0; i < count; i++) {
+                    WindowManager.LayoutParams params = new WindowManager.LayoutParams();
+                    params.height = WindowManager.LayoutParams.WRAP_CONTENT;
+                    params.width = WindowManager.LayoutParams.WRAP_CONTENT;
+                    params.format = PixelFormat.TRANSLUCENT;
+                    params.type = WindowManager.LayoutParams.TYPE_TOAST;
+                    params.flags = WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON;
+                    if (!focusable) {
+                        params.flags = WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE;
+                    }
+
+                    TextView textView = new TextView(mContext);
+                    textView.setText(BaseToastTest.class.getName());
+
+                    WindowManager windowManager = mContext
+                            .getSystemService(WindowManager.class);
+                    windowManager.addView(textView, params);
+                }
+            } catch (Exception e) {
+                exceptions[0] = e;
+            }
+        });
+        if (exceptions[0] != null) {
+            throw exceptions[0];
+        }
+    }
+}
diff --git a/tests/tests/toast/src/android/widget/toast/cts/LegacyToastTest.java b/tests/tests/toast/src/android/widget/toast/cts/LegacyToastTest.java
new file mode 100644
index 0000000..4ac88b3
--- /dev/null
+++ b/tests/tests/toast/src/android/widget/toast/cts/LegacyToastTest.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.widget.toast.cts;
+
+import android.support.test.runner.AndroidJUnit4;
+import android.view.WindowManager;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+import static org.junit.Assert.fail;
+
+/**
+ * Test whether toasts are properly shown. For apps targeting API 25+
+ * like this app the only way to add toast windows is via the dedicated
+ * toast APIs.
+ */
+@RunWith(AndroidJUnit4.class)
+public class LegacyToastTest extends BaseToastTest {
+    @Test
+    public void testAddSingleToastViaTestApisWhenUidFocused() throws Exception {
+        // Normal toast windows cannot be obtained vie the accessibility APIs because
+        // they are not touchable. In this case not crashing is good enough.
+        showToastsViaToastApis(1);
+    }
+
+    @Test
+    public void testAddTwoToastViaTestApisWhenUidFocused() throws Exception {
+        // Normal toast windows cannot be obtained vie the accessibility APIs because
+        // they are not touchable. In this case not crashing is good enough.
+        showToastsViaToastApis(2);
+
+        // Wait for the first one to expire
+        waitForToastTimeout();
+    }
+
+    @Test
+    public void testAddSingleToastViaAddingWindowApisWhenUidFocused() throws Exception {
+        try {
+            showToastsViaAddingWindow(1, false);
+            fail("Shouldn't be able to add toast windows directly");
+        } catch (WindowManager.BadTokenException e) {
+            /* expected */
+        }
+    }
+}
diff --git a/tests/tests/toastlegacy/Android.mk b/tests/tests/toastlegacy/Android.mk
new file mode 100644
index 0000000..ac5825d
--- /dev/null
+++ b/tests/tests/toastlegacy/Android.mk
@@ -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.
+#
+LOCAL_PATH:= $(call my-dir)
+
+include $(CLEAR_VARS)
+
+LOCAL_MODULE_TAGS := tests
+
+LOCAL_MODULE_PATH := $(TARGET_OUT_DATA_APPS)
+
+LOCAL_COMPATIBILITY_SUITE := cts
+
+LOCAL_STATIC_JAVA_LIBRARIES := ctstestrunner android-support-test
+
+LOCAL_SRC_FILES := $(call all-java-files-under, src) \
+    ../toast/src/android/widget/toast/cts/BaseToastTest.java
+
+LOCAL_PACKAGE_NAME := CtsToastLegacyTestCases
+
+include $(BUILD_CTS_PACKAGE)
+
diff --git a/tests/tests/toastlegacy/AndroidManifest.xml b/tests/tests/toastlegacy/AndroidManifest.xml
new file mode 100644
index 0000000..8529b5c
--- /dev/null
+++ b/tests/tests/toastlegacy/AndroidManifest.xml
@@ -0,0 +1,35 @@
+<?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.widget.toast.legacy.cts">
+
+    <uses-sdk android:minSdkVersion="25" android:targetSdkVersion="25" />
+
+    <application>
+        <activity android:name="android.widget.toast.cts.legacy.ToastActivity">
+        </activity>
+    </application>
+
+    <instrumentation android:name="android.support.test.runner.AndroidJUnitRunner"
+        android:targetPackage="android.widget.toast.legacy.cts"
+        android:label="CTS tests for legacy toast windows">
+        <meta-data android:name="listener"
+            android:value="com.android.cts.runner.CtsTestRunListener" />
+    </instrumentation>
+
+</manifest>
+
diff --git a/tests/tests/toastlegacy/AndroidTest.xml b/tests/tests/toastlegacy/AndroidTest.xml
new file mode 100644
index 0000000..a208077
--- /dev/null
+++ b/tests/tests/toastlegacy/AndroidTest.xml
@@ -0,0 +1,24 @@
+<?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.
+-->
+<configuration description="Config for Toast legacy test cases">
+    <target_preparer class="com.android.compatibility.common.tradefed.targetprep.ApkInstaller">
+        <option name="cleanup-apks" value="true" />
+        <option name="test-file-name" value="CtsToastLegacyTestCases.apk" />
+    </target_preparer>
+    <test class="com.android.tradefed.testtype.AndroidJUnitTest" >
+        <option name="package" value="android.widget.toast.legacy.cts" />
+    </test>
+</configuration>
diff --git a/tests/tests/toastlegacy/src/android/widget/toast/cts/legacy/ToastActivity.java b/tests/tests/toastlegacy/src/android/widget/toast/cts/legacy/ToastActivity.java
new file mode 100644
index 0000000..745385e
--- /dev/null
+++ b/tests/tests/toastlegacy/src/android/widget/toast/cts/legacy/ToastActivity.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.widget.toast.cts.legacy;
+
+import android.app.Activity;
+import android.graphics.Color;
+import android.os.Bundle;
+import android.view.View;
+import android.view.ViewGroup;
+
+/**
+ * Activity that shows toasts on demand.
+ */
+public class ToastActivity extends Activity {
+
+    @Override
+    public void onCreate(Bundle bundle) {
+        super.onCreate(bundle);
+        View view = new View(this);
+        view.setBackgroundColor(Color.BLUE);
+        view.setLayoutParams(new ViewGroup.LayoutParams(
+                ViewGroup.LayoutParams.MATCH_PARENT,
+                ViewGroup.LayoutParams.MATCH_PARENT));
+        setContentView(view);
+    }
+}
diff --git a/tests/tests/toastlegacy/src/android/widget/toast/cts/legacy/ToastTest.java b/tests/tests/toastlegacy/src/android/widget/toast/cts/legacy/ToastTest.java
new file mode 100644
index 0000000..207e6ea
--- /dev/null
+++ b/tests/tests/toastlegacy/src/android/widget/toast/cts/legacy/ToastTest.java
@@ -0,0 +1,144 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.widget.toast.cts.legacy;
+
+import android.support.test.rule.ActivityTestRule;
+import android.support.test.runner.AndroidJUnit4;
+import android.view.WindowManager;
+import android.view.accessibility.AccessibilityEvent;
+import android.widget.toast.cts.BaseToastTest;
+import org.junit.Assert;
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+/**
+ * Test whether toasts are properly shown. For apps targeting SDK
+ * 25 and below a toast window can be added via the window APIs
+ * but it will be removed after a timeout if the UID that added
+ * the window is not focused. Also only a single toast window
+ * is allowed at a time.
+ */
+@RunWith(AndroidJUnit4.class)
+public class ToastTest extends BaseToastTest {
+    @Rule
+    public final ActivityTestRule<ToastActivity> mActivityRule =
+            new ActivityTestRule<>(ToastActivity.class);
+
+    @Test
+    public void testAddSingleNotFocusableToastViaAddingWindowApisWhenUidFocused() throws Exception {
+        // Show a toast on top of the focused activity
+        showToastsViaAddingWindow(1, false);
+
+        // Wait for the toast to timeout
+        waitForToastTimeout();
+
+        // Finish the activity so the UID loses focus
+        finishActivity(false);
+
+        // Wait for the toast to timeout
+        waitForToastTimeout();
+
+        // Show another toast
+        showToastsViaAddingWindow(1, false);
+    }
+
+    @Test
+    public void testAddSingleFocusableToastViaAddingWindowApisWhenUidFocused() throws Exception {
+        // Show a toast on top of our activity
+        showToastsViaAddingWindow(1, true);
+
+        // Wait for the toast to timeout
+        waitForToastTimeout();
+
+        // Show a toast on top of our activity
+        showToastsViaAddingWindow(1, true);
+    }
+
+    @Test
+    public void testAddSingleToastViaAddingWindowApisWhenUidNotFocused() throws Exception {
+        // Finish the activity so the UID loses focus
+        finishActivity(false);
+
+        // Show a toast
+        showToastsViaAddingWindow(1, true);
+
+        // Wait for the toast to timeout
+        waitForToastTimeout();
+
+        // Show a toast on top of our activity
+        showToastsViaAddingWindow(1, true);
+    }
+
+    @Test
+    public void testAddTwoToastsViaToastApisWhenUidFocused() throws Exception {
+        // Finish the activity so the UID loses focus
+        finishActivity(false);
+
+        // Normal toast windows cannot be obtained vie the accessibility APIs because
+        // they are not touchable. In this case not crashing is good enough.
+        showToastsViaToastApis(2);
+
+        // Wait for the first one to expire
+        waitForToastTimeout();
+    }
+
+    @Test
+    public void testAddTwoToastsViaToastApisWhenUidNotFocused() throws Exception {
+        // Normal toast windows cannot be obtained vie the accessibility APIs because
+        // they are not touchable. In this case not crashing is good enough.
+        showToastsViaToastApis(2);
+
+        // Wait for the first one to expire
+        waitForToastTimeout();
+    }
+
+    @Test
+    public void testAddTwoToastsViaAddingWindowApisWhenUidFocusedQuickly() throws Exception {
+        try {
+            showToastsViaAddingWindow(2, false);
+            Assert.fail("Only one custom toast window at a time should be allowed");
+        } catch (WindowManager.BadTokenException e) {
+            /* expected */
+        } catch (Exception ex) {
+            Assert.fail("Unexpected exception when adding second toast window" + ex);
+        }
+    }
+
+    @Test
+    public void testAddTwoToastsViaAddingWindowApisWhenUidFocusedSlowly() throws Exception {
+        // Add one window
+        showToastsViaAddingWindow(1, true);
+
+        // Wait for the toast to timeout
+        waitForToastTimeout();
+
+        // Add another window
+        showToastsViaAddingWindow(1, true);
+    }
+
+    private void finishActivity(boolean waitForEvent) throws Exception {
+        if (waitForEvent) {
+            mUiAutomation.executeAndWaitForEvent(
+                    () -> mActivityRule.getActivity().finish(),
+                    (event) -> event.getEventType() == AccessibilityEvent.TYPE_WINDOWS_CHANGED
+                    , EVENT_TIMEOUT_MILLIS);
+        } else {
+            mActivityRule.getActivity().finish();
+        }
+    }
+}
diff --git a/tests/tests/transition/src/android/transition/cts/BaseTransitionTest.java b/tests/tests/transition/src/android/transition/cts/BaseTransitionTest.java
index 944ff91..a3a8cad 100644
--- a/tests/tests/transition/src/android/transition/cts/BaseTransitionTest.java
+++ b/tests/tests/transition/src/android/transition/cts/BaseTransitionTest.java
@@ -27,7 +27,9 @@
 import android.view.Choreographer.FrameCallback;
 import android.view.View;
 import android.view.ViewGroup;
+import android.view.ViewTreeObserver;
 import android.widget.FrameLayout;
+import junit.framework.Assert;
 
 import java.util.ArrayList;
 import java.util.concurrent.CountDownLatch;
@@ -139,13 +141,31 @@
     }
 
     protected void enterScene(final Scene scene) throws Throwable {
-        runTestOnUiThread(new Runnable() {
-            @Override
-            public void run() {
-                scene.enter();
-            }
+        final CountDownLatch latch = new CountDownLatch(1);
+
+        runTestOnUiThread(() -> {
+            final ViewTreeObserver.OnGlobalLayoutListener listener =
+                    new ViewTreeObserver.OnGlobalLayoutListener() {
+                @Override
+                public void onGlobalLayout() {
+                    mActivity.getWindow().getDecorView().
+                            getViewTreeObserver().removeOnGlobalLayoutListener(this);
+                    latch.countDown();
+                }
+            };
+
+            mActivity.getWindow().getDecorView().
+                    getViewTreeObserver().addOnGlobalLayoutListener(listener);
+
+            scene.enter();
         });
-        getInstrumentation().waitForIdleSync();
+
+        try {
+            Assert.assertTrue("Expected layout pass within 5 seconds",
+                    latch.await(5, TimeUnit.SECONDS));
+        } catch (InterruptedException e) {
+            throw new RuntimeException(e);
+        }
     }
 
     protected void exitScene(final Scene scene) throws Throwable {
diff --git a/tests/tests/view/AndroidManifest.xml b/tests/tests/view/AndroidManifest.xml
index ba1f3d2..526e85f 100644
--- a/tests/tests/view/AndroidManifest.xml
+++ b/tests/tests/view/AndroidManifest.xml
@@ -19,6 +19,8 @@
     package="android.view.cts">
 
     <uses-permission android:name="android.permission.DISABLE_KEYGUARD" />
+    <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
+
     <application android:label="Android TestCase"
                 android:icon="@drawable/size_48x48"
                 android:maxRecents="1"
@@ -239,8 +241,17 @@
             </intent-filter>
         </activity>
 
+        <activity android:name="android.view.cts.DragDropActivity"
+                  android:label="DragDropActivity">
+            <intent-filter>
+                <action android:name="android.intent.action.MAIN" />
+                <category android:name="android.intent.category.FRAMEWORK_INSTRUMENTATION_TEST" />
+            </intent-filter>
+        </activity>
+
         <activity android:name="android.view.cts.surfacevalidator.CapturedActivity"
-                  android:theme="@style/WhiteBackgroundTheme">
+            android:screenOrientation="portrait"
+            android:theme="@style/WhiteBackgroundTheme">
             <intent-filter>
                 <action android:name="android.intent.action.MAIN" />
                 <category android:name="android.intent.category.LAUNCHER" />
diff --git a/tests/tests/view/res/layout/drag_drop_layout.xml b/tests/tests/view/res/layout/drag_drop_layout.xml
new file mode 100644
index 0000000..3800d99
--- /dev/null
+++ b/tests/tests/view/res/layout/drag_drop_layout.xml
@@ -0,0 +1,50 @@
+<?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.
+ -->
+
+<LinearLayout
+        android:id="@+id/drag_drop_activity_main"
+        xmlns:android="http://schemas.android.com/apk/res/android"
+        android:layout_width="wrap_content"
+        android:layout_height="wrap_content"
+        android:orientation="vertical">
+    <FrameLayout
+            android:id="@+id/container"
+            android:layout_width="wrap_content"
+            android:layout_height="wrap_content"
+            android:layout_margin="64px"
+            android:background="#BBBBBB">
+        <FrameLayout
+                android:id="@+id/subcontainer"
+                android:layout_width="wrap_content"
+                android:layout_height="wrap_content"
+                android:layout_margin="64px"
+                android:background="#666666">
+            <View
+                    android:id="@+id/inner"
+                    android:layout_width="64px"
+                    android:layout_height="64px"
+                    android:layout_margin="64px"
+                    android:background="#00FF00" />
+        </FrameLayout>
+    </FrameLayout>
+    <View
+            android:id="@+id/draggable"
+            android:layout_width="64px"
+            android:layout_height="64px"
+            android:layout_margin="64px"
+            android:background="#0000FF" />
+</LinearLayout>
diff --git a/tests/tests/view/src/android/view/cts/DragDropActivity.java b/tests/tests/view/src/android/view/cts/DragDropActivity.java
new file mode 100644
index 0000000..b4324e3
--- /dev/null
+++ b/tests/tests/view/src/android/view/cts/DragDropActivity.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.view.cts;
+
+import android.view.cts.R;
+
+import android.app.Activity;
+import android.os.Bundle;
+
+public class DragDropActivity extends Activity {
+
+    @Override
+    protected void onCreate(Bundle savedInstanceState) {
+        super.onCreate(savedInstanceState);
+        setContentView(R.layout.drag_drop_layout);
+    }
+}
\ No newline at end of file
diff --git a/tests/tests/view/src/android/view/cts/DragDropTest.java b/tests/tests/view/src/android/view/cts/DragDropTest.java
new file mode 100644
index 0000000..968dc7b
--- /dev/null
+++ b/tests/tests/view/src/android/view/cts/DragDropTest.java
@@ -0,0 +1,592 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR 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 com.android.internal.util.ArrayUtils;
+
+import android.app.Instrumentation;
+import android.app.UiAutomation;
+import android.content.pm.PackageManager;
+import android.os.SystemClock;
+import android.support.test.InstrumentationRegistry;
+import android.support.test.rule.ActivityTestRule;
+import android.support.test.runner.AndroidJUnit4;
+import android.view.DragEvent;
+import android.view.InputDevice;
+import android.view.MotionEvent;
+import android.view.View;
+import android.view.ViewGroup;
+
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+import java.lang.InterruptedException;
+import java.lang.StringBuilder;
+import java.lang.Thread;
+import java.util.ArrayList;
+import java.util.concurrent.CountDownLatch;
+import java.util.concurrent.TimeUnit;
+import java.util.Objects;
+
+import static junit.framework.TestCase.*;
+
+@RunWith(AndroidJUnit4.class)
+public class DragDropTest {
+    static final String TAG = "DragDropTest";
+
+    final Instrumentation mInstrumentation = InstrumentationRegistry.getInstrumentation();
+    final UiAutomation mAutomation = mInstrumentation.getUiAutomation();
+
+    @Rule
+    public ActivityTestRule<DragDropActivity> mActivityRule =
+            new ActivityTestRule<>(DragDropActivity.class);
+
+    private DragDropActivity mActivity;
+
+    private CountDownLatch mEndReceived;
+
+    static boolean equal(DragEvent ev1, DragEvent ev2) {
+        return ev1.getAction() == ev2.getAction() &&
+                ev1.getX() == ev2.getX() &&
+                ev1.getY() == ev2.getY() &&
+                Objects.equals(ev1.getClipData(), ev2.getClipData()) &&
+                Objects.equals(ev1.getClipDescription(), ev2.getClipDescription()) &&
+                Objects.equals(ev1.getDragAndDropPermissions(), ev2.getDragAndDropPermissions()) &&
+                Objects.equals(ev1.getLocalState(), ev2.getLocalState()) &&
+                ev1.getResult() == ev2.getResult();
+    }
+
+    class LogEntry {
+        public View v;
+        public DragEvent ev;
+
+        public LogEntry(View v, DragEvent ev) {
+            this.v = v;
+            this.ev = DragEvent.obtain(ev);
+        }
+
+        public boolean eq(LogEntry other) {
+            return v == other.v && equal(ev, other.ev);
+        }
+    }
+
+    // Actual and expected sequences of events.
+    // While the test is running, logs should be accessed only from the main thread.
+    final private ArrayList<LogEntry> mActual = new ArrayList<LogEntry> ();
+    final private ArrayList<LogEntry> mExpected = new ArrayList<LogEntry> ();
+
+    static private DragEvent obtainDragEvent(int action, int x, int y, boolean result) {
+        return DragEvent.obtain(action, x, y, null, null, null, null, result);
+    }
+
+    private void logEvent(View v, DragEvent ev) {
+        if (ev.getAction() == DragEvent.ACTION_DRAG_ENDED) {
+            mEndReceived.countDown();
+        }
+        mActual.add(new LogEntry(v, ev));
+    }
+
+    // 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)));
+    }
+
+    // Add expected event for a view.
+    private void expectEndEvent(int viewId, int x, int y, boolean result) {
+        View v = mActivity.findViewById(viewId);
+        mExpected.add(new LogEntry(v, obtainDragEvent(DragEvent.ACTION_DRAG_ENDED, x, y, result)));
+    }
+
+    // Add expected successful-end event for a view.
+    private void expectEndEventSuccess(int viewId) {
+        expectEndEvent(viewId, 0, 0, true);
+    }
+
+    // Add expected failed-end event for a view, with the release coordinates shifted by 6 relative
+    // to the left-upper corner of a view with id releaseViewId.
+    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)));
+    }
+
+    // Add expected event for a view, with coordinates over view locationViewId, with the specified
+    // offset from the location view's upper-left corner.
+    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,
+                locationViewLocation[0] - viewLocation[0] + offset,
+                locationViewLocation[1] - viewLocation[1] + offset, false)));
+    }
+
+    private void expectEvent5(int action, int viewId, int locationViewId) {
+        expectEventWithOffset(action, viewId, locationViewId, 5);
+    }
+
+    // See comment for injectMouse6 on why we need both *5 and *6 methods.
+    private void expectEvent6(int action, int viewId, int locationViewId) {
+        expectEventWithOffset(action, viewId, locationViewId, 6);
+    }
+
+    // Inject mouse event over a given view, with specified offset from its left-upper corner.
+    private void injectMouseWithOffset(int viewId, int action, int offset) {
+        runOnMain(() -> {
+            View v = mActivity.findViewById(viewId);
+            int [] destLoc = v.getLocationOnScreen();
+            long downTime = SystemClock.uptimeMillis();
+            MotionEvent event = MotionEvent.obtain(downTime, downTime, action,
+                    destLoc[0] + offset, destLoc[1] + offset, 1);
+            event.setSource(InputDevice.SOURCE_TOUCHSCREEN);
+            mAutomation.injectInputEvent(event, false);
+        });
+
+        // Wait till the mouse event generates drag events. Also, some waiting needed because the
+        // system seems to collapse too frequent mouse events.
+        try {
+            Thread.sleep(100);
+        } catch (Exception e) {
+            fail("Exception while wait: " + e);
+        }
+    }
+
+    // Inject mouse event over a given view, with offset 5 from its left-upper corner.
+    private void injectMouse5(int viewId, int action) {
+        injectMouseWithOffset(viewId, action, 5);
+    }
+
+    // Inject mouse event over a given view, with offset 6 from its left-upper corner.
+    // We need both injectMouse5 and injectMouse6 if we want to inject 2 events in a row in the same
+    // view, and want them to produce distinct drag events or simply drag events with different
+    // coordinates.
+    private void injectMouse6(int viewId, int action) {
+        injectMouseWithOffset(viewId, action, 6);
+    }
+
+    private String logToString(ArrayList<LogEntry> log) {
+        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');
+        }
+        return sb.toString();
+    }
+
+    private void failWithLogs(String message) {
+        fail(message + ":\nExpected event sequence:\n" + logToString(mExpected) +
+                "\nActual event sequence:\n" + logToString(mActual));
+    }
+
+    private void verifyEventLog() {
+        try {
+            assertTrue("Timeout while waiting for END event",
+                    mEndReceived.await(1, TimeUnit.SECONDS));
+        } catch (InterruptedException e) {
+            fail("Got InterruptedException while waiting for END event");
+        }
+
+        // Verify the log.
+        runOnMain(() -> {
+            if (mExpected.size() != mActual.size()) {
+                failWithLogs("Actual log has different size than expected");
+            }
+
+            for (int i = 0; i < mActual.size(); ++i) {
+                if (!mActual.get(i).eq(mExpected.get(i))) {
+                    failWithLogs("Actual event #" + (i + 1) + " is different from expected");
+                }
+            }
+        });
+    }
+
+    private boolean init() {
+        // Only run for non-watch devices
+        if (mActivity.getPackageManager().hasSystemFeature(PackageManager.FEATURE_WATCH)) {
+            return false;
+        }
+        return true;
+    }
+
+    @Before
+    public void setUp() {
+        mActivity = mActivityRule.getActivity();
+        mEndReceived = new CountDownLatch(1);
+    }
+
+    @After
+    public void tearDown() throws Exception {
+        mActual.clear();
+        mExpected.clear();
+    }
+
+    // Sets handlers on all views in a tree, which log the event and return false.
+    private void setRejectingHandlersOnTree(View v) {
+        v.setOnDragListener((_v, ev) -> {
+            logEvent(_v, ev);
+            return false;
+        });
+
+        if (v instanceof ViewGroup) {
+            ViewGroup group = (ViewGroup) v;
+            for (int i = 0; i < group.getChildCount(); ++i) {
+                setRejectingHandlersOnTree(group.getChildAt(i));
+            }
+        }
+    }
+
+    private void runOnMain(Runnable runner) {
+        mInstrumentation.runOnMainSync(runner);
+    }
+
+    private void startDrag() {
+        // Mouse down. Required for the drag to start.
+        injectMouse5(R.id.draggable, MotionEvent.ACTION_DOWN);
+
+        runOnMain(() -> {
+            // Start drag.
+            View v = mActivity.findViewById(R.id.draggable);
+            assertTrue("Couldn't start drag",
+                    v.startDragAndDrop(null, new View.DragShadowBuilder(v), null, 0));
+        });
+    }
+
+    /**
+     * Tests that no drag-drop events are sent to views that aren't supposed to receive them.
+     */
+    @Test
+    public void testNoExtraEvents() throws Exception {
+        if (!init()) {
+            return;
+        }
+
+        runOnMain(() -> {
+            // Tell all views in layout to return false to all events, and log them.
+            setRejectingHandlersOnTree(mActivity.findViewById(R.id.drag_drop_activity_main));
+
+            // Override handlers for the inner view and its parent to return true.
+            mActivity.findViewById(R.id.inner).setOnDragListener((v, ev) -> {
+                logEvent(v, ev);
+                return true;
+            });
+            mActivity.findViewById(R.id.subcontainer).setOnDragListener((v, ev) -> {
+                logEvent(v, ev);
+                return true;
+            });
+        });
+
+        startDrag();
+
+        // Move mouse to the outmost view. This shouldn't generate any events since it returned
+        // false to STARTED.
+        injectMouse5(R.id.container, MotionEvent.ACTION_MOVE);
+        // Release mouse over the inner view. This produces DROP there.
+        injectMouse5(R.id.inner, MotionEvent.ACTION_UP);
+
+        expectEvent5(DragEvent.ACTION_DRAG_STARTED, R.id.inner, R.id.draggable);
+        expectEvent5(DragEvent.ACTION_DRAG_STARTED, R.id.subcontainer, R.id.draggable);
+        expectEvent5(DragEvent.ACTION_DRAG_STARTED, R.id.container, R.id.draggable);
+        expectEvent5(DragEvent.ACTION_DRAG_STARTED, R.id.draggable, R.id.draggable);
+        expectEvent5(DragEvent.ACTION_DRAG_STARTED, R.id.drag_drop_activity_main, R.id.draggable);
+
+        expectEvent5(DragEvent.ACTION_DRAG_ENTERED, R.id.inner);
+        expectEvent5(DragEvent.ACTION_DROP, R.id.inner, R.id.inner);
+
+        expectEndEventSuccess(R.id.inner);
+        expectEndEventSuccess(R.id.subcontainer);
+
+        verifyEventLog();
+    }
+
+    /**
+     * Tests events over a non-accepting view with an accepting child get delivered to that view's
+     * parent.
+     */
+    @Test
+    public void testBlackHole() throws Exception {
+        if (!init()) {
+            return;
+        }
+
+        runOnMain(() -> {
+            // Accepting child.
+            mActivity.findViewById(R.id.inner).setOnDragListener((v, ev) -> {
+                logEvent(v, ev);
+                return true;
+            });
+            // Non-accepting parent of that child.
+            mActivity.findViewById(R.id.subcontainer).setOnDragListener((v, ev) -> {
+                logEvent(v, ev);
+                return false;
+            });
+            // Accepting parent of the previous view.
+            mActivity.findViewById(R.id.container).setOnDragListener((v, ev) -> {
+                logEvent(v, ev);
+                return true;
+            });
+        });
+
+        startDrag();
+
+        // Move mouse to the non-accepting view.
+        injectMouse5(R.id.subcontainer, MotionEvent.ACTION_MOVE);
+        // Release mouse over the non-accepting view, with different coordinates.
+        injectMouse6(R.id.subcontainer, MotionEvent.ACTION_UP);
+
+        expectEvent5(DragEvent.ACTION_DRAG_STARTED, R.id.inner, R.id.draggable);
+        expectEvent5(DragEvent.ACTION_DRAG_STARTED, R.id.subcontainer, R.id.draggable);
+        expectEvent5(DragEvent.ACTION_DRAG_STARTED, R.id.container, R.id.draggable);
+
+        expectEvent5(DragEvent.ACTION_DRAG_ENTERED, R.id.container);
+        expectEvent5(DragEvent.ACTION_DRAG_LOCATION, R.id.container, R.id.subcontainer);
+        expectEvent6(DragEvent.ACTION_DROP, R.id.container, R.id.subcontainer);
+
+        expectEndEventSuccess(R.id.inner);
+        expectEndEventSuccess(R.id.container);
+
+        verifyEventLog();
+    }
+
+    /**
+     * Tests generation of ENTER/EXIT events.
+     */
+    @Test
+    public void testEnterExit() throws Exception {
+        if (!init()) {
+            return;
+        }
+
+        runOnMain(() -> {
+            // The setup is same as for testBlackHole.
+
+            // Accepting child.
+            mActivity.findViewById(R.id.inner).setOnDragListener((v, ev) -> {
+                logEvent(v, ev);
+                return true;
+            });
+            // Non-accepting parent of that child.
+            mActivity.findViewById(R.id.subcontainer).setOnDragListener((v, ev) -> {
+                logEvent(v, ev);
+                return false;
+            });
+            // Accepting parent of the previous view.
+            mActivity.findViewById(R.id.container).setOnDragListener((v, ev) -> {
+                logEvent(v, ev);
+                return true;
+            });
+
+        });
+
+        startDrag();
+
+        // Move mouse to the non-accepting view, then to the inner one, then back to the
+        // non-accepting view, then release over the inner.
+        injectMouse5(R.id.subcontainer, MotionEvent.ACTION_MOVE);
+        injectMouse5(R.id.inner, MotionEvent.ACTION_MOVE);
+        injectMouse5(R.id.subcontainer, MotionEvent.ACTION_MOVE);
+        injectMouse5(R.id.inner, MotionEvent.ACTION_UP);
+
+        expectEvent5(DragEvent.ACTION_DRAG_STARTED, R.id.inner, R.id.draggable);
+        expectEvent5(DragEvent.ACTION_DRAG_STARTED, R.id.subcontainer, R.id.draggable);
+        expectEvent5(DragEvent.ACTION_DRAG_STARTED, R.id.container, R.id.draggable);
+
+        expectEvent5(DragEvent.ACTION_DRAG_ENTERED, R.id.container);
+        expectEvent5(DragEvent.ACTION_DRAG_LOCATION, R.id.container, R.id.subcontainer);
+        expectEvent5(DragEvent.ACTION_DRAG_EXITED, R.id.container);
+
+        expectEvent5(DragEvent.ACTION_DRAG_ENTERED, R.id.inner);
+        expectEvent5(DragEvent.ACTION_DRAG_LOCATION, R.id.inner, R.id.inner);
+        expectEvent5(DragEvent.ACTION_DRAG_EXITED, R.id.inner);
+
+        expectEvent5(DragEvent.ACTION_DRAG_ENTERED, R.id.container);
+        expectEvent5(DragEvent.ACTION_DRAG_LOCATION, R.id.container, R.id.subcontainer);
+        expectEvent5(DragEvent.ACTION_DRAG_EXITED, R.id.container);
+
+        expectEvent5(DragEvent.ACTION_DRAG_ENTERED, R.id.inner);
+        expectEvent5(DragEvent.ACTION_DROP, R.id.inner, R.id.inner);
+
+        expectEndEventSuccess(R.id.inner);
+        expectEndEventSuccess(R.id.container);
+
+        verifyEventLog();
+    }
+    /**
+     * Tests events over a non-accepting view that has no accepting ancestors.
+     */
+    @Test
+    public void testOverNowhere() throws Exception {
+        if (!init()) {
+            return;
+        }
+
+        runOnMain(() -> {
+            // Accepting child.
+            mActivity.findViewById(R.id.inner).setOnDragListener((v, ev) -> {
+                logEvent(v, ev);
+                return true;
+            });
+            // Non-accepting parent of that child.
+            mActivity.findViewById(R.id.subcontainer).setOnDragListener((v, ev) -> {
+                logEvent(v, ev);
+                return false;
+            });
+        });
+
+        startDrag();
+
+        // Move mouse to the non-accepting view, then to accepting view, and back, and drop there.
+        injectMouse5(R.id.subcontainer, MotionEvent.ACTION_MOVE);
+        injectMouse5(R.id.inner, MotionEvent.ACTION_MOVE);
+        injectMouse5(R.id.subcontainer, MotionEvent.ACTION_MOVE);
+        injectMouse6(R.id.subcontainer, MotionEvent.ACTION_UP);
+
+        expectEvent5(DragEvent.ACTION_DRAG_STARTED, R.id.inner, R.id.draggable);
+        expectEvent5(DragEvent.ACTION_DRAG_STARTED, R.id.subcontainer, R.id.draggable);
+
+        expectEvent5(DragEvent.ACTION_DRAG_ENTERED, R.id.inner);
+        expectEvent5(DragEvent.ACTION_DRAG_LOCATION, R.id.inner, R.id.inner);
+        expectEvent5(DragEvent.ACTION_DRAG_EXITED, R.id.inner);
+
+        expectEndEventFailure6(R.id.inner, R.id.subcontainer);
+
+        verifyEventLog();
+    }
+
+    /**
+     * Tests that events are properly delivered to a view that is in the middle of the accepting
+     * hierarchy.
+     */
+    @Test
+    public void testAcceptingGroupInTheMiddle() throws Exception {
+        if (!init()) {
+            return;
+        }
+
+        runOnMain(() -> {
+            // Set accepting handlers to the inner view and its 2 ancestors.
+            mActivity.findViewById(R.id.inner).setOnDragListener((v, ev) -> {
+                logEvent(v, ev);
+                return true;
+            });
+            mActivity.findViewById(R.id.subcontainer).setOnDragListener((v, ev) -> {
+                logEvent(v, ev);
+                return true;
+            });
+            mActivity.findViewById(R.id.container).setOnDragListener((v, ev) -> {
+                logEvent(v, ev);
+                return true;
+            });
+        });
+
+        startDrag();
+
+        // Move mouse to the outmost container, then move to the subcontainer and drop there.
+        injectMouse5(R.id.container, MotionEvent.ACTION_MOVE);
+        injectMouse5(R.id.subcontainer, MotionEvent.ACTION_MOVE);
+        injectMouse6(R.id.subcontainer, MotionEvent.ACTION_UP);
+
+        expectEvent5(DragEvent.ACTION_DRAG_STARTED, R.id.inner, R.id.draggable);
+        expectEvent5(DragEvent.ACTION_DRAG_STARTED, R.id.subcontainer, R.id.draggable);
+        expectEvent5(DragEvent.ACTION_DRAG_STARTED, R.id.container, R.id.draggable);
+
+        expectEvent5(DragEvent.ACTION_DRAG_ENTERED, R.id.container);
+        expectEvent5(DragEvent.ACTION_DRAG_LOCATION, R.id.container, R.id.container);
+        expectEvent5(DragEvent.ACTION_DRAG_EXITED, R.id.container);
+
+        expectEvent5(DragEvent.ACTION_DRAG_ENTERED, R.id.subcontainer);
+        expectEvent5(DragEvent.ACTION_DRAG_LOCATION, R.id.subcontainer, R.id.subcontainer);
+        expectEvent6(DragEvent.ACTION_DROP, R.id.subcontainer, R.id.subcontainer);
+
+        expectEndEventSuccess(R.id.inner);
+        expectEndEventSuccess(R.id.subcontainer);
+        expectEndEventSuccess(R.id.container);
+
+        verifyEventLog();
+    }
+
+    /**
+     * Tests that state_drag_hovered and state_drag_can_accept are set correctly.
+     */
+    @Test
+    public void testDrawableState() throws Exception {
+        if (!init()) {
+            return;
+        }
+
+        runOnMain(() -> {
+            // Set accepting handler for the inner view.
+            mActivity.findViewById(R.id.inner).setOnDragListener((v, ev) -> {
+                logEvent(v, ev);
+                return true;
+            });
+            assertFalse(ArrayUtils.contains(
+                    mActivity.findViewById(R.id.inner).getDrawableState(),
+                    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));
+        });
+
+        // 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));
+        });
+
+        // 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));
+        });
+
+        // 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));
+        });
+
+        // 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));
+        });
+    }
+}
\ No newline at end of file
diff --git a/tests/tests/view/src/android/view/cts/PixelCopyTests.java b/tests/tests/view/src/android/view/cts/PixelCopyTests.java
index 3fa3634..f8bf79a 100644
--- a/tests/tests/view/src/android/view/cts/PixelCopyTests.java
+++ b/tests/tests/view/src/android/view/cts/PixelCopyTests.java
@@ -23,9 +23,12 @@
 import android.graphics.SurfaceTexture;
 import android.opengl.GLSurfaceView;
 import android.opengl.GLSurfaceView.Renderer;
+import android.os.Debug;
+import android.os.Debug.MemoryInfo;
 import android.os.Handler;
 import android.os.HandlerThread;
 import android.support.test.InstrumentationRegistry;
+import android.support.test.filters.LargeTest;
 import android.support.test.filters.MediumTest;
 import android.support.test.rule.ActivityTestRule;
 import android.util.Log;
@@ -171,6 +174,61 @@
     }
 
     @Test
+    @LargeTest
+    public void testNotLeaking() {
+        try {
+            CountDownLatch swapFence = new CountDownLatch(2);
+            GLSurfaceViewCtsActivity.setGlVersion(2);
+            GLSurfaceViewCtsActivity.setRenderMode(GLSurfaceView.RENDERMODE_WHEN_DIRTY);
+            GLSurfaceViewCtsActivity.setFixedSize(100, 100);
+            GLSurfaceViewCtsActivity.setRenderer(new QuadColorGLRenderer(
+                    Color.RED, Color.GREEN, Color.BLUE, Color.BLACK, swapFence));
+
+            GLSurfaceViewCtsActivity activity =
+                    mGLSurfaceViewActivityRule.launchActivity(null);
+
+            while (!swapFence.await(5, TimeUnit.MILLISECONDS)) {
+                activity.getView().requestRender();
+            }
+
+            // Test a fullsize copy
+            Bitmap bitmap = Bitmap.createBitmap(100, 100, Config.ARGB_8888);
+
+            MemoryInfo meminfoStart = new MemoryInfo();
+            MemoryInfo meminfoEnd = new MemoryInfo();
+
+            for (int i = 0; i < 1000; i++) {
+                if (i == 2) {
+                    // Not really the "start" but by having done a couple
+                    // we've fully initialized any state that may be required,
+                    // so memory usage should be stable now
+                    Debug.getMemoryInfo(meminfoStart);
+                }
+                if (i % 10 == 5) {
+                    Debug.getMemoryInfo(meminfoEnd);
+                    if (meminfoEnd.getTotalPss() - meminfoStart.getTotalPss() > 1000 /* kb */) {
+                        assertEquals("Memory leaked, iteration=" + i,
+                                meminfoStart.getTotalPss(), meminfoEnd.getTotalPss(),
+                                1000 /* kb */);
+                    }
+                }
+                SynchronousPixelCopy copyHelper = new SynchronousPixelCopy();
+                int result = copyHelper.request(activity.getView(), bitmap);
+                assertEquals("Copy request failed", PixelCopy.SUCCESS, result);
+                // Make sure nothing messed with the bitmap
+                assertEquals(100, bitmap.getWidth());
+                assertEquals(100, bitmap.getHeight());
+                assertEquals(Config.ARGB_8888, bitmap.getConfig());
+                assertBitmapQuadColor(bitmap,
+                        Color.RED, Color.GREEN, Color.BLUE, Color.BLACK);
+            }
+
+        } catch (InterruptedException e) {
+            fail("Interrupted, error=" + e.getMessage());
+        }
+    }
+
+    @Test
     public void testVideoProducer() throws InterruptedException {
         PixelCopyVideoSourceActivity activity =
                 mVideoSourceActivityRule.launchActivity(null);
diff --git a/tests/tests/view/src/android/view/cts/SurfaceViewSyncTests.java b/tests/tests/view/src/android/view/cts/SurfaceViewSyncTests.java
index da453dd..61c30a0 100644
--- a/tests/tests/view/src/android/view/cts/SurfaceViewSyncTests.java
+++ b/tests/tests/view/src/android/view/cts/SurfaceViewSyncTests.java
@@ -19,10 +19,12 @@
 import android.animation.PropertyValuesHolder;
 import android.animation.ValueAnimator;
 import android.annotation.SuppressLint;
+import android.graphics.Bitmap;
 import android.graphics.Canvas;
 import android.graphics.Color;
 import android.graphics.Paint;
 import android.media.MediaPlayer;
+import android.os.Environment;
 import android.support.test.InstrumentationRegistry;
 import android.support.test.rule.ActivityTestRule;
 import android.support.test.runner.AndroidJUnit4;
@@ -32,6 +34,7 @@
 import android.support.test.uiautomator.UiSelector;
 import android.test.suitebuilder.annotation.LargeTest;
 import android.util.Log;
+import android.util.SparseArray;
 import android.view.Gravity;
 import android.view.SurfaceHolder;
 import android.view.SurfaceView;
@@ -44,11 +47,19 @@
 import android.view.cts.surfacevalidator.ViewFactory;
 import android.widget.FrameLayout;
 
+import libcore.io.IoUtils;
+import org.junit.After;
 import org.junit.Before;
 import org.junit.Rule;
 import org.junit.Test;
+import org.junit.rules.TestName;
 import org.junit.runner.RunWith;
 
+import java.io.File;
+import java.io.FileOutputStream;
+import java.io.IOException;
+import java.util.concurrent.TimeUnit;
+
 import static org.junit.Assert.*;
 
 @RunWith(AndroidJUnit4.class)
@@ -56,16 +67,22 @@
 @SuppressLint("RtlHardcoded")
 public class SurfaceViewSyncTests {
     private static final String TAG = "SurfaceViewSyncTests";
-    private static final int PERMISSION_DIALOG_WAIT_MS = 500;
+    private static final int PERMISSION_DIALOG_WAIT_MS = 1000;
 
+    /**
+     * Want to be especially sure we don't leave up the permission dialog, so try and dismiss both
+     * before and after test.
+     */
     @Before
+    @After
     public void setUp() throws UiObjectNotFoundException {
         // The permission dialog will be auto-opened by the activity - find it and accept
         UiDevice uiDevice = UiDevice.getInstance(InstrumentationRegistry.getInstrumentation());
         UiSelector acceptButtonSelector = new UiSelector().resourceId("android:id/button1");
         UiObject acceptButton = uiDevice.findObject(acceptButtonSelector);
         if (acceptButton.waitForExists(PERMISSION_DIALOG_WAIT_MS)) {
-            assertTrue(acceptButton.click());
+            boolean success = acceptButton.click();
+            Log.d(TAG, "found permission dialog, click attempt success = " + success);
         }
     }
 
@@ -80,6 +97,9 @@
     @Rule
     public ActivityTestRule mActivityRule = new ActivityTestRule<>(CapturedActivity.class);
 
+    @Rule
+    public TestName mName = new TestName();
+
     static ValueAnimator makeInfinite(ValueAnimator a) {
         a.setRepeatMode(ObjectAnimator.REVERSE);
         a.setRepeatCount(ObjectAnimator.INFINITE);
@@ -165,13 +185,76 @@
     };
 
     ///////////////////////////////////////////////////////////////////////////
+    // Bad frame capture
+    ///////////////////////////////////////////////////////////////////////////
+
+    private void saveFailureCaptures(SparseArray<Bitmap> failFrames) {
+        if (failFrames.size() == 0) return;
+
+        String directoryName = Environment.getExternalStorageDirectory()
+                + "/" + getClass().getSimpleName()
+                + "/" + mName.getMethodName();
+        File testDirectory = new File(directoryName);
+        if (testDirectory.exists()) {
+            String[] children = testDirectory.list();
+            if (children == null) {
+                return;
+            }
+            for (String file : children) {
+                new File(testDirectory, file).delete();
+            }
+        } else {
+            testDirectory.mkdirs();
+        }
+
+        for (int i = 0; i < failFrames.size(); i++) {
+            int frameNr = failFrames.keyAt(i);
+            Bitmap bitmap = failFrames.valueAt(i);
+
+            String bitmapName =  "frame_" + frameNr + ".png";
+            Log.d(TAG, "Saving file : " + bitmapName + " in directory : " + directoryName);
+
+            File file = new File(directoryName, bitmapName);
+            FileOutputStream fileStream = null;
+            try {
+                fileStream = new FileOutputStream(file);
+                bitmap.compress(Bitmap.CompressFormat.PNG, 85, fileStream);
+                fileStream.flush();
+            } catch (IOException e) {
+                e.printStackTrace();
+            } finally {
+                IoUtils.closeQuietly(fileStream);
+            }
+        }
+    }
+
+
+    ///////////////////////////////////////////////////////////////////////////
     // Tests
     ///////////////////////////////////////////////////////////////////////////
 
+    public void verifyTest(AnimationTestCase testCase) throws InterruptedException {
+        CapturedActivity.TestResult result = getActivity().runTest(testCase);
+        saveFailureCaptures(result.failures);
+
+        float failRatio = 1.0f * result.failFrames / (result.failFrames + result.passFrames);
+        assertTrue("Error: " + failRatio + " fail ratio - extremely high, is activity obstructed?",
+                failRatio < 0.95f);
+        assertTrue("Error: " + result.failFrames
+                + " incorrect frames observed - incorrect positioning",
+                result.failFrames == 0);
+        float framesPerSecond = 1.0f * result.passFrames
+                / TimeUnit.MILLISECONDS.toSeconds(CapturedActivity.CAPTURE_DURATION_MS);
+        assertTrue("Error, only " + result.passFrames
+                + " frames observed, virtual display only capturing at "
+                + framesPerSecond + " frames per second",
+                result.passFrames > 100);
+    }
+
     /** Draws a moving 10x10 black rectangle, validates 100 pixels of black are seen each frame */
     @Test
-    public void testSmallRect() {
-        CapturedActivity.TestResult result = getActivity().runTest(new AnimationTestCase(
+    public void testSmallRect() throws InterruptedException {
+        verifyTest(new AnimationTestCase(
                 context -> new View(context) {
                     // draw a single pixel
                     final Paint sBlackPaint = new Paint();
@@ -190,9 +273,8 @@
                 },
                 new FrameLayout.LayoutParams(100, 100, Gravity.LEFT | Gravity.TOP),
                 view -> makeInfinite(ObjectAnimator.ofInt(view, "offset", 10, 30)),
-                (blackishPixelCount, width, height) -> blackishPixelCount == 100));
-        assertTrue(result.passFrames > 100);
-        assertTrue(result.failFrames == 0);
+                (blackishPixelCount, width, height) ->
+                        blackishPixelCount >= 90 && blackishPixelCount <= 110));
     }
 
     /**
@@ -200,53 +282,45 @@
      * approximate to avoid rounding brittleness.
      */
     @Test
-    public void testEmptySurfaceView() {
-        CapturedActivity.TestResult result = getActivity().runTest(new AnimationTestCase(
+    public void testEmptySurfaceView() throws InterruptedException {
+        verifyTest(new AnimationTestCase(
                 sEmptySurfaceViewFactory,
                 new FrameLayout.LayoutParams(100, 100, Gravity.LEFT | Gravity.TOP),
                 sTranslateAnimationFactory,
                 (blackishPixelCount, width, height) ->
                         blackishPixelCount > 9000 && blackishPixelCount < 11000));
-        assertTrue(result.passFrames > 100);
-        assertTrue(result.failFrames == 0);
     }
 
     @Test
-    public void testSurfaceViewSmallScale() {
-        CapturedActivity.TestResult result = getActivity().runTest(new AnimationTestCase(
+    public void testSurfaceViewSmallScale() throws InterruptedException {
+        verifyTest(new AnimationTestCase(
                 sGreenSurfaceViewFactory,
                 new FrameLayout.LayoutParams(640, 480, Gravity.LEFT | Gravity.TOP),
                 sSmallScaleAnimationFactory,
                 (blackishPixelCount, width, height) -> blackishPixelCount == 0));
-        assertTrue(result.passFrames > 100);
-        assertTrue(result.failFrames == 0);
     }
 
     @Test
-    public void testSurfaceViewBigScale() {
-        CapturedActivity.TestResult result = getActivity().runTest(new AnimationTestCase(
+    public void testSurfaceViewBigScale() throws InterruptedException {
+        verifyTest(new AnimationTestCase(
                 sGreenSurfaceViewFactory,
                 new FrameLayout.LayoutParams(640, 480, Gravity.LEFT | Gravity.TOP),
                 sBigScaleAnimationFactory,
                 (blackishPixelCount, width, height) -> blackishPixelCount == 0));
-        assertTrue(result.passFrames > 100);
-        assertTrue(result.failFrames == 0);
     }
 
     @Test
-    public void testVideoSurfaceViewTranslate() {
-        CapturedActivity.TestResult result = getActivity().runTest(new AnimationTestCase(
+    public void testVideoSurfaceViewTranslate() throws InterruptedException {
+        verifyTest(new AnimationTestCase(
                 sVideoViewFactory,
                 new FrameLayout.LayoutParams(640, 480, Gravity.LEFT | Gravity.TOP),
                 sTranslateAnimationFactory,
                 (blackishPixelCount, width, height) -> blackishPixelCount == 0));
-        assertTrue(result.passFrames > 100);
-        assertTrue(result.failFrames == 0);
     }
 
     @Test
-    public void testVideoSurfaceViewRotated() {
-        CapturedActivity.TestResult result = getActivity().runTest(new AnimationTestCase(
+    public void testVideoSurfaceViewRotated() throws InterruptedException {
+        verifyTest(new AnimationTestCase(
                 sVideoViewFactory,
                 new FrameLayout.LayoutParams(100, 100, Gravity.LEFT | Gravity.TOP),
                 view -> makeInfinite(ObjectAnimator.ofPropertyValuesHolder(view,
@@ -254,13 +328,11 @@
                         PropertyValuesHolder.ofFloat(View.TRANSLATION_Y, 10f, 30f),
                         PropertyValuesHolder.ofFloat(View.ROTATION, 45f, 45f))),
                 (blackishPixelCount, width, height) -> blackishPixelCount == 0));
-        assertTrue(result.passFrames > 100);
-        assertTrue(result.failFrames == 0);
     }
 
     @Test
-    public void testVideoSurfaceViewEdgeCoverage() {
-        CapturedActivity.TestResult result = getActivity().runTest(new AnimationTestCase(
+    public void testVideoSurfaceViewEdgeCoverage() throws InterruptedException {
+        verifyTest(new AnimationTestCase(
                 sVideoViewFactory,
                 new FrameLayout.LayoutParams(640, 480, Gravity.CENTER),
                 view -> {
@@ -274,13 +346,11 @@
                             PropertyValuesHolder.ofFloat(View.TRANSLATION_Y, 0, -y, 0, y, 0)));
                 },
                 (blackishPixelCount, width, height) -> blackishPixelCount == 0));
-        assertTrue(result.passFrames > 100);
-        assertTrue(result.failFrames == 0);
     }
 
     @Test
-    public void testVideoSurfaceViewCornerCoverage() {
-        CapturedActivity.TestResult result = getActivity().runTest(new AnimationTestCase(
+    public void testVideoSurfaceViewCornerCoverage() throws InterruptedException {
+        verifyTest(new AnimationTestCase(
                 sVideoViewFactory,
                 new FrameLayout.LayoutParams(640, 480, Gravity.CENTER),
                 view -> {
@@ -294,7 +364,5 @@
                             PropertyValuesHolder.ofFloat(View.TRANSLATION_Y, -y, -y, y, y, -y)));
                 },
                 (blackishPixelCount, width, height) -> blackishPixelCount == 0));
-        assertTrue(result.passFrames > 100);
-        assertTrue(result.failFrames == 0);
     }
 }
diff --git a/tests/tests/view/src/android/view/cts/surfacevalidator/CapturedActivity.java b/tests/tests/view/src/android/view/cts/surfacevalidator/CapturedActivity.java
index 6a23e02..82d113d 100644
--- a/tests/tests/view/src/android/view/cts/surfacevalidator/CapturedActivity.java
+++ b/tests/tests/view/src/android/view/cts/surfacevalidator/CapturedActivity.java
@@ -19,6 +19,7 @@
 import android.content.Context;
 import android.content.Intent;
 import android.content.pm.PackageManager;
+import android.graphics.Bitmap;
 import android.graphics.Point;
 import android.hardware.display.DisplayManager;
 import android.hardware.display.VirtualDisplay;
@@ -30,20 +31,27 @@
 import android.os.Looper;
 import android.util.DisplayMetrics;
 import android.util.Log;
+import android.util.SparseArray;
 import android.view.Display;
 import android.view.View;
 import android.widget.FrameLayout;
 
 import android.view.cts.R;
 
+import java.util.concurrent.CountDownLatch;
+import java.util.concurrent.TimeUnit;
+import static org.junit.Assert.*;
+
+
 public class CapturedActivity extends Activity {
     public static class TestResult {
         public int passFrames;
         public int failFrames;
+        public final SparseArray<Bitmap> failures = new SparseArray<>();
     }
 
     private static final String TAG = "CapturedActivity";
-    private static final long TIME_OUT_MS = 10000;
+    private static final long TIME_OUT_MS = 25000;
     private static final int PERMISSION_CODE = 1;
     private MediaProjectionManager mProjectionManager;
     private MediaProjection mMediaProjection;
@@ -52,30 +60,38 @@
     private SurfacePixelValidator mSurfacePixelValidator;
     private final Object mLock = new Object();
 
-    private static final long START_CAPTURE_DELAY_MS = 1000;
-    private static final long END_CAPTURE_DELAY_MS = START_CAPTURE_DELAY_MS + 4000;
-    private static final long END_DELAY_MS = END_CAPTURE_DELAY_MS + 500;
+    public static final long CAPTURE_DURATION_MS = 10000;
+
+    private static final long START_CAPTURE_DELAY_MS = 4000;
+    private static final long END_CAPTURE_DELAY_MS = START_CAPTURE_DELAY_MS + CAPTURE_DURATION_MS;
+    private static final long END_DELAY_MS = END_CAPTURE_DELAY_MS + 1000;
 
     private MediaPlayer mMediaPlayer;
 
     private final Handler mHandler = new Handler(Looper.getMainLooper());
     private volatile boolean mOnWatch;
+    private CountDownLatch mCountDownLatch;
 
     @Override
     public void onCreate(Bundle savedInstanceState) {
         super.onCreate(savedInstanceState);
+        mOnWatch = getPackageManager().hasSystemFeature(PackageManager.FEATURE_WATCH);
+        if (mOnWatch) {
+            // Don't try and set up test/capture infrastructure - they're not supported
+            return;
+        }
+
         getWindow().getDecorView().setSystemUiVisibility(
                 View.SYSTEM_UI_FLAG_HIDE_NAVIGATION | View.SYSTEM_UI_FLAG_FULLSCREEN);
 
         mProjectionManager =
                 (MediaProjectionManager) getSystemService(Context.MEDIA_PROJECTION_SERVICE);
 
+        mCountDownLatch = new CountDownLatch(1);
         startActivityForResult(mProjectionManager.createScreenCaptureIntent(), PERMISSION_CODE);
 
         mMediaPlayer = MediaPlayer.create(this, R.raw.colors_video);
         mMediaPlayer.setLooping(true);
-
-        mOnWatch = getPackageManager().hasSystemFeature(PackageManager.FEATURE_WATCH);
     }
 
     /**
@@ -97,17 +113,23 @@
 
     @Override
     public void onActivityResult(int requestCode, int resultCode, Intent data) {
+        if (mOnWatch) return;
+        getWindow().getDecorView().setSystemUiVisibility(
+                View.SYSTEM_UI_FLAG_HIDE_NAVIGATION | View.SYSTEM_UI_FLAG_FULLSCREEN);
+
         if (requestCode != PERMISSION_CODE) {
             throw new IllegalStateException("Unknown request code: " + requestCode);
         }
         if (resultCode != RESULT_OK) {
             throw new IllegalStateException("User denied screen sharing permission");
         }
+        Log.d(TAG, "onActivityResult");
         mMediaProjection = mProjectionManager.getMediaProjection(resultCode, data);
         mMediaProjection.registerCallback(new MediaProjectionCallback(), null);
+        mCountDownLatch.countDown();
     }
 
-    public TestResult runTest(AnimationTestCase animationTestCase) {
+    public TestResult runTest(AnimationTestCase animationTestCase) throws InterruptedException {
         TestResult testResult = new TestResult();
         if (mOnWatch) {
             /**
@@ -122,6 +144,9 @@
             return testResult;
         }
 
+        assertTrue("Can't initialize mediaProjection",
+                mCountDownLatch.await(TIME_OUT_MS, TimeUnit.MILLISECONDS));
+
         mHandler.post(() -> {
             Log.d(TAG, "Setting up test case");
 
@@ -143,6 +168,7 @@
             display.getRealSize(size);
             display.getMetrics(metrics);
 
+
             mSurfacePixelValidator = new SurfacePixelValidator(CapturedActivity.this,
                     size, animationTestCase.getChecker());
             Log.d("MediaProjection", "Size is " + size.toString());
@@ -172,11 +198,7 @@
         }, END_DELAY_MS);
 
         synchronized (mLock) {
-            try {
-                mLock.wait(TIME_OUT_MS);
-            } catch (InterruptedException e) {
-                e.printStackTrace();
-            }
+            mLock.wait(TIME_OUT_MS);
         }
         Log.d(TAG, "Test finished, passFrames " + testResult.passFrames
                 + ", failFrames " + testResult.failFrames);
diff --git a/tests/tests/view/src/android/view/cts/surfacevalidator/PixelCounter.rs b/tests/tests/view/src/android/view/cts/surfacevalidator/PixelCounter.rs
index 55bc251..f58b9cb 100644
--- a/tests/tests/view/src/android/view/cts/surfacevalidator/PixelCounter.rs
+++ b/tests/tests/view/src/android/view/cts/surfacevalidator/PixelCounter.rs
@@ -15,22 +15,18 @@
  */
 #pragma version(1)
 #pragma rs java_package_name(android.view.cts.surfacevalidator)
+#pragma rs reduce(countBlackishPixels) accumulator(countBlackishPixelsAccum) combiner(countBlackishPixelsCombiner)
 
-int WIDTH;
 uchar THRESHOLD;
 
-rs_allocation image;
-
-void countBlackishPixels(const int32_t *v_in, int *v_out){
-    int y = v_in[0];
-    v_out[0] = 0;
-
-    for(int i = 0 ; i < WIDTH; i++){
-        uchar4 pixel = rsGetElementAt_uchar4(image, i, y);
-        if (pixel.r < THRESHOLD
-                && pixel.g < THRESHOLD
-                && pixel.b < THRESHOLD) {
-            v_out[0]++;
-        }
+static void countBlackishPixelsAccum(int *accum, uchar4 pixel){
+    if (pixel.r < THRESHOLD
+            && pixel.g < THRESHOLD
+            && pixel.b < THRESHOLD) {
+        *accum += 1;
     }
 }
+
+static void countBlackishPixelsCombiner(int *accum, const int *other){
+    *accum += *other;
+}
diff --git a/tests/tests/view/src/android/view/cts/surfacevalidator/SurfacePixelValidator.java b/tests/tests/view/src/android/view/cts/surfacevalidator/SurfacePixelValidator.java
index c9bff1d..5a30b77 100644
--- a/tests/tests/view/src/android/view/cts/surfacevalidator/SurfacePixelValidator.java
+++ b/tests/tests/view/src/android/view/cts/surfacevalidator/SurfacePixelValidator.java
@@ -16,6 +16,7 @@
 package android.view.cts.surfacevalidator;
 
 import android.content.Context;
+import android.graphics.Bitmap;
 import android.graphics.Point;
 import android.os.Handler;
 import android.os.HandlerThread;
@@ -25,11 +26,13 @@
 import android.renderscript.RenderScript;
 import android.renderscript.Type;
 import android.util.Log;
+import android.util.SparseArray;
 import android.view.Surface;
 import android.view.cts.surfacevalidator.PixelChecker;
 
 import java.lang.reflect.InvocationTargetException;
 import java.lang.reflect.Method;
+import java.util.ArrayList;
 
 public class SurfacePixelValidator {
     private static final String TAG = "SurfacePixelValidator";
@@ -43,6 +46,8 @@
     // If no channel is greater than this value, pixel will be considered 'blackish'.
     private static final short PIXEL_CHANNEL_THRESHOLD = 4;
 
+    private static final int MAX_CAPTURED_FAILURES = 5;
+
     private final int mWidth;
     private final int mHeight;
 
@@ -54,14 +59,13 @@
     private final RenderScript mRS;
 
     private final Allocation mInPixelsAllocation;
-    private final Allocation mInRowsAllocation;
-    private final Allocation mOutRowsAllocation;
     private final ScriptC_PixelCounter mScript;
 
 
     private final Object mResultLock = new Object();
     private int mResultSuccessFrames;
     private int mResultFailureFrames;
+    private SparseArray<Bitmap> mFirstFailures = new SparseArray<>(MAX_CAPTURED_FAILURES);
 
     private Runnable mConsumeRunnable = new Runnable() {
         int numSkipped = 0;
@@ -69,15 +73,10 @@
         public void run() {
             Trace.beginSection("consume buffer");
             mInPixelsAllocation.ioReceive();
-            mScript.set_image(mInPixelsAllocation);
             Trace.endSection();
 
-            Trace.beginSection("compare");
-            mScript.forEach_countBlackishPixels(mInRowsAllocation, mOutRowsAllocation);
-            Trace.endSection();
-
-            Trace.beginSection("sum");
-            int blackishPixelCount = sum1DIntAllocation(mOutRowsAllocation, mHeight);
+            Trace.beginSection("compare and sum");
+            int blackishPixelCount = mScript.reduce_countBlackishPixels(mInPixelsAllocation).get();
             Trace.endSection();
 
             boolean success = mPixelChecker.checkPixels(blackishPixelCount, mWidth, mHeight);
@@ -85,7 +84,6 @@
                 if (numSkipped < NUM_FIRST_FRAMES_SKIPPED) {
                     numSkipped++;
                 } else {
-
                     if (success) {
                         mResultSuccessFrames++;
                     } else {
@@ -93,6 +91,15 @@
                         int totalFramesSeen = mResultSuccessFrames + mResultFailureFrames;
                         Log.d(TAG, "Failure (pixel count = " + blackishPixelCount
                                 + ") occurred on frame " + totalFramesSeen);
+
+                        if (mFirstFailures.size() < MAX_CAPTURED_FAILURES) {
+                            Log.d(TAG, "Capturing bitmap #" + mFirstFailures.size());
+                            // error, worth looking at...
+                            Bitmap capture = Bitmap.createBitmap(mWidth, mHeight,
+                                    Bitmap.Config.ARGB_8888);
+                            mInPixelsAllocation.copyTo(capture);
+                            mFirstFailures.put(totalFramesSeen, capture);
+                        }
                     }
                 }
             }
@@ -113,9 +120,6 @@
         mScript = new ScriptC_PixelCounter(mRS);
 
         mInPixelsAllocation = createBufferQueueAllocation();
-        mInRowsAllocation = createInputRowIndexAllocation();
-        mOutRowsAllocation = createOutputRowAllocation();
-        mScript.set_WIDTH(mWidth);
         mScript.set_THRESHOLD(PIXEL_CHANNEL_THRESHOLD);
 
         mInPixelsAllocation.setOnBufferAvailableListener(
@@ -126,41 +130,10 @@
         return mInPixelsAllocation.getSurface();
     }
 
-    static private int sum1DIntAllocation(Allocation array, int length) {
-        //Get the values returned from the function
-        int[] returnValue = new int[length];
-        array.copyTo(returnValue);
-        int sum = 0;
-        //If any row had any different pixels, then it fails
-        for (int i = 0; i < length; i++) {
-            sum += returnValue[i];
-        }
-        return sum;
-    }
-
-    /**
-     * Creates an allocation where the values in it are the indices of each row
-     */
-    private Allocation createInputRowIndexAllocation() {
-        //Create an array with the index of each row
-        int[] inputIndices = new int[mHeight];
-        for (int i = 0; i < mHeight; i++) {
-            inputIndices[i] = i;
-        }
-        //Create the allocation from that given array
-        Allocation inputAllocation = Allocation.createSized(mRS, Element.I32(mRS),
-                inputIndices.length, Allocation.USAGE_SCRIPT);
-        inputAllocation.copyFrom(inputIndices);
-        return inputAllocation;
-    }
-
-    private Allocation createOutputRowAllocation() {
-        return Allocation.createSized(mRS, Element.I32(mRS), mHeight, Allocation.USAGE_SCRIPT);
-    }
-
     private Allocation createBufferQueueAllocation() {
         return Allocation.createAllocations(mRS, Type.createXY(mRS,
-                Element.U8_4(mRS), mWidth, mHeight),
+                Element.RGBA_8888(mRS)
+                /*Element.U32(mRS)*/, mWidth, mHeight),
                 Allocation.USAGE_SCRIPT | Allocation.USAGE_IO_INPUT,
                 1)[0];
     }
@@ -177,6 +150,10 @@
             // Caller should only call this
             testResult.failFrames = mResultFailureFrames;
             testResult.passFrames = mResultSuccessFrames;
+
+            for (int i = 0; i < mFirstFailures.size(); i++) {
+                testResult.failures.put(mFirstFailures.keyAt(i), mFirstFailures.valueAt(i));
+            }
         }
         mWorkerThread.quitSafely();
     }
diff --git a/tests/tests/webkit/src/android/webkit/cts/ServiceWorkerClientTest.java b/tests/tests/webkit/src/android/webkit/cts/ServiceWorkerClientTest.java
index a77d648..e09e0d6 100644
--- a/tests/tests/webkit/src/android/webkit/cts/ServiceWorkerClientTest.java
+++ b/tests/tests/webkit/src/android/webkit/cts/ServiceWorkerClientTest.java
@@ -16,6 +16,7 @@
 
 package android.webkit.cts;
 
+import android.cts.util.NullWebViewUtils;
 import android.cts.util.PollingCheck;
 import android.test.ActivityInstrumentationTestCase2;
 
@@ -136,6 +137,10 @@
 
     // Test correct invocation of shouldInterceptRequest for Service Workers.
     public void testServiceWorkerClientInterceptCallback() throws Exception {
+        if (!NullWebViewUtils.isWebViewAvailable()) {
+            return;
+        }
+
         final InterceptServiceWorkerClient mInterceptServiceWorkerClient =
                 new InterceptServiceWorkerClient();
         ServiceWorkerController swController = ServiceWorkerController.getInstance();
diff --git a/tests/tests/webkit/src/android/webkit/cts/WebViewStartupTest.java b/tests/tests/webkit/src/android/webkit/cts/WebViewStartupTest.java
index a4ebaca..3b67d84 100644
--- a/tests/tests/webkit/src/android/webkit/cts/WebViewStartupTest.java
+++ b/tests/tests/webkit/src/android/webkit/cts/WebViewStartupTest.java
@@ -20,7 +20,6 @@
 import android.content.Context;
 import android.cts.util.NullWebViewUtils;
 import android.cts.util.PollingCheck;
-import android.os.StrictMode;
 import android.test.ActivityInstrumentationTestCase2;
 import android.test.UiThreadTest;
 import android.util.Log;
@@ -89,21 +88,4 @@
         assertTrue(m.matches());
         assertEquals("42", m.group(1)); // value got incremented
     }
-
-    @UiThreadTest
-    public void testStrictModeNotViolatedOnStartup() throws Throwable {
-        StrictMode.ThreadPolicy oldPolicy = StrictMode.getThreadPolicy();
-        StrictMode.ThreadPolicy testPolicy = new StrictMode.ThreadPolicy.Builder()
-            .detectDiskReads()
-            .penaltyLog()
-            .penaltyDeath()
-            .build();
-        StrictMode.setThreadPolicy(testPolicy);
-        try {
-            mActivity.createAndAttachWebView();
-        } finally {
-            StrictMode.setThreadPolicy(oldPolicy);
-        }
-    }
-
 }
diff --git a/tests/tests/widget/AndroidManifest.xml b/tests/tests/widget/AndroidManifest.xml
index 224966d..a81bed6 100644
--- a/tests/tests/widget/AndroidManifest.xml
+++ b/tests/tests/widget/AndroidManifest.xml
@@ -222,7 +222,8 @@
         </activity>
 
         <activity android:name="android.widget.cts.PopupWindowCtsActivity"
-            android:label="PopupWindowCtsActivity">
+            android:label="PopupWindowCtsActivity"
+            android:theme="@android:style/Theme.Holo">
             <intent-filter>
                 <action android:name="android.intent.action.MAIN" />
                 <category android:name="android.intent.category.FRAMEWORK_INSTRUMENTATION_TEST" />
@@ -380,6 +381,14 @@
             </intent-filter>
         </activity>
 
+        <activity android:name="android.widget.cts.TimePickerDialogCtsActivity"
+                  android:label="TimePickerDialogCtsActivity">
+            <intent-filter>
+                <action android:name="android.intent.action.MAIN" />
+                <category android:name="android.intent.category.FRAMEWORK_INSTRUMENTATION_TEST" />
+            </intent-filter>
+        </activity>
+
         <activity android:name="android.widget.cts.CalendarViewCtsActivity"
                   android:label="CalendarViewCtsActivity">
             <intent-filter>
diff --git a/tests/tests/widget/res/values-w320dp-h426dp/integers.xml b/tests/tests/widget/res/values-w320dp-h426dp/integers.xml
new file mode 100644
index 0000000..a9d049c
--- /dev/null
+++ b/tests/tests/widget/res/values-w320dp-h426dp/integers.xml
@@ -0,0 +1,20 @@
+<?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.
+-->
+
+<resources>
+    <integer name="date_picker_mode">2</integer>
+    <integer name="time_picker_mode">2</integer>
+</resources>
diff --git a/tests/tests/widget/res/values-w426dp-h320dp/integers.xml b/tests/tests/widget/res/values-w426dp-h320dp/integers.xml
new file mode 100644
index 0000000..a9d049c
--- /dev/null
+++ b/tests/tests/widget/res/values-w426dp-h320dp/integers.xml
@@ -0,0 +1,20 @@
+<?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.
+-->
+
+<resources>
+    <integer name="date_picker_mode">2</integer>
+    <integer name="time_picker_mode">2</integer>
+</resources>
diff --git a/tests/tests/widget/res/values/integers.xml b/tests/tests/widget/res/values/integers.xml
new file mode 100644
index 0000000..b2c1e65
--- /dev/null
+++ b/tests/tests/widget/res/values/integers.xml
@@ -0,0 +1,20 @@
+<?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.
+-->
+
+<resources>
+    <integer name="date_picker_mode">1</integer>
+    <integer name="time_picker_mode">1</integer>
+</resources>
diff --git a/tests/tests/widget/res/values/styles.xml b/tests/tests/widget/res/values/styles.xml
index 345b450..8529d73 100644
--- a/tests/tests/widget/res/values/styles.xml
+++ b/tests/tests/widget/res/values/styles.xml
@@ -193,6 +193,11 @@
         <item name="android:windowSwipeToDismiss">false</item>
     </style>
 
+    <style name="Theme_Holo_With_Material_Pickers" parent="@android:style/Theme.Holo">
+        <item name="android:timePickerStyle">@android:style/Widget.Material.TimePicker</item>
+        <item name="android:datePickerStyle">@android:style/Widget.Material.DatePicker</item>
+    </style>
+
     <style name="PopupEmptyStyle" />
 
     <style name="TabWidgetCustomStyle" parent="android:Widget.TabWidget">
diff --git a/tests/tests/widget/src/android/widget/cts/AbsListViewTest.java b/tests/tests/widget/src/android/widget/cts/AbsListViewTest.java
index b0c868c..0625c8e 100644
--- a/tests/tests/widget/src/android/widget/cts/AbsListViewTest.java
+++ b/tests/tests/widget/src/android/widget/cts/AbsListViewTest.java
@@ -33,6 +33,7 @@
 import android.graphics.drawable.Drawable;
 import android.test.ActivityInstrumentationTestCase2;
 import android.test.TouchUtils;
+import android.test.UiThreadTest;
 import android.text.Editable;
 import android.text.SpannableStringBuilder;
 import android.util.AttributeSet;
@@ -136,14 +137,37 @@
          */
     }
 
-    public void testAccessFastScrollEnabled() {
+    @UiThreadTest
+    public void testAccessFastScrollEnabled_UiThread() {
+        mListView.setFastScrollAlwaysVisible(false);
         mListView.setFastScrollEnabled(false);
         assertFalse(mListView.isFastScrollEnabled());
 
+        mListView.setFastScrollAlwaysVisible(true);
         mListView.setFastScrollEnabled(true);
         assertTrue(mListView.isFastScrollEnabled());
     }
 
+    public void testAccessFastScrollEnabled() {
+        mListView.setFastScrollAlwaysVisible(false);
+        mListView.setFastScrollEnabled(false);
+        new PollingCheck() {
+            @Override
+            protected boolean check() {
+                return !mListView.isFastScrollEnabled();
+            }
+        }.run();
+
+        mListView.setFastScrollAlwaysVisible(true);
+        mListView.setFastScrollEnabled(true);
+        new PollingCheck() {
+            @Override
+            protected boolean check() {
+                return mListView.isFastScrollEnabled();
+            }
+        }.run();
+    }
+
     public void testAccessSmoothScrollbarEnabled() {
         mListView.setSmoothScrollbarEnabled(false);
         assertFalse(mListView.isSmoothScrollbarEnabled());
diff --git a/tests/tests/widget/src/android/widget/cts/DatePickerDialogTest.java b/tests/tests/widget/src/android/widget/cts/DatePickerDialogTest.java
index 1477f73..74d0ff5 100644
--- a/tests/tests/widget/src/android/widget/cts/DatePickerDialogTest.java
+++ b/tests/tests/widget/src/android/widget/cts/DatePickerDialogTest.java
@@ -22,6 +22,7 @@
 import android.content.Context;
 import android.test.ActivityInstrumentationTestCase2;
 import android.test.UiThreadTest;
+import android.widget.DatePicker;
 
 /**
  * Test {@link DatePickerDialog}.
@@ -48,7 +49,16 @@
 
         new DatePickerDialog(mActivity, AlertDialog.THEME_TRADITIONAL, null, 1970, 1, 1);
 
-        new DatePickerDialog(mActivity, AlertDialog.THEME_HOLO_DARK, null, 1970, 1, 1);
+        // Ensure the picker is shown using the Holo-style layout.
+        DatePickerDialog holoDialog = new DatePickerDialog(mActivity, AlertDialog.THEME_HOLO_DARK,
+                null, 1970, 1, 1);
+        assertEquals(DatePicker.MODE_SPINNER, holoDialog.getDatePicker().getMode());
+
+        // Ensure the picker is shown using the Material-style layout where available.
+        DatePickerDialog holoCalendarDialog = new DatePickerDialog(mActivity,
+                R.style.Theme_Holo_With_Material_Pickers, null, 1970, 1, 1);
+        final int expectedMode = mActivity.getResources().getInteger(R.integer.date_picker_mode);
+        assertEquals(expectedMode, holoCalendarDialog.getDatePicker().getMode());
 
         new DatePickerDialog(mActivity,
                 android.R.style.Theme_Material_Dialog_Alert, null, 1970, 1, 1);
diff --git a/tests/tests/widget/src/android/widget/cts/ListPopupWindowTest.java b/tests/tests/widget/src/android/widget/cts/ListPopupWindowTest.java
index b50f8c9..73e2aeb 100644
--- a/tests/tests/widget/src/android/widget/cts/ListPopupWindowTest.java
+++ b/tests/tests/widget/src/android/widget/cts/ListPopupWindowTest.java
@@ -219,6 +219,14 @@
                 Gravity.getAbsoluteGravity(gravity, upperAnchor.getLayoutDirection());
         if (absoluteGravity == Gravity.RIGHT) {
             expectedListViewOnScreenX -= (listView.getWidth() - upperAnchor.getWidth());
+        } else {
+            // On narrow screens, it's possible for the popup to reach the edge
+            // of the screen.
+            int rightmostX =
+                    getDisplay().getWidth() - mPopupWindow.getWidth() + listViewInWindowXY[0];
+            if (expectedListViewOnScreenX > rightmostX) {
+                expectedListViewOnScreenX = rightmostX;
+            }
         }
         int expectedListViewOnScreenY = anchorXY[1] + listViewInWindowXY[1]
                 + upperAnchor.getHeight() + verticalOffset;
@@ -508,7 +516,10 @@
         final int[] lastChildOnScreenXY = new int[2];
         lastListChild.getLocationOnScreen(lastChildOnScreenXY);
 
-        assertTrue(lastChildOnScreenXY[1] + lastListChild.getHeight() <= promptViewOnScreenXY[1]);
+        // The child is above the prompt. They may overlap, as in the case
+        // when the list items do not all fit on screen, but this is still
+        // correct.
+        assertTrue(lastChildOnScreenXY[1] <= promptViewOnScreenXY[1]);
     }
 
     @Presubmit
diff --git a/tests/tests/widget/src/android/widget/cts/TextViewTest.java b/tests/tests/widget/src/android/widget/cts/TextViewTest.java
index 9f611ad..fea0a55b 100644
--- a/tests/tests/widget/src/android/widget/cts/TextViewTest.java
+++ b/tests/tests/widget/src/android/widget/cts/TextViewTest.java
@@ -264,7 +264,7 @@
         assertSame(movementMethod, mTextView.getMovementMethod());
         assertEquals(selectionStart, Selection.getSelectionStart(mTextView.getText()));
         assertEquals(selectionEnd, Selection.getSelectionEnd(mTextView.getText()));
-        sendKeys(KeyEvent.KEYCODE_SHIFT_LEFT, KeyEvent.KEYCODE_ALT_LEFT,
+        mKeyEventUtil.sendKeys(mTextView, KeyEvent.KEYCODE_SHIFT_LEFT, KeyEvent.KEYCODE_ALT_LEFT,
                 KeyEvent.KEYCODE_DPAD_UP);
         // the selection has been removed.
         assertEquals(selectionStart, Selection.getSelectionStart(mTextView.getText()));
@@ -283,7 +283,7 @@
         assertNull(mTextView.getMovementMethod());
         assertEquals(selectionStart, Selection.getSelectionStart(mTextView.getText()));
         assertEquals(selectionEnd, Selection.getSelectionEnd(mTextView.getText()));
-        sendKeys(KeyEvent.KEYCODE_SHIFT_LEFT, KeyEvent.KEYCODE_ALT_LEFT,
+        mKeyEventUtil.sendKeys(mTextView, KeyEvent.KEYCODE_SHIFT_LEFT, KeyEvent.KEYCODE_ALT_LEFT,
                 KeyEvent.KEYCODE_DPAD_UP);
         // the selection will not be changed.
         assertEquals(selectionStart, Selection.getSelectionStart(mTextView.getText()));
@@ -1838,7 +1838,7 @@
         initTextViewForTyping();
 
         // Type "abc".
-        mInstrumentation.sendStringSync("abc");
+        mKeyEventUtil.sendString(mTextView, "abc");
         mActivity.runOnUiThread(new Runnable() {
             public void run() {
                 // Select "bc"
@@ -1847,7 +1847,7 @@
         });
         mInstrumentation.waitForIdleSync();
         // Copy "bc"
-        sendKeys(KeyEvent.KEYCODE_COPY);
+        mKeyEventUtil.sendKeys(mTextView, KeyEvent.KEYCODE_COPY);
 
         mActivity.runOnUiThread(new Runnable() {
             public void run() {
@@ -1857,7 +1857,7 @@
         });
         mInstrumentation.waitForIdleSync();
         // Paste "bc"
-        sendKeys(KeyEvent.KEYCODE_PASTE);
+        mKeyEventUtil.sendKeys(mTextView, KeyEvent.KEYCODE_PASTE);
         assertEquals("abbcc", mTextView.getText().toString());
 
         mActivity.runOnUiThread(new Runnable() {
@@ -1922,7 +1922,7 @@
         initTextViewForTyping();
 
         // Type "abc".
-        mInstrumentation.sendStringSync("abc");
+        mKeyEventUtil.sendString(mTextView, "abc");
         mActivity.runOnUiThread(new Runnable() {
             public void run() {
                 // Select "bc"
@@ -1931,7 +1931,7 @@
         });
         mInstrumentation.waitForIdleSync();
         // Cut "bc"
-        sendKeys(KeyEvent.KEYCODE_CUT);
+        mKeyEventUtil.sendKeys(mTextView, KeyEvent.KEYCODE_CUT);
 
         mActivity.runOnUiThread(new Runnable() {
             public void run() {
@@ -1942,7 +1942,7 @@
         });
         mInstrumentation.waitForIdleSync();
         // Paste "bc"
-        sendKeys(KeyEvent.KEYCODE_PASTE);
+        mKeyEventUtil.sendKeys(mTextView, KeyEvent.KEYCODE_PASTE);
         assertEquals("bca", mTextView.getText().toString());
 
         mActivity.runOnUiThread(new Runnable() {
@@ -1967,6 +1967,7 @@
                 assertEquals("bca", mTextView.getText().toString());
             }
         });
+        mInstrumentation.waitForIdleSync();
     }
 
     private static boolean hasSpansAtMiddleOfText(final TextView textView, final Class<?> type) {
@@ -2223,7 +2224,7 @@
 
         assertEquals(errorText, mTextView.getError().toString());
 
-        mInstrumentation.sendStringSync("a");
+        mKeyEventUtil.sendString(mTextView, "a");
         // a key event that will not change the TextView's text
         assertEquals("", mTextView.getText().toString());
         // The icon and error message will not be reset to null
@@ -2239,7 +2240,7 @@
         });
         mInstrumentation.waitForIdleSync();
 
-        mInstrumentation.sendStringSync("1");
+        mKeyEventUtil.sendString(mTextView, "1");
         // a key event cause changes to the TextView's text
         assertEquals("1", mTextView.getText().toString());
         // the error message and icon will be cleared.
@@ -2265,13 +2266,13 @@
 
         assertSame(expected, mTextView.getFilters());
 
-        mInstrumentation.sendStringSync("a");
+        mKeyEventUtil.sendString(mTextView, "a");
         // the text is capitalized by InputFilter.AllCaps
         assertEquals("A", mTextView.getText().toString());
-        mInstrumentation.sendStringSync("b");
+        mKeyEventUtil.sendString(mTextView, "b");
         // the text is capitalized by InputFilter.AllCaps
         assertEquals("AB", mTextView.getText().toString());
-        mInstrumentation.sendStringSync("c");
+        mKeyEventUtil.sendString(mTextView, "c");
         // 'C' could not be accepted, because there is a length filter.
         assertEquals("AB", mTextView.getText().toString());
 
@@ -2468,11 +2469,11 @@
     public void testPressKey() {
         initTextViewForTyping();
 
-        mInstrumentation.sendStringSync("a");
+        mKeyEventUtil.sendString(mTextView, "a");
         assertEquals("a", mTextView.getText().toString());
-        mInstrumentation.sendStringSync("b");
+        mKeyEventUtil.sendString(mTextView, "b");
         assertEquals("ab", mTextView.getText().toString());
-        sendKeys(KeyEvent.KEYCODE_DEL);
+        mKeyEventUtil.sendKeys(mTextView, KeyEvent.KEYCODE_DEL);
         assertEquals("a", mTextView.getText().toString());
     }
 
@@ -2983,7 +2984,7 @@
         assertSame(PasswordTransformationMethod.getInstance(),
                 mTextView.getTransformationMethod());
 
-        sendKeys("H E 2*L O");
+        mKeyEventUtil.sendKeys(mTextView, "H E 2*L O");
         mActivity.runOnUiThread(new Runnable() {
             public void run() {
                 mTextView.append(" ");
diff --git a/tests/tests/widget/src/android/widget/cts/TimePickerDialogCtsActivity.java b/tests/tests/widget/src/android/widget/cts/TimePickerDialogCtsActivity.java
new file mode 100644
index 0000000..f5c544b
--- /dev/null
+++ b/tests/tests/widget/src/android/widget/cts/TimePickerDialogCtsActivity.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.widget.cts;
+
+import android.app.Activity;
+import android.os.Bundle;
+
+/**
+ * A minimal application for TimePickerDialog test.
+ */
+public class TimePickerDialogCtsActivity extends Activity {
+    /**
+     * Called with the activity is first created.
+     */
+    @Override
+    public void onCreate(Bundle savedInstanceState) {
+        super.onCreate(savedInstanceState);
+    }
+}
diff --git a/tests/tests/widget/src/android/widget/cts/TimePickerDialogTest.java b/tests/tests/widget/src/android/widget/cts/TimePickerDialogTest.java
new file mode 100644
index 0000000..73ddc04
--- /dev/null
+++ b/tests/tests/widget/src/android/widget/cts/TimePickerDialogTest.java
@@ -0,0 +1,72 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR 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.app.AlertDialog;
+import android.app.DatePickerDialog;
+import android.app.TimePickerDialog;
+import android.test.ActivityInstrumentationTestCase2;
+import android.test.UiThreadTest;
+import android.widget.TimePicker;
+
+/**
+ * Test {@link TimePickerDialog}.
+ */
+public class TimePickerDialogTest extends
+        ActivityInstrumentationTestCase2<TimePickerDialogCtsActivity> {
+
+    private Activity mActivity;
+
+    public TimePickerDialogTest() {
+        super(TimePickerDialogCtsActivity.class);
+    }
+
+    @Override
+    protected void setUp() throws Exception {
+        super.setUp();
+        mActivity = getActivity();
+    }
+
+    @UiThreadTest
+    public void testConstructor() {
+        new TimePickerDialog(mActivity, null, 7, 0, true);
+
+        new TimePickerDialog(mActivity, AlertDialog.THEME_TRADITIONAL, null, 7, 0, true);
+
+        // Ensure the picker is shown using the Holo-style layout.
+        TimePickerDialog holoDialog = new TimePickerDialog(mActivity, AlertDialog.THEME_HOLO_DARK,
+                null, 7, 0, true);
+        assertEquals(TimePicker.MODE_SPINNER, holoDialog.getTimePicker().getMode());
+
+        // Ensure the picker is shown using the Material-style layout where available.
+        TimePickerDialog holoClockDialog = new TimePickerDialog(mActivity,
+                R.style.Theme_Holo_With_Material_Pickers, null, 7, 0, true);
+        final int expectedMode = mActivity.getResources().getInteger(R.integer.time_picker_mode);
+        assertEquals(expectedMode, holoClockDialog.getTimePicker().getMode());
+
+        new TimePickerDialog(mActivity,
+                android.R.style.Theme_Material_Dialog_Alert, null, 7, 0, true);
+
+        try {
+            new TimePickerDialog(null, null, 7, 0, true);
+            fail("should throw NullPointerException");
+        } catch (Exception e) {
+        }
+    }
+
+}
diff --git a/tools/cts-api-coverage/src/Android.mk b/tools/cts-api-coverage/src/Android.mk
index d1fe4ed..8c038dc 100644
--- a/tools/cts-api-coverage/src/Android.mk
+++ b/tools/cts-api-coverage/src/Android.mk
@@ -23,6 +23,10 @@
 LOCAL_JAVA_RESOURCE_DIRS := res 
 LOCAL_JAR_MANIFEST := MANIFEST.mf
 
+LOCAL_STATIC_JAVA_LIBRARIES := \
+  compatibility-host-util \
+  dexlib2
+
 LOCAL_MODULE := cts-api-coverage
 LOCAL_MODULE_TAGS := optional
 
diff --git a/tools/cts-api-coverage/src/MANIFEST.mf b/tools/cts-api-coverage/src/MANIFEST.mf
index b6aa831..63d6674 100644
--- a/tools/cts-api-coverage/src/MANIFEST.mf
+++ b/tools/cts-api-coverage/src/MANIFEST.mf
@@ -1,2 +1,3 @@
 Manifest-Version: 1.0
 Main-Class: com.android.cts.apicoverage.CtsApiCoverage
+Class-Path: tradefed-prebuilt.jar
diff --git a/tools/cts-api-coverage/src/com/android/cts/apicoverage/CddCoverage.java b/tools/cts-api-coverage/src/com/android/cts/apicoverage/CddCoverage.java
new file mode 100644
index 0000000..3d1a07d
--- /dev/null
+++ b/tools/cts-api-coverage/src/com/android/cts/apicoverage/CddCoverage.java
@@ -0,0 +1,112 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.cts.apicoverage;
+
+import java.lang.String;
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.Map;
+import java.util.List;
+import java.util.Set;
+
+/** Representation of the entire CDD. */
+class CddCoverage {
+
+    private final Map<String, CddRequirement> requirements = new HashMap<>();
+
+    public void addCddRequirement(CddRequirement cddRequirement) {
+        requirements.put(cddRequirement.getRequirementId(), cddRequirement);
+    }
+
+    public Collection<CddRequirement> getCddRequirements() {
+        return Collections.unmodifiableCollection(requirements.values());
+    }
+
+    public void addCoverage(String cddRequirementId, TestMethod testMethod) {
+        if (!requirements.containsKey(cddRequirementId)) {
+            requirements.put(cddRequirementId, new CddRequirement(cddRequirementId));
+        }
+
+        requirements.get(cddRequirementId).addTestMethod(testMethod);
+    }
+
+    static class CddRequirement {
+        private final String mRequirementId;
+        private final List<TestMethod> mtestMethods;
+
+        CddRequirement(String requirementId) {
+            this.mRequirementId = requirementId;
+            this.mtestMethods = new ArrayList<>();
+        }
+
+        @Override
+        public boolean equals(Object other) {
+            if (other == null) {
+                return false;
+            } else if (!(other instanceof CddRequirement)) {
+                return false;
+            } else {
+                return mRequirementId.equals(((CddRequirement)other).mRequirementId);
+            }
+        }
+
+        @Override
+        public int hashCode() {
+            return mRequirementId.hashCode();
+        }
+
+        @Override
+        public String toString() {
+            return String.format("Requirement %s %s", mRequirementId, mtestMethods);
+        }
+
+        public String getRequirementId() { return mRequirementId; }
+
+        public void addTestMethod(TestMethod testMethod) {
+            mtestMethods.add(testMethod);
+        }
+
+        public Collection<TestMethod> getTestMethods() {
+            return Collections.unmodifiableCollection(mtestMethods);
+        }
+    }
+
+    static class TestMethod {
+        private final String mTestModule;
+        private final String mTestClass;
+        private final String mTestMethod;
+
+        TestMethod(String testModule, String testClass, String testMethod) {
+            this.mTestModule = testModule;
+            this.mTestClass = testClass;
+            this.mTestMethod = testMethod;
+        }
+
+        public String getTestModule() { return mTestModule; }
+
+        public String getTestClass() { return mTestClass; }
+
+        public String getTestMethod() { return mTestMethod; }
+
+        @Override
+        public String toString() {
+            return String.format("%s %s#%s", mTestModule, mTestClass, mTestMethod);
+        }
+    }
+}
diff --git a/tools/cts-api-coverage/src/com/android/cts/apicoverage/CtsApiCoverage.java b/tools/cts-api-coverage/src/com/android/cts/apicoverage/CtsApiCoverage.java
index 6b69a57..2b57c76 100644
--- a/tools/cts-api-coverage/src/com/android/cts/apicoverage/CtsApiCoverage.java
+++ b/tools/cts-api-coverage/src/com/android/cts/apicoverage/CtsApiCoverage.java
@@ -16,6 +16,19 @@
 
 package com.android.cts.apicoverage;
 
+import com.android.compatibility.common.util.CddTest;
+
+import org.jf.dexlib2.DexFileFactory;
+import org.jf.dexlib2.DexFileFactory.DexFileNotFound;
+import org.jf.dexlib2.DexFileFactory.MultipleDexFilesException;
+import org.jf.dexlib2.Opcodes;
+import org.jf.dexlib2.iface.Annotation;
+import org.jf.dexlib2.iface.AnnotationElement;
+import org.jf.dexlib2.iface.ClassDef;
+import org.jf.dexlib2.iface.DexFile;
+import org.jf.dexlib2.iface.Method;
+import org.jf.dexlib2.iface.value.StringEncodedValue;
+
 import org.xml.sax.InputSource;
 import org.xml.sax.SAXException;
 import org.xml.sax.XMLReader;
@@ -29,7 +42,9 @@
 import java.io.OutputStream;
 import java.util.Arrays;
 import java.util.ArrayList;
+import java.util.Collection;
 import java.util.List;
+import java.util.Set;
 
 import javax.xml.transform.TransformerException;
 
@@ -52,6 +67,10 @@
 
     private static final int FORMAT_HTML = 2;
 
+    private static final String CDD_REQUIREMENT_ANNOTATION = "Lcom/android/compatibility/common/util/CddTest;";
+
+    private static final String CDD_REQUIREMENT_ELEMENT_NAME = "requirement";
+
     private static void printUsage() {
         System.out.println("Usage: cts-api-coverage [OPTION]... [APK]...");
         System.out.println();
@@ -70,6 +89,7 @@
         System.out.println("  -a PATH                path to the API XML file");
         System.out.println("  -p PACKAGENAMEPREFIX   report coverage only for package that start with");
         System.out.println("  -t TITLE               report title");
+        System.out.println("  -a API                 the Android API Level");
         System.out.println();
         System.exit(1);
     }
@@ -82,6 +102,7 @@
         String apiXmlPath = "";
         PackageFilter packageFilter = new PackageFilter();
         String reportTitle = "CTS API Coverage";
+        int apiLevel = Integer.MAX_VALUE;
 
         for (int i = 0; i < args.length; i++) {
             if (args[i].startsWith("-")) {
@@ -106,6 +127,8 @@
                     packageFilter.addPrefixToFilter(getExpectedArg(args, ++i));
                 } else if ("-t".equals(args[i])) {
                     reportTitle = getExpectedArg(args, ++i);
+                } else if ("-a".equals(args[i])) {
+                    apiLevel = Integer.parseInt(getExpectedArg(args, ++i));
                 } else {
                     printUsage();
                 }
@@ -131,12 +154,16 @@
          */
 
         ApiCoverage apiCoverage = getEmptyApiCoverage(apiXmlPath);
+        CddCoverage cddCoverage = getEmptyCddCoverage();
         // Add superclass information into api coverage.
         apiCoverage.resolveSuperClasses();
         for (File testApk : testApks) {
             addApiCoverage(apiCoverage, testApk, dexDeps);
+            addCddCoverage(cddCoverage, testApk, apiLevel);
         }
-        outputCoverageReport(apiCoverage, testApks, outputFile, format, packageFilter, reportTitle);
+
+        outputCoverageReport(apiCoverage, cddCoverage, testApks, outputFile,
+            format, packageFilter, reportTitle);
     }
 
     /** Get the argument or print out the usage and exit. */
@@ -202,8 +229,106 @@
         }
     }
 
-    private static void outputCoverageReport(ApiCoverage apiCoverage, List<File> testApks,
-            File outputFile, int format, PackageFilter packageFilter, String reportTitle)
+    private static void addCddCoverage(CddCoverage cddCoverage, File testSource, int api)
+            throws IOException {
+
+        if (testSource.getName().endsWith(".apk")) {
+            addCddApkCoverage(cddCoverage, testSource, api);
+        } else if (testSource.getName().endsWith(".jar")) {
+            addCddJarCoverage(cddCoverage, testSource);
+        } else {
+            System.err.println("Unsupported file type for CDD coverage: " + testSource.getPath());
+        }
+    }
+
+    private static void addCddJarCoverage(CddCoverage cddCoverage, File testSource)
+            throws IOException {
+
+        Collection<Class<?>> classes = JarTestFinder.getClasses(testSource);
+        for (Class<?> c : classes) {
+            for (java.lang.reflect.Method m : c.getMethods()) {
+                if (m.isAnnotationPresent(CddTest.class)) {
+                    CddTest cddTest = m.getAnnotation(CddTest.class);
+                    CddCoverage.TestMethod testMethod =
+                            new CddCoverage.TestMethod(
+                                    testSource.getName(), c.getName(), m.getName());
+                    cddCoverage.addCoverage(cddTest.requirement(), testMethod);
+                }
+            }
+        }
+    }
+
+    private static void addCddApkCoverage(
+        CddCoverage cddCoverage, File testSource, int api)
+            throws IOException {
+
+        DexFile dexFile = null;
+        try {
+            dexFile = DexFileFactory.loadDexFile(
+                testSource, null /*dexEntry*/, Opcodes.forApi(api));
+        } catch (IOException | DexFileFactory.DexFileNotFound e) {
+            System.err.println("Unable to load dex file: " + testSource.getPath());
+            return;
+        }
+
+        String moduleName = testSource.getName();
+        for (ClassDef classDef : dexFile.getClasses()) {
+            String className = classDef.getType();
+            handleAnnotations(
+                cddCoverage, moduleName, className, null /*methodName*/,
+                classDef.getAnnotations());
+
+            for (Method method : classDef.getMethods()) {
+                String methodName = method.getName();
+                handleAnnotations(
+                    cddCoverage, moduleName, className, methodName, method.getAnnotations());
+            }
+        }
+    }
+
+    private static void handleAnnotations(
+            CddCoverage cddCoverage, String moduleName, String className,
+                    String methodName, Set<? extends Annotation> annotations) {
+        for (Annotation annotation : annotations) {
+            if (annotation.getType().equals(CDD_REQUIREMENT_ANNOTATION)) {
+                for (AnnotationElement annotationElement : annotation.getElements()) {
+                    if (annotationElement.getName().equals(CDD_REQUIREMENT_ELEMENT_NAME)) {
+                        String cddRequirement =
+                                ((StringEncodedValue) annotationElement.getValue()).getValue();
+                        CddCoverage.TestMethod testMethod =
+                                new CddCoverage.TestMethod(
+                                        moduleName, dexToJavaName(className), methodName);
+                        cddCoverage.addCoverage(cddRequirement, testMethod);
+                    }
+                }
+            }
+        }
+    }
+
+    /**
+     * Given a string like Landroid/app/cts/DownloadManagerTest;
+     * return android.app.cts.DownloadManagerTest.
+     */
+    private static String dexToJavaName(String dexName) {
+        if (!dexName.startsWith("L") || !dexName.endsWith(";")) {
+            return dexName;
+        }
+        dexName = dexName.replace('/', '.');
+        if (dexName.length() > 2) {
+            dexName = dexName.substring(1, dexName.length() - 1);
+        }
+        return dexName;
+    }
+
+    private static CddCoverage getEmptyCddCoverage() {
+        CddCoverage cddCoverage = new CddCoverage();
+        // TODO(nicksauer): Read in the valid list of requirements
+        return cddCoverage;
+    }
+
+    private static void outputCoverageReport(ApiCoverage apiCoverage, CddCoverage cddCoverage,
+            List<File> testApks, File outputFile, int format, PackageFilter packageFilter,
+            String reportTitle)
                 throws IOException, TransformerException, InterruptedException {
 
         OutputStream out = outputFile != null
@@ -213,15 +338,17 @@
         try {
             switch (format) {
                 case FORMAT_TXT:
-                    TextReport.printTextReport(apiCoverage, packageFilter, out);
+                    TextReport.printTextReport(apiCoverage, cddCoverage, packageFilter, out);
                     break;
 
                 case FORMAT_XML:
-                    XmlReport.printXmlReport(testApks, apiCoverage, packageFilter, reportTitle, out);
+                    XmlReport.printXmlReport(testApks, apiCoverage, cddCoverage,
+                        packageFilter, reportTitle, out);
                     break;
 
                 case FORMAT_HTML:
-                    HtmlReport.printHtmlReport(testApks, apiCoverage, packageFilter, reportTitle, out);
+                    HtmlReport.printHtmlReport(testApks, apiCoverage, cddCoverage,
+                        packageFilter, reportTitle, out);
                     break;
             }
         } finally {
diff --git a/tools/cts-api-coverage/src/com/android/cts/apicoverage/HtmlReport.java b/tools/cts-api-coverage/src/com/android/cts/apicoverage/HtmlReport.java
index 0e6b54a..f53a884 100644
--- a/tools/cts-api-coverage/src/com/android/cts/apicoverage/HtmlReport.java
+++ b/tools/cts-api-coverage/src/com/android/cts/apicoverage/HtmlReport.java
@@ -37,7 +37,8 @@
 class HtmlReport {
 
     public static void printHtmlReport(final List<File> testApks, final ApiCoverage apiCoverage,
-            final PackageFilter packageFilter, final String reportTitle, final OutputStream out)
+            final CddCoverage cddCoverage, final PackageFilter packageFilter,
+            final String reportTitle, final OutputStream out)
                 throws IOException, TransformerException {
         final PipedOutputStream xmlOut = new PipedOutputStream();
         final PipedInputStream xmlIn = new PipedInputStream(xmlOut);
@@ -45,7 +46,8 @@
         Thread t = new Thread(new Runnable() {
             @Override
             public void run() {
-                XmlReport.printXmlReport(testApks, apiCoverage, packageFilter, reportTitle, xmlOut);
+                XmlReport.printXmlReport(
+                    testApks, apiCoverage, cddCoverage, packageFilter, reportTitle, xmlOut);
 
                 // Close the output stream to avoid "Write dead end" errors.
                 try {
diff --git a/tools/cts-api-coverage/src/com/android/cts/apicoverage/JarTestFinder.java b/tools/cts-api-coverage/src/com/android/cts/apicoverage/JarTestFinder.java
new file mode 100644
index 0000000..2aa3e72
--- /dev/null
+++ b/tools/cts-api-coverage/src/com/android/cts/apicoverage/JarTestFinder.java
@@ -0,0 +1,87 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.cts.apicoverage;
+
+import java.io.File;
+import java.io.FileNotFoundException;
+import java.io.IOException;
+import java.lang.reflect.Modifier;
+import java.net.URL;
+import java.net.URLClassLoader;
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.Enumeration;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Set;
+import java.util.jar.JarEntry;
+import java.util.jar.JarFile;
+
+/** Representation of the entire CDD. */
+class JarTestFinder {
+
+    public static Collection<Class<?>> getClasses(File jarTestFile)
+            throws IllegalArgumentException  {
+        List<Class<?>> classes = new ArrayList<>();
+
+        try (JarFile jarFile = new JarFile(jarTestFile)) {
+            Enumeration<JarEntry> e = jarFile.entries();
+
+            URL[] urls = {
+                new URL(String.format("jar:file:%s!/", jarTestFile.getAbsolutePath()))
+            };
+            URLClassLoader cl = URLClassLoader.newInstance(urls, JarTestFinder.class.getClassLoader());
+
+            while (e.hasMoreElements()) {
+                JarEntry je = e.nextElement();
+                if (je.isDirectory() || !je.getName().endsWith(".class")
+                        || je.getName().contains("$")) {
+                    continue;
+                }
+                String className = getClassName(je.getName());
+                if (!className.endsWith("Test")) {
+                    continue;
+                }
+                try {
+                    Class<?> cls = cl.loadClass(className);
+                    int modifiers = cls.getModifiers();
+                    if (!Modifier.isStatic(modifiers)
+                            && !Modifier.isPrivate(modifiers)
+                            && !Modifier.isProtected(modifiers)
+                            && !Modifier.isInterface(modifiers)
+                            && !Modifier.isAbstract(modifiers)) {
+
+                        classes.add(cls);
+                    }
+                } catch (ClassNotFoundException | Error x) {
+                    System.err.println(
+                            String.format("Cannot find test class %s from %s",
+                                    className, jarTestFile.getName()));
+                    x.printStackTrace();
+                }
+            }
+        } catch (IOException e) {
+            e.printStackTrace();
+        }
+        return classes;
+    }
+
+    private static String getClassName(String name) {
+        // -6 because of .class
+        return name.substring(0, name.length() - 6).replace('/', '.');
+    }
+}
diff --git a/tools/cts-api-coverage/src/com/android/cts/apicoverage/TextReport.java b/tools/cts-api-coverage/src/com/android/cts/apicoverage/TextReport.java
index 3adc020..978a652 100644
--- a/tools/cts-api-coverage/src/com/android/cts/apicoverage/TextReport.java
+++ b/tools/cts-api-coverage/src/com/android/cts/apicoverage/TextReport.java
@@ -27,8 +27,8 @@
  */
 class TextReport {
 
-    public static void printTextReport(ApiCoverage api, PackageFilter packageFilter,
-            OutputStream outputStream) {
+    public static void printTextReport(ApiCoverage api, CddCoverage CddCoverage,
+            PackageFilter packageFilter, OutputStream outputStream) {
         PrintStream out = new PrintStream(outputStream);
 
         CoverageComparator comparator = new CoverageComparator();
diff --git a/tools/cts-api-coverage/src/com/android/cts/apicoverage/XmlReport.java b/tools/cts-api-coverage/src/com/android/cts/apicoverage/XmlReport.java
index 2ba16cf..e6ac3af 100644
--- a/tools/cts-api-coverage/src/com/android/cts/apicoverage/XmlReport.java
+++ b/tools/cts-api-coverage/src/com/android/cts/apicoverage/XmlReport.java
@@ -33,7 +33,8 @@
 class XmlReport {
 
     public static void printXmlReport(List<File> testApks, ApiCoverage apiCoverage,
-            PackageFilter packageFilter, String reportTitle, OutputStream outputStream) {
+            CddCoverage cddCoverage, PackageFilter packageFilter, String reportTitle,
+            OutputStream outputStream) {
         PrintStream out = new PrintStream(outputStream);
         out.println("<?xml version=\"1.0\" encoding=\"UTF-8\"?>");
         out.println("<?xml-stylesheet type=\"text/xsl\"  href=\"api-coverage.xsl\"?>");
@@ -137,6 +138,20 @@
         }
 
         out.println("</api>");
+        out.println("<cdd>");
+        for (CddCoverage.CddRequirement requirement : cddCoverage.getCddRequirements()) {
+            out.println("<requirement id=\"" + requirement.getRequirementId() + "\">");
+            for (CddCoverage.TestMethod method : requirement.getTestMethods()) {
+                out.print("<test module=\"" + method.getTestModule()
+                        + "\" class=\"" + method.getTestClass() + "\" ");
+                if (method.getTestMethod() != null) {
+                    out.print("method=\"" + method.getTestMethod() + "\"");
+                }
+                out.println("/>" );
+            }
+            out.println("</requirement>");
+        }
+        out.println("</cdd>");
         out.println("<total numCovered=\"" + totalCoveredMethods + "\" "
                 + "numTotal=\"" + totalMethods + "\" "
                 + "coveragePercentage=\""
diff --git a/tools/cts-device-info/src/com/android/cts/deviceinfo/SampleDeviceInfo.java b/tools/cts-device-info/src/com/android/cts/deviceinfo/SampleDeviceInfo.java
index 8d3d7ada..67db187 100644
--- a/tools/cts-device-info/src/com/android/cts/deviceinfo/SampleDeviceInfo.java
+++ b/tools/cts-device-info/src/com/android/cts/deviceinfo/SampleDeviceInfo.java
@@ -33,6 +33,7 @@
         double[] doubles = {Double.MAX_VALUE, Double.MIN_VALUE};
         int[] ints = {Integer.MAX_VALUE, Integer.MIN_VALUE};
         long[] longs = {Long.MAX_VALUE, Long.MIN_VALUE};
+        float[] floats = {Float.MAX_VALUE, Float.MIN_VALUE, Float.NaN};
 
         // Group Foo
         store.startGroup("foo");
@@ -49,12 +50,16 @@
         store.addArrayResult("bar_double", doubles);
         store.addArrayResult("bar_int", ints);
         store.addArrayResult("bar_long", longs);
+        store.addArrayResult("bar_float", floats);
         store.endGroup(); // bar
 
         store.addResult("foo_double", Double.MAX_VALUE);
         store.addResult("foo_int", Integer.MAX_VALUE);
         store.addResult("foo_long", Long.MAX_VALUE);
         store.addResult("foo_string", "foo-string");
+        store.addResult("foo_float_nan", Float.NaN);
+        store.addResult("foo_float_max", Float.MAX_VALUE + 1);
+        store.addResult("foo_float_min", Float.MIN_VALUE - 1);
         store.endGroup(); // foo
     }
 }
diff --git a/tools/cts-tradefed/res/config/cts-known-failures.xml b/tools/cts-tradefed/res/config/cts-known-failures.xml
index e7b08cb..84528d5 100644
--- a/tools/cts-tradefed/res/config/cts-known-failures.xml
+++ b/tools/cts-tradefed/res/config/cts-known-failures.xml
@@ -207,6 +207,9 @@
     <option name="compatibility:exclude-filter" value="CtsUsageStatsTestCases android.app.usage.cts.UsageStatsTest#testPackageUsageStatsIntervals" />
     <option name="compatibility:exclude-filter" value="CtsUsageStatsTestCases android.app.usage.cts.UsageStatsTest#testUsageEventsParceling" />
 
+    <!-- b/31469490 -->
+    <option name="compatibility:exclude-filter" value="CtsViewTestCases android.view.cts.DragDropTest" />
+
     <!-- b/23238984 -->
     <option name="compatibility:exclude-filter" value="CtsVoiceSettingsTestCases android.voicesettings.cts.ZenModeTest#testAll" />