resolved conflicts for 5365cd9e to lmp-mr1-dev

Change-Id: I9c6c4233bd2133fa9c1d840e80a74692d1fdbd37
diff --git a/CtsTestCaseList.mk b/CtsTestCaseList.mk
index 5b434cc..6f475cc 100644
--- a/CtsTestCaseList.mk
+++ b/CtsTestCaseList.mk
@@ -34,6 +34,7 @@
     CtsSplitApp_arm64-v8a \
     CtsSplitApp_mips64 \
     CtsSplitApp_mips \
+    CtsSplitAppDiffRevision \
     CtsSplitAppDiffVersion \
     CtsSplitAppDiffCert \
     CtsSplitAppFeature \
@@ -68,6 +69,7 @@
     CtsDeviceTaskswitchingAppB \
     CtsDeviceTaskswitchingControl \
     CtsDeviceUi \
+    CtsHostsideNetworkTestsApp \
     CtsIntentReceiverApp \
     CtsIntentSenderApp \
     CtsLauncherAppsTests \
@@ -103,6 +105,7 @@
     CtsDeviceBrowserBench \
     CtsDeviceVideoPerf \
     CtsDeviceOpenGl \
+    CtsDeviceTvProviderPerf \
     CtsAccelerationTestCases \
     CtsAccountManagerTestCases \
     CtsAccessibilityServiceTestCases \
@@ -110,6 +113,7 @@
     CtsAdminTestCases \
     CtsAnimationTestCases \
     CtsAppTestCases \
+    CtsAppWidgetTestCases \
     CtsBluetoothTestCases \
     CtsCalendarcommon2TestCases \
     CtsContentTestCases \
@@ -176,6 +180,7 @@
     CtsDevicePolicyManagerTestCases \
     CtsDumpsysHostTestCases \
     CtsHostJank \
+    CtsHostsideNetworkTests \
     CtsHostUi \
     CtsJdwpSecurityHostTestCases \
     CtsMonkeyTestCases \
diff --git a/apps/CameraITS/pymodules/its/caps.py b/apps/CameraITS/pymodules/its/caps.py
index b713db9..24f4e75 100644
--- a/apps/CameraITS/pymodules/its/caps.py
+++ b/apps/CameraITS/pymodules/its/caps.py
@@ -198,7 +198,6 @@
     return props.has_key("android.flash.info.available") and \
            props["android.flash.info.available"] == 1
 
-
 def per_frame_control(props):
     """Returns whether a device supports per frame control
 
@@ -211,6 +210,18 @@
     return props.has_key("android.sync.maxLatency") and \
            props["android.sync.maxLatency"] == 0
 
+def ev_compensation(props):
+    """Returns whether a device supports ev compensation
+
+    Args:
+        props: Camera properties object.
+
+    Return:
+        Boolean.
+    """
+    return props.has_key("android.control.aeCompensationRange") and \
+           props["android.control.aeCompensationRange"] != [0, 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 035e70b..ad9786e 100644
--- a/apps/CameraITS/pymodules/its/device.py
+++ b/apps/CameraITS/pymodules/its/device.py
@@ -49,6 +49,7 @@
 
     # Seconds timeout on each socket operation.
     SOCK_TIMEOUT = 10.0
+    SEC_TO_NSEC = 1000*1000*1000.0
 
     PACKAGE = 'com.android.cts.verifier.camera.its'
     INTENT_START = 'com.android.cts.verifier.camera.its.START'
@@ -483,6 +484,18 @@
                 "dng" in formats and "raw10" in formats or \
                 "raw" in formats and "raw10" in formats:
             raise its.error.Error('Different raw formats not supported')
+
+        # Detect long exposure time and set timeout accordingly
+        longest_exp_time = 0
+        for req in cmd["captureRequests"]:
+            if "android.sensor.exposureTime" in req and \
+                    req["android.sensor.exposureTime"] > longest_exp_time:
+                longest_exp_time = req["android.sensor.exposureTime"]
+
+        extended_timeout = longest_exp_time / self.SEC_TO_NSEC + \
+                self.SOCK_TIMEOUT
+        self.sock.settimeout(extended_timeout)
+
         print "Capturing %d frame%s with %d format%s [%s]" % (
                   ncap, "s" if ncap>1 else "", nsurf, "s" if nsurf>1 else "",
                   ",".join(formats))
@@ -524,6 +537,7 @@
                 obj["metadata"] = mds[i]
                 objs.append(obj)
             rets.append(objs if ncap>1 else objs[0])
+        self.sock.settimeout(self.SOCK_TIMEOUT)
         return rets if len(rets)>1 else rets[0]
 
 def report_result(camera_id, success, summary_path=None):
diff --git a/apps/CameraITS/pymodules/its/objects.py b/apps/CameraITS/pymodules/its/objects.py
index a531f3b..be730e2 100644
--- a/apps/CameraITS/pymodules/its/objects.py
+++ b/apps/CameraITS/pymodules/its/objects.py
@@ -123,22 +123,50 @@
         "android.tonemap.mode": 1,
         }
 
-def get_available_output_sizes(fmt, props):
+def fastest_auto_capture_request(props):
+    """Return an auto capture request for the fastest capture.
+
+    Args:
+        props: the object returned from its.device.get_camera_properties().
+
+    Returns:
+        A capture request with everything set to auto and all filters that
+            may slow down capture set to OFF or FAST if possible
+    """
+    req = auto_capture_request()
+    turn_slow_filters_off(props, req)
+
+    return req
+
+def get_available_output_sizes(fmt, props, max_size=None, match_ar_size=None):
     """Return a sorted list of available output sizes for a given format.
 
     Args:
         fmt: the output format, as a string in ["jpg", "yuv", "raw"].
         props: the object returned from its.device.get_camera_properties().
+        max_size: (Optional) A (w,h) tuple.
+            Sizes larger than max_size (either w or h)  will be discarded.
+        match_ar_size: (Optional) A (w,h) tuple.
+            Sizes not matching the aspect ratio of match_ar_size will be
+            discarded.
 
     Returns:
         A sorted list of (w,h) tuples (sorted large-to-small).
     """
+    AR_TOLERANCE = 0.03
     fmt_codes = {"raw":0x20, "raw10":0x25, "yuv":0x23, "jpg":0x100, "jpeg":0x100}
     configs = props['android.scaler.streamConfigurationMap']\
                    ['availableStreamConfigurations']
     fmt_configs = [cfg for cfg in configs if cfg['format'] == fmt_codes[fmt]]
     out_configs = [cfg for cfg in fmt_configs if cfg['input'] == False]
     out_sizes = [(cfg['width'],cfg['height']) for cfg in out_configs]
+    if max_size:
+        out_sizes = [s for s in out_sizes if
+                s[0] <= max_size[0] and s[1] <= max_size[1]]
+    if match_ar_size:
+        ar = match_ar_size[0] / float(match_ar_size[1])
+        out_sizes = [s for s in out_sizes if
+                abs(ar - s[0] / float(s[1])) <= AR_TOLERANCE]
     out_sizes.sort(reverse=True)
     return out_sizes
 
@@ -160,6 +188,41 @@
         elif 1 in props[available_modes]:
             req[filter] = 1
 
+def turn_slow_filters_off(props, req):
+    """Turn filters that may slow FPS down to OFF or FAST in input request.
+
+    This function modifies the request argument, such that filters that may
+    reduce the frames-per-second throughput of the camera device will be set to
+    OFF or FAST if possible.
+
+    Args:
+        props: the object returned from its.device.get_camera_properties().
+        req: the input request.
+
+    Returns:
+        Nothing.
+    """
+    set_filter_off_or_fast_if_possible(props, req,
+        "android.noiseReduction.availableNoiseReductionModes",
+        "android.noiseReduction.mode")
+    set_filter_off_or_fast_if_possible(props, req,
+        "android.colorCorrection.availableAberrationModes",
+        "android.colorCorrection.aberrationMode")
+    if props.has_key("android.request.availableCharacteristicsKeys"):
+        hot_pixel_modes = 393217 in props["android.request.availableCharacteristicsKeys"]
+        edge_modes = 196610 in props["android.request.availableCharacteristicsKeys"]
+    if props.has_key("android.request.availableRequestKeys"):
+        hot_pixel_mode = 393216 in props["android.request.availableRequestKeys"]
+        edge_mode = 196608 in props["android.request.availableRequestKeys"]
+    if hot_pixel_modes and hot_pixel_mode:
+        set_filter_off_or_fast_if_possible(props, req,
+            "android.hotPixel.availableHotPixelModes",
+            "android.hotPixel.mode")
+    if edge_modes and edge_mode:
+        set_filter_off_or_fast_if_possible(props, req,
+            "android.edge.availableEdgeModes",
+            "android.edge.mode")
+
 def get_fastest_manual_capture_settings(props):
     """Return a capture request and format spec for the fastest capture.
 
diff --git a/apps/CameraITS/tests/scene0/test_jitter.py b/apps/CameraITS/tests/scene0/test_jitter.py
index 82e8e38..c519792 100644
--- a/apps/CameraITS/tests/scene0/test_jitter.py
+++ b/apps/CameraITS/tests/scene0/test_jitter.py
@@ -33,7 +33,8 @@
 
     with its.device.ItsSession() as cam:
         props = cam.get_camera_properties()
-        its.caps.skip_unless(its.caps.manual_sensor(props))
+        its.caps.skip_unless(its.caps.manual_sensor(props) and
+                             its.caps.sensor_fusion(props))
 
         req, fmt = its.objects.get_fastest_manual_capture_settings(props)
         caps = cam.do_capture([req]*50, [fmt])
diff --git a/apps/CameraITS/tests/scene0/test_metadata.py b/apps/CameraITS/tests/scene0/test_metadata.py
index b4ca4cb..375a6af 100644
--- a/apps/CameraITS/tests/scene0/test_metadata.py
+++ b/apps/CameraITS/tests/scene0/test_metadata.py
@@ -48,16 +48,19 @@
     check('props["android.info.supportedHardwareLevel"] is not None')
     check('props["android.info.supportedHardwareLevel"] in [0,1,2]')
     full = getval('props["android.info.supportedHardwareLevel"]') == 1
+    manual_sensor = its.caps.manual_sensor(props)
 
     # Test: rollingShutterSkew, and frameDuration tags must all be present,
     # and rollingShutterSkew must be greater than zero and smaller than all
     # of the possible frame durations.
-    check('md.has_key("android.sensor.frameDuration")')
-    check('md["android.sensor.frameDuration"] is not None')
+    if manual_sensor:
+        check('md.has_key("android.sensor.frameDuration")')
+        check('md["android.sensor.frameDuration"] is not None')
     check('md.has_key("android.sensor.rollingShutterSkew")')
     check('md["android.sensor.rollingShutterSkew"] is not None')
-    check('md["android.sensor.frameDuration"] > '
-          'md["android.sensor.rollingShutterSkew"] > 0')
+    if manual_sensor:
+        check('md["android.sensor.frameDuration"] > '
+              'md["android.sensor.rollingShutterSkew"] > 0')
 
     # Test: timestampSource must be a valid value.
     check('props.has_key("android.sensor.info.timestampSource")')
diff --git a/apps/CameraITS/tests/scene0/test_param_sensitivity_burst.py b/apps/CameraITS/tests/scene0/test_param_sensitivity_burst.py
index a6a5214..c3c2147 100644
--- a/apps/CameraITS/tests/scene0/test_param_sensitivity_burst.py
+++ b/apps/CameraITS/tests/scene0/test_param_sensitivity_burst.py
@@ -24,6 +24,7 @@
     """
 
     NUM_STEPS = 3
+    ERROR_TOLERANCE = 0.97 # Allow ISO to be rounded down by 3%
 
     with its.device.ItsSession() as cam:
         props = cam.get_camera_properties()
@@ -41,7 +42,8 @@
         for i,cap in enumerate(caps):
             s_req = sens_list[i]
             s_res = cap["metadata"]["android.sensor.sensitivity"]
-            assert(s_req == s_res)
+            assert(s_req >= s_res)
+            assert(s_res/float(s_req) > ERROR_TOLERANCE)
 
 if __name__ == '__main__':
     main()
diff --git a/apps/CameraITS/tests/scene1/test_ae_precapture_trigger.py b/apps/CameraITS/tests/scene1/test_ae_precapture_trigger.py
index 563cebd..bb91c9a 100644
--- a/apps/CameraITS/tests/scene1/test_ae_precapture_trigger.py
+++ b/apps/CameraITS/tests/scene1/test_ae_precapture_trigger.py
@@ -17,8 +17,10 @@
 import its.objects
 import its.target
 
+AE_FRAMES_PER_ITERATION = 8
+AE_CONVERGE_ITERATIONS = 3
 # AE must converge within this number of auto requests under scene1
-THRESH_AE_CONVERGE = 8
+THRESH_AE_CONVERGE = AE_FRAMES_PER_ITERATION * AE_CONVERGE_ITERATIONS
 
 def main():
     """Test the AE state machine when using the precapture trigger.
@@ -71,9 +73,12 @@
 
         # Capture some more auto requests, and AE should converge.
         auto_req['android.control.aePrecaptureTrigger'] = 0
-        caps = cam.do_capture([auto_req] * THRESH_AE_CONVERGE, fmt)
-        state = caps[-1]['metadata']['android.control.aeState']
-        print "AE state after auto request:", state
+        for i in range(AE_CONVERGE_ITERATIONS):
+            caps = cam.do_capture([auto_req] * AE_FRAMES_PER_ITERATION, fmt)
+            state = caps[-1]['metadata']['android.control.aeState']
+            print "AE state after auto request:", state
+            if state == CONVERGED:
+                return
         assert(state == CONVERGED)
 
 if __name__ == '__main__':
diff --git a/apps/CameraITS/tests/scene1/test_ev_compensation_advanced.py b/apps/CameraITS/tests/scene1/test_ev_compensation_advanced.py
index 6341c67..9b43a74 100644
--- a/apps/CameraITS/tests/scene1/test_ev_compensation_advanced.py
+++ b/apps/CameraITS/tests/scene1/test_ev_compensation_advanced.py
@@ -33,9 +33,16 @@
         props = cam.get_camera_properties()
         its.caps.skip_unless(its.caps.manual_sensor(props) and
                              its.caps.manual_post_proc(props) and
-                             its.caps.per_frame_control(props))
+                             its.caps.per_frame_control(props) and
+                             its.caps.ev_compensation(props))
 
-        evs = range(-4,5)
+        ev_compensation_range = props['android.control.aeCompensationRange']
+        range_min = ev_compensation_range[0]
+        range_max = ev_compensation_range[1]
+        ev_per_step = its.objects.rational_to_float(
+                props['android.control.aeCompensationStep'])
+        steps_per_ev = int(1.0 / ev_per_step)
+        evs = range(range_min, range_max + 1, steps_per_ev)
         lumas = []
         for ev in evs:
             # Re-converge 3A, and lock AE once converged. skip AF trigger as
@@ -58,10 +65,8 @@
             tile = its.image.get_image_patch(y, 0.45,0.45,0.1,0.1)
             lumas.append(its.image.compute_image_means(tile)[0])
 
-        ev_step_size_in_stops = its.objects.rational_to_float(
-                props['android.control.aeCompensationStep'])
-        luma_increase_per_step = pow(2, ev_step_size_in_stops)
-        print "ev_step_size_in_stops", ev_step_size_in_stops
+        luma_increase_per_step = pow(2, ev_per_step)
+        print "ev_step_size_in_stops", ev_per_step
         imid = len(lumas) / 2
         expected_lumas = [lumas[imid] / pow(luma_increase_per_step, i)
                           for i in range(imid , 0, -1)]  + \
diff --git a/apps/CameraITS/tests/scene1/test_ev_compensation_basic.py b/apps/CameraITS/tests/scene1/test_ev_compensation_basic.py
index 13f318f..d09f2fd 100644
--- a/apps/CameraITS/tests/scene1/test_ev_compensation_basic.py
+++ b/apps/CameraITS/tests/scene1/test_ev_compensation_basic.py
@@ -13,6 +13,7 @@
 # limitations under the License.
 
 import its.image
+import its.caps
 import its.device
 import its.objects
 import os.path
@@ -28,8 +29,12 @@
 
     with its.device.ItsSession() as cam:
         props = cam.get_camera_properties()
+        its.caps.skip_unless(its.caps.ev_compensation(props))
 
-        evs = range(-4,5)
+        ev_per_step = its.objects.rational_to_float(
+                props['android.control.aeCompensationStep'])
+        steps_per_ev = int(1.0 / ev_per_step)
+        evs = range(-2 * steps_per_ev, 2 * steps_per_ev + 1, steps_per_ev)
         lumas = []
         for ev in evs:
             # Re-converge 3A, and lock AE once converged. skip AF trigger as
@@ -49,6 +54,11 @@
         pylab.plot(evs, lumas, 'r')
         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)
+        # Only allow positive EVs to give saturated image
+        assert(len(lumas) > 2)
         luma_diffs = numpy.diff(lumas)
         min_luma_diffs = min(luma_diffs)
         print "Min of the luma value difference between adjacent ev comp: ", \
diff --git a/apps/CameraITS/tests/scene1/test_exposure.py b/apps/CameraITS/tests/scene1/test_exposure.py
index c55e7ad..d217bdb 100644
--- a/apps/CameraITS/tests/scene1/test_exposure.py
+++ b/apps/CameraITS/tests/scene1/test_exposure.py
@@ -35,7 +35,7 @@
     THRESHOLD_MAX_OUTLIER_DIFF = 0.1
     THRESHOLD_MIN_LEVEL = 0.1
     THRESHOLD_MAX_LEVEL = 0.9
-    THRESHOLD_MAX_ABS_GRAD = 0.001
+    THRESHOLD_MAX_LEVEL_DIFF = 0.025
 
     mults = []
     r_means = []
@@ -72,15 +72,18 @@
     pylab.ylim([0,1])
     matplotlib.pyplot.savefig("%s_plot_means.png" % (NAME))
 
-    # Check for linearity. For each R,G,B channel, fit a line y=mx+b, and
-    # assert that the gradient is close to 0 (flat) and that there are no
-    # crazy outliers. Also ensure that the images aren't clamped to 0 or 1
+    # Check for linearity. Verify sample pixel mean values are close to each
+    # other. Also ensure that the images aren't clamped to 0 or 1
     # (which would make them look like flat lines).
     for chan in xrange(3):
         values = [r_means, g_means, b_means][chan]
         m, b = numpy.polyfit(mults, values, 1).tolist()
+        max_val = max(values)
+        min_val = min(values)
+        max_diff = max_val - min_val
         print "Channel %d line fit (y = mx+b): m = %f, b = %f" % (chan, m, b)
-        assert(abs(m) < THRESHOLD_MAX_ABS_GRAD)
+        print "Channel max %f min %f diff %f" % (max_val, min_val, max_diff)
+        assert(max_diff < THRESHOLD_MAX_LEVEL_DIFF)
         assert(b > THRESHOLD_MIN_LEVEL and b < THRESHOLD_MAX_LEVEL)
         for v in values:
             assert(v > THRESHOLD_MIN_LEVEL and v < THRESHOLD_MAX_LEVEL)
diff --git a/apps/CameraITS/tests/scene1/test_linearity.py b/apps/CameraITS/tests/scene1/test_linearity.py
index a9063a9..2176f5e 100644
--- a/apps/CameraITS/tests/scene1/test_linearity.py
+++ b/apps/CameraITS/tests/scene1/test_linearity.py
@@ -33,7 +33,7 @@
     """
     NAME = os.path.basename(__file__).split(".")[0]
 
-    RESIDUAL_THRESHOLD = 0.00005
+    RESIDUAL_THRESHOLD = 0.0003 # approximately each sample is off by 2/255
 
     # The HAL3.2 spec requires that curves up to 64 control points in length
     # must be supported.
diff --git a/apps/CameraITS/tests/scene1/test_locked_burst.py b/apps/CameraITS/tests/scene1/test_locked_burst.py
index 5cea30c..8d3bc8e 100644
--- a/apps/CameraITS/tests/scene1/test_locked_burst.py
+++ b/apps/CameraITS/tests/scene1/test_locked_burst.py
@@ -15,6 +15,7 @@
 import its.image
 import its.device
 import its.objects
+import its.caps
 import os.path
 import numpy
 import pylab
@@ -25,13 +26,14 @@
     """Test 3A lock + YUV burst (using auto settings).
 
     This is a test that is designed to pass even on limited devices that
-    don't have MANUAL_SENSOR or PER_FRAME_CONTROLS. (They must be able to
-    capture bursts with full res @ full frame rate to pass, however).
+    don't have MANUAL_SENSOR or PER_FRAME_CONTROLS. The test checks
+    YUV image consistency while the frame rate check is in CTS.
     """
     NAME = os.path.basename(__file__).split(".")[0]
 
     BURST_LEN = 8
-    SPREAD_THRESH = 0.005
+    SPREAD_THRESH_MANUAL_SENSOR = 0.005
+    SPREAD_THRESH = 0.03
     FPS_MAX_DIFF = 2.0
 
     with its.device.ItsSession() as cam:
@@ -64,28 +66,10 @@
         for means in [r_means, g_means, b_means]:
             spread = max(means) - min(means)
             print "Patch mean spread", spread, \
-                   " (min/max: ",  min(means), "/", max(means), ")"
-            assert(spread < SPREAD_THRESH)
-
-        # Also ensure that the burst was at full frame rate.
-        fmt_code = 0x23
-        configs = props['android.scaler.streamConfigurationMap']\
-                       ['availableStreamConfigurations']
-        min_duration = None
-        for cfg in configs:
-            if cfg['format'] == fmt_code and cfg['input'] == False and \
-                    cfg['width'] == caps[0]["width"] and \
-                    cfg['height'] == caps[0]["height"]:
-                min_duration = cfg["minFrameDuration"]
-        assert(min_duration is not None)
-        tstamps = [c['metadata']['android.sensor.timestamp'] for c in caps]
-        deltas = [tstamps[i]-tstamps[i-1] for i in range(1,len(tstamps))]
-        actual_fps = 1.0 / (max(deltas) / 1000000000.0)
-        actual_fps_max = 1.0 / (min(deltas) / 1000000000.0)
-        max_fps = 1.0 / (min_duration / 1000000000.0)
-        print "Measure FPS min/max", actual_fps, "/", actual_fps_max
-        print "FPS measured %.1f, max advertized %.1f" %(actual_fps, max_fps)
-        assert(max_fps - FPS_MAX_DIFF <= actual_fps <= max_fps + FPS_MAX_DIFF)
+                    " (min/max: ",  min(means), "/", max(means), ")"
+            threshold = SPREAD_THRESH_MANUAL_SENSOR \
+                    if its.caps.manual_sensor(props) else SPREAD_THRESH
+            assert(spread < threshold)
 
 if __name__ == '__main__':
     main()
diff --git a/apps/CameraITS/tests/scene1/test_param_noise_reduction.py b/apps/CameraITS/tests/scene1/test_param_noise_reduction.py
index f5176a7..219927d 100644
--- a/apps/CameraITS/tests/scene1/test_param_noise_reduction.py
+++ b/apps/CameraITS/tests/scene1/test_param_noise_reduction.py
@@ -34,10 +34,10 @@
     """
     NAME = os.path.basename(__file__).split(".")[0]
 
-    # List of variances for Y,U,V.
+    # List of variances for R,G,B.
     variances = [[],[],[]]
 
-    # Reference (baseline) variance for each of Y,U,V.
+    # Reference (baseline) variance for each of R,G,B.
     ref_variance = []
 
     nr_modes_reported = []
@@ -52,33 +52,32 @@
         req = its.objects.manual_capture_request(s, e)
         req["android.noiseReduction.mode"] = 0
         cap = cam.do_capture(req)
+        rgb_image = its.image.convert_capture_to_rgb_image(cap)
         its.image.write_image(
-                its.image.convert_capture_to_rgb_image(cap),
+                rgb_image,
                 "%s_low_gain.jpg" % (NAME))
-        planes = its.image.convert_capture_to_planes(cap)
-        for j in range(3):
-            img = planes[j]
-            tile = its.image.get_image_patch(img, 0.45, 0.45, 0.1, 0.1)
-            ref_variance.append(its.image.compute_image_variances(tile)[0])
+        rgb_tile = its.image.get_image_patch(rgb_image, 0.45, 0.45, 0.1, 0.1)
+        ref_variance = its.image.compute_image_variances(rgb_tile)
         print "Ref variances:", ref_variance
 
+        e, s = its.target.get_target_exposure_combos(cam)["maxSensitivity"]
         for i in range(3):
             # NR modes 0, 1, 2 with high gain
-            e, s = its.target.get_target_exposure_combos(cam)["maxSensitivity"]
             req = its.objects.manual_capture_request(s, e)
             req["android.noiseReduction.mode"] = i
             cap = cam.do_capture(req)
+            rgb_image = its.image.convert_capture_to_rgb_image(cap)
             nr_modes_reported.append(
                     cap["metadata"]["android.noiseReduction.mode"])
             its.image.write_image(
-                    its.image.convert_capture_to_rgb_image(cap),
+                    rgb_image,
                     "%s_high_gain_nr=%d.jpg" % (NAME, i))
-            planes = its.image.convert_capture_to_planes(cap)
-            for j in range(3):
-                img = planes[j]
-                tile = its.image.get_image_patch(img, 0.45, 0.45, 0.1, 0.1)
-                variance = its.image.compute_image_variances(tile)[0]
-                variances[j].append(variance / ref_variance[j])
+            rgb_tile = its.image.get_image_patch(
+                    rgb_image, 0.45, 0.45, 0.1, 0.1)
+            rgb_vars = its.image.compute_image_variances(rgb_tile)
+            for chan in range(3):
+                variance = rgb_vars[chan]
+                variances[chan].append(variance / ref_variance[chan])
         print "Variances with NR mode [0,1,2]:", variances
 
     # Draw a plot.
diff --git a/apps/CameraITS/tests/scene1/test_raw_burst_sensitivity.py b/apps/CameraITS/tests/scene1/test_raw_burst_sensitivity.py
index 6c2b5c1..e176312 100644
--- a/apps/CameraITS/tests/scene1/test_raw_burst_sensitivity.py
+++ b/apps/CameraITS/tests/scene1/test_raw_burst_sensitivity.py
@@ -44,6 +44,8 @@
 
         # Expose for the scene with min sensitivity
         sens_min, sens_max = props['android.sensor.info.sensitivityRange']
+        # Digital gains might not be visible on RAW data
+        sens_max = props['android.sensor.maxAnalogSensitivity']
         sens_step = (sens_max - sens_min) / NUM_STEPS
         s_ae,e_ae,_,_,_  = cam.do_3a(get_results=True)
         s_e_prod = s_ae * e_ae
diff --git a/apps/CameraITS/tests/scene1/test_raw_sensitivity.py b/apps/CameraITS/tests/scene1/test_raw_sensitivity.py
index 14c5eb0..cc0ce14 100644
--- a/apps/CameraITS/tests/scene1/test_raw_sensitivity.py
+++ b/apps/CameraITS/tests/scene1/test_raw_sensitivity.py
@@ -42,6 +42,8 @@
 
         # Expose for the scene with min sensitivity
         sens_min, sens_max = props['android.sensor.info.sensitivityRange']
+        # Digital gains might not be visible on RAW data
+        sens_max = props['android.sensor.maxAnalogSensitivity']
         sens_step = (sens_max - sens_min) / NUM_STEPS
         s_ae,e_ae,_,_,_  = cam.do_3a(get_results=True)
         s_e_prod = s_ae * e_ae
diff --git a/apps/CameraITS/tests/scene1/test_yuv_plus_dng.py b/apps/CameraITS/tests/scene1/test_yuv_plus_dng.py
index 33e7763..268b64a 100644
--- a/apps/CameraITS/tests/scene1/test_yuv_plus_dng.py
+++ b/apps/CameraITS/tests/scene1/test_yuv_plus_dng.py
@@ -31,6 +31,12 @@
         cam.do_3a()
 
         req = its.objects.auto_capture_request()
+        max_dng_size = \
+                its.objects.get_available_output_sizes("raw", props)[0]
+        w,h = its.objects.get_available_output_sizes(
+                "yuv", props, (1920, 1080), max_dng_size)[0]
+        out_surfaces = [{"format":"dng"},
+                        {"format":"yuv", "width":w, "height":h}]
         cap_dng, cap_yuv = cam.do_capture(req, cam.CAP_DNG_YUV)
 
         img = its.image.convert_capture_to_rgb_image(cap_yuv)
diff --git a/apps/CameraITS/tests/scene1/test_yuv_plus_jpeg.py b/apps/CameraITS/tests/scene1/test_yuv_plus_jpeg.py
index 6daa243..e58cb1d 100644
--- a/apps/CameraITS/tests/scene1/test_yuv_plus_jpeg.py
+++ b/apps/CameraITS/tests/scene1/test_yuv_plus_jpeg.py
@@ -27,13 +27,17 @@
 
     THRESHOLD_MAX_RMS_DIFF = 0.01
 
-    fmt_yuv =  {"format":"yuv"}
-    fmt_jpeg = {"format":"jpeg"}
-
     with its.device.ItsSession() as cam:
         props = cam.get_camera_properties()
         its.caps.skip_unless(its.caps.compute_target_exposure(props))
 
+        max_jpeg_size = \
+                its.objects.get_available_output_sizes("jpeg", props)[0]
+        w,h = its.objects.get_available_output_sizes(
+                "yuv", props, (1920, 1080), max_jpeg_size)[0]
+        fmt_yuv =  {"format":"yuv", "width":w, "height":h}
+        fmt_jpeg = {"format":"jpeg"}
+
         # Use a manual request with a linear tonemap so that the YUV and JPEG
         # should look the same (once converted by the its.image module).
         e, s = its.target.get_target_exposure_combos(cam)["midExposureTime"]
diff --git a/apps/CameraITS/tests/scene1/test_yuv_plus_raw.py b/apps/CameraITS/tests/scene1/test_yuv_plus_raw.py
index eb01c1a..2e7804b 100644
--- a/apps/CameraITS/tests/scene1/test_yuv_plus_raw.py
+++ b/apps/CameraITS/tests/scene1/test_yuv_plus_raw.py
@@ -38,7 +38,13 @@
         e, s = its.target.get_target_exposure_combos(cam)["midExposureTime"]
         req = its.objects.manual_capture_request(s, e, True)
 
-        cap_raw, cap_yuv = cam.do_capture(req, cam.CAP_RAW_YUV)
+        max_raw_size = \
+                its.objects.get_available_output_sizes("raw", props)[0]
+        w,h = its.objects.get_available_output_sizes(
+                "yuv", props, (1920, 1080), max_raw_size)[0]
+        out_surfaces = [{"format":"raw"},
+                        {"format":"yuv", "width":w, "height":h}]
+        cap_raw, cap_yuv = cam.do_capture(req, out_surfaces)
 
         img = its.image.convert_capture_to_rgb_image(cap_yuv)
         its.image.write_image(img, "%s_yuv.jpg" % (NAME), True)
diff --git a/apps/CameraITS/tests/scene1/test_yuv_plus_raw10.py b/apps/CameraITS/tests/scene1/test_yuv_plus_raw10.py
index 910a8ea..d02f84e 100644
--- a/apps/CameraITS/tests/scene1/test_yuv_plus_raw10.py
+++ b/apps/CameraITS/tests/scene1/test_yuv_plus_raw10.py
@@ -38,8 +38,13 @@
         e, s = its.target.get_target_exposure_combos(cam)["midExposureTime"]
         req = its.objects.manual_capture_request(s, e, True)
 
+        max_raw10_size = \
+                its.objects.get_available_output_sizes("raw10", props)[0]
+        w,h = its.objects.get_available_output_sizes(
+                "yuv", props, (1920, 1080), max_raw10_size)[0]
         cap_raw, cap_yuv = cam.do_capture(req,
-                [{"format":"raw10"}, {"format":"yuv"}])
+                [{"format":"raw10"},
+                 {"format":"yuv", "width":w, "height":h}])
 
         img = its.image.convert_capture_to_rgb_image(cap_yuv)
         its.image.write_image(img, "%s_yuv.jpg" % (NAME), True)
diff --git a/apps/CameraITS/tests/sensor_fusion/test_sensor_fusion.py b/apps/CameraITS/tests/sensor_fusion/test_sensor_fusion.py
index 49f47a9..efeb665 100644
--- a/apps/CameraITS/tests/sensor_fusion/test_sensor_fusion.py
+++ b/apps/CameraITS/tests/sensor_fusion/test_sensor_fusion.py
@@ -81,9 +81,23 @@
     else:
         events, frames = load_data()
 
+    # Sanity check camera timestamps are enclosed by sensor timestamps
+    # This will catch bugs where camera and gyro timestamps go completely out
+    # of sync
+    cam_times = get_cam_times(events["cam"])
+    min_cam_time = min(cam_times) * NSEC_TO_SEC
+    max_cam_time = max(cam_times) * NSEC_TO_SEC
+    gyro_times = [e["time"] for e in events["gyro"]]
+    min_gyro_time = min(gyro_times) * NSEC_TO_SEC
+    max_gyro_time = max(gyro_times) * NSEC_TO_SEC
+    if not (min_cam_time > min_gyro_time and max_cam_time < max_gyro_time):
+        print "Test failed: camera timestamps [%f,%f] " \
+              "are not enclosed by gyro timestamps [%f, %f]" % (
+            min_cam_time, max_cam_time, min_gyro_time, max_gyro_time)
+        assert(0)
+
     # Compute the camera rotation displacements (rad) between each pair of
     # adjacent frames.
-    cam_times = get_cam_times(events["cam"])
     cam_rots = get_cam_rotations(frames)
     if max(abs(cam_rots)) < THRESH_MIN_ROT:
         print "Device wasn't moved enough"
diff --git a/apps/CameraITS/tools/run_all_tests.py b/apps/CameraITS/tools/run_all_tests.py
index b56281d..2bbd387 100644
--- a/apps/CameraITS/tools/run_all_tests.py
+++ b/apps/CameraITS/tools/run_all_tests.py
@@ -37,11 +37,9 @@
         ],
         "scene1":[
             "test_ae_precapture_trigger",
-            "test_black_white",
             "test_crop_region_raw",
             "test_ev_compensation_advanced",
             "test_ev_compensation_basic",
-            "test_locked_burst",
             "test_yuv_plus_jpeg"
         ]
     }
diff --git a/apps/CtsVerifier/Android.mk b/apps/CtsVerifier/Android.mk
index 87f962f..dc5fda5 100644
--- a/apps/CtsVerifier/Android.mk
+++ b/apps/CtsVerifier/Android.mk
@@ -26,6 +26,7 @@
 LOCAL_SRC_FILES := $(call all-java-files-under, src) $(call all-Iaidl-files-under, src)
 
 LOCAL_STATIC_JAVA_LIBRARIES := android-ex-camera2 \
+                               android-support-v4 \
                                compatibility-common-util-devicesidelib_v2 \
                                cts-sensors-tests \
                                ctstestrunner \
diff --git a/apps/CtsVerifier/AndroidManifest.xml b/apps/CtsVerifier/AndroidManifest.xml
index ed0da02..b525cc4 100644
--- a/apps/CtsVerifier/AndroidManifest.xml
+++ b/apps/CtsVerifier/AndroidManifest.xml
@@ -18,7 +18,7 @@
 <manifest xmlns:android="http://schemas.android.com/apk/res/android"
       package="com.android.cts.verifier"
       android:versionCode="5"
-      android:versionName="5.0_r2.5">
+      android:versionName="5.1_r3">
 
     <uses-sdk android:minSdkVersion="19" android:targetSdkVersion="21"/>
 
@@ -1297,10 +1297,28 @@
                 <action android:name="com.android.cts.verifier.managedprovisioning.BYOD_QUERY" />
                 <action android:name="com.android.cts.verifier.managedprovisioning.BYOD_REMOVE" />
                 <action android:name="com.android.cts.verifier.managedprovisioning.BYOD_INSTALL_APK" />
+                <action android:name="com.android.cts.verifier.managedprovisioning.BYOD_CAPTURE_AND_CHECK_IMAGE" />
+                <action android:name="com.android.cts.verifier.managedprovisioning.BYOD_CAPTURE_AND_CHECK_VIDEO" />
+                <action android:name="com.android.cts.verifier.managedprovisioning.BYOD_CAPTURE_AND_CHECK_AUDIO" />
+                <action android:name="com.android.cts.verifier.managedprovisioning.TEST_NFC_BEAM" />
                 <category android:name="android.intent.category.DEFAULT"></category>
             </intent-filter>
         </activity>
 
+        <activity android:name=".managedprovisioning.NfcTestActivity">
+            <meta-data android:name="test_required_features" android:value="android.hardware.nfc" />
+        </activity>
+
+        <provider
+            android:name="android.support.v4.content.FileProvider"
+            android:authorities="com.android.cts.verifier.managedprovisioning.fileprovider"
+            android:grantUriPermissions="true"
+            android:exported="false">
+            <meta-data
+                android:name="android.support.FILE_PROVIDER_PATHS"
+                android:resource="@xml/filepaths" />
+        </provider>
+
         <activity android:name=".managedprovisioning.ByodIconSamplerActivity">
             <intent-filter>
                 <action android:name="com.android.cts.verifier.managedprovisioning.BYOD_SAMPLE_ICON" />
@@ -1449,6 +1467,54 @@
                 android:resource="@xml/mock_content_rating_systems" />
         </receiver>
 
+        <activity android:name=".car.CarDockTestActivity"
+                android:label="@string/car_dock_test">
+            <intent-filter>
+                <action android:name="android.intent.action.MAIN" />
+                <category android:name="android.cts.intent.category.MANUAL_TEST" />
+            </intent-filter>
+            <meta-data android:name="test_category" android:value="@string/test_category_car" />
+            <meta-data android:name="test_excluded_features"
+                       android:value="android.hardware.type.television:android.software.leanback:android.hardware.type.watch" />
+
+        </activity>
+
+        <activity android:name=".car.CarDockActivity"
+                  android:launchMode="singleTask"
+                  android:autoRemoveFromRecents="true"
+                  android:exported="true" >
+            <intent-filter>
+                <action android:name="android.intent.action.MAIN" />
+                <category android:name="android.intent.category.DEFAULT" />
+            </intent-filter>
+        </activity>
+
+        <!-- See explaination in CarDockTestActivity.java -->
+        <activity-alias android:name=".car.CarDockActivity1"
+            android:targetActivity=".car.CarDockActivity" >
+            <meta-data
+                android:name="android.dock_home"
+                android:value="true" />
+            <intent-filter>
+                <action android:name="android.intent.action.MAIN" />
+                <category android:name="android.intent.category.DEFAULT" />
+                <category android:name="android.intent.category.CAR_DOCK" />
+            </intent-filter>
+        </activity-alias>
+
+        <activity-alias android:name=".car.CarDockActivity2"
+            android:targetActivity=".car.CarDockActivity"
+            android:enabled="false" >
+            <meta-data
+                android:name="android.dock_home"
+                android:value="true" />
+            <intent-filter>
+                <action android:name="android.intent.action.MAIN" />
+                <category android:name="android.intent.category.DEFAULT" />
+                <category android:name="android.intent.category.CAR_DOCK" />
+            </intent-filter>
+        </activity-alias>
+
     </application>
 
 </manifest>
diff --git a/apps/CtsVerifier/res/layout-small/sensor_test.xml b/apps/CtsVerifier/res/layout-small/sensor_test.xml
new file mode 100644
index 0000000..eefa5fa
--- /dev/null
+++ b/apps/CtsVerifier/res/layout-small/sensor_test.xml
@@ -0,0 +1,45 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2014 The Android Open Source Project
+
+     Licensed under the Apache License, Version 2.0 (the "License");
+     you may not use this file except in compliance with the License.
+     You may obtain a copy of the License at
+
+          http://www.apache.org/licenses/LICENSE-2.0
+
+     Unless required by applicable law or agreed to in writing, software
+     distributed under the License is distributed on an "AS IS" BASIS,
+     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+     See the License for the specific language governing permissions and
+     limitations under the License.
+-->
+<android.support.wearable.view.BoxInsetLayout xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:app="http://schemas.android.com/apk/res-auto"
+    android:layout_width="match_parent"
+    android:layout_height="match_parent">
+    <ScrollView android:id="@+id/log_scroll_view"
+            app:layout_box="all"
+            android:fillViewport="true"
+            android:layout_height="match_parent"
+            android:layout_width="match_parent">
+        <LinearLayout
+            android:orientation="vertical"
+            android:layout_height="wrap_content"
+            android:layout_width="match_parent">
+
+            <LinearLayout android:id="@+id/log_layout"
+                    android:orientation="vertical"
+                    android:layout_height="wrap_content"
+                    android:layout_width="match_parent"/>
+
+            <android.opengl.GLSurfaceView android:id="@+id/gl_surface_view"
+                android:visibility="gone"
+                android:layout_height="0dp"
+                android:layout_weight="1"
+                android:layout_width="match_parent"/>
+
+            <include layout="@layout/snsr_next_button"/>
+        </LinearLayout>
+
+    </ScrollView>
+</android.support.wearable.view.BoxInsetLayout>
diff --git a/apps/CtsVerifier/res/layout/byod_nfc_test_activity.xml b/apps/CtsVerifier/res/layout/byod_nfc_test_activity.xml
new file mode 100644
index 0000000..52251b4
--- /dev/null
+++ b/apps/CtsVerifier/res/layout/byod_nfc_test_activity.xml
@@ -0,0 +1,31 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2015 The Android Open Source Project
+
+     Licensed under the Apache License, Version 2.0 (the "License");
+     you may not use this file except in compliance with the License.
+     You may obtain a copy of the License at
+
+          http://www.apache.org/licenses/LICENSE-2.0
+
+     Unless required by applicable law or agreed to in writing, software
+     distributed under the License is distributed on an "AS IS" BASIS,
+     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+     See the License for the specific language governing permissions and
+     limitations under the License.
+-->
+<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:tools="http://schemas.android.com/tools" android:layout_width="match_parent"
+    android:layout_height="match_parent" >
+
+    <Button android:text="@string/provisioning_byod_send_manual_beam"
+        android:layout_width="wrap_content"
+        android:layout_height="wrap_content"
+        android:id="@+id/manual_beam_button" />
+
+    <Button android:text="@string/provisioning_byod_send_share_intent"
+        android:layout_width="wrap_content"
+        android:layout_height="wrap_content"
+        android:id="@+id/intent_share_button"
+        android:layout_below="@+id/manual_beam_button" />
+
+</RelativeLayout>
diff --git a/apps/CtsVerifier/res/layout/byod_present_media.xml b/apps/CtsVerifier/res/layout/byod_present_media.xml
new file mode 100644
index 0000000..f6c7eb3
--- /dev/null
+++ b/apps/CtsVerifier/res/layout/byod_present_media.xml
@@ -0,0 +1,52 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2015 The Android Open Source Project
+
+     Licensed under the Apache License, Version 2.0 (the "License");
+     you may not use this file except in compliance with the License.
+     You may obtain a copy of the License at
+
+          http://www.apache.org/licenses/LICENSE-2.0
+
+     Unless required by applicable law or agreed to in writing, software
+     distributed under the License is distributed on an "AS IS" BASIS,
+     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+     See the License for the specific language governing permissions and
+     limitations under the License.
+-->
+<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
+        android:layout_width="match_parent"
+        android:layout_height="match_parent"
+        android:orientation="vertical">
+
+    <ImageView android:id="@+id/imageView"
+        android:layout_width="match_parent"
+        android:layout_height="match_parent"
+        android:layout_weight="1"
+        android:scaleType="fitCenter"
+        android:minHeight="300dp"
+        android:minWidth="300dp"
+        android:visibility="gone"/>
+
+    <VideoView android:id="@+id/videoView"
+        android:layout_width="match_parent"
+        android:layout_height="match_parent"
+        android:layout_weight="1"
+        android:minHeight="300dp"
+        android:minWidth="300dp"
+        android:layout_gravity="center"
+        android:visibility="gone"/>
+
+    <Button android:id="@+id/playButton"
+        android:layout_width="match_parent"
+        android:layout_height="wrap_content"
+        android:layout_weight="0"
+        android:text="@string/provisioning_byod_play"
+        android:visibility="gone"/>
+
+    <Button android:id="@+id/dismissButton"
+        android:layout_width="match_parent"
+        android:layout_height="wrap_content"
+        android:layout_weight="0"
+        android:text="@string/provisioning_byod_dismiss_result_dialog"/>
+
+</LinearLayout>
diff --git a/tests/tests/uirendering/res/layout/draw_activity_view.xml b/apps/CtsVerifier/res/layout/car_dock_main.xml
similarity index 64%
copy from tests/tests/uirendering/res/layout/draw_activity_view.xml
copy to apps/CtsVerifier/res/layout/car_dock_main.xml
index 54a72e3..97f0712 100644
--- a/tests/tests/uirendering/res/layout/draw_activity_view.xml
+++ b/apps/CtsVerifier/res/layout/car_dock_main.xml
@@ -13,5 +13,14 @@
      See the License for the specific language governing permissions and
      limitations under the License.
 -->
+<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
+    android:layout_width="fill_parent"
+    android:layout_height="fill_parent" >
 
-<android.uirendering.cts.CanvasClientView xmlns:android="http://schemas.android.com/apk/res/android"/>
+    <TextView
+        android:layout_width="wrap_content"
+        android:layout_height="wrap_content"
+        android:gravity="center"
+        android:text="@string/car_dock_activity_text" />
+
+</RelativeLayout>
diff --git a/apps/CtsVerifier/res/layout/car_dock_test_main.xml b/apps/CtsVerifier/res/layout/car_dock_test_main.xml
new file mode 100644
index 0000000..c568b54
--- /dev/null
+++ b/apps/CtsVerifier/res/layout/car_dock_test_main.xml
@@ -0,0 +1,32 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2015 The Android Open Source Project
+
+     Licensed under the Apache License, Version 2.0 (the "License");
+     you may not use this file except in compliance with the License.
+     You may obtain a copy of the License at
+
+          http://www.apache.org/licenses/LICENSE-2.0
+
+     Unless required by applicable law or agreed to in writing, software
+     distributed under the License is distributed on an "AS IS" BASIS,
+     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+     See the License for the specific language governing permissions and
+     limitations under the License.
+-->
+<android.support.wearable.view.BoxInsetLayout xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:app="http://schemas.android.com/apk/res-auto"
+    android:layout_width="match_parent"
+    android:layout_height="match_parent">
+    <LinearLayout app:layout_box="all"
+        android:layout_width="fill_parent"
+        android:layout_height="fill_parent"
+        android:orientation="vertical" >
+        <include layout="@layout/pass_fail_buttons" />
+        <Button
+            android:id="@+id/car_mode"
+            android:layout_height="100dp"
+            android:layout_width="500dp"
+            android:layout_gravity="center"
+            android:text="@string/car_mode_enable" />
+    </LinearLayout>
+</android.support.wearable.view.BoxInsetLayout>
diff --git a/apps/CtsVerifier/res/layout/fs_info.xml b/apps/CtsVerifier/res/layout/fs_info.xml
index 3fe6815..ea02fee 100644
--- a/apps/CtsVerifier/res/layout/fs_info.xml
+++ b/apps/CtsVerifier/res/layout/fs_info.xml
@@ -13,62 +13,44 @@
      See the License for the specific language governing permissions and
      limitations under the License.
 -->
-<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
-    android:layout_width="fill_parent" android:layout_height="wrap_content">
+<ScrollView xmlns:android="http://schemas.android.com/apk/res/android"
+    android:layout_width="fill_parent" android:layout_height="fill_parent">
+    <GridLayout
+        android:columnCount="2"
+        android:orientation="horizontal"
+        android:layout_width="fill_parent"
+        android:layout_height="wrap_content">
 
-    <ImageView android:id="@+id/fs_legend_good_image"
-        android:layout_width="wrap_content"
-        android:layout_height="wrap_content"
-        android:src="@drawable/fs_good"
-        android:layout_alignParentTop="true"
-        android:layout_alignParentLeft="true" />
-    <TextView android:id="@+id/fs_legend_good_text"
-        android:layout_width="wrap_content"
-        android:layout_height="wrap_content"
-        android:text="@string/fs_legend_good"
-        android:layout_toRightOf="@id/fs_legend_good_image"
-        android:layout_alignTop="@id/fs_legend_good_image"
-        android:layout_marginLeft="3dip" />
+        <ImageView android:id="@+id/fs_legend_good_image"
+            android:src="@drawable/fs_good"
+            android:layout_gravity="top|left" />
+        <TextView android:id="@+id/fs_legend_good_text"
+            android:text="@string/fs_legend_good"
+            android:layout_marginLeft="3dip"
+            android:layout_gravity="top|left" />
 
-    <ImageView android:id="@+id/fs_legend_indeterminate_image"
-        android:layout_width="wrap_content"
-        android:layout_height="wrap_content"
-        android:src="@drawable/fs_indeterminate"
-        android:layout_alignParentLeft="true"
-        android:layout_below="@id/fs_legend_good_image" />
-    <TextView android:id="@+id/fs_legend_indeterminate_text"
-        android:layout_width="wrap_content"
-        android:layout_height="wrap_content"
-        android:text="@string/fs_legend_indeterminate"
-        android:layout_toRightOf="@id/fs_legend_indeterminate_image"
-        android:layout_alignTop="@id/fs_legend_indeterminate_image"
-        android:layout_marginLeft="3dip" />
+        <ImageView android:id="@+id/fs_legend_indeterminate_image"
+            android:src="@drawable/fs_indeterminate"
+            android:layout_gravity="top|left" />
+        <TextView android:id="@+id/fs_legend_indeterminate_text"
+            android:text="@string/fs_legend_indeterminate"
+            android:layout_marginLeft="3dip"
+            android:layout_gravity="top|left" />
 
-    <ImageView android:id="@+id/fs_legend_warning_image"
-        android:layout_width="wrap_content"
-        android:layout_height="wrap_content"
-        android:src="@drawable/fs_warning"
-        android:layout_alignParentLeft="true"
-        android:layout_below="@id/fs_legend_indeterminate_image" />
-    <TextView android:id="@+id/fs_legend_warning_text"
-        android:layout_width="wrap_content"
-        android:layout_height="wrap_content"
-        android:text="@string/fs_legend_warning"
-        android:layout_toRightOf="@id/fs_legend_warning_image"
-        android:layout_alignTop="@id/fs_legend_warning_image"
-        android:layout_marginLeft="3dip" />
+        <ImageView android:id="@+id/fs_legend_warning_image"
+            android:src="@drawable/fs_warning"
+            android:layout_gravity="top|left" />
+        <TextView android:id="@+id/fs_legend_warning_text"
+            android:text="@string/fs_legend_warning"
+            android:layout_marginLeft="3dip"
+            android:layout_gravity="top|left" />
 
-    <ImageView android:id="@+id/fs_legend_error_image"
-        android:layout_width="wrap_content"
-        android:layout_height="wrap_content"
-        android:src="@drawable/fs_error"
-        android:layout_alignParentLeft="true"
-        android:layout_below="@id/fs_legend_warning_image" />
-    <TextView android:id="@+id/fs_legend_error_text"
-        android:layout_width="wrap_content"
-        android:layout_height="wrap_content"
-        android:text="@string/fs_legend_error"
-        android:layout_toRightOf="@id/fs_legend_error_image"
-        android:layout_alignTop="@id/fs_legend_error_image"
-        android:layout_marginLeft="3dip" />
-</RelativeLayout>
+        <ImageView android:id="@+id/fs_legend_error_image"
+            android:src="@drawable/fs_error"
+            android:layout_gravity="top|left" />
+        <TextView android:id="@+id/fs_legend_error_text"
+            android:text="@string/fs_legend_error"
+            android:layout_marginLeft="3dip"
+            android:layout_gravity="top|left" />
+    </GridLayout>
+</ScrollView>
diff --git a/apps/CtsVerifier/res/layout/pwa_widgets.xml b/apps/CtsVerifier/res/layout/pwa_widgets.xml
index 537fc32..6204d3e 100644
--- a/apps/CtsVerifier/res/layout/pwa_widgets.xml
+++ b/apps/CtsVerifier/res/layout/pwa_widgets.xml
@@ -56,20 +56,6 @@
                  android:layout_weight="1"
                  android:text="@string/pwa_button_down" />
 
-             <Button
-                 android:id="@+id/left_button"
-                 android:layout_width="match_parent"
-                 android:layout_height="match_parent"
-                 android:layout_weight="1"
-                 android:text="@string/pwa_button_left" />
-
-             <Button
-                 android:id="@+id/right_button"
-                 android:layout_width="match_parent"
-                 android:layout_height="wrap_content"
-                 android:layout_weight="1"
-                 android:text="@string/pwa_button_right" />
-
          </LinearLayout>
      </LinearLayout>
 
diff --git a/apps/CtsVerifier/res/layout/snsr_next_button.xml b/apps/CtsVerifier/res/layout/snsr_next_button.xml
index e7a6d72..391c394 100644
--- a/apps/CtsVerifier/res/layout/snsr_next_button.xml
+++ b/apps/CtsVerifier/res/layout/snsr_next_button.xml
@@ -13,9 +13,9 @@
      See the License for the specific language governing permissions and
      limitations under the License.
 -->
-<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
+<GridLayout xmlns:android="http://schemas.android.com/apk/res/android"
         android:orientation="horizontal"
-        android:gravity="center"
+        android:columnCount="@integer/test_list_footer_button_count"
         android:layout_width="match_parent"
         android:layout_height="wrap_content"
         android:paddingBottom="@dimen/snsr_view_padding_bottom"
@@ -25,6 +25,7 @@
     <Button android:id="@+id/next_button"
             android:layout_height="wrap_content"
             android:layout_width="wrap_content"
+            android:layout_columnWeight="1"
             android:text="@string/next_button_text"
             />
 
@@ -32,6 +33,7 @@
             android:visibility="gone"
             android:layout_width="wrap_content"
             android:layout_height="wrap_content"
+            android:layout_columnWeight="1"
             android:drawableTop="@drawable/fs_good"
             />
 
@@ -39,7 +41,8 @@
             android:visibility="gone"
             android:layout_width="wrap_content"
             android:layout_height="wrap_content"
+            android:layout_columnWeight="1"
             android:drawableTop="@drawable/fs_error"
             />
 
-</LinearLayout>
+</GridLayout>
diff --git a/apps/CtsVerifier/res/layout/test_list_footer.xml b/apps/CtsVerifier/res/layout/test_list_footer.xml
index fdb8e43..cb73ed1 100644
--- a/apps/CtsVerifier/res/layout/test_list_footer.xml
+++ b/apps/CtsVerifier/res/layout/test_list_footer.xml
@@ -17,22 +17,26 @@
   -->
 <GridLayout xmlns:android="http://schemas.android.com/apk/res/android"
     android:orientation="horizontal"
+    android:columnCount="@integer/test_list_footer_button_count"
     android:layout_width="match_parent"
     android:layout_height="wrap_content">
 
     <Button
         android:id="@+id/clear"
         android:text="@string/clear"
+        android:layout_gravity="center"
         android:layout_width="wrap_content"
         android:layout_height="wrap_content" />
     <Button
         android:id="@+id/view"
         android:text="@string/view"
+        android:layout_gravity="center"
         android:layout_width="wrap_content"
         android:layout_height="wrap_content" />
     <Button
         android:id="@+id/export"
         android:text="@string/export"
+        android:layout_gravity="center"
         android:layout_width="wrap_content"
         android:layout_height="wrap_content" />
 </GridLayout>
diff --git a/tests/tests/uirendering/res/layout/draw_activity_view.xml b/apps/CtsVerifier/res/values-small/integers.xml
similarity index 78%
rename from tests/tests/uirendering/res/layout/draw_activity_view.xml
rename to apps/CtsVerifier/res/values-small/integers.xml
index 54a72e3..274db44 100644
--- a/tests/tests/uirendering/res/layout/draw_activity_view.xml
+++ b/apps/CtsVerifier/res/values-small/integers.xml
@@ -1,5 +1,5 @@
 <?xml version="1.0" encoding="utf-8"?>
-<!-- Copyright (C) 2014 The Android Open Source Project
+<!-- 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.
@@ -13,5 +13,6 @@
      See the License for the specific language governing permissions and
      limitations under the License.
 -->
-
-<android.uirendering.cts.CanvasClientView xmlns:android="http://schemas.android.com/apk/res/android"/>
+<resources>
+    <integer name="test_list_footer_button_count">1</integer>
+</resources>
diff --git a/tests/tests/uirendering/res/layout/draw_activity_view.xml b/apps/CtsVerifier/res/values/integers.xml
similarity index 78%
copy from tests/tests/uirendering/res/layout/draw_activity_view.xml
copy to apps/CtsVerifier/res/values/integers.xml
index 54a72e3..2ced54b 100644
--- a/tests/tests/uirendering/res/layout/draw_activity_view.xml
+++ b/apps/CtsVerifier/res/values/integers.xml
@@ -1,5 +1,5 @@
 <?xml version="1.0" encoding="utf-8"?>
-<!-- Copyright (C) 2014 The Android Open Source Project
+<!-- 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.
@@ -13,5 +13,6 @@
      See the License for the specific language governing permissions and
      limitations under the License.
 -->
-
-<android.uirendering.cts.CanvasClientView xmlns:android="http://schemas.android.com/apk/res/android"/>
+<resources>
+    <integer name="test_list_footer_button_count">3</integer>
+</resources>
diff --git a/apps/CtsVerifier/res/values/strings.xml b/apps/CtsVerifier/res/values/strings.xml
index bd748ac..07f7654 100644
--- a/apps/CtsVerifier/res/values/strings.xml
+++ b/apps/CtsVerifier/res/values/strings.xml
@@ -26,6 +26,7 @@
     <!-- Strings for TestListActivity -->
     <string name="test_category_audio">Audio</string>
     <string name="test_category_camera">Camera</string>
+    <string name="test_category_car">Car</string>
     <string name="test_category_device_admin">Device Administration</string>
     <string name="test_category_hardware">Hardware</string>
     <string name="test_category_networking">Networking</string>
@@ -86,6 +87,17 @@
         device and verify that all rows in the policy list are green. Red items indicate policy
         settings that were not loaded properly.
     </string>
+    <string name="car_dock_test">Car Dock Test</string>
+    <string name="car_dock_test_desc">This test ensures that car mode opens the app associated with
+        car dock when going into car mode.\n\n
+        Click on "Enable Car Mode" to start the test. Clicking on the button will either bring up a
+        disambiguation dialog asking which app to open or immediately open the CAR_DOCK application.
+        Select the "CTS Verifier" app and then "Always" if the dialog pops up.
+        This will open the CAR_DOCK application.\n\n
+        In the CAR_DOCK application, press the home button, which will enable the pass button if the
+        framework correctly tries to open the CAR_DOCK app again.</string>
+    <string name="car_mode_enable">Enable Car Mode</string>
+    <string name="car_dock_activity_text">Press the Home button</string>
     <string name="da_no_policy">1. Press the \"Generate Policy\" to create a random device
         policy\n\n2. Press \"Apply Policy\" to put the policy into effect.\n\n3. Reboot your
         device and return to this test in the CTS Verifier.
@@ -979,6 +991,7 @@
     <string name="package_priority_info">This test checks that the NotificationManagerService respects
         user preferences about relative package priorities.
     </string>
+    <string name="package_priority_bot">Verifying that the CTS Robot helper package is installed.</string>
     <string name="package_priority_high">Find \"%s\" under \"App notifications\" in the \"Sound &amp; notifications\" settings panel, and mark it as having notification priority.</string>
     <string name="package_priority_default">Find \"%s\" under \"App notifications\" in the \"Sound &amp; notifications\" settings panel, and make sure it has default priority.</string>
     <string name="package_priority_user_order">Check that ranker respects user priorities.</string>
@@ -1033,7 +1046,8 @@
         Location Provider API when the device is in High Accuracy location mode.
     </string>
     <string name="location_mode_select_high_accuracy">
-        Please select the \"High accuracy\" mode at Settings > Location and return here.
+        Please select the \"High accuracy\" mode at Settings > Location
+        (hint: tap the "Mode" item) and return here.
     </string>
     <string name="location_mode_battery_saving_test">Battery Saving Mode Test</string>
     <string name="location_mode_battery_saving_info">
@@ -1041,7 +1055,8 @@
         Location Provider API when the device is in Battery Saving location mode.
     </string>
     <string name="location_mode_select_battery_saving">
-        Please select the \"Battery Saving\" mode at Settings > Location and return here.
+        Please select the \"Battery Saving\" mode at Settings > Location
+        (hint: tap the "Mode" item) and return here.
     </string>
     <string name="location_mode_device_only_test">Device Only Mode Test</string>
     <string name="location_mode_device_only_info">
@@ -1050,7 +1065,7 @@
     </string>
     <string name="location_mode_select_device_only">
         Please select the \"Device Only\" mode at
-        Settings > Location and return here.
+        Settings > Location (hint: tap the "Mode" item) and return here.
     </string>
     <string name="location_mode_off_test">Location Mode Off Test</string>
     <string name="location_mode_off_info">
@@ -1117,6 +1132,39 @@
         2. Verify that the installation of the package is refused.
     </string>
 
+    <string name="provisioning_byod_capture_image_support">Camera support cross profile image capture</string>
+    <string name="provisioning_byod_capture_image_support_info">
+        This test verifies that images can be captured from the managed profile using the primary profile camera.\n
+        1. Capture a picture using the camera.\n
+        2. Verify that the captured picture is shown.\n
+        3. Click on the close button.
+    </string>
+    <string name="provisioning_byod_capture_video_support">Camera support cross profile video capture</string>
+    <string name="provisioning_byod_capture_video_support_info">
+        This test verifies that videos can be captured from the managed profile using the primary profile camera.\n
+        1. Capture a video using the camera.\n
+        2. Click on the play button.\n
+        3. Verify that the captured video is played.\n
+        4. Click on the close button.
+    </string>
+    <string name="provisioning_byod_capture_audio_support">Sound recorder support cross profile audio capture</string>
+    <string name="provisioning_byod_capture_audio_support_info">
+        This test verifies that audio can be captured from the managed profile using the primary profile sound recorder.\n
+        1. Capture audio.\n
+        2. Click on the play button.\n
+        3. Verify that the captured audio is played.\n
+        4. Click on the close button.\n
+    </string>
+    <string name="provisioning_byod_dismiss_result_dialog">Close</string>
+    <string name="provisioning_byod_play">Play</string>
+    <string name="provisioning_byod_verify_image_title">Verify captured image</string>
+    <string name="provisioning_byod_verify_video_title">Verify captured video</string>
+    <string name="provisioning_byod_verify_audio_title">Verify captured audio</string>
+    <string name="provisioning_byod_no_image_capture_resolver">No image capture app present. Skip test.</string>
+    <string name="provisioning_byod_no_video_capture_resolver">No video capture app present. Skip test.</string>
+    <string name="provisioning_byod_no_audio_capture_resolver">No audio capture app present. Skip test.</string>
+    <string name="provisioning_byod_capture_media_error">Error while capturing media from managed profile.</string>
+
     <!-- Strings for DeskClock -->
     <string name="deskclock_tests">Alarms and Timers Tests</string>
     <string name="deskclock_tests_info">
@@ -1361,6 +1409,27 @@
         Then use the Back button to return to this test and mark accordingly.
     </string>
 
+    <string name="provisioning_byod_nfc_beam">Disable Nfc beam</string>
+    <string name="provisioning_byod_nfc_beam_allowed_instruction">
+        Please press the Go button to test if Nfc beam can be triggered in the work profile.\n
+        \n
+        For the first test, press \"Send manual beam\" to trigger a beam, then bump into another device to send the file. Verify that the file is successfully received.\n
+        \n
+        For the second test, press \"Send share intent\" to trigger a beam, then bump into another device to send the file. Verify that the file is successfully received.\n
+        \n
+        Then use the Back button to return to this test and mark accordingly.
+    </string>
+    <string name="provisioning_byod_nfc_beam_disallowed_instruction">
+        Please press the Go button to test if Nfc beam is disallowed by policy
+        \n
+        Verify that Nfc beam is not triggered when pressing the button.\n
+        \n
+        Then use the Back button to return to this test and mark accordingly.
+    </string>
+    <string name="provisioning_byod_send_manual_beam">Send manual beam</string>
+    <string name="provisioning_byod_send_share_intent">Send share intent</string>
+    <string name="provisioning_byod_cannot_resolve_beam_activity">Cannot find beam activity</string>
+
     <string name="provisioning_byod_no_activity">Cannot communicate with activity in the work profile.</string>
     <string name="provisioning_byod_delete_profile">Initiate deletion of work profile.</string>
     <string name="provisioning_byod_profile_deleted">Work profile deleted.</string>
@@ -1402,80 +1471,90 @@
     <string name="js_any_connectivity_test">Device with no connectivity will not execute a job with an unmetered connectivity constraint.</string>
     <string name="js_no_connectivity_test">Device with no connectivity will still execute a job with no connectivity constraints.</string>
 
-    <!-- String for Live Channels app Tests -->
+    <!-- String for the bundled TV app Tests -->
 
-    <string name="tv_input_discover_test">3rd-party TV input app discoverability test</string>
+    <string name="tv_input_discover_test">3rd-party TV input test</string>
     <string name="tv_input_discover_test_info">
-    This test verifies that the pre-loaded Live Channels app is invoked via intents and issues
-    appropriate calls to framework APIs, so that TV input apps work properly with the pre-loaded
-    Live Channels app.
+    Verify that the bundled TV app launches via Intent and calls the proper API to discover
+    3rd-party TV inputs.
     </string>
     <string name="tv_input_discover_test_go_to_setup">
-    Press the \"Launch Live Channels\" button, and set up the newly installed TV input:
+    Select the \"Launch TV app\" button and set up the newly installed TV input:
     \"CTS Verifier\".
     </string>
     <string name="tv_input_discover_test_verify_setup">
     Setup activity must have been started.
     </string>
     <string name="tv_input_discover_test_tune_to_channel">
-    Press the \"Launch Live Channels\" button, and tune to the channel named \"Dummy\" from
-    \"CTS Verifier\" input. If necessary, configure the channel to be visible.
+    Select the \"Launch TV app\" button and tune to the \"Dummy\" channel from \"CTS Verifier\"
+    input. If necessary, configure the channel to be visible.
     </string>
     <string name="tv_input_discover_test_verify_tune">
     Tune command must be called.
     </string>
     <string name="tv_input_discover_test_verify_overlay_view">
-    Overlay view must be shown. Verify that there is a text view displaying \"Overlay View Dummy Text\"
-    when you tune to the \"Dummy\" channel.
+    Verify that the overlay appears and displays the text \"Overlay View Dummy Text\" when you tune
+    to the \"Dummy\" channel.
     </string>
+    <string name="tv_input_discover_test_verify_global_search">
+    Verify the TV app provides query results for 3rd-party input\'s channels and programs in
+    global search results.
+    </string>
+    <string name="tv_input_discover_test_go_to_epg">
+    Select the \"Launch EPG\" button and locate the \"Dummy\" channel.
+    </string>
+    <string name="tv_input_discover_test_verify_epg">
+    Do you see the programs named \"Dummy Program\" and their descriptions
+    "Dummy Program Description" in the EPG?
+    </string>
+    <string name="tv_input_discover_test_yes">Yes</string>
 
-    <string name="tv_parental_control_test">Live Channels app parental control test</string>
+    <string name="tv_parental_control_test">TV app parental controls test</string>
     <string name="tv_parental_control_test_info">
-    This test verifies that the default Live Channels app invokes proper parental control APIs in
-    the framework.
+    Verify that the bundled TV app calls the parental controls API.
     </string>
     <string name="tv_parental_control_test_turn_on_parental_control">
-    Press the \"Launch Live Channels\" button, and turn on the parental control. If it\'s on
-    already, turn it off and on again.
+    Select the \"Launch TV app\" button and turn on the parental controls. If parental controls are
+    on already, turn it off and on again.
     </string>
     <string name="tv_parental_control_test_verify_receive_broadcast1">
     TV input service must have received ACTION_PARENTAL_CONTROLS_ENABLED_CHANGED broadcast.
     </string>
     <string name="tv_parental_control_test_block_tv_ma">
-    Press the \"Launch Live Channels\" button, and block \"Fake\" rating for \"CtsVerifier\" rating
-    system in the parental control settings. You may have to enable the rating system if it is
-    disabled by default. If it\'s already blocked, unblock, save, and then block again.
+    Select the \"Launch TV app\" button and block the \"Fake\" rating for \"CtsVerifier\" rating
+    system in the parental control settings. If the rating system is disabled by default, enable it.
+    If the \"Fake\" rating is already blocked, unblock it, save, and then block again.
     </string>
     <string name="tv_parental_control_test_verify_receive_broadcast2">
     TV input service must have received ACTION_BLOCKED_RATINGS_CHANGED broadcast.
     </string>
     <string name="tv_parental_control_test_block_unblock">
-    Press the \"Launch Live Channels\" button; verify that the channel is blocked visually.
-    Try unblock the screen by entering PIN; verify that it\'s unblocked visually.
+    Select the \"Launch TV app\" button; verify that the channel is blocked.
+    Try to unblock the screen by entering PIN; verify that it\'s unblocked.
     </string>
 
-    <string name="tv_launch_tv_app">Launch Live Channels</string>
+    <string name="tv_launch_tv_app">Launch TV app</string>
+    <string name="tv_launch_epg">Launch EPG</string>
     <string name="tv_channel_not_found">
     CtsVerifier channel is not set up. Please set up before proceeding.
     </string>
 
-    <string name="tv_multiple_tracks_test">Live Channels app multiple tracks / subtitle test</string>
+    <string name="tv_multiple_tracks_test">TV app closed captions and multi-audio test</string>
     <string name="tv_multiple_tracks_test_info">
-    This test verifies that the default Live Channels app invokes proper mulitple tracks / subtitle
-    APIs in the framework.
+    Verify that the bundled TV app calls the multi-track API.
     </string>
     <string name="tv_multiple_tracks_test_select_subtitle">
-    Press the \"Launch Live Channels\" button. Verify that the closed caption is off by default.
-    Set closed caption to English.
+    Select the \"Launch TV app\" button. Verify that closed captions are off by default. Set closed
+    caption language to English.
     </string>
     <string name="tv_multiple_tracks_test_verify_set_caption_enabled">
-    Caption should be enabled.
+    Captions are enabled.
     </string>
     <string name="tv_multiple_tracks_test_verify_select_subtitle">
-    The English subtitle track should be selected.
+    The English closed caption track should be selected.
     </string>
     <string name="tv_multiple_tracks_test_select_audio">
-    Press the \"Launch Live Channels\" button. Verify that the audio track is English by default.
+    Select the \"Launch TV app\" button. Verify that the audio track is English by default.
     Select Spanish audio track.
     </string>
     <string name="tv_multiple_tracks_test_verify_select_audio">
diff --git a/apps/CtsVerifier/res/xml/filepaths.xml b/apps/CtsVerifier/res/xml/filepaths.xml
new file mode 100644
index 0000000..2d555a2
--- /dev/null
+++ b/apps/CtsVerifier/res/xml/filepaths.xml
@@ -0,0 +1,3 @@
+<paths xmlns:android="http://schemas.android.com/apk/res/android">
+    <files-path path="images/" name="images" />
+</paths>
diff --git a/apps/CtsVerifier/src/com/android/cts/verifier/AbstractTestListActivity.java b/apps/CtsVerifier/src/com/android/cts/verifier/AbstractTestListActivity.java
index 6495a05..409b0db 100644
--- a/apps/CtsVerifier/src/com/android/cts/verifier/AbstractTestListActivity.java
+++ b/apps/CtsVerifier/src/com/android/cts/verifier/AbstractTestListActivity.java
@@ -20,8 +20,10 @@
 
 import android.app.ListActivity;
 import android.content.Intent;
+import android.content.res.Configuration;
 import android.os.Bundle;
 import android.view.View;
+import android.view.Window;
 import android.widget.ListView;
 
 /** {@link ListActivity} that displays a list of manual tests. */
@@ -57,6 +59,10 @@
     @Override
     protected void onCreate(Bundle savedInstanceState) {
         super.onCreate(savedInstanceState);
+        if ((getResources().getConfiguration().uiMode & Configuration.UI_MODE_TYPE_MASK)
+              == Configuration.UI_MODE_TYPE_TELEVISION) {
+            getWindow().requestFeature(Window.FEATURE_OPTIONS_PANEL);
+        }
         setContentView(R.layout.list_content);
     }
 
diff --git a/apps/CtsVerifier/src/com/android/cts/verifier/camera/formats/CameraFormatsActivity.java b/apps/CtsVerifier/src/com/android/cts/verifier/camera/formats/CameraFormatsActivity.java
index d325b65..9c5b31d 100755
--- a/apps/CtsVerifier/src/com/android/cts/verifier/camera/formats/CameraFormatsActivity.java
+++ b/apps/CtsVerifier/src/com/android/cts/verifier/camera/formats/CameraFormatsActivity.java
@@ -236,8 +236,15 @@
     public void onSurfaceTextureAvailable(SurfaceTexture surface,
             int width, int height) {
         mPreviewTexture = surface;
-        mPreviewTexWidth = width;
-        mPreviewTexHeight = height;
+        if (mFormatView.getMeasuredWidth() != width
+                || mFormatView.getMeasuredHeight() != height) {
+            mPreviewTexWidth = mFormatView.getMeasuredWidth();
+            mPreviewTexHeight = mFormatView.getMeasuredHeight();
+         } else {
+            mPreviewTexWidth = width;
+            mPreviewTexHeight = height;
+        }
+
         if (mCamera != null) {
             startPreview();
         }
diff --git a/apps/CtsVerifier/src/com/android/cts/verifier/camera/fov/DetermineFovActivity.java b/apps/CtsVerifier/src/com/android/cts/verifier/camera/fov/DetermineFovActivity.java
index 8b989e1..959e98f 100644
--- a/apps/CtsVerifier/src/com/android/cts/verifier/camera/fov/DetermineFovActivity.java
+++ b/apps/CtsVerifier/src/com/android/cts/verifier/camera/fov/DetermineFovActivity.java
@@ -145,7 +145,7 @@
         mTargetDistanceCm = getTargetDistance();
         mReportedFovDegrees = PhotoCaptureActivity.getReportedFovDegrees();
 
-        mFovDegrees = mReportedFovDegrees > 80 ? 60 : mReportedFovDegrees;
+        mFovDegrees = mReportedFovDegrees > 120 ? 60 : mReportedFovDegrees;
         mFovMaxDegrees = mFovDegrees + FOV_ADJUSTMENT_RANGE / 2;
         mFovMinDegrees = mFovDegrees - FOV_ADJUSTMENT_RANGE / 2;
 
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 e3d0b6d..ce10535 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
@@ -1094,25 +1094,34 @@
             }
 
             // Initiate the captures.
+            long maxExpTimeNs = -1;
             for (int i = 0; i < requests.size(); i++) {
+                CaptureRequest.Builder req = requests.get(i);
                 // For DNG captures, need the LSC map to be available.
                 if (mCaptureRawIsDng) {
-                    requests.get(i).set(CaptureRequest.STATISTICS_LENS_SHADING_MAP_MODE, 1);
+                    req.set(CaptureRequest.STATISTICS_LENS_SHADING_MAP_MODE, 1);
+                }
+                Long expTimeNs = req.get(CaptureRequest.SENSOR_EXPOSURE_TIME);
+                if (expTimeNs != null && expTimeNs > maxExpTimeNs) {
+                    maxExpTimeNs = expTimeNs;
                 }
 
-                CaptureRequest.Builder req = requests.get(i);
                 for (int j = 0; j < numSurfaces; j++) {
                     req.addTarget(mCaptureReaders[j].getSurface());
                 }
                 mSession.capture(req.build(), mCaptureResultListener, mResultHandler);
             }
 
+            long timeout = TIMEOUT_CALLBACK * 1000;
+            if (maxExpTimeNs > 0) {
+                timeout += maxExpTimeNs / 1000000; // ns to ms
+            }
             // Make sure all callbacks have been hit (wait until captures are done).
             // If no timeouts are received after a timeout, then fail.
             int currentCount = mCountCallbacksRemaining.get();
             while (currentCount > 0) {
                 try {
-                    Thread.sleep(TIMEOUT_CALLBACK*1000);
+                    Thread.sleep(timeout);
                 } catch (InterruptedException e) {
                     throw new ItsException("Timeout failure", e);
                 }
@@ -1232,13 +1241,32 @@
 
                 StringBuilder logMsg = new StringBuilder();
                 logMsg.append(String.format(
-                        "Capt result: AE=%d, AF=%d, AWB=%d, sens=%d, exp=%.1fms, dur=%.1fms, ",
+                        "Capt result: AE=%d, AF=%d, AWB=%d, ",
                         result.get(CaptureResult.CONTROL_AE_STATE),
                         result.get(CaptureResult.CONTROL_AF_STATE),
-                        result.get(CaptureResult.CONTROL_AWB_STATE),
-                        result.get(CaptureResult.SENSOR_SENSITIVITY),
-                        result.get(CaptureResult.SENSOR_EXPOSURE_TIME).intValue() / 1000000.0f,
-                        result.get(CaptureResult.SENSOR_FRAME_DURATION).intValue() / 1000000.0f));
+                        result.get(CaptureResult.CONTROL_AWB_STATE)));
+                int[] capabilities = mCameraCharacteristics.get(
+                        CameraCharacteristics.REQUEST_AVAILABLE_CAPABILITIES);
+                if (capabilities == null) {
+                    throw new ItsException("Failed to get capabilities");
+                }
+                boolean readSensorSettings = false;
+                for (int capability : capabilities) {
+                    if (capability ==
+                            CameraCharacteristics.
+                                    REQUEST_AVAILABLE_CAPABILITIES_READ_SENSOR_SETTINGS) {
+                        readSensorSettings = true;
+                        break;
+                    }
+                }
+                if (readSensorSettings) {
+                    logMsg.append(String.format(
+                            "sens=%d, exp=%.1fms, dur=%.1fms, ",
+                            result.get(CaptureResult.SENSOR_SENSITIVITY),
+                            result.get(CaptureResult.SENSOR_EXPOSURE_TIME).intValue() / 1000000.0f,
+                            result.get(CaptureResult.SENSOR_FRAME_DURATION).intValue() /
+                                        1000000.0f));
+                }
                 if (result.get(CaptureResult.COLOR_CORRECTION_GAINS) != null) {
                     logMsg.append(String.format(
                             "gains=[%.1f, %.1f, %.1f, %.1f], ",
diff --git a/apps/CtsVerifier/src/com/android/cts/verifier/camera/its/ItsUtils.java b/apps/CtsVerifier/src/com/android/cts/verifier/camera/its/ItsUtils.java
index 2541142..2011314 100644
--- a/apps/CtsVerifier/src/com/android/cts/verifier/camera/its/ItsUtils.java
+++ b/apps/CtsVerifier/src/com/android/cts/verifier/camera/its/ItsUtils.java
@@ -166,7 +166,9 @@
                         int length = w * bytesPerPixel;
                         buffer.get(data, offset, length);
                         // Advance buffer the remainder of the row stride
-                        buffer.position(buffer.position() + rowStride - length);
+                        if (row < h - 1) {
+                            buffer.position(buffer.position() + rowStride - length);
+                        }
                         offset += length;
                     } else {
                         // Generic case: should work for any pixelStride but slower.
diff --git a/apps/CtsVerifier/src/com/android/cts/verifier/camera/orientation/CameraOrientationActivity.java b/apps/CtsVerifier/src/com/android/cts/verifier/camera/orientation/CameraOrientationActivity.java
index 273d78a..49b34fd 100644
--- a/apps/CtsVerifier/src/com/android/cts/verifier/camera/orientation/CameraOrientationActivity.java
+++ b/apps/CtsVerifier/src/com/android/cts/verifier/camera/orientation/CameraOrientationActivity.java
@@ -389,6 +389,7 @@
         int targetWidth = w;
 
         boolean aspectRatio = true;
+        boolean maintainCeiling = true;
         while(true) {
             for (Camera.Size size : sizes) {
                 if(aspectRatio) {
@@ -399,20 +400,27 @@
                 }
                 curDiff = Math.abs(size.height - targetHeight) +
                         Math.abs(size.width - targetWidth);
-                if (curDiff < minDiff
+                if (maintainCeiling && curDiff < minDiff
                         && size.height <= targetHeight
                         && size.width <= targetWidth) {
                     optimalSize = size;
                     minDiff = curDiff;
+                } else if (maintainCeiling == false
+                               && curDiff < minDiff) {
+                    //try to get as close as possible
+                    optimalSize = size;
+                    minDiff = curDiff;
                 }
             }
-            if (optimalSize == null) {
+            if (optimalSize == null && aspectRatio == true) {
                 // Cannot find a match, so repeat search and
                 // ignore aspect ratio requirement
                 aspectRatio = false;
-                continue;
-            }
-            else {
+            } else if (maintainCeiling == true) {
+                //Camera resolutions are greater than ceiling provided
+                //lets try to get as close as we can
+                maintainCeiling = false;
+            } else {
                 break;
             }
         }
diff --git a/apps/CtsVerifier/src/com/android/cts/verifier/camera/video/CameraVideoActivity.java b/apps/CtsVerifier/src/com/android/cts/verifier/camera/video/CameraVideoActivity.java
index 0a0e830..0a397e8 100644
--- a/apps/CtsVerifier/src/com/android/cts/verifier/camera/video/CameraVideoActivity.java
+++ b/apps/CtsVerifier/src/com/android/cts/verifier/camera/video/CameraVideoActivity.java
@@ -268,6 +268,7 @@
                 @Override
                 public void onCompletion(MediaPlayer mp) {
                     isPlayingBack = false;
+                    mPlaybackView.stopPlayback();
                     captureButton.setEnabled(true);
                     mStatusLabel.setText(getResources().getString(R.string.status_ready));
                 }
@@ -547,11 +548,22 @@
                 break;
             }
         }
-        // Second try to find one with similar if not the same aspect ratio
+        // Second try to find same ratio in size
+        if (matchedSize == null) {
+            for (int i = mPreviewSizes.size() - 1; i >= 0; i--) {
+                if (mPreviewSizes.get(i).width * recordSize.height ==
+                        mPreviewSizes.get(i).height * recordSize.width) {
+                    matchedSize = mCamera.new Size(mPreviewSizes.get(i).width,
+                            mPreviewSizes.get(i).height);
+                    break;
+                }
+            }
+        }
+        //Third try to find one with similar if not the same apect ratio
         if (matchedSize == null) {
             for (int i = mPreviewSizes.size() - 1; i >= 0; i--) {
                 if (Math.abs((float)mPreviewSizes.get(i).width * recordSize.height /
-                        mPreviewSizes.get(i).height / recordSize.width - 1) < 0.1) {
+                        mPreviewSizes.get(i).height / recordSize.width - 1) < 0.12) {
                     matchedSize = mCamera.new Size(mPreviewSizes.get(i).width,
                             mPreviewSizes.get(i).height);
                     break;
diff --git a/apps/CtsVerifier/src/com/android/cts/verifier/car/CarDockActivity.java b/apps/CtsVerifier/src/com/android/cts/verifier/car/CarDockActivity.java
new file mode 100644
index 0000000..cdee037
--- /dev/null
+++ b/apps/CtsVerifier/src/com/android/cts/verifier/car/CarDockActivity.java
@@ -0,0 +1,53 @@
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.cts.verifier.car;
+
+import android.app.Activity;
+import android.content.Intent;
+import android.os.Bundle;
+import android.widget.Toast;
+
+import com.android.cts.verifier.R;
+
+/**
+ * Dummy activity which notifies {@link CarDockTestActivity} that the CAR_DOCK application
+ * successfully opened.
+ */
+public class CarDockActivity extends Activity {
+    public static Runnable sOnHomePressedRunnable;
+    @Override
+    protected void onCreate(Bundle savedInstanceState) {
+        super.onCreate(savedInstanceState);
+        setContentView(R.layout.car_dock_main);
+        Toast.makeText(this, "CAR_DOCK app started from entering car mode!",
+                Toast.LENGTH_SHORT).show();
+    }
+
+    @Override
+    /**
+     * Pressing the home button while already in this app will trigger this callback.
+     */
+    protected void onNewIntent(Intent intent) {
+        if (sOnHomePressedRunnable != null) {
+            sOnHomePressedRunnable.run();
+            sOnHomePressedRunnable = null;
+            Toast.makeText(this, "CAR_DOCK app started from home button press!",
+                    Toast.LENGTH_SHORT).show();
+        }
+        finish();
+    }
+}
diff --git a/apps/CtsVerifier/src/com/android/cts/verifier/car/CarDockTestActivity.java b/apps/CtsVerifier/src/com/android/cts/verifier/car/CarDockTestActivity.java
new file mode 100644
index 0000000..4473a3c
--- /dev/null
+++ b/apps/CtsVerifier/src/com/android/cts/verifier/car/CarDockTestActivity.java
@@ -0,0 +1,112 @@
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.cts.verifier.car;
+
+import android.app.UiModeManager;
+import android.content.ComponentName;
+import android.content.Context;
+import android.content.Intent;
+import android.content.pm.ActivityInfo;
+import android.content.pm.PackageManager;
+import android.content.pm.ResolveInfo;
+import android.os.Bundle;
+import android.view.View;
+import android.view.View.OnClickListener;
+import android.widget.Button;
+
+import com.android.cts.verifier.PassFailButtons;
+import com.android.cts.verifier.R;
+import com.android.cts.verifier.TestResult;
+
+/**
+ * Tests that CAR_DOCK mode opens the app associated with car dock when going into
+ * car mode.
+ */
+public class CarDockTestActivity extends PassFailButtons.Activity {
+
+    private static final String CAR_DOCK1 =
+            "com.android.cts.verifier.car.CarDockActivity1";
+    private static final String CAR_DOCK2 =
+            "com.android.cts.verifier.car.CarDockActivity2";
+
+    private UiModeManager mManager;
+
+    @Override
+    protected void onCreate(Bundle savedInstanceState) {
+        super.onCreate(savedInstanceState);
+        View view = getLayoutInflater().inflate(R.layout.car_dock_test_main, null);
+        setContentView(view);
+        setInfoResources(R.string.car_dock_test, R.string.car_dock_test_desc, -1);
+        setPassFailButtonClickListeners();
+        getPassButton().setEnabled(false);
+        mManager = (UiModeManager) getSystemService(Context.UI_MODE_SERVICE);
+        CarDockActivity.sOnHomePressedRunnable = new Runnable() {
+            @Override
+            public void run() {
+                TestResult.setPassedResult(CarDockTestActivity.this, getTestId(),
+                        getTestDetails(), getReportLog());
+                mManager.disableCarMode(0);
+                finish();
+            }
+        };
+        Button button = (Button) view.findViewById(R.id.car_mode);
+        button.setOnClickListener(new OnClickListener() {
+            @Override
+            public void onClick(View view) {
+                mManager.enableCarMode(UiModeManager.ENABLE_CAR_MODE_GO_CAR_HOME);
+            }
+        });
+        Intent i = new Intent(Intent.ACTION_MAIN, null);
+        i.addCategory(Intent.CATEGORY_CAR_DOCK);
+        i.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK |
+                Intent.FLAG_ACTIVITY_RESET_TASK_IF_NEEDED);
+        ActivityInfo ai = null;
+        ResolveInfo info = getPackageManager().resolveActivity(i,
+                PackageManager.MATCH_DEFAULT_ONLY | PackageManager.GET_META_DATA);
+        if (info != null) {
+            ai = info.activityInfo;
+            // Check if we are the default CAR_DOCK handler
+            if (!ai.packageName.equals(getPackageName())) {
+                // Switch components to fake new CAR_DOCK install to force bringing up the
+                // disambiguation dialog.
+                PackageManager pm = getApplicationContext().getPackageManager();
+                ComponentName component1 = new ComponentName(getPackageName(), CAR_DOCK1);
+                ComponentName component2 = new ComponentName(getPackageName(), CAR_DOCK2);
+
+                if (pm.getComponentEnabledSetting(component1) ==
+                        PackageManager.COMPONENT_ENABLED_STATE_DISABLED) {
+                    swapCarDockHandler(component2, component1);
+                } else {
+                    swapCarDockHandler(component1, component2);
+                }
+            }
+        }
+    }
+
+    private void swapCarDockHandler(
+            ComponentName toBeDisabledComponent, ComponentName toBeEnabledComponent) {
+        PackageManager pm = getApplicationContext().getPackageManager();
+
+        pm.setComponentEnabledSetting(toBeDisabledComponent,
+                PackageManager.COMPONENT_ENABLED_STATE_DISABLED,
+                PackageManager.DONT_KILL_APP);
+
+        pm.setComponentEnabledSetting(toBeEnabledComponent,
+                PackageManager.COMPONENT_ENABLED_STATE_ENABLED,
+                PackageManager.DONT_KILL_APP);
+    }
+}
diff --git a/apps/CtsVerifier/src/com/android/cts/verifier/jobscheduler/ConnectivityConstraintTestActivity.java b/apps/CtsVerifier/src/com/android/cts/verifier/jobscheduler/ConnectivityConstraintTestActivity.java
old mode 100644
new mode 100755
index e97539d..8d10bda
--- a/apps/CtsVerifier/src/com/android/cts/verifier/jobscheduler/ConnectivityConstraintTestActivity.java
+++ b/apps/CtsVerifier/src/com/android/cts/verifier/jobscheduler/ConnectivityConstraintTestActivity.java
@@ -31,6 +31,8 @@
             ConnectivityConstraintTestActivity.class.hashCode() + 1;
     private static final int NO_CONNECTIVITY_JOB_ID =
             ConnectivityConstraintTestActivity.class.hashCode() + 2;
+    private static final int NO_CONNECTIVITY_JOB_ID_2 =
+            ConnectivityConstraintTestActivity.class.hashCode() + 3;
 
     @Override
     protected void onCreate(Bundle savedInstanceState) {
@@ -89,18 +91,25 @@
     }
 
     private void testNoConnectivityConstraintExecutes_noConnectivity() {
-        JobInfo testJob = new JobInfo.Builder(NO_CONNECTIVITY_JOB_ID, mMockComponent)
+        JobInfo testJob1 = new JobInfo.Builder(NO_CONNECTIVITY_JOB_ID, mMockComponent)
                 .setRequiredNetworkType(JobInfo.NETWORK_TYPE_NONE)
                 .setOverrideDeadline(100000L)  // Will not expire.
                 .build();
+        JobInfo testJob2 = new JobInfo.Builder(NO_CONNECTIVITY_JOB_ID_2, mMockComponent)
+        .setRequiredNetworkType(JobInfo.NETWORK_TYPE_NONE)
+        .setOverrideDeadline(100000L)  // Will not expire.
+        .build();
 
         mTestEnvironment.setUp();
-        mTestEnvironment.setExpectedExecutions(1);
+        mTestEnvironment.setExpectedExecutions(2);
 
-        mJobScheduler.schedule(testJob);
+        mJobScheduler.schedule(testJob1);
+        mJobScheduler.schedule(testJob2);
 
+        /*
         // Send intent to kick off ready jobs that the JobScheduler might be lazily holding on to.
         sendBroadcastAndBlockForResult(EXPEDITE_STABLE_CHARGING);
+        */
 
         boolean testPassed;
         try {
diff --git a/apps/CtsVerifier/src/com/android/cts/verifier/managedprovisioning/ByodFlowTestActivity.java b/apps/CtsVerifier/src/com/android/cts/verifier/managedprovisioning/ByodFlowTestActivity.java
index c1cc1f9..af3a7d5 100644
--- a/apps/CtsVerifier/src/com/android/cts/verifier/managedprovisioning/ByodFlowTestActivity.java
+++ b/apps/CtsVerifier/src/com/android/cts/verifier/managedprovisioning/ByodFlowTestActivity.java
@@ -80,6 +80,10 @@
     private TestItem mLocationSettingsVisibleTest;
     private TestItem mCredSettingsVisibleTest;
     private TestItem mPrintSettingsVisibleTest;
+    private TestItem mCrossProfileImageCaptureSupportTest;
+    private TestItem mCrossProfileVideoCaptureSupportTest;
+    private TestItem mCrossProfileAudioCaptureSupportTest;
+    private TestItem mDisableNfcBeamTest;
 
     private int mCurrentTestPosition;
 
@@ -241,6 +245,79 @@
         mTests.add(mCrossProfileIntentFiltersTest);
         mTests.add(mDisableNonMarketTest);
         mTests.add(mEnableNonMarketTest);
+
+        if (canResolveIntent(ByodHelperActivity.getCaptureImageIntent())) {
+            // Capture image intent can be resolved in primary profile, so test.
+            mCrossProfileImageCaptureSupportTest = new TestItem(this,
+                    R.string.provisioning_byod_capture_image_support,
+                    R.string.provisioning_byod_capture_image_support_info,
+                    new Intent(ByodHelperActivity.ACTION_CAPTURE_AND_CHECK_IMAGE));
+            mTests.add(mCrossProfileImageCaptureSupportTest);
+        } else {
+            // Capture image intent cannot be resolved in primary profile, so skip test.
+            Toast.makeText(ByodFlowTestActivity.this,
+                    R.string.provisioning_byod_no_image_capture_resolver, Toast.LENGTH_SHORT)
+                    .show();
+        }
+
+        if (canResolveIntent(ByodHelperActivity.getCaptureVideoIntent())) {
+            // Capture video intent can be resolved in primary profile, so test.
+            mCrossProfileVideoCaptureSupportTest = new TestItem(this,
+                    R.string.provisioning_byod_capture_video_support,
+                    R.string.provisioning_byod_capture_video_support_info,
+                    new Intent(ByodHelperActivity.ACTION_CAPTURE_AND_CHECK_VIDEO));
+            mTests.add(mCrossProfileVideoCaptureSupportTest);
+        } else {
+            // Capture video intent cannot be resolved in primary profile, so skip test.
+            Toast.makeText(ByodFlowTestActivity.this,
+                    R.string.provisioning_byod_no_video_capture_resolver, Toast.LENGTH_SHORT)
+                    .show();
+        }
+
+        if (getPackageManager().hasSystemFeature(PackageManager.FEATURE_NFC)) {
+            mDisableNfcBeamTest = new TestItem(this, R.string.provisioning_byod_nfc_beam,
+                    R.string.provisioning_byod_nfc_beam_allowed_instruction,
+                    new Intent(ByodHelperActivity.ACTION_TEST_NFC_BEAM)) {
+                @Override
+                public void performTest(final ByodFlowTestActivity activity) {
+                    activity.showManualTestDialog(mDisableNfcBeamTest,
+                            new DefaultTestCallback(mDisableNfcBeamTest) {
+                        @Override
+                        public void onPass() {
+                            // Start a second test with beam disallowed by policy.
+                            Intent testNfcBeamIntent = new Intent(
+                                    ByodHelperActivity.ACTION_TEST_NFC_BEAM);
+                            testNfcBeamIntent.putExtra(NfcTestActivity.EXTRA_DISALLOW_BY_POLICY,
+                                    true);
+                            TestItem disableNfcBeamTest2 = new TestItem(activity,
+                                    R.string.provisioning_byod_nfc_beam,
+                                    R.string.provisioning_byod_nfc_beam_disallowed_instruction,
+                                    testNfcBeamIntent);
+                            // The result should be reflected on the original test.
+                            activity.showManualTestDialog(disableNfcBeamTest2,
+                                    new DefaultTestCallback(mDisableNfcBeamTest));
+                        }
+                    });
+                }
+            };
+            mTests.add(mDisableNfcBeamTest);
+        }
+
+        /* TODO: reinstate when bug b/20131958 is fixed
+        if (canResolveIntent(ByodHelperActivity.getCaptureAudioIntent())) {
+            // Capture audio intent can be resolved in primary profile, so test.
+            mCrossProfileAudioCaptureSupportTest = new TestItem(this,
+                    R.string.provisioning_byod_capture_audio_support,
+                    R.string.provisioning_byod_capture_audio_support_info,
+                    new Intent(ByodHelperActivity.ACTION_CAPTURE_AND_CHECK_AUDIO));
+            mTests.add(mCrossProfileAudioCaptureSupportTest);
+        } else {
+            // Capture audio intent cannot be resolved in primary profile, so skip test.
+            Toast.makeText(ByodFlowTestActivity.this,
+                    R.string.provisioning_byod_no_audio_capture_resolver, Toast.LENGTH_SHORT)
+                    .show();
+        }
+        */
     }
 
     @Override
@@ -251,7 +328,36 @@
         test.performTest(this);
     }
 
+    // Return whether the intent can be resolved in the current profile
+    private boolean canResolveIntent(Intent intent) {
+        return intent.resolveActivity(getPackageManager()) != null;
+    }
+
+    private class DefaultTestCallback implements TestItem.TestCallback {
+        final private TestItem mTest;
+
+        public DefaultTestCallback(TestItem test) {
+            mTest = test;
+        }
+
+        @Override
+        public void onPass() {
+            clearRemainingState(mTest);
+            setTestResult(mTest, TestResult.Passed);
+        }
+
+        @Override
+        public void onFail() {
+            clearRemainingState(mTest);
+            setTestResult(mTest, TestResult.Failed);
+        }
+    }
+
     private void showManualTestDialog(final TestItem test) {
+        showManualTestDialog(test, new DefaultTestCallback(test));
+    }
+
+    private void showManualTestDialog(final TestItem test, final TestItem.TestCallback callback) {
         AlertDialog.Builder dialogBuilder = new AlertDialog.Builder(this)
                 .setIcon(android.R.drawable.ic_dialog_info)
                 .setTitle(R.string.provisioning_byod)
@@ -259,15 +365,13 @@
                 .setPositiveButton(R.string.pass_button_text, new AlertDialog.OnClickListener() {
                     @Override
                     public void onClick(DialogInterface dialog, int which) {
-                        clearRemainingState(test);
-                        setTestResult(test, TestResult.Passed);
+                        callback.onPass();
                     }
                 })
                 .setNegativeButton(R.string.fail_button_text, new AlertDialog.OnClickListener() {
                     @Override
                     public void onClick(DialogInterface dialog, int which) {
-                        clearRemainingState(test);
-                        setTestResult(test, TestResult.Failed);
+                        callback.onFail();
                     }
                 });
         View customView = test.getCustomView();
@@ -383,6 +487,11 @@
 
     static class TestItem {
 
+        public interface TestCallback {
+            void onPass();
+            void onFail();
+        }
+
         private String mDisplayName;
         private TestResult mPassed;
         private boolean mManualTest;
diff --git a/apps/CtsVerifier/src/com/android/cts/verifier/managedprovisioning/ByodHelperActivity.java b/apps/CtsVerifier/src/com/android/cts/verifier/managedprovisioning/ByodHelperActivity.java
index 277324c..800137a 100644
--- a/apps/CtsVerifier/src/com/android/cts/verifier/managedprovisioning/ByodHelperActivity.java
+++ b/apps/CtsVerifier/src/com/android/cts/verifier/managedprovisioning/ByodHelperActivity.java
@@ -18,6 +18,7 @@
 
 import android.app.Activity;
 import android.app.admin.DevicePolicyManager;
+import android.app.Dialog;
 import android.content.BroadcastReceiver;
 import android.content.ComponentName;
 import android.content.Context;
@@ -26,24 +27,31 @@
 import android.content.pm.PackageManager;
 import android.net.Uri;
 import android.os.Bundle;
+import android.provider.MediaStore;
 import android.provider.Settings;
+import android.support.v4.content.FileProvider;
 import android.util.Log;
 import android.widget.Toast;
 
 import static android.provider.Settings.Secure.INSTALL_NON_MARKET_APPS;
 
+import java.io.File;
+import java.util.ArrayList;
+
 import com.android.cts.verifier.R;
 import com.android.cts.verifier.managedprovisioning.ByodFlowTestActivity.TestResult;
+import com.android.cts.verifier.managedprovisioning.ByodPresentMediaDialog.DialogCallback;
 
 /**
  * A helper activity from the managed profile side that responds to requests from CTS verifier in
  * primary user. Profile owner APIs are accessible inside this activity (given this activity is
  * started within the work profile). Its current functionalities include making sure the profile
- * owner is setup correctly, and removing the work profile upon request.
+ * owner is setup correctly, removing the work profile upon request, and verifying the image and
+ * video capture functionality.
  *
  * Note: We have to use a dummy activity because cross-profile intents only work for activities.
  */
-public class ByodHelperActivity extends Activity {
+public class ByodHelperActivity extends Activity implements DialogCallback {
     static final String TAG = "ByodHelperActivity";
 
     // Primary -> managed intent: query if the profile owner has been set up.
@@ -54,6 +62,13 @@
     public static final String ACTION_REMOVE_PROFILE_OWNER = "com.android.cts.verifier.managedprovisioning.BYOD_REMOVE";
     // Managed -> managed intent: provisioning completed successfully
     public static final String ACTION_PROFILE_PROVISIONED = "com.android.cts.verifier.managedprovisioning.BYOD_PROVISIONED";
+    // Primage -> managed intent: request to capture and check an image
+    public static final String ACTION_CAPTURE_AND_CHECK_IMAGE = "com.android.cts.verifier.managedprovisioning.BYOD_CAPTURE_AND_CHECK_IMAGE";
+    // Primage -> managed intent: request to capture and check a video
+    public static final String ACTION_CAPTURE_AND_CHECK_VIDEO = "com.android.cts.verifier.managedprovisioning.BYOD_CAPTURE_AND_CHECK_VIDEO";
+    // Primage -> managed intent: request to capture and check an audio recording
+    public static final String ACTION_CAPTURE_AND_CHECK_AUDIO = "com.android.cts.verifier.managedprovisioning.BYOD_CAPTURE_AND_CHECK_AUDIO";
+    public static final String ACTION_TEST_NFC_BEAM = "com.android.cts.verifier.managedprovisioning.TEST_NFC_BEAM";
 
     public static final String EXTRA_PROVISIONED = "extra_provisioned";
 
@@ -62,6 +77,9 @@
     public static final String EXTRA_ALLOW_NON_MARKET_APPS = INSTALL_NON_MARKET_APPS;
 
     private static final int REQUEST_INSTALL_PACKAGE = 1;
+    private static final int REQUEST_IMAGE_CAPTURE = 2;
+    private static final int REQUEST_VIDEO_CAPTURE = 3;
+    private static final int REQUEST_AUDIO_CAPTURE = 4;
 
     private static final String ORIGINAL_SETTINGS_NAME = "original settings";
     private Bundle mOriginalSettings;
@@ -69,6 +87,11 @@
     private ComponentName mAdminReceiverComponent;
     private DevicePolicyManager mDevicePolicyManager;
 
+    private Uri mImageUri;
+    private Uri mVideoUri;
+
+    private ArrayList<File> mTempFiles = new ArrayList<File>();
+
     @Override
     protected void onCreate(Bundle savedInstanceState) {
         super.onCreate(savedInstanceState);
@@ -120,6 +143,46 @@
 
             // Not yet ready to finish- wait until the result comes back
             return;
+        } else if (action.equals(ACTION_CAPTURE_AND_CHECK_IMAGE)) {
+            Intent captureImageIntent = getCaptureImageIntent();
+            mImageUri = getTempUri("image.jpg");
+            captureImageIntent.putExtra(MediaStore.EXTRA_OUTPUT, mImageUri);
+            if (captureImageIntent.resolveActivity(getPackageManager()) != null) {
+                startActivityForResult(captureImageIntent, REQUEST_IMAGE_CAPTURE);
+            } else {
+                Log.e(TAG, "Capture image intent could not be resolved in managed profile.");
+                showToast(R.string.provisioning_byod_capture_media_error);
+                finish();
+            }
+            return;
+        } else if (action.equals(ACTION_CAPTURE_AND_CHECK_VIDEO)) {
+            Intent captureVideoIntent = getCaptureVideoIntent();
+            mVideoUri = getTempUri("video.mp4");
+            captureVideoIntent.putExtra(MediaStore.EXTRA_OUTPUT, mVideoUri);
+            if (captureVideoIntent.resolveActivity(getPackageManager()) != null) {
+                startActivityForResult(captureVideoIntent, REQUEST_VIDEO_CAPTURE);
+            } else {
+                Log.e(TAG, "Capture video intent could not be resolved in managed profile.");
+                showToast(R.string.provisioning_byod_capture_media_error);
+                finish();
+            }
+            return;
+        } else if (action.equals(ACTION_CAPTURE_AND_CHECK_AUDIO)) {
+            Intent captureAudioIntent = getCaptureAudioIntent();
+            if (captureAudioIntent.resolveActivity(getPackageManager()) != null) {
+                startActivityForResult(captureAudioIntent, REQUEST_AUDIO_CAPTURE);
+            } else {
+                Log.e(TAG, "Capture audio intent could not be resolved in managed profile.");
+                showToast(R.string.provisioning_byod_capture_media_error);
+                finish();
+            }
+            return;
+        } else if (action.equals(ACTION_TEST_NFC_BEAM)) {
+            Intent testNfcBeamIntent = new Intent(this, NfcTestActivity.class);
+            testNfcBeamIntent.putExtras(intent);
+            startActivity(testNfcBeamIntent);
+            finish();
+            return;
         }
         // This activity has no UI and is only used to respond to CtsVerifier in the primary side.
         finish();
@@ -145,6 +208,36 @@
                 finish();
                 break;
             }
+            case REQUEST_IMAGE_CAPTURE: {
+                if (resultCode == RESULT_OK) {
+                    ByodPresentMediaDialog.newImageInstance(mImageUri)
+                            .show(getFragmentManager(), "ViewImageDialogFragment");
+                } else {
+                    // Failed capturing image.
+                    finish();
+                }
+                break;
+            }
+            case REQUEST_VIDEO_CAPTURE: {
+                if (resultCode == RESULT_OK) {
+                    ByodPresentMediaDialog.newVideoInstance(mVideoUri)
+                            .show(getFragmentManager(), "PlayVideoDialogFragment");
+                } else {
+                    // Failed capturing video.
+                    finish();
+                }
+                break;
+            }
+            case REQUEST_AUDIO_CAPTURE: {
+                if (resultCode == RESULT_OK) {
+                    ByodPresentMediaDialog.newAudioInstance(data.getData())
+                            .show(getFragmentManager(), "PlayAudioDialogFragment");
+                } else {
+                    // Failed capturing audio.
+                    finish();
+                }
+                break;
+            }
             default: {
                 Log.wtf(TAG, "Unknown requestCode " + requestCode + "; data = " + data);
                 break;
@@ -152,6 +245,39 @@
         }
     }
 
+    @Override
+    protected void onDestroy() {
+        cleanUpTempUris();
+        super.onDestroy();
+    }
+
+    public static Intent getCaptureImageIntent() {
+        return new Intent(MediaStore.ACTION_IMAGE_CAPTURE);
+    }
+
+    public static Intent getCaptureVideoIntent() {
+        return new Intent(MediaStore.ACTION_VIDEO_CAPTURE);
+    }
+
+    public static Intent getCaptureAudioIntent() {
+        return new Intent(MediaStore.Audio.Media.RECORD_SOUND_ACTION);
+    }
+
+    private Uri getTempUri(String fileName) {
+        final File file = new File(getFilesDir() + File.separator + "images"
+                + File.separator + fileName);
+        file.getParentFile().mkdirs(); //if the folder doesn't exists it is created
+        mTempFiles.add(file);
+        return FileProvider.getUriForFile(this,
+                    "com.android.cts.verifier.managedprovisioning.fileprovider", file);
+    }
+
+    private void cleanUpTempUris() {
+        for (File file : mTempFiles) {
+            file.delete();
+        }
+    }
+
     private boolean isProfileOwner() {
         return mDevicePolicyManager.isAdminActive(mAdminReceiverComponent) &&
                 mDevicePolicyManager.isProfileOwnerApp(mAdminReceiverComponent.getPackageName());
@@ -181,4 +307,9 @@
         String message = getString(messageId);
         Toast.makeText(this, message, Toast.LENGTH_SHORT).show();
     }
+
+    @Override
+    public void onDialogClose() {
+        finish();
+    }
 }
diff --git a/apps/CtsVerifier/src/com/android/cts/verifier/managedprovisioning/ByodPresentMediaDialog.java b/apps/CtsVerifier/src/com/android/cts/verifier/managedprovisioning/ByodPresentMediaDialog.java
new file mode 100644
index 0000000..b3f126b
--- /dev/null
+++ b/apps/CtsVerifier/src/com/android/cts/verifier/managedprovisioning/ByodPresentMediaDialog.java
@@ -0,0 +1,167 @@
+/*
+ * 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.
+ */
+
+package com.android.cts.verifier.managedprovisioning;
+
+import android.app.Dialog;
+import android.app.DialogFragment;
+import android.content.DialogInterface;
+import android.graphics.Bitmap;
+import android.net.Uri;
+import android.media.MediaPlayer;
+import android.media.MediaPlayer.OnPreparedListener;
+import android.os.Bundle;
+import android.util.Log;
+import android.view.View;
+import android.view.View.OnClickListener;
+import android.widget.Button;
+import android.widget.ImageView;
+import android.widget.Toast;
+import android.widget.VideoView;
+
+import com.android.cts.verifier.R;
+
+import java.io.IOException;
+
+/**
+ * This dialog shows/plays an image, video or audio uri.
+ */
+public class ByodPresentMediaDialog extends DialogFragment {
+    static final String TAG = "ByodPresentMediaDialog";
+
+    private static final String KEY_VIDEO_URI = "video";
+    private static final String KEY_IMAGE_URI = "image";
+    private static final String KEY_AUDIO_URI = "audio";
+
+    /**
+     * Get a dialogFragment showing an image.
+     */
+    public static ByodPresentMediaDialog newImageInstance(Uri uri) {
+        ByodPresentMediaDialog dialog = new ByodPresentMediaDialog();
+        Bundle args = new Bundle();
+        args.putParcelable(KEY_IMAGE_URI, uri);
+        dialog.setArguments(args);
+        return dialog;
+    }
+
+    /**
+     * Get a dialogFragment playing a video.
+     */
+    public static ByodPresentMediaDialog newVideoInstance(Uri uri) {
+        ByodPresentMediaDialog dialog = new ByodPresentMediaDialog();
+        Bundle args = new Bundle();
+        args.putParcelable(KEY_VIDEO_URI, uri);
+        dialog.setArguments(args);
+        return dialog;
+    }
+
+    /**
+     * Get a dialogFragment playing audio.
+     */
+    public static ByodPresentMediaDialog newAudioInstance(Uri uri) {
+        ByodPresentMediaDialog dialog = new ByodPresentMediaDialog();
+        Bundle args = new Bundle();
+        args.putParcelable(KEY_AUDIO_URI, uri);
+        dialog.setArguments(args);
+        return dialog;
+    }
+
+    @Override
+    public Dialog onCreateDialog(Bundle savedInstanceState) {
+        final Dialog dialog = new Dialog(getActivity());
+        dialog.setContentView(R.layout.byod_present_media);
+
+        Button dismissButton = (Button) dialog.findViewById(R.id.dismissButton);
+        dismissButton.setOnClickListener(new View.OnClickListener() {
+            public void onClick(View v) {
+                dismiss();
+                ((DialogCallback) getActivity()).onDialogClose();
+            }
+        });
+
+        Bundle arguments = getArguments();
+
+        // Initially all video and image specific UI is invisible.
+        if (arguments.containsKey(KEY_VIDEO_URI)) {
+            // Show video UI.
+            dialog.setTitle(getString(R.string.provisioning_byod_verify_video_title));
+
+            Uri uri = (Uri) getArguments().getParcelable(KEY_VIDEO_URI);
+            final VideoView videoView = (VideoView) dialog.findViewById(R.id.videoView);
+            videoView.setVisibility(View.VISIBLE);
+            videoView.setVideoURI(uri);
+
+            Button playButton = (Button) dialog.findViewById(R.id.playButton);
+            playButton.setVisibility(View.VISIBLE);
+            playButton.setOnClickListener(new View.OnClickListener() {
+                public void onClick(View v) {
+                    videoView.start();
+                }
+            });
+        } else if (arguments.containsKey(KEY_IMAGE_URI)) {
+            // Show image UI.
+            dialog.setTitle(getString(R.string.provisioning_byod_verify_image_title));
+
+            Uri uri = (Uri) getArguments().getParcelable(KEY_IMAGE_URI);
+            ImageView imageView = (ImageView) dialog.findViewById(R.id.imageView);
+            imageView.setVisibility(View.VISIBLE);
+            imageView.setImageURI(uri);
+        } else if (arguments.containsKey(KEY_AUDIO_URI)) {
+            // Show audio playback UI.
+            dialog.setTitle(getString(R.string.provisioning_byod_verify_audio_title));
+
+            Uri uri = (Uri) getArguments().getParcelable(KEY_AUDIO_URI);
+            final MediaPlayer mediaPlayer = new MediaPlayer();
+            final Button playButton = (Button) dialog.findViewById(R.id.playButton);
+            playButton.setVisibility(View.VISIBLE);
+            playButton.setEnabled(false);
+
+            try {
+                mediaPlayer.setDataSource(getActivity(), uri);
+                mediaPlayer.prepare();
+            } catch (IllegalArgumentException|SecurityException|IllegalStateException
+                    |IOException e) {
+                Log.e(TAG, "Cannot play given audio with media player.", e);
+                Toast.makeText(getActivity(), R.string.provisioning_byod_capture_media_error,
+                        Toast.LENGTH_SHORT).show();
+                getActivity().finish();
+            }
+
+            mediaPlayer.setOnPreparedListener(new OnPreparedListener() {
+                @Override
+                public void onPrepared(MediaPlayer mp) {
+                    playButton.setEnabled(true);
+                    playButton.setOnClickListener(new View.OnClickListener() {
+                        public void onClick(View v) {
+                            mediaPlayer.start();
+                        }
+                    });
+                }
+            });
+        }
+
+        return dialog;
+    }
+
+    @Override
+    public void onCancel(DialogInterface dialog) {
+        ((DialogCallback) getActivity()).onDialogClose();
+    }
+
+    public interface DialogCallback {
+        public abstract void onDialogClose();
+    }
+}
diff --git a/apps/CtsVerifier/src/com/android/cts/verifier/managedprovisioning/DeviceAdminTestReceiver.java b/apps/CtsVerifier/src/com/android/cts/verifier/managedprovisioning/DeviceAdminTestReceiver.java
index fa7bc4c..15349ab 100644
--- a/apps/CtsVerifier/src/com/android/cts/verifier/managedprovisioning/DeviceAdminTestReceiver.java
+++ b/apps/CtsVerifier/src/com/android/cts/verifier/managedprovisioning/DeviceAdminTestReceiver.java
@@ -49,6 +49,10 @@
             filter.addAction(ByodHelperActivity.ACTION_QUERY_PROFILE_OWNER);
             filter.addAction(ByodHelperActivity.ACTION_REMOVE_PROFILE_OWNER);
             filter.addAction(ByodHelperActivity.ACTION_INSTALL_APK);
+            filter.addAction(ByodHelperActivity.ACTION_CAPTURE_AND_CHECK_IMAGE);
+            filter.addAction(ByodHelperActivity.ACTION_CAPTURE_AND_CHECK_VIDEO);
+            filter.addAction(ByodHelperActivity.ACTION_CAPTURE_AND_CHECK_AUDIO);
+            filter.addAction(ByodHelperActivity.ACTION_TEST_NFC_BEAM);
             filter.addAction(CrossProfileTestActivity.ACTION_CROSS_PROFILE);
             filter.addAction(WorkNotificationTestActivity.ACTION_WORK_NOTIFICATION);
             filter.addAction(WorkNotificationTestActivity.ACTION_CLEAR_WORK_NOTIFICATION);
diff --git a/apps/CtsVerifier/src/com/android/cts/verifier/managedprovisioning/NfcTestActivity.java b/apps/CtsVerifier/src/com/android/cts/verifier/managedprovisioning/NfcTestActivity.java
new file mode 100644
index 0000000..2f7619c
--- /dev/null
+++ b/apps/CtsVerifier/src/com/android/cts/verifier/managedprovisioning/NfcTestActivity.java
@@ -0,0 +1,158 @@
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.cts.verifier.managedprovisioning;
+
+import android.app.Activity;
+import android.app.admin.DevicePolicyManager;
+import android.content.ActivityNotFoundException;
+import android.content.ComponentName;
+import android.content.Context;
+import android.content.Intent;
+import android.graphics.Bitmap;
+import android.graphics.Canvas;
+import android.graphics.Color;
+import android.graphics.Paint;
+import android.graphics.Rect;
+import android.net.Uri;
+import android.nfc.NfcAdapter;
+import android.os.Bundle;
+import android.os.UserManager;
+import android.support.v4.content.FileProvider;
+import android.util.Log;
+import android.view.View;
+import android.view.View.OnClickListener;
+import android.widget.Toast;
+
+import com.android.cts.verifier.R;
+
+import java.io.File;
+import java.io.FileNotFoundException;
+import java.io.FileOutputStream;
+
+public class NfcTestActivity extends Activity {
+    private static final String TAG = "NfcTestActivity";
+
+    /* package */ static final String EXTRA_DISALLOW_BY_POLICY = "disallowByPolicy";
+
+    private static final String NFC_BEAM_PACKAGE = "com.android.nfc";
+    private static final String NFC_BEAM_ACTIVITY = "com.android.nfc.BeamShareActivity";
+    private static final String SAMPLE_IMAGE_FILENAME = "image_to_share.jpg";
+    private static final String SAMPLE_IMAGE_CONTENT = "sample image";
+    private static final int MARGIN = 80;
+    private static final int TEXT_SIZE = 200;
+
+    private ComponentName mAdminReceiverComponent;
+    private DevicePolicyManager mDevicePolicyManager;
+    private UserManager mUserMangaer;
+    private NfcAdapter mNfcAdapter;
+    private boolean mDisallowByPolicy;
+
+    @Override
+    protected void onCreate(Bundle savedInstanceState) {
+        super.onCreate(savedInstanceState);
+        setContentView(R.layout.byod_nfc_test_activity);
+
+        mAdminReceiverComponent = new ComponentName(this, DeviceAdminTestReceiver.class.getName());
+        mDevicePolicyManager = (DevicePolicyManager) getSystemService(
+                Context.DEVICE_POLICY_SERVICE);
+        mUserMangaer = (UserManager) getSystemService(Context.USER_SERVICE);
+        mDisallowByPolicy = getIntent().getBooleanExtra(EXTRA_DISALLOW_BY_POLICY, false);
+        if (mDisallowByPolicy) {
+            mDevicePolicyManager.addUserRestriction(mAdminReceiverComponent,
+                    UserManager.DISALLOW_OUTGOING_BEAM);
+        }
+
+        final Uri uri = createUriForImage(SAMPLE_IMAGE_FILENAME, SAMPLE_IMAGE_CONTENT);
+        Uri[] uris = new Uri[] { uri };
+
+        mNfcAdapter = NfcAdapter.getDefaultAdapter(this);
+        mNfcAdapter.setBeamPushUris(uris, this);
+
+        findViewById(R.id.manual_beam_button).setOnClickListener(new OnClickListener() {
+            @Override
+            public void onClick(View view) {
+                mNfcAdapter.invokeBeam(NfcTestActivity.this);
+            }
+        });
+        findViewById(R.id.intent_share_button).setOnClickListener(new OnClickListener() {
+            @Override
+            public void onClick(View view) {
+                Intent shareIntent = new Intent(Intent.ACTION_SEND);
+                shareIntent.putExtra(Intent.EXTRA_STREAM, uri);
+                shareIntent.setType("image/jpg");
+                shareIntent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION);
+                // Specify the package name of NfcBeamActivity so that the tester don't need to
+                // select the activity manually.
+                shareIntent.setClassName(NFC_BEAM_PACKAGE, NFC_BEAM_ACTIVITY);
+                try {
+                    startActivity(shareIntent);
+                } catch (ActivityNotFoundException e) {
+                    Toast.makeText(NfcTestActivity.this,
+                            R.string.provisioning_byod_cannot_resolve_beam_activity,
+                            Toast.LENGTH_SHORT).show();
+                    Log.e(TAG, "Nfc beam activity not found", e);
+                }
+            }
+        });
+    }
+
+    @Override
+    public void finish() {
+        if (mUserMangaer.hasUserRestriction(UserManager.DISALLOW_OUTGOING_BEAM)) {
+            mDevicePolicyManager.clearUserRestriction(mAdminReceiverComponent,
+                    UserManager.DISALLOW_OUTGOING_BEAM);
+        }
+        super.finish();
+    }
+
+    /**
+     * Creates a Bitmap image that contains red on white text with a specified margin.
+     * @param text Text to be displayed in the image.
+     * @return A Bitmap image with the above specification.
+     */
+    private Bitmap createSampleImage(String text) {
+        Paint paint = new Paint();
+        paint.setStyle(Paint.Style.FILL);
+        paint.setTextSize(TEXT_SIZE);
+        Rect rect = new Rect();
+        paint.getTextBounds(text, 0, text.length(), rect);
+        int w = 2 * MARGIN + rect.right - rect.left;
+        int h = 2 * MARGIN + rect.bottom - rect.top;
+        Bitmap dest = Bitmap.createBitmap(w, h, Bitmap.Config.ARGB_8888);
+        Canvas canvas = new Canvas();
+        canvas.setBitmap(dest);
+        paint.setColor(Color.WHITE);
+        canvas.drawPaint(paint);
+        paint.setColor(Color.RED);
+        canvas.drawText(text, MARGIN - rect.left, MARGIN - rect.top, paint);
+        return dest;
+    }
+
+    private Uri createUriForImage(String name, String text) {
+        final File file = new File(getFilesDir() + File.separator + "images"
+                + File.separator + name);
+        file.getParentFile().mkdirs(); //if the folder doesn't exists it is created
+        try {
+            createSampleImage(text).compress(Bitmap.CompressFormat.JPEG, 100,
+                    new FileOutputStream(file));
+        } catch (FileNotFoundException e) {
+            return null;
+        }
+        return FileProvider.getUriForFile(this,
+                "com.android.cts.verifier.managedprovisioning.fileprovider", file);
+    }
+}
diff --git a/apps/CtsVerifier/src/com/android/cts/verifier/notifications/InteractiveVerifierActivity.java b/apps/CtsVerifier/src/com/android/cts/verifier/notifications/InteractiveVerifierActivity.java
index b658e4f..07d9cc9 100644
--- a/apps/CtsVerifier/src/com/android/cts/verifier/notifications/InteractiveVerifierActivity.java
+++ b/apps/CtsVerifier/src/com/android/cts/verifier/notifications/InteractiveVerifierActivity.java
@@ -172,7 +172,8 @@
     @Override
     protected void onResume() {
         super.onResume();
-        if (mCurrentTest.autoStart()) {
+        //To avoid NPE during onResume,before start to iterate next test order
+        if (mCurrentTest!= null && mCurrentTest.autoStart()) {
             mCurrentTest.status = READY;
         }
         next();
diff --git a/apps/CtsVerifier/src/com/android/cts/verifier/notifications/MockListener.java b/apps/CtsVerifier/src/com/android/cts/verifier/notifications/MockListener.java
index 7207083..c80f371 100644
--- a/apps/CtsVerifier/src/com/android/cts/verifier/notifications/MockListener.java
+++ b/apps/CtsVerifier/src/com/android/cts/verifier/notifications/MockListener.java
@@ -241,7 +241,6 @@
         mNotifications.remove(sbn.getKey());
         mNotificationKeys.remove(sbn.getTag());
         onNotificationRankingUpdate(rankingMap);
-        mNotificationKeys.remove(sbn.getTag());
     }
 
     public static void resetListenerData(Context context) {
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 a6affb3..5870981 100644
--- a/apps/CtsVerifier/src/com/android/cts/verifier/notifications/PackagePriorityVerifierActivity.java
+++ b/apps/CtsVerifier/src/com/android/cts/verifier/notifications/PackagePriorityVerifierActivity.java
@@ -18,6 +18,7 @@
 
 import android.app.Notification;
 import android.content.Intent;
+import android.content.pm.PackageManager;
 import android.util.Log;
 import android.view.View;
 import android.view.ViewGroup;
@@ -57,6 +58,7 @@
     protected List<InteractiveTestCase> createTestItems() {
         mAppLabel = getString(R.string.app_name);
         List<InteractiveTestCase> tests = new ArrayList<>(17);
+        tests.add(new CheckForBot());
         tests.add(new IsEnabledTest());
         tests.add(new ServiceStartedTest());
         tests.add(new WaitForSetPriorityDefault());
@@ -68,6 +70,27 @@
 
     // Tests
 
+    /** Make sure the helper package is installed. */
+    protected class CheckForBot extends InteractiveTestCase {
+        @Override
+        View inflate(ViewGroup parent) {
+            return createAutoItem(parent, R.string.package_priority_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();
+        }
+    }
+
     /** Wait for the user to set the target package priority to default. */
     protected class WaitForSetPriorityDefault extends InteractiveTestCase {
         @Override
@@ -233,6 +256,7 @@
         postIntent.setPackage(NOTIFICATION_BOT_PACKAGE);
         postIntent.putExtra(EXTRA_ID, 0);
         postIntent.putExtra(EXTRA_NOTIFICATION, bob.build());
+        postIntent.addFlags(Intent.FLAG_INCLUDE_STOPPED_PACKAGES);
         sendBroadcast(postIntent);
     }
 
@@ -243,6 +267,7 @@
         Intent cancelIntent = new Intent(ACTION_CANCEL);
         cancelIntent.setPackage(NOTIFICATION_BOT_PACKAGE);
         cancelIntent.putExtra(EXTRA_ID, 0);
+        cancelIntent.addFlags(Intent.FLAG_INCLUDE_STOPPED_PACKAGES);
         sendBroadcast(cancelIntent);
     }
 
diff --git a/apps/CtsVerifier/src/com/android/cts/verifier/projection/widgets/ProjectionWidgetActivity.java b/apps/CtsVerifier/src/com/android/cts/verifier/projection/widgets/ProjectionWidgetActivity.java
index 9b862de..9f8cb51 100644
--- a/apps/CtsVerifier/src/com/android/cts/verifier/projection/widgets/ProjectionWidgetActivity.java
+++ b/apps/CtsVerifier/src/com/android/cts/verifier/projection/widgets/ProjectionWidgetActivity.java
@@ -67,13 +67,5 @@
             button = (Button) view.findViewById(R.id.down_button);
             button.setOnClickListener(new InjectDPadClickListener(KeyEvent.KEYCODE_DPAD_DOWN));
         }
-        {
-            button = (Button) view.findViewById(R.id.right_button);
-            button.setOnClickListener(new InjectDPadClickListener(KeyEvent.KEYCODE_DPAD_RIGHT));
-        }
-        {
-            button = (Button) view.findViewById(R.id.left_button);
-            button.setOnClickListener(new InjectDPadClickListener(KeyEvent.KEYCODE_DPAD_LEFT));
-        }
     }
 }
diff --git a/apps/CtsVerifier/src/com/android/cts/verifier/screenpinning/ScreenPinningTestActivity.java b/apps/CtsVerifier/src/com/android/cts/verifier/screenpinning/ScreenPinningTestActivity.java
index 200865e..8e72ebb 100644
--- a/apps/CtsVerifier/src/com/android/cts/verifier/screenpinning/ScreenPinningTestActivity.java
+++ b/apps/CtsVerifier/src/com/android/cts/verifier/screenpinning/ScreenPinningTestActivity.java
@@ -104,6 +104,7 @@
         TextView tv = new TextView(this);
         tv.setPadding(10, 10, 10, 30);
         tv.setText(id);
+        mInstructions.removeAllViews();
         mInstructions.addView(tv);
     }
 
diff --git a/apps/CtsVerifier/src/com/android/cts/verifier/sensors/AccelerometerMeasurementTestActivity.java b/apps/CtsVerifier/src/com/android/cts/verifier/sensors/AccelerometerMeasurementTestActivity.java
index dfcf120..52b3dee 100644
--- a/apps/CtsVerifier/src/com/android/cts/verifier/sensors/AccelerometerMeasurementTestActivity.java
+++ b/apps/CtsVerifier/src/com/android/cts/verifier/sensors/AccelerometerMeasurementTestActivity.java
@@ -97,11 +97,11 @@
                 Sensor.TYPE_ACCELEROMETER,
                 SensorManager.SENSOR_DELAY_FASTEST);
         TestSensorOperation verifyMeasurements =
-                new TestSensorOperation(environment, 100 /* event count */);
+                TestSensorOperation.createOperation(environment, 100 /* event count */);
         verifyMeasurements.addVerification(new MeanVerification(
                 expectations,
                 new float[]{1.95f, 1.95f, 1.95f} /* m / s^2 */));
-        verifyMeasurements.execute();
+        verifyMeasurements.execute(getCurrentTestNode());
         return null;
     }
 
diff --git a/apps/CtsVerifier/src/com/android/cts/verifier/sensors/BatchingTestActivity.java b/apps/CtsVerifier/src/com/android/cts/verifier/sensors/BatchingTestActivity.java
index 6f0a7aa..7ef63d7 100644
--- a/apps/CtsVerifier/src/com/android/cts/verifier/sensors/BatchingTestActivity.java
+++ b/apps/CtsVerifier/src/com/android/cts/verifier/sensors/BatchingTestActivity.java
@@ -22,9 +22,7 @@
 import android.hardware.Sensor;
 import android.hardware.SensorManager;
 import android.hardware.cts.helpers.TestSensorEnvironment;
-import android.hardware.cts.helpers.sensoroperations.TestSensorFlushOperation;
 import android.hardware.cts.helpers.sensoroperations.TestSensorOperation;
-import android.hardware.cts.helpers.sensoroperations.VerifiableSensorOperation;
 
 import java.util.concurrent.TimeUnit;
 
@@ -128,7 +126,7 @@
 
         int testDurationSec = maxBatchReportLatencySec + BATCHING_PADDING_TIME_S;
         TestSensorOperation operation =
-                new TestSensorOperation(environment, testDurationSec,TimeUnit.SECONDS);
+                TestSensorOperation.createOperation(environment, testDurationSec,TimeUnit.SECONDS);
         return executeTest(operation);
     }
 
@@ -145,15 +143,14 @@
                 maxBatchReportLatencyUs);
 
         int flushDurationSec = maxBatchReportLatencySec / 2;
-        TestSensorFlushOperation operation =
-                new TestSensorFlushOperation(environment, flushDurationSec, TimeUnit.SECONDS);
+        TestSensorOperation operation = TestSensorOperation
+                .createFlushOperation(environment, flushDurationSec, TimeUnit.SECONDS);
         return executeTest(operation);
     }
 
-    private String executeTest(VerifiableSensorOperation operation) throws InterruptedException {
+    private String executeTest(TestSensorOperation operation) throws InterruptedException {
         operation.addDefaultVerifications();
-        operation.setLogEvents(true);
-        operation.execute();
+        operation.execute(getCurrentTestNode());
         return null;
     }
 }
diff --git a/apps/CtsVerifier/src/com/android/cts/verifier/sensors/GyroscopeMeasurementTestActivity.java b/apps/CtsVerifier/src/com/android/cts/verifier/sensors/GyroscopeMeasurementTestActivity.java
index 4b2a7f4..7be0fb1 100644
--- a/apps/CtsVerifier/src/com/android/cts/verifier/sensors/GyroscopeMeasurementTestActivity.java
+++ b/apps/CtsVerifier/src/com/android/cts/verifier/sensors/GyroscopeMeasurementTestActivity.java
@@ -156,8 +156,8 @@
                 getApplicationContext(),
                 Sensor.TYPE_GYROSCOPE,
                 SensorManager.SENSOR_DELAY_FASTEST);
-        TestSensorOperation sensorOperation =
-                new TestSensorOperation(environment, ROTATION_COLLECTION_SEC, TimeUnit.SECONDS);
+        TestSensorOperation sensorOperation = TestSensorOperation
+                .createOperation(environment, ROTATION_COLLECTION_SEC, TimeUnit.SECONDS);
 
         int gyroscopeAxes = environment.getSensorAxesCount();
         int[] expectationsDeg = getExpectationsDeg(gyroscopeAxes, rotationAxis, expectationDeg);
@@ -167,7 +167,7 @@
         sensorOperation.addVerification(integrationVerification);
 
         try {
-            sensorOperation.execute();
+            sensorOperation.execute(getCurrentTestNode());
         } finally {
             playSound();
         }
diff --git a/apps/CtsVerifier/src/com/android/cts/verifier/sensors/MagneticFieldMeasurementTestActivity.java b/apps/CtsVerifier/src/com/android/cts/verifier/sensors/MagneticFieldMeasurementTestActivity.java
index 553147b..229a9dc 100644
--- a/apps/CtsVerifier/src/com/android/cts/verifier/sensors/MagneticFieldMeasurementTestActivity.java
+++ b/apps/CtsVerifier/src/com/android/cts/verifier/sensors/MagneticFieldMeasurementTestActivity.java
@@ -21,7 +21,6 @@
 
 import android.hardware.Sensor;
 import android.hardware.SensorEvent;
-import android.hardware.SensorEventListener2;
 import android.hardware.SensorManager;
 import android.hardware.cts.helpers.SensorCalibratedUncalibratedVerifier;
 import android.hardware.cts.helpers.SensorCtsHelper;
@@ -79,7 +78,7 @@
                 Sensor.TYPE_MAGNETIC_FIELD,
                 SensorManager.SENSOR_DELAY_FASTEST);
         TestSensorOperation verifyNorm =
-                new TestSensorOperation(environment, 100 /* event count */);
+                TestSensorOperation.createOperation(environment, 100 /* event count */);
 
         float expectedMagneticFieldEarth =
                 (SensorManager.MAGNETIC_FIELD_EARTH_MAX + SensorManager.MAGNETIC_FIELD_EARTH_MIN) / 2;
@@ -88,7 +87,7 @@
         verifyNorm.addVerification(new MagnitudeVerification(
                 expectedMagneticFieldEarth,
                 magneticFieldEarthThreshold));
-        verifyNorm.execute();
+        verifyNorm.execute(getCurrentTestNode());
         return null;
     }
 
@@ -124,11 +123,11 @@
                 Sensor.TYPE_MAGNETIC_FIELD,
                 SensorManager.SENSOR_DELAY_FASTEST);
         TestSensorOperation verifyStdDev =
-                new TestSensorOperation(environment, 100 /* event count */);
+                TestSensorOperation.createOperation(environment, 100 /* event count */);
 
         verifyStdDev.addVerification(new StandardDeviationVerification(
                 new float[]{2f, 2f, 2f} /* uT */));
-        verifyStdDev.execute();
+        verifyStdDev.execute(getCurrentTestNode());
         return null;
     }
 
@@ -166,7 +165,12 @@
      * A routine to help operators calibrate the magnetometer.
      */
     private void calibrateMagnetometer() throws InterruptedException {
-        SensorEventListener2 listener = new SensorEventListener2() {
+        TestSensorEnvironment environment = new TestSensorEnvironment(
+                getApplicationContext(),
+                Sensor.TYPE_MAGNETIC_FIELD,
+                SensorManager.SENSOR_DELAY_NORMAL);
+
+        TestSensorEventListener listener = new TestSensorEventListener(environment) {
             @Override
             public void onSensorChanged(SensorEvent event) {
                 clearText();
@@ -184,21 +188,11 @@
                 // TODO: automate finding out when the magnetometer is calibrated
                 logger.logInstructions(R.string.snsr_mag_calibration_complete);
             }
-
-            @Override
-            public void onAccuracyChanged(Sensor sensor, int accuracy) {}
-
-            @Override
-            public void onFlushCompleted(Sensor sensor) {}
         };
 
-        TestSensorEnvironment environment = new TestSensorEnvironment(
-                getApplicationContext(),
-                Sensor.TYPE_MAGNETIC_FIELD,
-                SensorManager.SENSOR_DELAY_NORMAL);
         TestSensorManager magnetometer = new TestSensorManager(environment);
         try {
-            magnetometer.registerListener(new TestSensorEventListener(listener));
+            magnetometer.registerListener(listener);
             waitForUserToContinue();
         } finally {
             magnetometer.unregisterListener();
diff --git a/apps/CtsVerifier/src/com/android/cts/verifier/sensors/base/SensorCtsTestResult.java b/apps/CtsVerifier/src/com/android/cts/verifier/sensors/base/SensorCtsTestResult.java
index 5bbaaf7..380b282 100644
--- a/apps/CtsVerifier/src/com/android/cts/verifier/sensors/base/SensorCtsTestResult.java
+++ b/apps/CtsVerifier/src/com/android/cts/verifier/sensors/base/SensorCtsTestResult.java
@@ -26,6 +26,8 @@
 
 import android.content.Context;
 import android.hardware.cts.SensorTestCase;
+import android.hardware.cts.helpers.SensorTestPlatformException;
+import android.hardware.cts.helpers.reporting.ISensorTestNode;
 
 import java.util.Enumeration;
 
@@ -138,10 +140,24 @@
             SensorTestCase sensorTestCase = (SensorTestCase) testCase;
             sensorTestCase.setContext(mContext);
             sensorTestCase.setEmulateSensorUnderLoad(false);
+            sensorTestCase.setCurrentTestNode(new TestNode(testCase));
             // TODO: set delayed assertion provider
         } else {
             throw new IllegalStateException("TestCase must be an instance of SensorTestCase.");
         }
         super.run(testCase);
     }
+
+    private class TestNode implements ISensorTestNode {
+        private final TestCase mTestCase;
+
+        public TestNode(TestCase testCase) {
+            mTestCase = testCase;
+        }
+
+        @Override
+        public String getName() throws SensorTestPlatformException {
+            return mTestCase.getClass().getSimpleName() + "_" + mTestCase.getName();
+        }
+    }
 }
diff --git a/apps/CtsVerifier/src/com/android/cts/verifier/sensors/base/SensorCtsVerifierTestActivity.java b/apps/CtsVerifier/src/com/android/cts/verifier/sensors/base/SensorCtsVerifierTestActivity.java
index a88abd0..8cf287a 100644
--- a/apps/CtsVerifier/src/com/android/cts/verifier/sensors/base/SensorCtsVerifierTestActivity.java
+++ b/apps/CtsVerifier/src/com/android/cts/verifier/sensors/base/SensorCtsVerifierTestActivity.java
@@ -19,6 +19,8 @@
 
 import com.android.cts.verifier.sensors.reporting.SensorTestDetails;
 
+import android.hardware.cts.helpers.reporting.ISensorTestNode;
+
 import java.lang.reflect.InvocationTargetException;
 import java.lang.reflect.Method;
 import java.lang.reflect.Modifier;
@@ -35,6 +37,7 @@
     private volatile int mTestPassedCounter;
     private volatile int mTestSkippedCounter;
     private volatile int mTestFailedCounter;
+    private volatile ISensorTestNode mCurrentTestNode;
 
     /**
      * {@inheritDoc}
@@ -63,8 +66,12 @@
                 mTestFailedCounter);
     }
 
+    protected ISensorTestNode getCurrentTestNode() {
+        return mCurrentTestNode;
+    }
+
     private List<Method> findTestMethods() {
-        ArrayList<Method> testMethods = new ArrayList<Method>();
+        ArrayList<Method> testMethods = new ArrayList<>();
         for (Method method : mTestClass.getDeclaredMethods()) {
             if (Modifier.isPublic(method.getModifiers())
                     && method.getParameterTypes().length == 0
@@ -79,6 +86,7 @@
     private SensorTestDetails executeTest(Method testMethod) throws InterruptedException {
         String testMethodName = testMethod.getName();
         String testName = String.format("%s#%s", getTestClassName(), testMethodName);
+        mCurrentTestNode = new TestNode(testMethod);
 
         SensorTestDetails testDetails;
         try {
@@ -112,4 +120,17 @@
 
         return testDetails;
     }
+
+    private class TestNode implements ISensorTestNode {
+        private final Method mTestMethod;
+
+        public TestNode(Method testMethod) {
+            mTestMethod = testMethod;
+        }
+
+        @Override
+        public String getName() {
+            return mTestClass.getSimpleName() + "_" + mTestMethod.getName();
+        }
+    }
 }
diff --git a/apps/CtsVerifier/src/com/android/cts/verifier/tv/MockTvInputService.java b/apps/CtsVerifier/src/com/android/cts/verifier/tv/MockTvInputService.java
index f4460de..e7d1d79 100644
--- a/apps/CtsVerifier/src/com/android/cts/verifier/tv/MockTvInputService.java
+++ b/apps/CtsVerifier/src/com/android/cts/verifier/tv/MockTvInputService.java
@@ -33,15 +33,11 @@
 import android.media.tv.TvTrackInfo;
 import android.net.Uri;
 import android.os.Bundle;
-import android.os.Handler;
-import android.os.Looper;
 import android.view.Surface;
 import android.view.LayoutInflater;
 import android.view.View;
 import android.widget.TextView;
 
-import com.android.cts.verifier.R;
-
 import java.util.ArrayList;
 import java.util.List;
 
@@ -78,9 +74,9 @@
             new TvTrackInfo.Builder(TvTrackInfo.TYPE_SUBTITLE, "subtitle_eng")
             .setLanguage("eng")
             .build();
-    static final TvTrackInfo sSpaSubtitleTrack =
-            new TvTrackInfo.Builder(TvTrackInfo.TYPE_SUBTITLE, "subtitle_spa")
-            .setLanguage("spa")
+    static final TvTrackInfo sKorSubtitleTrack =
+            new TvTrackInfo.Builder(TvTrackInfo.TYPE_SUBTITLE, "subtitle_kor")
+            .setLanguage("kor")
             .build();
 
     private final BroadcastReceiver mBroadcastReceiver = new BroadcastReceiver() {
@@ -179,7 +175,7 @@
             mTracks.add(sEngAudioTrack);
             mTracks.add(sSpaAudioTrack);
             mTracks.add(sEngSubtitleTrack);
-            mTracks.add(sSpaSubtitleTrack);
+            mTracks.add(sKorSubtitleTrack);
         }
 
         @Override
diff --git a/apps/CtsVerifier/src/com/android/cts/verifier/tv/MockTvInputSetupActivity.java b/apps/CtsVerifier/src/com/android/cts/verifier/tv/MockTvInputSetupActivity.java
index 81a8edc..c05b753 100644
--- a/apps/CtsVerifier/src/com/android/cts/verifier/tv/MockTvInputSetupActivity.java
+++ b/apps/CtsVerifier/src/com/android/cts/verifier/tv/MockTvInputSetupActivity.java
@@ -21,17 +21,25 @@
 import android.content.ContentValues;
 import android.database.Cursor;
 import android.media.tv.TvContract;
+import android.media.tv.TvContract.Programs;
 import android.media.tv.TvInputInfo;
 import android.net.Uri;
 import android.os.Bundle;
 import android.util.Pair;
 import android.view.View;
 
+import java.util.ArrayList;
+
 public class MockTvInputSetupActivity extends Activity {
     private static final String TAG = "MockTvInputSetupActivity";
 
-    private static final String CHANNEL_NUMBER = "999-0";
-    private static final String CHANNEL_NAME = "Dummy";
+    /* package-private */ static final String CHANNEL_NUMBER = "999-0";
+    /* package-private */ static final String CHANNEL_NAME = "Dummy";
+
+    /* package-private */ static final String PROGRAM_TITLE = "Dummy Program";
+    /* package-private */ static final String PROGRAM_DESCRIPTION = "Dummy Program Description";
+    private static final long PROGRAM_LENGTH_MILLIS = 60 * 60 * 1000;
+    private static final int PROGRAM_COUNT = 24;
 
     private static Object sLock = new Object();
     private static Pair<View, Runnable> sLaunchCallback = null;
@@ -55,6 +63,8 @@
                     return;
                 }
             }
+
+            // Add a channel.
             ContentValues values = new ContentValues();
             values.put(TvContract.Channels.COLUMN_INPUT_ID, inputId);
             values.put(TvContract.Channels.COLUMN_DISPLAY_NUMBER, CHANNEL_NUMBER);
@@ -62,9 +72,27 @@
             Uri channelUri = getContentResolver().insert(uri, values);
             // If the channel's ID happens to be zero, we add another and delete the one.
             if (ContentUris.parseId(channelUri) == 0) {
-                getContentResolver().insert(uri, values);
                 getContentResolver().delete(channelUri, null, null);
+                channelUri = getContentResolver().insert(uri, values);
             }
+
+            // Add Programs.
+            values = new ContentValues();
+            values.put(Programs.COLUMN_CHANNEL_ID, ContentUris.parseId(channelUri));
+            values.put(Programs.COLUMN_TITLE, PROGRAM_TITLE);
+            values.put(Programs.COLUMN_SHORT_DESCRIPTION, PROGRAM_DESCRIPTION);
+            long nowMs = System.currentTimeMillis();
+            long startTimeMs = nowMs - nowMs % PROGRAM_LENGTH_MILLIS;
+            ArrayList<ContentValues> list = new ArrayList<>();
+            for (int i = 0; i < PROGRAM_COUNT; ++i) {
+                values.put(Programs.COLUMN_START_TIME_UTC_MILLIS, startTimeMs);
+                values.put(Programs.COLUMN_END_TIME_UTC_MILLIS,
+                        startTimeMs + PROGRAM_LENGTH_MILLIS);
+                startTimeMs += PROGRAM_LENGTH_MILLIS;
+                list.add(new ContentValues(values));
+            }
+            getContentResolver().bulkInsert(Programs.CONTENT_URI, list.toArray(
+                    new ContentValues[0]));
         } finally {
             Pair<View, Runnable> launchCallback = null;
             synchronized (sLock) {
diff --git a/apps/CtsVerifier/src/com/android/cts/verifier/tv/SearchUtil.java b/apps/CtsVerifier/src/com/android/cts/verifier/tv/SearchUtil.java
new file mode 100644
index 0000000..4513123
--- /dev/null
+++ b/apps/CtsVerifier/src/com/android/cts/verifier/tv/SearchUtil.java
@@ -0,0 +1,136 @@
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.cts.verifier.tv;
+
+import android.app.SearchManager;
+import android.app.SearchableInfo;
+import android.content.ContentProviderClient;
+import android.content.ContentResolver;
+import android.content.Context;
+import android.content.Intent;
+import android.content.pm.PackageManager;
+import android.content.pm.ResolveInfo;
+import android.database.Cursor;
+import android.database.sqlite.SQLiteException;
+import android.media.tv.TvContract;
+import android.net.Uri;
+import android.os.RemoteException;
+import android.text.TextUtils;
+
+import java.util.ArrayList;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Set;
+
+/**
+ * A utility class for verifying channel/program results on global search requests.
+ */
+public class SearchUtil {
+    private SearchUtil() {}
+
+    /**
+     * Returns {@code true} if one of the search results matches the given {@code expectedResult}.
+     *
+     * @param context The context object to be used for getting content resolver
+     * @param searchable The {@link android.app.SearchableInfo} the TV app implements
+     * @param query A query string to search for
+     * @param expectedResult The expected search result
+     */
+    public static boolean verifySearchResult(Context context, SearchableInfo searchable,
+            String query, String expectedResult) {
+        Uri.Builder uriBuilder = getSearchUri(searchable).buildUpon();
+        String selection = searchable.getSuggestSelection();
+        String[] selectionArg = null;
+        if (selection != null) {
+            selectionArg = new String[] { query };
+        } else {
+            uriBuilder.appendPath(query);
+        }
+
+        Uri uri = uriBuilder.build();
+        ContentProviderClient provider = context.getContentResolver()
+                .acquireUnstableContentProviderClient(uri);
+        try (Cursor c = provider.query(uri, null, selection, selectionArg, null, null)) {
+            while (c.moveToNext()) {
+                int index = c.getColumnIndex(SearchManager.SUGGEST_COLUMN_TEXT_1);
+                if (index >= 0) {
+                    if (TextUtils.equals(expectedResult, c.getString(index))) {
+                        return true;
+                    }
+                }
+            }
+        } catch (SQLiteException | RemoteException e) {
+            return false;
+        } finally {
+            provider.release();
+        }
+        return false;
+    }
+
+    /**
+     * Returns the {@link android.app.SearchableInfo} instances which should provide search results
+     * for channels and programs in TvProvider.
+     *
+     * @param context The context object to used for accessing system services
+     */
+    public static List<SearchableInfo> getSearchableInfos(Context context) {
+        // Just in case EPG is provided by a separate package, collect all possible TV packages
+        // that can be searchable.
+        PackageManager pm = context.getPackageManager();
+        Set<String> tvPackages = new HashSet<>();
+        List<ResolveInfo> infos = pm.queryIntentActivities(new Intent(Intent.ACTION_VIEW,
+                TvContract.Channels.CONTENT_URI), 0);
+        for (ResolveInfo info : infos) {
+            tvPackages.add(info.activityInfo.packageName);
+        }
+        infos = pm.queryIntentActivities(new Intent(Intent.ACTION_VIEW,
+                TvContract.Programs.CONTENT_URI), 0);
+        for (ResolveInfo info : infos) {
+            tvPackages.add(info.activityInfo.packageName);
+        }
+        SearchManager sm = (SearchManager) context.getSystemService(Context.SEARCH_SERVICE);
+        List<SearchableInfo> globalSearchableInfos = sm.getSearchablesInGlobalSearch();
+        List<SearchableInfo> tvSearchableInfos = new ArrayList<>();
+        for (SearchableInfo info : globalSearchableInfos) {
+            if (tvPackages.contains(info.getSearchActivity().getPackageName())) {
+                tvSearchableInfos.add(info);
+            }
+        }
+        return tvSearchableInfos;
+    }
+
+    private static Uri getSearchUri(SearchableInfo searchable) {
+        if (searchable == null) {
+            return null;
+        }
+        String authority = searchable.getSuggestAuthority();
+        if (authority == null) {
+            return null;
+        }
+        Uri.Builder uriBuilder = new Uri.Builder()
+                .scheme(ContentResolver.SCHEME_CONTENT)
+                .authority(authority);
+
+        final String contentPath = searchable.getSuggestPath();
+        if (contentPath != null) {
+            uriBuilder.appendEncodedPath(contentPath);
+        }
+
+        uriBuilder.appendPath(SearchManager.SUGGEST_URI_PATH_QUERY);
+        return uriBuilder.build();
+    }
+}
diff --git a/apps/CtsVerifier/src/com/android/cts/verifier/tv/TvAppVerifierActivity.java b/apps/CtsVerifier/src/com/android/cts/verifier/tv/TvAppVerifierActivity.java
index 3529237..12e9652 100644
--- a/apps/CtsVerifier/src/com/android/cts/verifier/tv/TvAppVerifierActivity.java
+++ b/apps/CtsVerifier/src/com/android/cts/verifier/tv/TvAppVerifierActivity.java
@@ -63,6 +63,7 @@
 
     protected void setButtonEnabled(View item, boolean enabled) {
         View button = item.findViewById(R.id.user_action_button);
+        button.setFocusable(enabled);
         button.setClickable(enabled);
         button.setEnabled(enabled);
     }
@@ -70,9 +71,7 @@
     protected void setPassState(View item, boolean passed) {
         ImageView status = (ImageView) item.findViewById(R.id.status);
         status.setImageResource(passed ? R.drawable.fs_good : R.drawable.fs_error);
-        View button = item.findViewById(R.id.user_action_button);
-        button.setClickable(false);
-        button.setEnabled(false);
+        setButtonEnabled(item, false);
         status.invalidate();
     }
 
diff --git a/apps/CtsVerifier/src/com/android/cts/verifier/tv/TvInputDiscoveryTestActivity.java b/apps/CtsVerifier/src/com/android/cts/verifier/tv/TvInputDiscoveryTestActivity.java
index 3d17a1a..06f4f6f 100644
--- a/apps/CtsVerifier/src/com/android/cts/verifier/tv/TvInputDiscoveryTestActivity.java
+++ b/apps/CtsVerifier/src/com/android/cts/verifier/tv/TvInputDiscoveryTestActivity.java
@@ -16,12 +16,17 @@
 
 package com.android.cts.verifier.tv;
 
+import android.app.SearchableInfo;
+import android.content.Context;
 import android.content.Intent;
 import android.media.tv.TvContract;
+import android.os.AsyncTask;
 import android.view.View;
 
 import com.android.cts.verifier.R;
 
+import java.util.List;
+
 /**
  * Tests for verifying TV app behavior for third-party TV input apps.
  */
@@ -30,7 +35,9 @@
     private static final String TAG = "TvInputDiscoveryTestActivity";
 
     private static final Intent TV_APP_INTENT = new Intent(Intent.ACTION_VIEW,
-            TvContract.buildChannelUri(0));
+            TvContract.Channels.CONTENT_URI);
+    private static final Intent EPG_INTENT = new Intent(Intent.ACTION_VIEW,
+            TvContract.Programs.CONTENT_URI);
 
     private static final long TIMEOUT_MS = 5l * 60l * 1000l;  // 5 mins.
 
@@ -39,8 +46,12 @@
     private View mTuneToChannelItem;
     private View mVerifyTuneItem;
     private View mVerifyOverlayViewItem;
+    private View mVerifyGlobalSearchItem;
+    private View mGoToEpgItem;
+    private View mVerifyEpgItem;
     private boolean mTuneVerified;
     private boolean mOverlayViewVerified;
+    private boolean mGlobalSearchVerified;
 
     @Override
     public void onClick(View v) {
@@ -63,6 +74,7 @@
                     setButtonEnabled(mTuneToChannelItem, true);
                 }
             });
+            startActivity(TV_APP_INTENT);
         } else if (containsButton(mTuneToChannelItem, v)) {
             final Runnable failCallback = new Runnable() {
                 @Override
@@ -78,7 +90,7 @@
                     setPassState(mVerifyTuneItem, true);
 
                     mTuneVerified = true;
-                    updatePassState(postTarget, failCallback);
+                    goToNextState(postTarget, failCallback);
                 }
             });
             MockTvInputService.expectOverlayView(postTarget, new Runnable() {
@@ -88,11 +100,19 @@
                     setPassState(mVerifyOverlayViewItem, true);
 
                     mOverlayViewVerified = true;
-                    updatePassState(postTarget, failCallback);
+                    goToNextState(postTarget, failCallback);
                 }
             });
+            verifyGlobalSearch(postTarget, failCallback);
+            startActivity(TV_APP_INTENT);
+        } else if (containsButton(mGoToEpgItem, v)) {
+            startActivity(EPG_INTENT);
+            setPassState(mGoToEpgItem, true);
+            setButtonEnabled(mVerifyEpgItem, true);
+        } else if (containsButton(mVerifyEpgItem, v)) {
+            setPassState(mVerifyEpgItem, true);
+            getPassButton().setEnabled(true);
         }
-        startActivity(TV_APP_INTENT);
     }
 
     @Override
@@ -106,13 +126,12 @@
         mVerifyTuneItem = createAutoItem(R.string.tv_input_discover_test_verify_tune);
         mVerifyOverlayViewItem = createAutoItem(
                 R.string.tv_input_discover_test_verify_overlay_view);
-    }
-
-    private void updatePassState(View postTarget, Runnable failCallback) {
-        if (mTuneVerified && mOverlayViewVerified) {
-            postTarget.removeCallbacks(failCallback);
-            getPassButton().setEnabled(true);
-        }
+        mVerifyGlobalSearchItem = createAutoItem(
+                R.string.tv_input_discover_test_verify_global_search);
+        mGoToEpgItem = createUserItem(R.string.tv_input_discover_test_go_to_epg,
+                R.string.tv_launch_epg, this);
+        mVerifyEpgItem = createUserItem(R.string.tv_input_discover_test_verify_epg,
+                R.string.tv_input_discover_test_yes, this);
     }
 
     @Override
@@ -120,4 +139,39 @@
         setInfoResources(R.string.tv_input_discover_test,
                 R.string.tv_input_discover_test_info, -1);
     }
+
+    private void goToNextState(View postTarget, Runnable failCallback) {
+        if (mTuneVerified && mOverlayViewVerified && mGlobalSearchVerified) {
+            postTarget.removeCallbacks(failCallback);
+            setButtonEnabled(mGoToEpgItem, true);
+        }
+    }
+
+    private void verifyGlobalSearch(final View postTarget, final Runnable failCallback) {
+        new AsyncTask<Void, Void, Boolean>() {
+            @Override
+            protected Boolean doInBackground(Void... params) {
+                Context context = TvInputDiscoveryTestActivity.this;
+                for (SearchableInfo info : SearchUtil.getSearchableInfos(context)) {
+                    if (SearchUtil.verifySearchResult(context, info,
+                            MockTvInputSetupActivity.CHANNEL_NAME,
+                            MockTvInputSetupActivity.PROGRAM_TITLE)
+                            && SearchUtil.verifySearchResult(context, info,
+                                    MockTvInputSetupActivity.PROGRAM_TITLE,
+                                    MockTvInputSetupActivity.PROGRAM_TITLE)) {
+                        return true;
+                    }
+                }
+                return false;
+            }
+
+            @Override
+            protected void onPostExecute(Boolean result) {
+                super.onPostExecute(result);
+                setPassState(mVerifyGlobalSearchItem, result);
+                mGlobalSearchVerified = result;
+                goToNextState(postTarget, failCallback);
+            }
+        }.execute();
+    }
 }
diff --git a/build/test_executable.mk b/build/test_executable.mk
index e0352ba..02b3e4c 100644
--- a/build/test_executable.mk
+++ b/build/test_executable.mk
@@ -43,3 +43,6 @@
 						-b $(CTS_UNSUPPORTED_ABIS) \
 						-a $(CTS_TARGET_ARCH) \
 						-o $@
+
+# Have the module name depend on the cts files; so the cts files get generated when you run mm/mmm/mma/mmma.
+$(my_register_name) : $(cts_executable_xml)
diff --git a/build/test_gtest_package.mk b/build/test_gtest_package.mk
index 6868081..dd1269b 100644
--- a/build/test_gtest_package.mk
+++ b/build/test_gtest_package.mk
@@ -52,3 +52,6 @@
 						-b $(CTS_UNSUPPORTED_ABIS) \
 						-a $(CTS_TARGET_ARCH) \
 						-o $@
+
+# Have the module name depend on the cts files; so the cts files get generated when you run mm/mmm/mma/mmma.
+$(my_register_name) : $(cts_package_apk) $(cts_package_xml)
diff --git a/build/test_host_java_library.mk b/build/test_host_java_library.mk
index 8e071e4..8ed5670 100644
--- a/build/test_host_java_library.mk
+++ b/build/test_host_java_library.mk
@@ -42,3 +42,6 @@
 						-b $(CTS_UNSUPPORTED_ABIS) \
 						-a $(CTS_TARGET_ARCH) \
 						-o $@
+
+# Have the module name depend on the cts files; so the cts files get generated when you run mm/mmm/mma/mmma.
+$(my_register_name) : $(cts_library_xml)
diff --git a/build/test_package.mk b/build/test_package.mk
index 72972b2..7589787 100644
--- a/build/test_package.mk
+++ b/build/test_package.mk
@@ -64,3 +64,6 @@
 						-b $(CTS_UNSUPPORTED_ABIS) \
 						-a $(CTS_TARGET_ARCH) \
 						-o $@
+
+# Have the module name depend on the cts files; so the cts files get generated when you run mm/mmm/mma/mmma.
+$(my_register_name) : $(cts_package_apk) $(cts_package_xml)
diff --git a/build/test_target_java_library.mk b/build/test_target_java_library.mk
index c0d7a2a..04fffb9 100644
--- a/build/test_target_java_library.mk
+++ b/build/test_target_java_library.mk
@@ -44,3 +44,6 @@
 						-a $(CTS_TARGET_ARCH) \
 						-x "runtimeArgs->$(PRIVATE_RUNTIME_ARGS)" \
 						-o $@
+
+# Have the module name depend on the cts files; so the cts files get generated when you run mm/mmm/mma/mmma.
+$(my_register_name) : $(cts_library_jar) $(cts_library_xml)
diff --git a/build/test_uiautomator.mk b/build/test_uiautomator.mk
index 085d672..cad6e4f 100644
--- a/build/test_uiautomator.mk
+++ b/build/test_uiautomator.mk
@@ -55,3 +55,6 @@
 						-b $(CTS_UNSUPPORTED_ABIS) \
 						-a $(CTS_TARGET_ARCH) \
 						-o $@
+
+# Have the module name depend on the cts files; so the cts files get generated when you run mm/mmm/mma/mmma.
+$(my_register_name) : $(cts_library_jar) $(cts_library_xml)
diff --git a/hostsidetests/aadb/src/com/android/cts/aadb/TestDeviceFuncTest.java b/hostsidetests/aadb/src/com/android/cts/aadb/TestDeviceFuncTest.java
index 9f69242..a036382 100644
--- a/hostsidetests/aadb/src/com/android/cts/aadb/TestDeviceFuncTest.java
+++ b/hostsidetests/aadb/src/com/android/cts/aadb/TestDeviceFuncTest.java
@@ -294,7 +294,11 @@
             String deviceTimezone = mTestDevice.getProperty("persist.sys.timezone");
             if (deviceTimezone != null) {
                 TimeZone tz = TimeZone.getTimeZone(deviceTimezone);
-                tmpFile.setLastModified(tmpFile.lastModified() + tz.getRawOffset());
+                long timestamp = tmpFile.lastModified() + tz.getRawOffset();
+                if (tz.observesDaylightTime()) {
+                    timestamp += tz.getDSTSavings();
+                }
+                tmpFile.setLastModified(timestamp);
             }
 
             assertTrue(mTestDevice.syncFiles(tmpDir, externalStorePath));
diff --git a/hostsidetests/appsecurity/src/com/android/cts/appsecurity/AppSecurityTests.java b/hostsidetests/appsecurity/src/com/android/cts/appsecurity/AppSecurityTests.java
index 9dc51c9..4d9ef00 100644
--- a/hostsidetests/appsecurity/src/com/android/cts/appsecurity/AppSecurityTests.java
+++ b/hostsidetests/appsecurity/src/com/android/cts/appsecurity/AppSecurityTests.java
@@ -181,7 +181,7 @@
                     true /* reinstall */, options);
             assertNotNull("app upgrade with different cert than existing app installed " +
                     "successfully", installResult);
-            assertEquals("INSTALL_PARSE_FAILED_INCONSISTENT_CERTIFICATES", installResult);
+            assertEquals("INSTALL_FAILED_UPDATE_INCOMPATIBLE", installResult);
         }
         finally {
             getDevice().uninstallPackage(SIMPLE_APP_PKG);
diff --git a/hostsidetests/appsecurity/src/com/android/cts/appsecurity/SplitTests.java b/hostsidetests/appsecurity/src/com/android/cts/appsecurity/SplitTests.java
index 264c0b1..90cbed9 100644
--- a/hostsidetests/appsecurity/src/com/android/cts/appsecurity/SplitTests.java
+++ b/hostsidetests/appsecurity/src/com/android/cts/appsecurity/SplitTests.java
@@ -57,7 +57,13 @@
     private static final String APK_mips64 = "CtsSplitApp_mips64.apk";
     private static final String APK_mips = "CtsSplitApp_mips.apk";
 
+    private static final String APK_DIFF_REVISION = "CtsSplitAppDiffRevision.apk";
+    private static final String APK_DIFF_REVISION_v7 = "CtsSplitAppDiffRevision_v7.apk";
+
+    private static final String APK_DIFF_VERSION = "CtsSplitAppDiffVersion.apk";
     private static final String APK_DIFF_VERSION_v7 = "CtsSplitAppDiffVersion_v7.apk";
+
+    private static final String APK_DIFF_CERT = "CtsSplitAppDiffCert.apk";
     private static final String APK_DIFF_CERT_v7 = "CtsSplitAppDiffCert_v7.apk";
 
     private static final String APK_FEATURE = "CtsSplitAppFeature.apk";
@@ -218,8 +224,6 @@
 
     public void testDiffCertInherit() throws Exception {
         new InstallMultiple().addApk(APK).run();
-        // TODO: remove this once we fix 17900178
-        runDeviceTests(PKG, ".SplitAppTest", "testSingleBase");
         new InstallMultiple().inheritFrom(PKG).addApk(APK_DIFF_CERT_v7).runExpectingFailure();
     }
 
@@ -229,11 +233,33 @@
 
     public void testDiffVersionInherit() throws Exception {
         new InstallMultiple().addApk(APK).run();
-        // TODO: remove this once we fix 17900178
-        runDeviceTests(PKG, ".SplitAppTest", "testSingleBase");
         new InstallMultiple().inheritFrom(PKG).addApk(APK_DIFF_VERSION_v7).runExpectingFailure();
     }
 
+    public void testDiffRevision() throws Exception {
+        new InstallMultiple().addApk(APK).addApk(APK_DIFF_REVISION_v7).run();
+        runDeviceTests(PKG, ".SplitAppTest", "testRevision0_12");
+    }
+
+    public void testDiffRevisionInheritBase() throws Exception {
+        new InstallMultiple().addApk(APK).addApk(APK_v7).run();
+        runDeviceTests(PKG, ".SplitAppTest", "testRevision0_0");
+        new InstallMultiple().inheritFrom(PKG).addApk(APK_DIFF_REVISION_v7).run();
+        runDeviceTests(PKG, ".SplitAppTest", "testRevision0_12");
+    }
+
+    public void testDiffRevisionInheritSplit() throws Exception {
+        new InstallMultiple().addApk(APK).addApk(APK_v7).run();
+        runDeviceTests(PKG, ".SplitAppTest", "testRevision0_0");
+        new InstallMultiple().inheritFrom(PKG).addApk(APK_DIFF_REVISION).run();
+        runDeviceTests(PKG, ".SplitAppTest", "testRevision12_0");
+    }
+
+    public void testDiffRevisionDowngrade() throws Exception {
+        new InstallMultiple().addApk(APK).addApk(APK_DIFF_REVISION_v7).run();
+        new InstallMultiple().inheritFrom(PKG).addApk(APK_v7).runExpectingFailure();
+    }
+
     public void testFeatureBase() throws Exception {
         new InstallMultiple().addApk(APK).addApk(APK_FEATURE).run();
         runDeviceTests(PKG, ".SplitAppTest", "testFeatureBase");
@@ -252,6 +278,16 @@
         // TODO: flesh out this test
     }
 
+    /**
+     * Verify that installing a new version of app wipes code cache.
+     */
+    public void testClearCodeCache() throws Exception {
+        new InstallMultiple().addApk(APK).run();
+        runDeviceTests(PKG, ".SplitAppTest", "testCodeCacheWrite");
+        new InstallMultiple().addArg("-r").addApk(APK_DIFF_VERSION).run();
+        runDeviceTests(PKG, ".SplitAppTest", "testCodeCacheRead");
+    }
+
     class InstallMultiple {
         private List<String> mArgs = new ArrayList<>();
         private List<File> mApks = new ArrayList<>();
diff --git a/hostsidetests/appsecurity/test-apps/DocumentClient/src/com/android/cts/documentclient/DocumentsClientTest.java b/hostsidetests/appsecurity/test-apps/DocumentClient/src/com/android/cts/documentclient/DocumentsClientTest.java
index 98610a0..f308211 100644
--- a/hostsidetests/appsecurity/test-apps/DocumentClient/src/com/android/cts/documentclient/DocumentsClientTest.java
+++ b/hostsidetests/appsecurity/test-apps/DocumentClient/src/com/android/cts/documentclient/DocumentsClientTest.java
@@ -33,6 +33,7 @@
 import android.test.InstrumentationTestCase;
 import android.test.MoreAsserts;
 import android.text.format.DateUtils;
+import android.util.Log;
 
 import com.android.cts.documentclient.MyActivity.Result;
 
@@ -47,10 +48,12 @@
  * like {@link Intent#ACTION_OPEN_DOCUMENT}.
  */
 public class DocumentsClientTest extends InstrumentationTestCase {
+    private static final String TAG = "DocumentsClientTest";
+
     private UiDevice mDevice;
     private MyActivity mActivity;
 
-    private static final long TIMEOUT = 10 * DateUtils.SECOND_IN_MILLIS;
+    private static final long TIMEOUT = 30 * DateUtils.SECOND_IN_MILLIS;
 
     @Override
     public void setUp() throws Exception {
@@ -73,6 +76,15 @@
                 "com.android.documentsui:id/container_roots").childSelector(
                 new UiSelector().resourceId("android:id/list"));
 
+        // We might need to expand drawer if not visible
+        if (!new UiObject(rootsList).waitForExists(TIMEOUT)) {
+            Log.d(TAG, "Failed to find roots list; trying to expand");
+            final UiSelector hamburger = new UiSelector().resourceId(
+                    "com.android.documentsui:id/toolbar").childSelector(
+                    new UiSelector().className("android.widget.ImageButton").clickable(true));
+            new UiObject(hamburger).click();
+        }
+
         // Wait for the first list item to appear
         assertTrue("First list item",
                 new UiObject(rootsList.childSelector(new UiSelector())).waitForExists(TIMEOUT));
diff --git a/hostsidetests/appsecurity/test-apps/SplitApp/Android.mk b/hostsidetests/appsecurity/test-apps/SplitApp/Android.mk
index 8b25f4b..bf89576 100644
--- a/hostsidetests/appsecurity/test-apps/SplitApp/Android.mk
+++ b/hostsidetests/appsecurity/test-apps/SplitApp/Android.mk
@@ -30,7 +30,31 @@
 LOCAL_ASSET_DIR := $(LOCAL_PATH)/assets
 
 LOCAL_CERTIFICATE := cts/hostsidetests/appsecurity/certs/cts-testkey1
-LOCAL_AAPT_FLAGS := --version-code 100 --version-name OneHundred --replace-version -c mdpi -c hdpi -c xhdpi -c xxhdpi
+LOCAL_AAPT_FLAGS := --version-code 100 --version-name OneHundred --replace-version
+
+LOCAL_PROGUARD_ENABLED := disabled
+LOCAL_DEX_PREOPT := false
+
+include $(BUILD_PACKAGE)
+
+
+#################################################
+# Define a variant with a different revision code
+
+include $(CLEAR_VARS)
+
+LOCAL_MODULE_TAGS := tests
+LOCAL_SDK_VERSION := current
+LOCAL_STATIC_JAVA_LIBRARIES := android-support-test
+
+LOCAL_SRC_FILES := $(call all-java-files-under, src)
+
+LOCAL_PACKAGE_NAME := CtsSplitAppDiffRevision
+LOCAL_PACKAGE_SPLITS := v7
+
+LOCAL_MANIFEST_FILE := revision/AndroidManifest.xml
+LOCAL_CERTIFICATE := cts/hostsidetests/appsecurity/certs/cts-testkey1
+LOCAL_AAPT_FLAGS := --version-code 100 --version-name OneHundredRevisionTwelve --replace-version
 
 LOCAL_PROGUARD_ENABLED := disabled
 LOCAL_DEX_PREOPT := false
@@ -53,7 +77,7 @@
 LOCAL_PACKAGE_SPLITS := v7
 
 LOCAL_CERTIFICATE := cts/hostsidetests/appsecurity/certs/cts-testkey1
-LOCAL_AAPT_FLAGS := --version-code 101 --version-name OneHundredOne --replace-version -c mdpi -c hdpi -c xhdpi -c xxhdpi
+LOCAL_AAPT_FLAGS := --version-code 101 --version-name OneHundredOne --replace-version
 
 LOCAL_PROGUARD_ENABLED := disabled
 LOCAL_DEX_PREOPT := false
@@ -76,7 +100,7 @@
 LOCAL_PACKAGE_SPLITS := v7
 
 LOCAL_CERTIFICATE := cts/hostsidetests/appsecurity/certs/cts-testkey2
-LOCAL_AAPT_FLAGS := --version-code 100 --version-name OneHundred --replace-version -c mdpi -c hdpi -c xhdpi -c xxhdpi
+LOCAL_AAPT_FLAGS := --version-code 100 --version-name OneHundred --replace-version
 
 LOCAL_PROGUARD_ENABLED := disabled
 LOCAL_DEX_PREOPT := false
diff --git a/hostsidetests/appsecurity/test-apps/SplitApp/feature/Android.mk b/hostsidetests/appsecurity/test-apps/SplitApp/feature/Android.mk
index e93f6c3..809a6b8 100644
--- a/hostsidetests/appsecurity/test-apps/SplitApp/feature/Android.mk
+++ b/hostsidetests/appsecurity/test-apps/SplitApp/feature/Android.mk
@@ -24,7 +24,7 @@
 LOCAL_ASSET_DIR := $(LOCAL_PATH)/assets
 
 LOCAL_CERTIFICATE := cts/hostsidetests/appsecurity/certs/cts-testkey1
-LOCAL_AAPT_FLAGS := --version-code 100 --version-name OneHundred --replace-version -c mdpi -c hdpi -c xhdpi -c xxhdpi
+LOCAL_AAPT_FLAGS := --version-code 100 --version-name OneHundred --replace-version
 
 LOCAL_MODULE_TAGS := tests
 
diff --git a/hostsidetests/appsecurity/test-apps/SplitApp/revision/AndroidManifest.xml b/hostsidetests/appsecurity/test-apps/SplitApp/revision/AndroidManifest.xml
new file mode 100644
index 0000000..8e053ba
--- /dev/null
+++ b/hostsidetests/appsecurity/test-apps/SplitApp/revision/AndroidManifest.xml
@@ -0,0 +1,44 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2014 The Android Open Source Project
+
+     Licensed under the Apache License, Version 2.0 (the "License");
+     you may not use this file except in compliance with the License.
+     You may obtain a copy of the License at
+
+          http://www.apache.org/licenses/LICENSE-2.0
+
+     Unless required by applicable law or agreed to in writing, software
+     distributed under the License is distributed on an "AS IS" BASIS,
+     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+     See the License for the specific language governing permissions and
+     limitations under the License.
+-->
+
+<manifest xmlns:android="http://schemas.android.com/apk/res/android"
+        package="com.android.cts.splitapp"
+        android:revisionCode="12">
+
+    <uses-permission android:name="android.permission.CAMERA" />
+
+    <application android:label="SplitApp">
+        <activity android:name=".MyActivity">
+            <intent-filter>
+                <action android:name="android.intent.action.MAIN" />
+                <category android:name="android.intent.category.LAUNCHER" />
+            </intent-filter>
+            <meta-data android:name="android.service.wallpaper" android:resource="@xml/my_activity_meta" />
+        </activity>
+        <receiver android:name=".MyReceiver"
+                android:enabled="@bool/my_receiver_enabled">
+            <intent-filter>
+                <action android:name="android.intent.action.DATE_CHANGED" />
+            </intent-filter>
+        </receiver>
+
+        <uses-library android:name="android.test.runner" />
+    </application>
+
+    <instrumentation android:name="android.support.test.runner.AndroidJUnitRunner"
+        android:targetPackage="com.android.cts.splitapp" />
+
+</manifest>
diff --git a/hostsidetests/appsecurity/test-apps/SplitApp/src/com/android/cts/splitapp/SplitAppTest.java b/hostsidetests/appsecurity/test-apps/SplitApp/src/com/android/cts/splitapp/SplitAppTest.java
index 277a1a2..3d6cee7 100644
--- a/hostsidetests/appsecurity/test-apps/SplitApp/src/com/android/cts/splitapp/SplitAppTest.java
+++ b/hostsidetests/appsecurity/test-apps/SplitApp/src/com/android/cts/splitapp/SplitAppTest.java
@@ -21,6 +21,7 @@
 
 import android.content.Intent;
 import android.content.pm.ApplicationInfo;
+import android.content.pm.PackageInfo;
 import android.content.pm.PackageManager;
 import android.content.pm.ProviderInfo;
 import android.content.pm.ResolveInfo;
@@ -30,6 +31,7 @@
 import android.graphics.Canvas;
 import android.graphics.drawable.Drawable;
 import android.test.AndroidTestCase;
+import android.test.MoreAsserts;
 import android.util.DisplayMetrics;
 import android.util.Log;
 
@@ -37,6 +39,7 @@
 import org.xmlpull.v1.XmlPullParserException;
 
 import java.io.BufferedReader;
+import java.io.File;
 import java.io.IOException;
 import java.io.InputStreamReader;
 import java.lang.reflect.Field;
@@ -211,9 +214,8 @@
         assertEquals("base", getXmlTestValue(r.getXml(R.xml.my_activity_meta)));
 
         // And that we can access resources from feature
-        // TODO: enable these once 17924027 is fixed
-//        assertEquals("red", r.getString(r.getIdentifier("feature_string", "string", PKG)));
-//        assertEquals(123, r.getInteger(r.getIdentifier("feature_integer", "integer", PKG)));
+        assertEquals("red", r.getString(r.getIdentifier("feature_string", "string", PKG)));
+        assertEquals(123, r.getInteger(r.getIdentifier("feature_integer", "integer", PKG)));
 
         final Class<?> featR = Class.forName("com.android.cts.splitapp.FeatureR");
         final int boolId = (int) featR.getDeclaredField("feature_receiver_enabled").get(null);
@@ -292,8 +294,7 @@
         assertEquals(false, r.getBoolean(R.bool.my_receiver_enabled));
 
         // And that we can access resources from feature
-        // TODO: enable these once 17924027 is fixed
-//        assertEquals(321, r.getInteger(r.getIdentifier("feature_integer", "integer", PKG)));
+        assertEquals(321, r.getInteger(r.getIdentifier("feature_integer", "integer", PKG)));
 
         final Class<?> featR = Class.forName("com.android.cts.splitapp.FeatureR");
         final int boolId = (int) featR.getDeclaredField("feature_receiver_enabled").get(null);
@@ -310,6 +311,40 @@
         assertEquals(0, result.size());
     }
 
+    public void testCodeCacheWrite() throws Exception {
+        assertTrue(new File(getContext().getFilesDir(), "normal.raw").createNewFile());
+        assertTrue(new File(getContext().getCodeCacheDir(), "cache.raw").createNewFile());
+    }
+
+    public void testCodeCacheRead() throws Exception {
+        assertTrue(new File(getContext().getFilesDir(), "normal.raw").exists());
+        assertFalse(new File(getContext().getCodeCacheDir(), "cache.raw").exists());
+    }
+
+    public void testRevision0_0() throws Exception {
+        final PackageInfo info = getContext().getPackageManager()
+                .getPackageInfo(getContext().getPackageName(), 0);
+        assertEquals(0, info.baseRevisionCode);
+        assertEquals(1, info.splitRevisionCodes.length);
+        assertEquals(0, info.splitRevisionCodes[0]);
+    }
+
+    public void testRevision12_0() throws Exception {
+        final PackageInfo info = getContext().getPackageManager()
+                .getPackageInfo(getContext().getPackageName(), 0);
+        assertEquals(12, info.baseRevisionCode);
+        assertEquals(1, info.splitRevisionCodes.length);
+        assertEquals(0, info.splitRevisionCodes[0]);
+    }
+
+    public void testRevision0_12() throws Exception {
+        final PackageInfo info = getContext().getPackageManager()
+                .getPackageInfo(getContext().getPackageName(), 0);
+        assertEquals(0, info.baseRevisionCode);
+        assertEquals(1, info.splitRevisionCodes.length);
+        assertEquals(12, info.splitRevisionCodes[0]);
+    }
+
     private static void updateDpi(Resources r, int densityDpi) {
         final Configuration c = new Configuration(r.getConfiguration());
         c.densityDpi = densityDpi;
diff --git a/hostsidetests/appsecurity/test-apps/keysets/permDef/Android.mk b/hostsidetests/appsecurity/test-apps/keysets/permDef/Android.mk
index eb71540..715905a 100644
--- a/hostsidetests/appsecurity/test-apps/keysets/permDef/Android.mk
+++ b/hostsidetests/appsecurity/test-apps/keysets/permDef/Android.mk
@@ -18,7 +18,6 @@
 include $(CLEAR_VARS)
 
 LOCAL_MODULE_TAGS := tests
-LOCAL_SRC_FILES := $(call all-java-files-under, src)
 LOCAL_SDK_VERSION := current
 LOCAL_STATIC_JAVA_LIBRARIES := android-support-test
 LOCAL_PACKAGE_NAME := CtsKeySetPermDefSigningA
@@ -31,7 +30,6 @@
 include $(CLEAR_VARS)
 
 LOCAL_MODULE_TAGS := tests
-LOCAL_SRC_FILES := $(call all-java-files-under, src)
 LOCAL_SDK_VERSION := current
 LOCAL_STATIC_JAVA_LIBRARIES := android-support-test
 LOCAL_PACKAGE_NAME := CtsKeySetPermDefSigningB
diff --git a/hostsidetests/appsecurity/test-apps/keysets/permUse/Android.mk b/hostsidetests/appsecurity/test-apps/keysets/permUse/Android.mk
index 000b12a..eceea38 100644
--- a/hostsidetests/appsecurity/test-apps/keysets/permUse/Android.mk
+++ b/hostsidetests/appsecurity/test-apps/keysets/permUse/Android.mk
@@ -18,7 +18,6 @@
 include $(CLEAR_VARS)
 
 LOCAL_MODULE_TAGS := tests
-LOCAL_SRC_FILES := $(call all-java-files-under, src)
 LOCAL_SDK_VERSION := current
 LOCAL_STATIC_JAVA_LIBRARIES := android-support-test
 LOCAL_PACKAGE_NAME := CtsKeySetPermUseSigningA
@@ -31,7 +30,6 @@
 include $(CLEAR_VARS)
 
 LOCAL_MODULE_TAGS := tests
-LOCAL_SRC_FILES := $(call all-java-files-under, src)
 LOCAL_SDK_VERSION := current
 LOCAL_STATIC_JAVA_LIBRARIES := android-support-test
 LOCAL_PACKAGE_NAME := CtsKeySetPermUseSigningB
diff --git a/hostsidetests/appsecurity/test-apps/keysets/uA/Android.mk b/hostsidetests/appsecurity/test-apps/keysets/uA/Android.mk
index 6220790..efba345 100644
--- a/hostsidetests/appsecurity/test-apps/keysets/uA/Android.mk
+++ b/hostsidetests/appsecurity/test-apps/keysets/uA/Android.mk
@@ -18,7 +18,6 @@
 include $(CLEAR_VARS)
 
 LOCAL_MODULE_TAGS := tests
-LOCAL_SRC_FILES := $(call all-java-files-under, src)
 LOCAL_SDK_VERSION := current
 LOCAL_STATIC_JAVA_LIBRARIES := android-support-test
 LOCAL_PACKAGE_NAME := CtsKeySetSigningAUpgradeA
@@ -30,7 +29,6 @@
 #apks signed by cts-keyset-test-b
 include $(CLEAR_VARS)
 LOCAL_MODULE_TAGS := tests
-LOCAL_SRC_FILES := $(call all-java-files-under, src)
 LOCAL_SDK_VERSION := current
 LOCAL_STATIC_JAVA_LIBRARIES := android-support-test
 LOCAL_PACKAGE_NAME := CtsKeySetSigningBUpgradeA
@@ -43,7 +41,6 @@
 include $(CLEAR_VARS)
 
 LOCAL_MODULE_TAGS := tests
-LOCAL_SRC_FILES := $(call all-java-files-under, src)
 LOCAL_SDK_VERSION := current
 LOCAL_STATIC_JAVA_LIBRARIES := android-support-test
 LOCAL_PACKAGE_NAME := CtsKeySetSigningAAndBUpgradeA
diff --git a/hostsidetests/appsecurity/test-apps/keysets/uAB/Android.mk b/hostsidetests/appsecurity/test-apps/keysets/uAB/Android.mk
index 534dba3..219689e 100644
--- a/hostsidetests/appsecurity/test-apps/keysets/uAB/Android.mk
+++ b/hostsidetests/appsecurity/test-apps/keysets/uAB/Android.mk
@@ -18,7 +18,6 @@
 include $(CLEAR_VARS)
 
 LOCAL_MODULE_TAGS := tests
-LOCAL_SRC_FILES := $(call all-java-files-under, src)
 LOCAL_SDK_VERSION := current
 LOCAL_STATIC_JAVA_LIBRARIES := android-support-test
 LOCAL_PACKAGE_NAME := CtsKeySetSigningAUpgradeAAndB
diff --git a/hostsidetests/appsecurity/test-apps/keysets/uAuB/Android.mk b/hostsidetests/appsecurity/test-apps/keysets/uAuB/Android.mk
index 75729e0..040c378 100644
--- a/hostsidetests/appsecurity/test-apps/keysets/uAuB/Android.mk
+++ b/hostsidetests/appsecurity/test-apps/keysets/uAuB/Android.mk
@@ -18,7 +18,6 @@
 include $(CLEAR_VARS)
 
 LOCAL_MODULE_TAGS := tests
-LOCAL_SRC_FILES := $(call all-java-files-under, src)
 LOCAL_SDK_VERSION := current
 LOCAL_STATIC_JAVA_LIBRARIES := android-support-test
 LOCAL_PACKAGE_NAME := CtsKeySetSigningAUpgradeAOrB
diff --git a/hostsidetests/appsecurity/test-apps/keysets/uB/Android.mk b/hostsidetests/appsecurity/test-apps/keysets/uB/Android.mk
index 121c342..62b5461 100644
--- a/hostsidetests/appsecurity/test-apps/keysets/uB/Android.mk
+++ b/hostsidetests/appsecurity/test-apps/keysets/uB/Android.mk
@@ -18,7 +18,6 @@
 include $(CLEAR_VARS)
 
 LOCAL_MODULE_TAGS := tests
-LOCAL_SRC_FILES := $(call all-java-files-under, src)
 LOCAL_SDK_VERSION := current
 LOCAL_STATIC_JAVA_LIBRARIES := android-support-test
 LOCAL_PACKAGE_NAME := CtsKeySetSigningAUpgradeB
@@ -31,7 +30,6 @@
 include $(CLEAR_VARS)
 
 LOCAL_MODULE_TAGS := tests
-LOCAL_SRC_FILES := $(call all-java-files-under, src)
 LOCAL_SDK_VERSION := current
 LOCAL_STATIC_JAVA_LIBRARIES := android-support-test
 LOCAL_PACKAGE_NAME := CtsKeySetSigningBUpgradeB
@@ -44,7 +42,6 @@
 include $(CLEAR_VARS)
 
 LOCAL_MODULE_TAGS := tests
-LOCAL_SRC_FILES := $(call all-java-files-under, src)
 LOCAL_SDK_VERSION := current
 LOCAL_STATIC_JAVA_LIBRARIES := android-support-test
 LOCAL_PACKAGE_NAME := CtsKeySetSigningAAndCUpgradeB
diff --git a/hostsidetests/appsecurity/test-apps/keysets/uNone/Android.mk b/hostsidetests/appsecurity/test-apps/keysets/uNone/Android.mk
index a8746ec..5fc4ab9 100644
--- a/hostsidetests/appsecurity/test-apps/keysets/uNone/Android.mk
+++ b/hostsidetests/appsecurity/test-apps/keysets/uNone/Android.mk
@@ -18,7 +18,6 @@
 include $(CLEAR_VARS)
 
 LOCAL_MODULE_TAGS := tests
-LOCAL_SRC_FILES := $(call all-java-files-under, src)
 LOCAL_SDK_VERSION := current
 LOCAL_STATIC_JAVA_LIBRARIES := android-support-test
 LOCAL_PACKAGE_NAME := CtsKeySetSigningAUpgradeNone
diff --git a/hostsidetests/devicepolicy/app/IntentReceiver/AndroidManifest.xml b/hostsidetests/devicepolicy/app/IntentReceiver/AndroidManifest.xml
index e1f6886..5bbdf76 100644
--- a/hostsidetests/devicepolicy/app/IntentReceiver/AndroidManifest.xml
+++ b/hostsidetests/devicepolicy/app/IntentReceiver/AndroidManifest.xml
@@ -24,9 +24,10 @@
     <application>
         <activity android:name="com.android.cts.intent.receiver.IntentReceiverActivity">
             <intent-filter>
+                <action android:name="com.android.cts.action.COPY_TO_CLIPBOARD" />
                 <action android:name="com.android.cts.action.READ_FROM_URI" />
-                <action android:name="com.android.cts.action.WRITE_TO_URI" />
                 <action android:name="com.android.cts.action.TAKE_PERSISTABLE_URI_PERMISSION" />
+                <action android:name="com.android.cts.action.WRITE_TO_URI" />
                 <category android:name="android.intent.category.DEFAULT" />
             </intent-filter>
         </activity>
diff --git a/hostsidetests/devicepolicy/app/IntentReceiver/src/com/android/cts/intent/receiver/IntentReceiverActivity.java b/hostsidetests/devicepolicy/app/IntentReceiver/src/com/android/cts/intent/receiver/IntentReceiverActivity.java
index 59f0752..294c678 100644
--- a/hostsidetests/devicepolicy/app/IntentReceiver/src/com/android/cts/intent/receiver/IntentReceiverActivity.java
+++ b/hostsidetests/devicepolicy/app/IntentReceiver/src/com/android/cts/intent/receiver/IntentReceiverActivity.java
@@ -16,6 +16,8 @@
 package com.android.cts.intent.receiver;
 
 import android.app.Activity;
+import android.content.ClipboardManager;
+import android.content.ClipData;
 import android.content.Intent;
 import android.net.Uri;
 import android.os.Bundle;
@@ -35,21 +37,36 @@
 
     private static final String TAG = "IntentReceiverActivity";
 
-    private static final String ACTION_READ_FROM_URI = "com.android.cts.action.READ_FROM_URI";
+    private static final String ACTION_COPY_TO_CLIPBOARD =
+            "com.android.cts.action.COPY_TO_CLIPBOARD";
 
-    private static final String ACTION_WRITE_TO_URI = "com.android.cts.action.WRITE_TO_URI";
+    private static final String ACTION_READ_FROM_URI =
+            "com.android.cts.action.READ_FROM_URI";
 
     private static final String ACTION_TAKE_PERSISTABLE_URI_PERMISSION =
             "com.android.cts.action.TAKE_PERSISTABLE_URI_PERMISSION";
 
+    private static final String ACTION_WRITE_TO_URI =
+            "com.android.cts.action.WRITE_TO_URI";
+
+
     private static final String EXTRA_CAUGHT_SECURITY_EXCEPTION = "extra_caught_security_exception";
 
     public void onCreate(Bundle savedInstanceState) {
         super.onCreate(savedInstanceState);
-        Intent received = getIntent();
-        String action = received.getAction();
-        Uri uri = getIntent().getClipData().getItemAt(0).getUri();
-        if (ACTION_READ_FROM_URI.equals(action)) {
+        final Intent received = getIntent();
+        final String action = received.getAction();
+        final ClipData clipData = getIntent().getClipData();
+        final Uri uri = clipData != null ? clipData.getItemAt(0).getUri() : null;
+        if (ACTION_COPY_TO_CLIPBOARD.equals(action)) {
+            String text = received.getStringExtra("extra_text");
+            Log.i(TAG, "Copying \"" + text + "\" to the clipboard");
+            ClipData clip = ClipData.newPlainText("", text);
+            ClipboardManager clipboard =
+                    (ClipboardManager) getSystemService(CLIPBOARD_SERVICE);
+            clipboard.setPrimaryClip(clip);
+            setResult(Activity.RESULT_OK);
+        } else if (ACTION_READ_FROM_URI.equals(action)) {
             Intent result = new Intent();
             String message = null;
             try {
@@ -63,6 +80,11 @@
             Log.i(TAG, "Message received in reading test: " + message);
             result.putExtra("extra_response", message);
             setResult(Activity.RESULT_OK, result);
+        } else if (ACTION_TAKE_PERSISTABLE_URI_PERMISSION.equals(action)) {
+            Log.i(TAG, "Taking persistable uri permission to " + uri);
+            getContentResolver().takePersistableUriPermission(uri,
+                    Intent.FLAG_GRANT_READ_URI_PERMISSION);
+            setResult(Activity.RESULT_OK);
         } else if (ACTION_WRITE_TO_URI.equals(action)) {
             Intent result = new Intent();
             String message = received.getStringExtra("extra_message");
@@ -76,11 +98,6 @@
                 Log.i(TAG, "Caught a IOException while trying to write to " + uri, e);
             }
             setResult(Activity.RESULT_OK, result);
-        } else if (ACTION_TAKE_PERSISTABLE_URI_PERMISSION.equals(action)) {
-            Log.i(TAG, "Taking persistable uri permission to " + uri);
-            getContentResolver().takePersistableUriPermission(uri,
-                    Intent.FLAG_GRANT_READ_URI_PERMISSION);
-            setResult(Activity.RESULT_OK);
         }
         finish();
     }
diff --git a/hostsidetests/devicepolicy/app/IntentSender/src/com/android/cts/intent/sender/IntentSenderTest.java b/hostsidetests/devicepolicy/app/IntentSender/src/com/android/cts/intent/sender/ContentTest.java
similarity index 94%
rename from hostsidetests/devicepolicy/app/IntentSender/src/com/android/cts/intent/sender/IntentSenderTest.java
rename to hostsidetests/devicepolicy/app/IntentSender/src/com/android/cts/intent/sender/ContentTest.java
index 47de0da..1aaa5ab 100644
--- a/hostsidetests/devicepolicy/app/IntentSender/src/com/android/cts/intent/sender/IntentSenderTest.java
+++ b/hostsidetests/devicepolicy/app/IntentSender/src/com/android/cts/intent/sender/ContentTest.java
@@ -32,7 +32,7 @@
 import java.io.InputStream;
 import java.io.InputStreamReader;
 
-public class IntentSenderTest extends InstrumentationTestCase {
+public class ContentTest extends InstrumentationTestCase {
 
     private static final String MESSAGE = "Sample Message";
 
@@ -57,8 +57,8 @@
 
     @Override
     public void tearDown() throws Exception {
-        super.tearDown();
         mActivity.finish();
+        super.tearDown();
     }
 
     /**
@@ -73,7 +73,7 @@
         intent.setClipData(ClipData.newRawUri("", uri));
         intent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION);
 
-        final Intent result = mActivity.getResult(intent);
+        final Intent result = mActivity.getCrossProfileResult(intent);
         assertNotNull(result);
         assertEquals(MESSAGE, result.getStringExtra("extra_response"));
     }
@@ -95,7 +95,7 @@
         intent.addFlags(Intent.FLAG_GRANT_WRITE_URI_PERMISSION
                 | Intent.FLAG_GRANT_READ_URI_PERMISSION);
 
-        mActivity.getResult(intent);
+        mActivity.getCrossProfileResult(intent);
         assertEquals(MESSAGE, getFirstLineFromUri(uri));
     }
 
@@ -107,7 +107,7 @@
         Intent intent = new Intent(ACTION_READ_FROM_URI);
         intent.setClipData(ClipData.newRawUri("", uri));
 
-        final Intent result = mActivity.getResult(intent);
+        final Intent result = mActivity.getCrossProfileResult(intent);
         assertNotNull(result);
         assertEquals(MESSAGE, result.getStringExtra("extra_response"));
     }
@@ -136,7 +136,7 @@
         Intent notGrant = new Intent(ACTION_READ_FROM_URI);
         notGrant.setClipData(ClipData.newRawUri("", uriNotGranted));
 
-        final Intent result = mActivity.getResult(notGrant);
+        final Intent result = mActivity.getCrossProfileResult(notGrant);
         assertNotNull(result);
         // The receiver did not have permission to read the uri. So it should have caught a security
         // exception.
@@ -155,7 +155,7 @@
         intent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION);
 
         // We're expecting to run into a security exception
-        final Intent result = mActivity.getResult(intent);
+        final Intent result = mActivity.getCrossProfileResult(intent);
         if (result == null) {
             // This is fine; probably of a SecurityException when off in the
             // system somewhere.
@@ -170,7 +170,7 @@
         grantPersistable.setClipData(ClipData.newRawUri("", uri));
         grantPersistable.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION
                 | Intent.FLAG_GRANT_PERSISTABLE_URI_PERMISSION);
-        mActivity.getResult(grantPersistable);
+        mActivity.getCrossProfileResult(grantPersistable);
     }
 
     private Uri getBasicContentProviderUri(String path) {
diff --git a/hostsidetests/devicepolicy/app/IntentSender/src/com/android/cts/intent/sender/CopyPasteTest.java b/hostsidetests/devicepolicy/app/IntentSender/src/com/android/cts/intent/sender/CopyPasteTest.java
new file mode 100644
index 0000000..a5d83db
--- /dev/null
+++ b/hostsidetests/devicepolicy/app/IntentSender/src/com/android/cts/intent/sender/CopyPasteTest.java
@@ -0,0 +1,119 @@
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.cts.intent.sender;
+
+import android.content.ClipboardManager;
+import android.content.ClipData;
+import android.content.Context;
+import android.content.Intent;
+import android.os.Bundle;
+import android.test.InstrumentationTestCase;
+import android.util.Log;
+
+import java.util.concurrent.Semaphore;
+import java.util.concurrent.TimeUnit;
+
+public class CopyPasteTest extends InstrumentationTestCase
+        implements ClipboardManager.OnPrimaryClipChangedListener {
+
+    private IntentSenderActivity mActivity;
+    private ClipboardManager mClipboard;
+    private Semaphore mNotified;
+
+    private static String ACTION_COPY_TO_CLIPBOARD = "com.android.cts.action.COPY_TO_CLIPBOARD";
+
+    private static String INITIAL_TEXT = "initial text";
+    private static String NEW_TEXT = "sample text";
+
+    @Override
+    protected void setUp() throws Exception {
+        super.setUp();
+        Context context = getInstrumentation().getTargetContext();
+        mActivity = launchActivity(context.getPackageName(), IntentSenderActivity.class, null);
+        mClipboard = (ClipboardManager) context.getSystemService(Context.CLIPBOARD_SERVICE);
+    }
+
+    @Override
+    public void tearDown() throws Exception {
+        mActivity.finish();
+        super.tearDown();
+    }
+
+    public void testCanReadAcrossProfiles() throws Exception {
+        ClipData clip = ClipData.newPlainText(""/*label*/, INITIAL_TEXT);
+        mClipboard.setPrimaryClip(clip);
+        assertEquals(INITIAL_TEXT , getTextFromClipboard());
+
+        askCrossProfileReceiverToCopy(NEW_TEXT);
+
+        assertEquals(NEW_TEXT, getTextFromClipboard());
+    }
+
+    public void testCannotReadAcrossProfiles() throws Exception {
+        ClipData clip = ClipData.newPlainText(""/*label*/, INITIAL_TEXT);
+        mClipboard.setPrimaryClip(clip);
+        assertEquals(INITIAL_TEXT , getTextFromClipboard());
+
+        askCrossProfileReceiverToCopy(NEW_TEXT);
+
+        String clipboardText = getTextFromClipboard();
+        assertTrue("The clipboard text is " + clipboardText + " but should be <null> or "
+                + INITIAL_TEXT, clipboardText == null || clipboardText.equals(INITIAL_TEXT));
+    }
+
+    public void testIsNotified() throws Exception {
+        try {
+            mNotified = new Semaphore(0);
+            mActivity.addPrimaryClipChangedListener(this);
+
+            askCrossProfileReceiverToCopy(NEW_TEXT);
+
+            assertTrue(mNotified.tryAcquire(5, TimeUnit.SECONDS));
+        } finally {
+            mActivity.removePrimaryClipChangedListener(this);
+        }
+    }
+
+    private void askCrossProfileReceiverToCopy(String text) throws Exception {
+        Intent intent = new Intent(ACTION_COPY_TO_CLIPBOARD);
+        intent.putExtra("extra_text", text);
+        mActivity.getCrossProfileResult(intent);
+    }
+
+    private String getTextFromClipboard() {
+        ClipData clip = mClipboard.getPrimaryClip();
+        if (clip == null) {
+            return null;
+        }
+        ClipData.Item item = clip.getItemAt(0);
+        if (item == null) {
+            return null;
+        }
+        CharSequence text = item.getText();
+        if (text == null) {
+            return null;
+        }
+        return text.toString();
+    }
+
+
+    @Override
+    public void onPrimaryClipChanged() {
+        mNotified.release();
+    }
+
+}
diff --git a/hostsidetests/devicepolicy/app/IntentSender/src/com/android/cts/intent/sender/IntentSenderActivity.java b/hostsidetests/devicepolicy/app/IntentSender/src/com/android/cts/intent/sender/IntentSenderActivity.java
index 00fa6b7..fd421ac 100644
--- a/hostsidetests/devicepolicy/app/IntentSender/src/com/android/cts/intent/sender/IntentSenderActivity.java
+++ b/hostsidetests/devicepolicy/app/IntentSender/src/com/android/cts/intent/sender/IntentSenderActivity.java
@@ -17,15 +17,27 @@
 package com.android.cts.intent.sender;
 
 import android.app.Activity;
+import android.content.ClipboardManager;
+import android.content.ClipboardManager.OnPrimaryClipChangedListener;
+import android.content.ComponentName;
 import android.content.Intent;
+import android.content.pm.PackageManager;
+import android.content.pm.ResolveInfo;
+import android.os.Bundle;
+import android.util.Log;
 
 import java.util.concurrent.SynchronousQueue;
 import java.util.concurrent.TimeUnit;
+import java.util.List;
 
 public class IntentSenderActivity extends Activity {
 
+    private static String TAG = "IntentSenderActivity";
+
     private final SynchronousQueue<Result> mResult = new SynchronousQueue<>();
 
+    private ClipboardManager mClipboardManager;
+
     public static class Result {
         public final int resultCode;
         public final Intent data;
@@ -37,6 +49,12 @@
     }
 
     @Override
+    protected void onCreate(Bundle savedInstanceState) {
+        super.onCreate(savedInstanceState);
+        mClipboardManager = (ClipboardManager) getSystemService(CLIPBOARD_SERVICE);
+    }
+
+    @Override
     protected void onActivityResult(int requestCode, int resultCode, Intent data) {
         if (resultCode == Activity.RESULT_OK) {
             try {
@@ -52,4 +70,38 @@
         final Result result = mResult.poll(30, TimeUnit.SECONDS);
         return (result != null) ? result.data : null;
     }
+
+    /**
+     * This method will send an intent accross profiles to IntentReceiverActivity, and return the
+     * result intent set by IntentReceiverActivity.
+     */
+    public Intent getCrossProfileResult(Intent intent)
+            throws Exception {
+        PackageManager pm = getPackageManager();
+        List<ResolveInfo> ris = pm.queryIntentActivities(intent, PackageManager.MATCH_DEFAULT_ONLY);
+        //  There should be two matches:
+        //  - com.android.cts.intent.receiver (on the current profile).
+        //  - One that will send the intent to the other profile.
+        //  It's the second one we want to send the intent to.
+
+        for (ResolveInfo ri : ris) {
+            if (!ri.activityInfo.packageName.equals("com.android.cts.intent.receiver")) {
+                intent.setComponent(new ComponentName(ri.activityInfo.packageName,
+                        ri.activityInfo.name));
+                return getResult(intent);
+            }
+        }
+        Log.e(TAG, "The intent " + intent + " cannot be sent accross profiles");
+        return null;
+    }
+
+    public void addPrimaryClipChangedListener(OnPrimaryClipChangedListener listener) {
+        mClipboardManager.addPrimaryClipChangedListener(listener);
+    }
+
+    public void removePrimaryClipChangedListener(
+            OnPrimaryClipChangedListener listener) {
+        mClipboardManager.removePrimaryClipChangedListener(listener);
+    }
+
 }
diff --git a/hostsidetests/devicepolicy/app/LauncherTests/src/com/android/cts/launchertests/LauncherAppsTests.java b/hostsidetests/devicepolicy/app/LauncherTests/src/com/android/cts/launchertests/LauncherAppsTests.java
index 3d44ecd..e076fc3 100644
--- a/hostsidetests/devicepolicy/app/LauncherTests/src/com/android/cts/launchertests/LauncherAppsTests.java
+++ b/hostsidetests/devicepolicy/app/LauncherTests/src/com/android/cts/launchertests/LauncherAppsTests.java
@@ -61,7 +61,7 @@
     public static final int MSG_CHECK_PACKAGE_ADDED = 1;
     public static final int MSG_CHECK_PACKAGE_REMOVED = 2;
     public static final int MSG_CHECK_PACKAGE_CHANGED = 3;
-    public static final int MSG_CHECK_NO_CALLBACK = 4;
+    public static final int MSG_CHECK_NO_PACKAGE_ADDED = 4;
 
     public static final int RESULT_PASS = 1;
     public static final int RESULT_FAIL = 2;
@@ -139,8 +139,8 @@
         assertEquals(RESULT_PASS, result);
     }
 
-    public void testNoCallbackForUser() throws Throwable {
-        int result = sendMessageToCallbacksService(MSG_CHECK_NO_CALLBACK,
+    public void testNoPackageAddedCallbackForUser() throws Throwable {
+        int result = sendMessageToCallbacksService(MSG_CHECK_NO_PACKAGE_ADDED,
                 mUser, SIMPLE_APP_PACKAGE);
         assertEquals(RESULT_PASS, result);
     }
@@ -238,8 +238,8 @@
 
         public int waitForResult() {
             try {
-                if (mSemaphore.tryAcquire(5, TimeUnit.SECONDS)) {
-                    return result;
+                if (mSemaphore.tryAcquire(120, TimeUnit.SECONDS)) {
+                     return result;
                 }
             } catch (InterruptedException e) {
             }
diff --git a/hostsidetests/devicepolicy/app/LauncherTestsSupport/src/com/android/cts/launchertests/support/LauncherCallbackTestsService.java b/hostsidetests/devicepolicy/app/LauncherTestsSupport/src/com/android/cts/launchertests/support/LauncherCallbackTestsService.java
index 8d61496..195a18b 100644
--- a/hostsidetests/devicepolicy/app/LauncherTestsSupport/src/com/android/cts/launchertests/support/LauncherCallbackTestsService.java
+++ b/hostsidetests/devicepolicy/app/LauncherTestsSupport/src/com/android/cts/launchertests/support/LauncherCallbackTestsService.java
@@ -22,6 +22,7 @@
 import android.content.pm.PackageManager.NameNotFoundException;
 import android.os.Bundle;
 import android.os.Handler;
+import android.os.HandlerThread;
 import android.os.IBinder;
 import android.os.Message;
 import android.os.Messenger;
@@ -31,6 +32,9 @@
 import android.util.Pair;
 
 import java.util.ArrayList;
+import java.util.concurrent.BlockingQueue;
+import java.util.concurrent.LinkedBlockingQueue;
+import java.util.concurrent.TimeUnit;
 import java.util.List;
 
 /**
@@ -49,23 +53,28 @@
     public static final int MSG_CHECK_PACKAGE_ADDED = 1;
     public static final int MSG_CHECK_PACKAGE_REMOVED = 2;
     public static final int MSG_CHECK_PACKAGE_CHANGED = 3;
-    public static final int MSG_CHECK_NO_CALLBACK = 4;
+    public static final int MSG_CHECK_NO_PACKAGE_ADDED = 4;
 
     public static final int RESULT_PASS = 1;
     public static final int RESULT_FAIL = 2;
 
     private static final String TAG = "LauncherCallbackTests";
 
-    private static List<Pair<String, UserHandle>> mPackagesAdded
-            = new ArrayList<Pair<String, UserHandle>>();
-    private static List<Pair<String, UserHandle>> mPackagesRemoved
-            = new ArrayList<Pair<String, UserHandle>>();
-    private static List<Pair<String, UserHandle>> mPackagesChanged
-            = new ArrayList<Pair<String, UserHandle>>();
-    private static Object mPackagesLock = new Object();
+    private static BlockingQueue<Pair<String, UserHandle>> mPackagesAdded
+            = new LinkedBlockingQueue();
+    private static BlockingQueue<Pair<String, UserHandle>> mPackagesRemoved
+            = new LinkedBlockingQueue();
+    private static BlockingQueue<Pair<String, UserHandle>> mPackagesChanged
+            = new LinkedBlockingQueue();
 
     private TestCallback mCallback;
+    private Object mCallbackLock = new Object();
     private final Messenger mMessenger = new Messenger(new CheckHandler());
+    private final HandlerThread mCallbackThread = new HandlerThread("callback");
+
+    public LauncherCallbackTestsService() {
+        mCallbackThread.start();
+    }
 
     class CheckHandler extends Handler {
         @Override
@@ -77,6 +86,7 @@
             try {
                 switch (msg.what) {
                     case MSG_CHECK_PACKAGE_ADDED: {
+                        Log.i(TAG, "MSG_CHECK_PACKAGE_ADDED");
                         boolean exists = eventExists(params, mPackagesAdded);
                         teardown();
                         msg.replyTo.send(Message.obtain(null, MSG_RESULT,
@@ -84,6 +94,7 @@
                         break;
                     }
                     case MSG_CHECK_PACKAGE_REMOVED: {
+                        Log.i(TAG, "MSG_CHECK_PACKAGE_REMOVED");
                         boolean exists = eventExists(params, mPackagesRemoved);
                         teardown();
                         msg.replyTo.send(Message.obtain(null, MSG_RESULT,
@@ -91,16 +102,16 @@
                         break;
                     }
                     case MSG_CHECK_PACKAGE_CHANGED: {
+                        Log.i(TAG, "MSG_CHECK_PACKAGE_CHANGED");
                         boolean exists = eventExists(params, mPackagesChanged);
                         teardown();
                         msg.replyTo.send(Message.obtain(null, MSG_RESULT,
                                         exists ? RESULT_PASS : RESULT_FAIL, 0));
                         break;
                     }
-                    case MSG_CHECK_NO_CALLBACK: {
-                        boolean exists = eventExists(params, mPackagesAdded)
-                                || eventExists(params, mPackagesRemoved)
-                                || eventExists(params, mPackagesChanged);
+                    case MSG_CHECK_NO_PACKAGE_ADDED: {
+                        Log.i(TAG, "MSG_CHECK_NO_PACKAGE_ADDED");
+                        boolean exists = eventExists(params, mPackagesAdded);
                         teardown();
                         msg.replyTo.send(Message.obtain(null, MSG_RESULT,
                                         exists ? RESULT_FAIL : RESULT_PASS, 0));
@@ -129,24 +140,26 @@
     private void setup() {
         LauncherApps launcherApps = (LauncherApps) getSystemService(
                 Context.LAUNCHER_APPS_SERVICE);
-        synchronized (mPackagesLock) {
-            mPackagesAdded.clear();
-            mPackagesRemoved.clear();
-            mPackagesChanged.clear();
+        synchronized (mCallbackLock) {
             if (mCallback != null) {
                 launcherApps.unregisterCallback(mCallback);
             }
+            mPackagesAdded.clear();
+            mPackagesRemoved.clear();
+            mPackagesChanged.clear();
             mCallback = new TestCallback();
-            launcherApps.registerCallback(mCallback);
+            launcherApps.registerCallback(mCallback, new Handler(mCallbackThread.getLooper()));
+            Log.i(TAG, "started listening for events");
         }
     }
 
     private void teardown() {
         LauncherApps launcherApps = (LauncherApps) getSystemService(
                 Context.LAUNCHER_APPS_SERVICE);
-        synchronized (mPackagesLock) {
+        synchronized (mCallbackLock) {
             if (mCallback != null) {
                 launcherApps.unregisterCallback(mCallback);
+                Log.i(TAG, "stopped listening for events");
                 mCallback = null;
             }
             mPackagesAdded.clear();
@@ -155,20 +168,23 @@
         }
     }
 
-    private boolean eventExists(Bundle params, List<Pair<String, UserHandle>> events) {
+    private boolean eventExists(Bundle params, BlockingQueue<Pair<String, UserHandle>> events) {
         UserHandle user = params.getParcelable(USER_EXTRA);
         String packageName = params.getString(PACKAGE_EXTRA);
-        synchronized (mPackagesLock) {
-            if (events != null) {
-                for (Pair<String, UserHandle> added : events) {
-                    if (added.first.equals(packageName) && added.second.equals(user)) {
-                        Log.i(TAG, "Event exists " + packageName + " for user " + user);
-                        return true;
-                    }
+        Log.i(TAG, "checking for " + packageName + " " + user);
+        try {
+            Pair<String, UserHandle> event = events.poll(60, TimeUnit.SECONDS);
+            while (event != null) {
+                if (event.first.equals(packageName) && event.second.equals(user)) {
+                    Log.i(TAG, "Event exists " + packageName + " for user " + user);
+                    return true;
                 }
+                event = events.poll(20, TimeUnit.SECONDS);
             }
-            return false;
+        } catch (InterruptedException e) {
+            Log.e(TAG, "Failed checking for event", e);
         }
+        return false;
     }
 
     @Override
@@ -178,20 +194,29 @@
 
     private class TestCallback extends LauncherApps.Callback {
         public void onPackageRemoved(String packageName, UserHandle user) {
-            synchronized (mPackagesLock) {
-                mPackagesRemoved.add(new Pair<String, UserHandle>(packageName, user));
+            Log.i(TAG, "package removed event " + packageName + " " + user);
+            try {
+                mPackagesRemoved.put(new Pair(packageName, user));
+            } catch (InterruptedException e) {
+                Log.e(TAG, "Failed saving event", e);
             }
         }
 
         public void onPackageAdded(String packageName, UserHandle user) {
-            synchronized (mPackagesLock) {
-                mPackagesAdded.add(new Pair<String, UserHandle>(packageName, user));
+            Log.i(TAG, "package added event " + packageName + " " + user);
+            try {
+                mPackagesAdded.put(new Pair(packageName, user));
+            } catch (InterruptedException e) {
+                Log.e(TAG, "Failed saving event", e);
             }
         }
 
         public void onPackageChanged(String packageName, UserHandle user) {
-            synchronized (mPackagesLock) {
-                mPackagesChanged.add(new Pair<String, UserHandle>(packageName, user));
+            Log.i(TAG, "package changed event " + packageName + " " + user);
+            try {
+                mPackagesChanged.put(new Pair(packageName, user));
+            } catch (InterruptedException e) {
+                Log.e(TAG, "Failed saving event", e);
             }
         }
 
diff --git a/hostsidetests/devicepolicy/app/ManagedProfile/AndroidManifest.xml b/hostsidetests/devicepolicy/app/ManagedProfile/AndroidManifest.xml
index 008ed38..e430785 100644
--- a/hostsidetests/devicepolicy/app/ManagedProfile/AndroidManifest.xml
+++ b/hostsidetests/devicepolicy/app/ManagedProfile/AndroidManifest.xml
@@ -18,6 +18,10 @@
     package="com.android.cts.managedprofile">
 
     <uses-sdk android:minSdkVersion="20"/>
+    <uses-permission android:name="android.permission.BLUETOOTH" />
+    <uses-permission android:name="android.permission.BLUETOOTH_ADMIN" />
+    <uses-permission android:name="android.permission.READ_CONTACTS" />
+    <uses-permission android:name="android.permission.WRITE_CONTACTS" />
 
     <application>
         <uses-library android:name="android.test.runner" />
diff --git a/hostsidetests/devicepolicy/app/ManagedProfile/res/raw/ic_contact_picture.png b/hostsidetests/devicepolicy/app/ManagedProfile/res/raw/ic_contact_picture.png
new file mode 100644
index 0000000..37b558b
--- /dev/null
+++ b/hostsidetests/devicepolicy/app/ManagedProfile/res/raw/ic_contact_picture.png
Binary files differ
diff --git a/hostsidetests/devicepolicy/app/ManagedProfile/src/com/android/cts/managedprofile/BluetoothTest.java b/hostsidetests/devicepolicy/app/ManagedProfile/src/com/android/cts/managedprofile/BluetoothTest.java
new file mode 100644
index 0000000..4d7ddeb
--- /dev/null
+++ b/hostsidetests/devicepolicy/app/ManagedProfile/src/com/android/cts/managedprofile/BluetoothTest.java
@@ -0,0 +1,203 @@
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.cts.managedprofile;
+
+import android.bluetooth.BluetoothAdapter;
+import android.bluetooth.BluetoothDevice;
+import android.bluetooth.BluetoothServerSocket;
+import android.test.AndroidTestCase;
+
+import java.io.IOException;
+import java.util.Set;
+import java.util.UUID;
+
+/**
+ * Test that the basic bluetooth API is callable in managed profiles.
+ * These tests should only be executed if the device supports bluetooth,
+ * i.e. if it has the {@link android.content.pm.PackageManager#FEATURE_BLUETOOTH} feature.
+ *
+ * This includes tests for the {@link BluetoothAdapter}.
+ * The corresponding CTS tests in the primary profile are in
+ * {@link android.bluetooth.cts.BasicAdapterTest}.
+ * TODO: Merge the primary and managed profile tests into one.
+ */
+public class BluetoothTest extends AndroidTestCase {
+    private static final int DISABLE_TIMEOUT_MS = 8000;
+    private static final int ENABLE_TIMEOUT_MS = 10000;
+    private static final int POLL_TIME_MS = 400;
+    private static final int CHECK_WAIT_TIME_MS = 1000;
+
+    private BluetoothAdapter mAdapter;
+    private boolean mBtWasEnabled;
+
+    public void setUp() throws Exception {
+        super.setUp();
+        mAdapter = BluetoothAdapter.getDefaultAdapter();
+        assertNotNull(mAdapter);
+        mBtWasEnabled = mAdapter.isEnabled();
+    }
+
+    public void tearDown() throws Exception {
+        if (mBtWasEnabled != mAdapter.isEnabled()) {
+            if (mBtWasEnabled) {
+                enable();
+            } else {
+                disable();
+            }
+        }
+        super.tearDown();
+    }
+
+    /**
+     * Checks enable(), disable(), getState(), isEnabled()
+     */
+    public void testEnableDisable() {
+        disable();
+        enable();
+    }
+
+    /**
+     * Test the getAddress() function.
+     */
+    public void testGetAddress() {
+        assertTrue(BluetoothAdapter.checkBluetoothAddress(mAdapter.getAddress()));
+    }
+
+    /**
+     * Tests the listenUsingRfcommWithServiceRecord function.
+     */
+    public void testListenUsingRfcommWithServiceRecord() throws IOException {
+        enable();
+        BluetoothServerSocket socket = mAdapter.listenUsingRfcommWithServiceRecord(
+                "test", UUID.randomUUID());
+        assertNotNull(socket);
+        socket.close();
+    }
+
+    /**
+     * Test the getRemoteDevice() function.
+     */
+    public void testGetRemoteDevice() {
+        // getRemoteDevice() should work even with Bluetooth disabled
+        disable();
+
+        // test bad addresses
+        try {
+            mAdapter.getRemoteDevice((String)null);
+            fail("IllegalArgumentException not thrown");
+        } catch (IllegalArgumentException e) {
+        }
+        try {
+            mAdapter.getRemoteDevice("00:00:00:00:00:00:00:00");
+            fail("IllegalArgumentException not thrown");
+        } catch (IllegalArgumentException e) {
+        }
+        try {
+            mAdapter.getRemoteDevice((byte[])null);
+            fail("IllegalArgumentException not thrown");
+        } catch (IllegalArgumentException e) {
+        }
+        try {
+            mAdapter.getRemoteDevice(new byte[] {0x00, 0x00, 0x00, 0x00, 0x00});
+            fail("IllegalArgumentException not thrown");
+        } catch (IllegalArgumentException e) {
+        }
+
+        // test success
+        BluetoothDevice device = mAdapter.getRemoteDevice("00:11:22:AA:BB:CC");
+        assertNotNull(device);
+        assertEquals("00:11:22:AA:BB:CC", device.getAddress());
+        device = mAdapter.getRemoteDevice(
+                new byte[] {0x01, 0x02, 0x03, 0x04, 0x05, 0x06});
+        assertNotNull(device);
+        assertEquals("01:02:03:04:05:06", device.getAddress());
+    }
+
+    /**
+     * Helper to turn BT off.
+     * This method will either fail on an assert, or return with BT turned off.
+     * Behavior of getState() and isEnabled() are validated along the way.
+     */
+    private void disable() {
+        sleep(CHECK_WAIT_TIME_MS);
+        if (mAdapter.getState() == BluetoothAdapter.STATE_OFF) {
+            assertFalse(mAdapter.isEnabled());
+            return;
+        }
+
+        assertEquals(BluetoothAdapter.STATE_ON, mAdapter.getState());
+        assertTrue(mAdapter.isEnabled());
+        assertTrue(mAdapter.disable());
+        boolean turnOff = false;
+        for (int i=0; i<DISABLE_TIMEOUT_MS/POLL_TIME_MS; i++) {
+            sleep(POLL_TIME_MS);
+            int state = mAdapter.getState();
+            switch (state) {
+            case BluetoothAdapter.STATE_OFF:
+                assertFalse(mAdapter.isEnabled());
+                return;
+            default:
+                if (state != BluetoothAdapter.STATE_ON || turnOff) {
+                    assertEquals(BluetoothAdapter.STATE_TURNING_OFF, state);
+                    turnOff = true;
+                }
+                break;
+            }
+        }
+        fail("disable() timeout");
+    }
+
+    /**
+     * Helper to turn BT on.
+     * This method will either fail on an assert, or return with BT turned on.
+     * Behavior of getState() and isEnabled() are validated along the way.
+     */
+    private void enable() {
+        sleep(CHECK_WAIT_TIME_MS);
+        if (mAdapter.getState() == BluetoothAdapter.STATE_ON) {
+            assertTrue(mAdapter.isEnabled());
+            return;
+        }
+
+        assertEquals(BluetoothAdapter.STATE_OFF, mAdapter.getState());
+        assertFalse(mAdapter.isEnabled());
+        assertTrue(mAdapter.enable());
+        boolean turnOn = false;
+        for (int i=0; i<ENABLE_TIMEOUT_MS/POLL_TIME_MS; i++) {
+            sleep(POLL_TIME_MS);
+            int state = mAdapter.getState();
+            switch (state) {
+            case BluetoothAdapter.STATE_ON:
+                assertTrue(mAdapter.isEnabled());
+                return;
+            default:
+                if (state != BluetoothAdapter.STATE_OFF || turnOn) {
+                    assertEquals(BluetoothAdapter.STATE_TURNING_ON, state);
+                    turnOn = true;
+                }
+                break;
+            }
+        }
+        fail("enable() timeout");
+    }
+
+    private void sleep(long t) {
+        try {
+            Thread.sleep(t);
+        } catch (InterruptedException e) {}
+    }
+}
diff --git a/hostsidetests/devicepolicy/app/ManagedProfile/src/com/android/cts/managedprofile/ContactsTest.java b/hostsidetests/devicepolicy/app/ManagedProfile/src/com/android/cts/managedprofile/ContactsTest.java
new file mode 100644
index 0000000..faee6dc
--- /dev/null
+++ b/hostsidetests/devicepolicy/app/ManagedProfile/src/com/android/cts/managedprofile/ContactsTest.java
@@ -0,0 +1,285 @@
+/*
+ * 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.managedprofile;
+
+import android.annotation.TargetApi;
+import android.app.admin.DevicePolicyManager;
+import android.content.ContentProviderOperation;
+import android.content.ContentResolver;
+import android.content.Context;
+import android.content.OperationApplicationException;
+import android.content.res.Resources.NotFoundException;
+import android.database.Cursor;
+import android.net.Uri;
+import android.os.Build;
+import android.os.RemoteException;
+import android.provider.ContactsContract;
+import android.provider.ContactsContract.CommonDataKinds.Phone;
+import android.provider.ContactsContract.CommonDataKinds.Photo;
+import android.provider.ContactsContract.PhoneLookup;
+import android.provider.ContactsContract.RawContacts;
+import android.test.AndroidTestCase;
+
+import java.io.ByteArrayOutputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.util.ArrayList;
+
+@TargetApi(Build.VERSION_CODES.LOLLIPOP)
+public class ContactsTest extends AndroidTestCase {
+
+    private static final String TEST_ACCOUNT_NAME = "CTS";
+    private static final String TEST_ACCOUNT_TYPE = "com.android.cts.test";
+    private static final String PRIMARY_CONTACT_DISPLAY_NAME = "Primary";
+    private static final String PRIMARY_CONTACT_PHONE = "00000001";
+    private static final String MANAGED_CONTACT_DISPLAY_NAME = "Managed";
+    private static final String MANAGED_CONTACT_PHONE = "6891999";
+
+    private DevicePolicyManager mDevicePolicyManager;
+    private ContentResolver mResolver;
+
+    private static class ContactInfo {
+        String contactId;
+        String displayName;
+        String photoUri;
+        String photoThumbnailUri;
+        String photoId;
+
+        public ContactInfo(String contactId, String displayName, String photoUri,
+                String photoThumbnailUri, String photoId) {
+            this.contactId = contactId;
+            this.displayName = displayName;
+            this.photoUri = photoUri;
+            this.photoThumbnailUri = photoThumbnailUri;
+            this.photoId = photoId;
+        }
+
+        private boolean hasPhotoUri() {
+            return photoUri != null && photoThumbnailUri != null;
+        }
+
+        private boolean hasPhotoId() {
+            return photoId != null;
+        }
+    }
+
+    @Override
+    protected void setUp() throws Exception {
+        super.setUp();
+        mResolver = getContext().getContentResolver();
+        mDevicePolicyManager = (DevicePolicyManager) mContext
+                .getSystemService(Context.DEVICE_POLICY_SERVICE);
+    }
+
+    public void testPrimaryProfilePhoneLookup_insertedAndfound() throws RemoteException,
+            OperationApplicationException, NotFoundException, IOException {
+        assertFalse(isManagedProfile());
+        // Do not insert to primary contact
+        insertContact(PRIMARY_CONTACT_DISPLAY_NAME, PRIMARY_CONTACT_PHONE, 0);
+
+        ContactInfo contactInfo = getContactInfo(PRIMARY_CONTACT_PHONE);
+        assertNotNull(contactInfo);
+        assertEquals(PRIMARY_CONTACT_DISPLAY_NAME, contactInfo.displayName);
+        assertFalse(contactInfo.hasPhotoUri());
+        assertFalse(contactInfo.hasPhotoId());
+        assertFalse(isEnterpriseContactId(contactInfo.contactId));
+    }
+
+    public void testManagedProfilePhoneLookup_insertedAndfound() throws RemoteException,
+            OperationApplicationException, NotFoundException, IOException {
+        assertTrue(isManagedProfile());
+        // Insert ic_contact_picture as photo in managed contact
+        insertContact(MANAGED_CONTACT_DISPLAY_NAME, MANAGED_CONTACT_PHONE,
+                com.android.cts.managedprofile.R.raw.ic_contact_picture);
+
+        ContactInfo contactInfo = getContactInfo(MANAGED_CONTACT_PHONE);
+        assertNotNull(contactInfo);
+        assertEquals(MANAGED_CONTACT_DISPLAY_NAME, contactInfo.displayName);
+        assertTrue(contactInfo.hasPhotoUri());
+        assertTrue(contactInfo.hasPhotoId());
+        assertFalse(isEnterpriseContactId(contactInfo.contactId));
+    }
+
+    public void testPrimaryProfileEnterprisePhoneLookup_canAccessEnterpriseContact() {
+        assertFalse(isManagedProfile());
+        ContactInfo contactInfo = getEnterpriseContactInfo(MANAGED_CONTACT_PHONE);
+        assertEquals(MANAGED_CONTACT_DISPLAY_NAME, contactInfo.displayName);
+        assertTrue(contactInfo.hasPhotoUri());
+        // Cannot get photo id in ENTERPRISE_CONTENT_FILTER_URI
+        assertFalse(contactInfo.hasPhotoId());
+        assertTrue(isEnterpriseContactId(contactInfo.contactId));
+    }
+
+    public void testPrimaryProfilePhoneLookup_canAccessPrimaryContact() {
+        assertFalse(isManagedProfile());
+        ContactInfo contactInfo = getEnterpriseContactInfo(PRIMARY_CONTACT_PHONE);
+        assertEquals(PRIMARY_CONTACT_DISPLAY_NAME, contactInfo.displayName);
+        assertFalse(contactInfo.hasPhotoUri());
+        assertFalse(contactInfo.hasPhotoId());
+        assertFalse(isEnterpriseContactId(contactInfo.contactId));
+    }
+
+    public void testManagedProfilePhoneLookup_canAccessEnterpriseContact() {
+        assertTrue(isManagedProfile());
+        ContactInfo contactInfo = getEnterpriseContactInfo(MANAGED_CONTACT_PHONE);
+        assertEquals(MANAGED_CONTACT_DISPLAY_NAME, contactInfo.displayName);
+        assertTrue(contactInfo.hasPhotoUri());
+        assertTrue(contactInfo.hasPhotoId());
+        assertFalse(isEnterpriseContactId(contactInfo.contactId));
+    }
+
+    public void testPrimaryProfilePhoneLookup_canNotAccessEnterpriseContact() {
+        assertFalse(isManagedProfile());
+        ContactInfo contactInfo = getEnterpriseContactInfo(MANAGED_CONTACT_PHONE);
+        assertNull(contactInfo);
+    }
+
+    public void testManagedProfilePhoneLookup_canNotAccessPrimaryContact() {
+        assertTrue(isManagedProfile());
+        ContactInfo contactInfo = getEnterpriseContactInfo(PRIMARY_CONTACT_PHONE);
+        assertNull(contactInfo);
+    }
+
+    public void testSetCrossProfileCallerIdDisabled_true() {
+        assertTrue(isManagedProfile());
+        mDevicePolicyManager.setCrossProfileCallerIdDisabled(
+                BaseManagedProfileTest.ADMIN_RECEIVER_COMPONENT, true);
+    }
+
+    public void testSetCrossProfileCallerIdDisabled_false() {
+        assertTrue(isManagedProfile());
+        mDevicePolicyManager.setCrossProfileCallerIdDisabled(
+                BaseManagedProfileTest.ADMIN_RECEIVER_COMPONENT, false);
+    }
+
+    public void testCurrentProfileContacts_removeContacts() {
+        removeAllTestContactsInProfile();
+    }
+
+    private boolean isManagedProfile() {
+        String adminPackage = BaseManagedProfileTest.ADMIN_RECEIVER_COMPONENT.getPackageName();
+        return mDevicePolicyManager.isProfileOwnerApp(adminPackage);
+    }
+
+    private void insertContact(String displayName, String phoneNumber, int photoResId)
+            throws RemoteException,
+            OperationApplicationException, NotFoundException, IOException {
+        ArrayList<ContentProviderOperation> ops = new ArrayList<ContentProviderOperation>();
+        ops.add(ContentProviderOperation
+                .newInsert(ContactsContract.RawContacts.CONTENT_URI)
+                .withValue(ContactsContract.RawContacts.ACCOUNT_TYPE, TEST_ACCOUNT_TYPE)
+                .withValue(ContactsContract.RawContacts.ACCOUNT_NAME, TEST_ACCOUNT_NAME)
+                .build());
+        ops.add(ContentProviderOperation
+                .newInsert(ContactsContract.Data.CONTENT_URI)
+                .withValueBackReference(ContactsContract.Data.RAW_CONTACT_ID, 0)
+                .withValue(
+                        ContactsContract.Data.MIMETYPE,
+                        ContactsContract.CommonDataKinds.StructuredName.CONTENT_ITEM_TYPE)
+                .withValue(
+                        ContactsContract.CommonDataKinds.StructuredName.DISPLAY_NAME,
+                        displayName)
+                .build());
+        ops.add(ContentProviderOperation
+                .newInsert(ContactsContract.Data.CONTENT_URI)
+                .withValueBackReference(ContactsContract.Data.RAW_CONTACT_ID, 0)
+                .withValue(
+                        ContactsContract.Data.MIMETYPE,
+                        ContactsContract.CommonDataKinds.Phone.CONTENT_ITEM_TYPE)
+                .withValue(ContactsContract.CommonDataKinds.Phone.NUMBER,
+                        phoneNumber)
+                .withValue(ContactsContract.CommonDataKinds.Phone.TYPE,
+                        Phone.TYPE_MOBILE)
+                .build());
+        if (photoResId != 0) {
+            InputStream phoneInputStream = mContext.getResources().openRawResource(photoResId);
+            byte[] rawPhoto = getByteFromStream(phoneInputStream);
+            ops.add(ContentProviderOperation
+                    .newInsert(ContactsContract.Data.CONTENT_URI)
+                    .withValueBackReference(ContactsContract.Data.RAW_CONTACT_ID, 0)
+                    .withValue(
+                            ContactsContract.Data.MIMETYPE,
+                            ContactsContract.CommonDataKinds.Photo.CONTENT_ITEM_TYPE)
+                    .withValue(Photo.PHOTO, rawPhoto)
+                    .build());
+        }
+
+        mResolver.applyBatch(ContactsContract.AUTHORITY, ops);
+    }
+
+    private ContactInfo getContactInfoFromUri(Uri phoneLookupUri, String phoneNumber) {
+        Uri uri = Uri.withAppendedPath(phoneLookupUri,
+                Uri.encode(phoneNumber));
+        Cursor cursor = mResolver.query(uri,
+                new String[] {
+                        PhoneLookup._ID, PhoneLookup.DISPLAY_NAME, PhoneLookup.PHOTO_URI,
+                        PhoneLookup.PHOTO_THUMBNAIL_URI, PhoneLookup.PHOTO_ID
+                }, null, null, null);
+        if (cursor == null) {
+            return null;
+        }
+        ContactInfo result = null;
+        if (cursor.moveToFirst()) {
+            result = new ContactInfo(
+                    cursor.getString(cursor.getColumnIndexOrThrow(PhoneLookup._ID)),
+                    cursor.getString(cursor.getColumnIndexOrThrow(PhoneLookup.DISPLAY_NAME)),
+                    cursor.getString(cursor.getColumnIndexOrThrow(PhoneLookup.PHOTO_URI)),
+                    cursor.getString(cursor.getColumnIndexOrThrow(PhoneLookup.PHOTO_THUMBNAIL_URI)),
+                    cursor.getString(cursor.getColumnIndexOrThrow(PhoneLookup.PHOTO_ID)));
+        }
+        cursor.close();
+        return result;
+    }
+
+    private ContactInfo getContactInfo(String phoneNumber) {
+        return getContactInfoFromUri(PhoneLookup.CONTENT_FILTER_URI,
+                phoneNumber);
+    }
+
+    private ContactInfo getEnterpriseContactInfo(String phoneNumber) {
+        return getContactInfoFromUri(
+                PhoneLookup.ENTERPRISE_CONTENT_FILTER_URI,
+                phoneNumber);
+    }
+
+    private void removeAllTestContactsInProfile() {
+        ArrayList<ContentProviderOperation> ops = new ArrayList<ContentProviderOperation>();
+        ops.add(ContentProviderOperation.newDelete(RawContacts.CONTENT_URI)
+                .withSelection(RawContacts.ACCOUNT_TYPE + "=?", new String[] {TEST_ACCOUNT_TYPE})
+                .build());
+        try {
+            mResolver.applyBatch(ContactsContract.AUTHORITY, ops);
+        } catch (Exception e) {
+            // Catch all exceptions to let tearDown() run smoothly
+            e.printStackTrace();
+        }
+    }
+
+    private static byte[] getByteFromStream(InputStream is) throws IOException {
+        ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
+        byte[] buf = new byte[1024 * 10];
+        int i = 0;
+        while ((i = is.read(buf, 0, buf.length)) > 0) {
+            outputStream.write(buf, 0, i);
+        }
+        return outputStream.toByteArray();
+    }
+
+    private boolean isEnterpriseContactId(String contactId) {
+        return ContactsContract.Contacts.isEnterpriseContactId(Long.valueOf(contactId));
+    }
+}
diff --git a/hostsidetests/devicepolicy/app/ManagedProfile/src/com/android/cts/managedprofile/CrossProfileUtils.java b/hostsidetests/devicepolicy/app/ManagedProfile/src/com/android/cts/managedprofile/CrossProfileUtils.java
index 9615991..49001e9 100644
--- a/hostsidetests/devicepolicy/app/ManagedProfile/src/com/android/cts/managedprofile/CrossProfileUtils.java
+++ b/hostsidetests/devicepolicy/app/ManagedProfile/src/com/android/cts/managedprofile/CrossProfileUtils.java
@@ -21,8 +21,15 @@
 import android.app.admin.DevicePolicyManager;
 import android.content.Context;
 import android.content.IntentFilter;
+import android.os.UserManager;
 import android.test.AndroidTestCase;
 
+/**
+ * The methods in this class are not really tests.
+ * They are just performing an action that is needed for a test.
+ * But we're still using an AndroidTestCase because it's an easy way to call
+ * device-side methods from the host.
+ */
 public class CrossProfileUtils extends AndroidTestCase {
     private static final String ACTION_READ_FROM_URI = "com.android.cts.action.READ_FROM_URI";
 
@@ -31,16 +38,14 @@
     private static final String ACTION_TAKE_PERSISTABLE_URI_PERMISSION =
             "com.android.cts.action.TAKE_PERSISTABLE_URI_PERMISSION";
 
+    private static String ACTION_COPY_TO_CLIPBOARD = "com.android.cts.action.COPY_TO_CLIPBOARD";
+
     public void addParentCanAccessManagedFilters() {
         removeAllFilters();
 
         final DevicePolicyManager dpm = (DevicePolicyManager) getContext().getSystemService(
                 Context.DEVICE_POLICY_SERVICE);
-        IntentFilter intentFilter = new IntentFilter();
-        intentFilter.addAction(ACTION_READ_FROM_URI);
-        intentFilter.addAction(ACTION_WRITE_TO_URI);
-        intentFilter.addAction(ACTION_TAKE_PERSISTABLE_URI_PERMISSION);
-        dpm.addCrossProfileIntentFilter(ADMIN_RECEIVER_COMPONENT, intentFilter,
+        dpm.addCrossProfileIntentFilter(ADMIN_RECEIVER_COMPONENT, getIntentFilter(),
                 DevicePolicyManager.FLAG_PARENT_CAN_ACCESS_MANAGED);
     }
 
@@ -49,12 +54,17 @@
 
         final DevicePolicyManager dpm = (DevicePolicyManager) getContext().getSystemService(
                 Context.DEVICE_POLICY_SERVICE);
+        dpm.addCrossProfileIntentFilter(ADMIN_RECEIVER_COMPONENT, getIntentFilter(),
+                DevicePolicyManager.FLAG_MANAGED_CAN_ACCESS_PARENT);
+    }
+
+    public IntentFilter getIntentFilter() {
         IntentFilter intentFilter = new IntentFilter();
         intentFilter.addAction(ACTION_READ_FROM_URI);
         intentFilter.addAction(ACTION_WRITE_TO_URI);
         intentFilter.addAction(ACTION_TAKE_PERSISTABLE_URI_PERMISSION);
-        dpm.addCrossProfileIntentFilter(ADMIN_RECEIVER_COMPONENT, intentFilter,
-                DevicePolicyManager.FLAG_MANAGED_CAN_ACCESS_PARENT);
+        intentFilter.addAction(ACTION_COPY_TO_CLIPBOARD);
+        return intentFilter;
     }
 
     public void removeAllFilters() {
@@ -62,4 +72,18 @@
                 Context.DEVICE_POLICY_SERVICE);
         dpm.clearCrossProfileIntentFilters(ADMIN_RECEIVER_COMPONENT);
     }
+
+    public void disallowCrossProfileCopyPaste() {
+        DevicePolicyManager dpm = (DevicePolicyManager)
+               getContext().getSystemService(Context.DEVICE_POLICY_SERVICE);
+        dpm.addUserRestriction(ADMIN_RECEIVER_COMPONENT,
+                UserManager.DISALLOW_CROSS_PROFILE_COPY_PASTE);
+    }
+
+    public void allowCrossProfileCopyPaste() {
+        DevicePolicyManager dpm = (DevicePolicyManager)
+               getContext().getSystemService(Context.DEVICE_POLICY_SERVICE);
+        dpm.clearUserRestriction(ADMIN_RECEIVER_COMPONENT,
+                UserManager.DISALLOW_CROSS_PROFILE_COPY_PASTE);
+    }
 }
diff --git a/hostsidetests/devicepolicy/app/ManagedProfile/src/com/android/cts/managedprofile/NfcTest.java b/hostsidetests/devicepolicy/app/ManagedProfile/src/com/android/cts/managedprofile/NfcTest.java
new file mode 100644
index 0000000..c74211d
--- /dev/null
+++ b/hostsidetests/devicepolicy/app/ManagedProfile/src/com/android/cts/managedprofile/NfcTest.java
@@ -0,0 +1,59 @@
+/*
+ * 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.managedprofile;
+
+import android.content.Intent;
+import android.content.pm.PackageManager;
+import android.content.pm.ResolveInfo;
+import android.test.AndroidTestCase;
+import android.util.Log;
+
+public class NfcTest extends AndroidTestCase {
+    private static final String SAMPLE_TEXT = "This is my text to send.";
+    private static final String TEXT_MIME_TYPE = "text/plain";
+    private static final String NFC_BEAM_ACTIVITY = "com.android.nfc.BeamShareActivity";
+
+    public void testNfcShareDisabled() throws Exception {
+        Intent intent = getTextShareIntent();
+        assertFalse("Nfc beam activity should not be resolved", isNfcBeamActivityResolved(intent));
+    }
+
+    public void testNfcShareEnabled() throws Exception {
+        Intent intent = getTextShareIntent();
+        assertTrue("Nfc beam activity should be resolved", isNfcBeamActivityResolved(intent));
+    }
+
+    private Intent getTextShareIntent() {
+        Intent intent = new Intent();
+        intent.setAction(Intent.ACTION_SEND);
+        intent.putExtra(Intent.EXTRA_TEXT, SAMPLE_TEXT);
+        intent.setType(TEXT_MIME_TYPE);
+        return intent;
+    }
+
+    private boolean isNfcBeamActivityResolved(Intent intent) {
+        PackageManager pm = mContext.getPackageManager();
+        for (ResolveInfo resolveInfo : pm.queryIntentActivities(intent, 0)) {
+            if (NFC_BEAM_ACTIVITY.equals(resolveInfo.activityInfo.name)) {
+                return true;
+            }
+        }
+
+        return false;
+    }
+}
+
diff --git a/hostsidetests/devicepolicy/src/com/android/cts/devicepolicy/LauncherAppsMultiUserTest.java b/hostsidetests/devicepolicy/src/com/android/cts/devicepolicy/LauncherAppsMultiUserTest.java
index 0af38a4..8da189f 100644
--- a/hostsidetests/devicepolicy/src/com/android/cts/devicepolicy/LauncherAppsMultiUserTest.java
+++ b/hostsidetests/devicepolicy/src/com/android/cts/devicepolicy/LauncherAppsMultiUserTest.java
@@ -81,7 +81,7 @@
         try {
             assertTrue(runDeviceTests(LAUNCHER_TESTS_PKG,
                     LAUNCHER_TESTS_CLASS,
-                            "testNoCallbackForUser",
+                            "testNoPackageAddedCallbackForUser",
                             0, "-e testUser " + mSecondaryUserSerialNumber));
         } finally {
             getDevice().uninstallPackage(SIMPLE_APP_PKG);
diff --git a/hostsidetests/devicepolicy/src/com/android/cts/devicepolicy/LauncherAppsProfileTest.java b/hostsidetests/devicepolicy/src/com/android/cts/devicepolicy/LauncherAppsProfileTest.java
index f8c2e7d..43f1f5a 100644
--- a/hostsidetests/devicepolicy/src/com/android/cts/devicepolicy/LauncherAppsProfileTest.java
+++ b/hostsidetests/devicepolicy/src/com/android/cts/devicepolicy/LauncherAppsProfileTest.java
@@ -32,10 +32,7 @@
     @Override
     protected void setUp() throws Exception {
         super.setUp();
-
-        // We need multi user to be supported in order to create a profile of the user owner.
-        mHasFeature = mHasFeature && (getMaxNumberOfUsersSupported() > 1);
-
+        mHasFeature = mHasFeature && hasDeviceFeature("android.software.managed_users");
         if (mHasFeature) {
             removeTestUsers();
             installTestApps();
diff --git a/hostsidetests/devicepolicy/src/com/android/cts/devicepolicy/ManagedProfileTest.java b/hostsidetests/devicepolicy/src/com/android/cts/devicepolicy/ManagedProfileTest.java
index 7da4228..f8fa222 100644
--- a/hostsidetests/devicepolicy/src/com/android/cts/devicepolicy/ManagedProfileTest.java
+++ b/hostsidetests/devicepolicy/src/com/android/cts/devicepolicy/ManagedProfileTest.java
@@ -37,19 +37,24 @@
     private static final String ADMIN_RECEIVER_TEST_CLASS =
             MANAGED_PROFILE_PKG + ".BaseManagedProfileTest$BasicAdminReceiver";
 
+    private static final String FEATURE_BLUETOOTH = "android.hardware.bluetooth";
     private int mUserId;
-
+    private boolean mHasNfcFeature;
+    
     @Override
     protected void setUp() throws Exception {
         super.setUp();
 
         // We need multi user to be supported in order to create a profile of the user owner.
-        mHasFeature = mHasFeature && (getMaxNumberOfUsersSupported() > 1) && hasDeviceFeature(
+        mHasFeature = mHasFeature && hasDeviceFeature(
                 "android.software.managed_users");
+        mHasNfcFeature = hasDeviceFeature("android.hardware.nfc");
 
         if (mHasFeature) {
             mUserId = createManagedProfile();
             installApp(MANAGED_PROFILE_APK);
+            installApp(INTENT_RECEIVER_APK);
+            installApp(INTENT_SENDER_APK);
             setProfileOwner(MANAGED_PROFILE_PKG + "/" + ADMIN_RECEIVER_TEST_CLASS, mUserId);
             startUser(mUserId);
         }
@@ -60,8 +65,9 @@
         if (mHasFeature) {
             removeUser(mUserId);
             getDevice().uninstallPackage(MANAGED_PROFILE_PKG);
+            getDevice().uninstallPackage(INTENT_SENDER_PKG);
+            getDevice().uninstallPackage(INTENT_RECEIVER_PKG);
         }
-
         super.tearDown();
     }
 
@@ -88,14 +94,6 @@
         assertFalse(listUsers().contains(mUserId));
     }
 
-    public void testMaxUsersStrictlyMoreThanOne() throws Exception {
-        if (hasDeviceFeature("android.software.managed_users")) {
-            assertTrue("Device must support more than 1 user "
-                    + "if android.software.managed_users feature is available",
-            getMaxNumberOfUsersSupported() > 1);
-        }
-    }
-
     public void testCrossProfileIntentFilters() throws Exception {
         if (!mHasFeature) {
             return;
@@ -130,30 +128,58 @@
             return;
         }
 
-        try {
-            getDevice().uninstallPackage(INTENT_SENDER_PKG);
-            getDevice().uninstallPackage(INTENT_RECEIVER_PKG);
-            installAppAsUser(INTENT_SENDER_APK, 0);
-            installAppAsUser(INTENT_RECEIVER_APK, mUserId);
+        // Test from parent to managed
+        assertTrue(runDeviceTestsAsUser(MANAGED_PROFILE_PKG, ".CrossProfileUtils",
+                "removeAllFilters", mUserId));
+        assertTrue(runDeviceTestsAsUser(MANAGED_PROFILE_PKG, ".CrossProfileUtils",
+                "addManagedCanAccessParentFilters", mUserId));
+        assertTrue(runDeviceTestsAsUser(INTENT_SENDER_PKG, ".ContentTest", 0));
 
-            // Test from parent to managed
-            assertTrue(runDeviceTestsAsUser(MANAGED_PROFILE_PKG, ".CrossProfileUtils",
-                    "addManagedCanAccessParentFilters", mUserId));
-            assertTrue(runDeviceTestsAsUser(INTENT_SENDER_PKG, ".IntentSenderTest", 0));
+        // Test from managed to parent
+        assertTrue(runDeviceTestsAsUser(MANAGED_PROFILE_PKG, ".CrossProfileUtils",
+                "removeAllFilters", mUserId));
+        assertTrue(runDeviceTestsAsUser(MANAGED_PROFILE_PKG, ".CrossProfileUtils",
+                "addParentCanAccessManagedFilters", mUserId));
+        assertTrue(runDeviceTestsAsUser(INTENT_SENDER_PKG, ".ContentTest", mUserId));
 
-            getDevice().uninstallPackage(INTENT_SENDER_PKG);
-            getDevice().uninstallPackage(INTENT_RECEIVER_PKG);
-            installAppAsUser(INTENT_SENDER_APK, mUserId);
-            installAppAsUser(INTENT_RECEIVER_APK, 0);
+    }
 
-            // Test from managed to parent
-            assertTrue(runDeviceTestsAsUser(MANAGED_PROFILE_PKG, ".CrossProfileUtils",
-                    "addParentCanAccessManagedFilters", mUserId));
-            assertTrue(runDeviceTestsAsUser(INTENT_SENDER_PKG, ".IntentSenderTest", mUserId));
+    public void testCrossProfileCopyPaste() throws Exception {
+        if (!mHasFeature) {
+            return;
+        }
 
-        } finally {
-            getDevice().uninstallPackage(INTENT_SENDER_PKG);
-            getDevice().uninstallPackage(INTENT_RECEIVER_PKG);
+        assertTrue(runDeviceTestsAsUser(MANAGED_PROFILE_PKG, ".CrossProfileUtils",
+                "allowCrossProfileCopyPaste", mUserId));
+        // Test that managed can see what is copied in the parent.
+        testCrossProfileCopyPasteInternal(mUserId, true);
+        // Test that the parent can see what is copied in managed.
+        testCrossProfileCopyPasteInternal(0, true);
+
+        assertTrue(runDeviceTestsAsUser(MANAGED_PROFILE_PKG, ".CrossProfileUtils",
+                "disallowCrossProfileCopyPaste", mUserId));
+        // Test that managed can still see what is copied in the parent.
+        testCrossProfileCopyPasteInternal(mUserId, true);
+        // Test that the parent cannot see what is copied in managed.
+        testCrossProfileCopyPasteInternal(0, false);
+    }
+
+    private void testCrossProfileCopyPasteInternal(int userId, boolean shouldSucceed)
+            throws DeviceNotAvailableException {
+        final String direction = (userId == 0) ? "addManagedCanAccessParentFilters"
+                : "addParentCanAccessManagedFilters";
+        assertTrue(runDeviceTestsAsUser(MANAGED_PROFILE_PKG, ".CrossProfileUtils",
+                "removeAllFilters", mUserId));
+        assertTrue(runDeviceTestsAsUser(MANAGED_PROFILE_PKG, ".CrossProfileUtils",
+                direction, mUserId));
+        if (shouldSucceed) {
+            assertTrue(runDeviceTestsAsUser(INTENT_SENDER_PKG, ".CopyPasteTest",
+                    "testCanReadAcrossProfiles", userId));
+            assertTrue(runDeviceTestsAsUser(INTENT_SENDER_PKG, ".CopyPasteTest",
+                    "testIsNotified", userId));
+        } else {
+            assertTrue(runDeviceTestsAsUser(INTENT_SENDER_PKG, ".CopyPasteTest",
+                    "testCannotReadAcrossProfiles", userId));
         }
     }
 
@@ -183,6 +209,97 @@
                 addRestrictionCommandOutput.contains("SecurityException"));
     }
 
+    // Test the bluetooth API from a managed profile.
+    public void testBluetooth() throws Exception {
+        boolean mHasBluetooth = hasDeviceFeature(FEATURE_BLUETOOTH);
+        if (!mHasFeature || !mHasBluetooth) {
+            return ;
+        }
+
+        assertTrue(runDeviceTestsAsUser(MANAGED_PROFILE_PKG, ".BluetoothTest",
+                "testEnableDisable", mUserId));
+        assertTrue(runDeviceTestsAsUser(MANAGED_PROFILE_PKG, ".BluetoothTest",
+                "testGetAddress", mUserId));
+        assertTrue(runDeviceTestsAsUser(MANAGED_PROFILE_PKG, ".BluetoothTest",
+                "testListenUsingRfcommWithServiceRecord", mUserId));
+        assertTrue(runDeviceTestsAsUser(MANAGED_PROFILE_PKG, ".BluetoothTest",
+                "testGetRemoteDevice", mUserId));
+    }
+
+    public void testManagedContacts() throws Exception {
+        if (!mHasFeature) {
+            return;
+        }
+
+        try {
+            // Insert Primary profile Contacts
+            assertTrue(runDeviceTestsAsUser(MANAGED_PROFILE_PKG, ".ContactsTest",
+                    "testPrimaryProfilePhoneLookup_insertedAndfound", 0));
+            // Insert Managed profile Contacts
+            assertTrue(runDeviceTestsAsUser(MANAGED_PROFILE_PKG, ".ContactsTest",
+                    "testManagedProfilePhoneLookup_insertedAndfound", mUserId));
+
+            // Set cross profile caller id to enabled
+            assertTrue(runDeviceTestsAsUser(MANAGED_PROFILE_PKG, ".ContactsTest",
+                    "testSetCrossProfileCallerIdDisabled_false", mUserId));
+
+            // Managed user can use ENTERPRISE_CONTENT_FILTER_URI
+            // To access managed contacts but not primary contacts
+            assertTrue(runDeviceTestsAsUser(MANAGED_PROFILE_PKG, ".ContactsTest",
+                    "testManagedProfilePhoneLookup_canAccessEnterpriseContact", mUserId));
+            assertTrue(runDeviceTestsAsUser(MANAGED_PROFILE_PKG, ".ContactsTest",
+                    "testManagedProfilePhoneLookup_canNotAccessPrimaryContact", mUserId));
+
+            // Primary user can use ENTERPRISE_CONTENT_FILTER_URI
+            // To access both primary and managed contacts
+            assertTrue(runDeviceTestsAsUser(MANAGED_PROFILE_PKG, ".ContactsTest",
+                    "testPrimaryProfileEnterprisePhoneLookup_canAccessEnterpriseContact", 0));
+            assertTrue(runDeviceTestsAsUser(MANAGED_PROFILE_PKG, ".ContactsTest",
+                    "testPrimaryProfilePhoneLookup_canAccessPrimaryContact", 0));
+
+            // Set cross profile caller id to disabled
+            assertTrue(runDeviceTestsAsUser(MANAGED_PROFILE_PKG, ".ContactsTest",
+                    "testSetCrossProfileCallerIdDisabled_true", mUserId));
+
+            // Primary user cannot use ENTERPRISE_CONTENT_FILTER_URI to access managed contacts
+            assertTrue(runDeviceTestsAsUser(MANAGED_PROFILE_PKG, ".ContactsTest",
+                    "testPrimaryProfilePhoneLookup_canNotAccessEnterpriseContact", 0));
+            // Managed user cannot use ENTERPRISE_CONTENT_FILTER_URI to access primary contacts
+            assertTrue(runDeviceTestsAsUser(MANAGED_PROFILE_PKG, ".ContactsTest",
+                    "testManagedProfilePhoneLookup_canNotAccessPrimaryContact", mUserId));
+        } finally {
+            // Clean up in managed profile and primary profile
+            runDeviceTestsAsUser(MANAGED_PROFILE_PKG, ".ContactsTest",
+                    "testCurrentProfileContacts_removeContacts", mUserId);
+            runDeviceTestsAsUser(MANAGED_PROFILE_PKG, ".ContactsTest",
+                    "testCurrentProfileContacts_removeContacts", 0);
+        }
+    }
+
+    public void testNfcRestriction() throws Exception {
+        if (!mHasFeature || !mHasNfcFeature) {
+            return;
+        }
+
+        assertTrue(runDeviceTestsAsUser(MANAGED_PROFILE_PKG, ".NfcTest",
+                "testNfcShareEnabled", mUserId));
+        assertTrue(runDeviceTestsAsUser(MANAGED_PROFILE_PKG, ".NfcTest",
+                "testNfcShareEnabled", 0));
+
+        String restriction = "no_outgoing_beam";  // UserManager.DISALLOW_OUTGOING_BEAM
+        String command = "add-restriction";
+
+        String addRestrictionCommandOutput =
+                changeUserRestrictionForUser(restriction, command, mUserId);
+        assertTrue("Command was expected to succeed " + addRestrictionCommandOutput,
+                addRestrictionCommandOutput.contains("Status: ok"));
+
+        assertTrue(runDeviceTestsAsUser(MANAGED_PROFILE_PKG, ".NfcTest",
+                "testNfcShareDisabled", mUserId));
+        assertTrue(runDeviceTestsAsUser(MANAGED_PROFILE_PKG, ".NfcTest",
+                "testNfcShareEnabled", 0));
+    }
+
     private void disableActivityForUser(String activityName, int userId)
             throws DeviceNotAvailableException {
         String command = "am start -W --user " + userId
diff --git a/hostsidetests/dumpsys/src/android/dumpsys/cts/DumpsysHostTest.java b/hostsidetests/dumpsys/src/android/dumpsys/cts/DumpsysHostTest.java
index 4668faf..e12135a 100644
--- a/hostsidetests/dumpsys/src/android/dumpsys/cts/DumpsysHostTest.java
+++ b/hostsidetests/dumpsys/src/android/dumpsys/cts/DumpsysHostTest.java
@@ -523,7 +523,7 @@
     }
 
     private void checkProcess(String[] parts) {
-        assertEquals(9, parts.length);
+        assertTrue(parts.length >= 9);
         assertNotNull(parts[4]); // process
         assertInteger(parts[5]); // userMillis
         assertInteger(parts[6]); // systemMillis
@@ -586,10 +586,10 @@
     }
 
     private void checkKernelWakelock(String[] parts) {
-        assertEquals(7, parts.length);
-        assertNotNull(parts[4]); // kernel wakelock
-        assertInteger(parts[5]); // totalTime
-        assertInteger(parts[6]); // count
+        assertTrue(parts.length >= 7);
+	assertNotNull(parts[4]); // Kernel wakelock
+	assertInteger(parts[parts.length-2]); // totalTime
+        assertInteger(parts[parts.length-1]); // count
     }
 
     private void checkWakeupReason(String[] parts) {
@@ -658,7 +658,7 @@
     }
 
     private void checkMisc(String[] parts) {
-        assertEquals(20, parts.length);
+        assertTrue(parts.length >= 20);
         assertInteger(parts[4]);      // screenOnTime
         assertInteger(parts[5]);      // phoneOnTime
         assertInteger(parts[6]);      // wifiOnTime
@@ -699,7 +699,7 @@
     }
 
     private void checkSignalStrength(String[] parts) {
-        assertEquals(9, parts.length);
+        assertTrue(parts.length >= 9);
         assertInteger(parts[4]); // none
         assertInteger(parts[5]); // poor
         assertInteger(parts[6]); // moderate
diff --git a/hostsidetests/monkey/src/com/android/cts/monkey/AbstractMonkeyTest.java b/hostsidetests/monkey/src/com/android/cts/monkey/AbstractMonkeyTest.java
old mode 100644
new mode 100755
index 9e04274..b31a32d
--- a/hostsidetests/monkey/src/com/android/cts/monkey/AbstractMonkeyTest.java
+++ b/hostsidetests/monkey/src/com/android/cts/monkey/AbstractMonkeyTest.java
@@ -20,7 +20,7 @@
     /**
      * Base monkey command with flags to avoid side effects like airplane mode.
      */
-    static final String MONKEY_CMD = "monkey --pct-touch 0 --pct-motion 0 --pct-majornav 0 --pct-syskeys 0 --pct-anyevent 0 --pct-rotation 0";
+    static final String MONKEY_CMD = "monkey --pct-motion 0 --pct-majornav 0 --pct-syskeys 0 --pct-anyevent 0 --pct-rotation 0";
 
     IAbi mAbi;
     CtsBuildHelper mBuild;
diff --git a/hostsidetests/net/Android.mk b/hostsidetests/net/Android.mk
new file mode 100644
index 0000000..6637d61
--- /dev/null
+++ b/hostsidetests/net/Android.mk
@@ -0,0 +1,31 @@
+# Copyright (C) 2014 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#      http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+LOCAL_PATH := $(call my-dir)
+
+include $(CLEAR_VARS)
+
+# Only compile source java files in this apk.
+LOCAL_SRC_FILES := $(call all-java-files-under, src)
+
+LOCAL_MODULE := CtsHostsideNetworkTests
+
+LOCAL_JAVA_LIBRARIES := cts-tradefed tradefed-prebuilt
+
+LOCAL_CTS_TEST_PACKAGE := android.net.hostsidenetwork
+
+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/net/app/Android.mk b/hostsidetests/net/app/Android.mk
new file mode 100644
index 0000000..29b620d
--- /dev/null
+++ b/hostsidetests/net/app/Android.mk
@@ -0,0 +1,32 @@
+#
+# Copyright (C) 2014 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#      http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+#
+
+LOCAL_PATH := $(call my-dir)
+
+include $(CLEAR_VARS)
+
+LOCAL_MODULE_TAGS := tests
+LOCAL_SDK_VERSION := current
+LOCAL_STATIC_JAVA_LIBRARIES := ctsdeviceutil ctstestrunner ub-uiautomator
+
+LOCAL_SRC_FILES := $(call all-java-files-under, src)
+
+LOCAL_PACKAGE_NAME := CtsHostsideNetworkTestsApp
+
+LOCAL_PROGUARD_ENABLED := disabled
+LOCAL_DEX_PREOPT := false
+
+include $(BUILD_PACKAGE)
diff --git a/hostsidetests/net/app/AndroidManifest.xml b/hostsidetests/net/app/AndroidManifest.xml
new file mode 100644
index 0000000..cdde7dc
--- /dev/null
+++ b/hostsidetests/net/app/AndroidManifest.xml
@@ -0,0 +1,38 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2014 The Android Open Source Project
+
+     Licensed under the Apache License, Version 2.0 (the "License");
+     you may not use this file except in compliance with the License.
+     You may obtain a copy of the License at
+
+          http://www.apache.org/licenses/LICENSE-2.0
+
+     Unless required by applicable law or agreed to in writing, software
+     distributed under the License is distributed on an "AS IS" BASIS,
+     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+     See the License for the specific language governing permissions and
+     limitations under the License.
+-->
+
+<manifest xmlns:android="http://schemas.android.com/apk/res/android"
+        package="com.android.cts.net.hostside">
+
+    <uses-permission android:name="android.permission.INTERNET"/>
+    <uses-permission android:name="android.permission.ACCESS_NETWORK_STATE"/>
+
+    <application>
+        <uses-library android:name="android.test.runner" />
+        <activity android:name=".MyActivity" />
+        <service android:name=".MyVpnService"
+                android:permission="android.permission.BIND_VPN_SERVICE">
+            <intent-filter>
+                <action android:name="android.net.VpnService"/>
+            </intent-filter>
+        </service>
+    </application>
+
+    <instrumentation
+        android:name="android.support.test.runner.AndroidJUnitRunner"
+        android:targetPackage="com.android.cts.net.hostside" />
+
+</manifest>
diff --git a/hostsidetests/net/app/src/com/android/cts/net/hostside/MyActivity.java b/hostsidetests/net/app/src/com/android/cts/net/hostside/MyActivity.java
new file mode 100644
index 0000000..375c852
--- /dev/null
+++ b/hostsidetests/net/app/src/com/android/cts/net/hostside/MyActivity.java
@@ -0,0 +1,53 @@
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.cts.net.hostside;
+
+import android.app.Activity;
+import android.content.Intent;
+import android.net.VpnService;
+import android.os.Bundle;
+import android.os.ParcelFileDescriptor;
+import android.view.WindowManager;
+
+import java.util.Arrays;
+import java.util.ArrayList;
+import java.util.concurrent.LinkedBlockingQueue;
+import java.util.concurrent.TimeUnit;
+
+public class MyActivity extends Activity {
+    private final LinkedBlockingQueue<Integer> mResult = new LinkedBlockingQueue<>(1);
+
+    @Override
+    protected void onCreate(Bundle savedInstanceState) {
+        super.onCreate(savedInstanceState);
+
+        getWindow().addFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON
+                | WindowManager.LayoutParams.FLAG_TURN_SCREEN_ON
+                | WindowManager.LayoutParams.FLAG_DISMISS_KEYGUARD);
+    }
+
+    @Override
+    protected void onActivityResult(int requestCode, int resultCode, Intent data) {
+        if (mResult.offer(resultCode) == false) {
+            throw new RuntimeException("Queue is full! This should never happen");
+        }
+    }
+
+    public Integer getResult(int timeoutMs) throws InterruptedException {
+        return mResult.poll(timeoutMs, TimeUnit.MILLISECONDS);
+    }
+}
diff --git a/hostsidetests/net/app/src/com/android/cts/net/hostside/MyVpnService.java b/hostsidetests/net/app/src/com/android/cts/net/hostside/MyVpnService.java
new file mode 100644
index 0000000..a3f400c
--- /dev/null
+++ b/hostsidetests/net/app/src/com/android/cts/net/hostside/MyVpnService.java
@@ -0,0 +1,155 @@
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.cts.net.hostside;
+
+import android.content.Intent;
+import android.net.VpnService;
+import android.os.ParcelFileDescriptor;
+import android.content.pm.PackageManager.NameNotFoundException;
+import android.text.TextUtils;
+import android.util.Log;
+
+import java.io.IOException;
+import java.net.InetAddress;
+import java.net.UnknownHostException;
+import java.util.ArrayList;
+
+public class MyVpnService extends VpnService {
+
+    private static String TAG = "MyVpnService";
+    private static int MTU = 1799;
+
+    private ParcelFileDescriptor mFd = null;
+    private PacketReflector mPacketReflector = null;
+
+    @Override
+    public int onStartCommand(Intent intent, int flags, int startId) {
+        String packageName = getPackageName();
+        String cmd = intent.getStringExtra(packageName + ".cmd");
+        if ("disconnect".equals(cmd)) {
+            stop();
+        } else if ("connect".equals(cmd)) {
+            start(packageName, intent);
+        }
+
+        return START_NOT_STICKY;
+    }
+
+    private void start(String packageName, Intent intent) {
+        Builder builder = new Builder();
+
+        String addresses = intent.getStringExtra(packageName + ".addresses");
+        if (addresses != null) {
+            String[] addressArray = addresses.split(",");
+            for (int i = 0; i < addressArray.length; i++) {
+                String[] prefixAndMask = addressArray[i].split("/");
+                try {
+                    InetAddress address = InetAddress.getByName(prefixAndMask[0]);
+                    int prefixLength = Integer.parseInt(prefixAndMask[1]);
+                    builder.addAddress(address, prefixLength);
+                } catch (UnknownHostException|NumberFormatException|
+                         ArrayIndexOutOfBoundsException e) {
+                    continue;
+                }
+            }
+        }
+
+        String routes = intent.getStringExtra(packageName + ".routes");
+        if (routes != null) {
+            String[] routeArray = routes.split(",");
+            for (int i = 0; i < routeArray.length; i++) {
+                String[] prefixAndMask = routeArray[i].split("/");
+                try {
+                    InetAddress address = InetAddress.getByName(prefixAndMask[0]);
+                    int prefixLength = Integer.parseInt(prefixAndMask[1]);
+                    builder.addRoute(address, prefixLength);
+                } catch (UnknownHostException|NumberFormatException|
+                         ArrayIndexOutOfBoundsException e) {
+                    continue;
+                }
+            }
+        }
+
+        String allowed = intent.getStringExtra(packageName + ".allowedapplications");
+        if (allowed != null) {
+            String[] packageArray = allowed.split(",");
+            for (int i = 0; i < packageArray.length; i++) {
+                String allowedPackage = packageArray[i];
+                if (!TextUtils.isEmpty(allowedPackage)) {
+                    try {
+                        builder.addAllowedApplication(allowedPackage);
+                    } catch(NameNotFoundException e) {
+                        continue;
+                    }
+                }
+            }
+        }
+
+        String disallowed = intent.getStringExtra(packageName + ".disallowedapplications");
+        if (disallowed != null) {
+            String[] packageArray = disallowed.split(",");
+            for (int i = 0; i < packageArray.length; i++) {
+                String disallowedPackage = packageArray[i];
+                if (!TextUtils.isEmpty(disallowedPackage)) {
+                    try {
+                        builder.addDisallowedApplication(disallowedPackage);
+                    } catch(NameNotFoundException e) {
+                        continue;
+                    }
+                }
+            }
+        }
+
+        builder.setMtu(MTU);
+        builder.setBlocking(true);
+        builder.setSession("MyVpnService");
+
+        Log.i(TAG, "Establishing VPN,"
+                + " addresses=" + addresses
+                + " routes=" + routes
+                + " allowedApplications=" + allowed
+                + " disallowedApplications=" + disallowed);
+
+        mFd = builder.establish();
+        Log.i(TAG, "Established, fd=" + (mFd == null ? "null" : mFd.getFd()));
+
+        mPacketReflector = new PacketReflector(mFd.getFileDescriptor(), MTU);
+        mPacketReflector.start();
+    }
+
+    private void stop() {
+        if (mPacketReflector != null) {
+            mPacketReflector.interrupt();
+            mPacketReflector = null;
+        }
+        try {
+            if (mFd != null) {
+                Log.i(TAG, "Closing filedescriptor");
+                mFd.close();
+            }
+        } catch(IOException e) {
+        } finally {
+            mFd = null;
+        }
+    }
+
+    @Override
+    public void onDestroy() {
+        stop();
+        super.onDestroy();
+    }
+}
diff --git a/hostsidetests/net/app/src/com/android/cts/net/hostside/PacketReflector.java b/hostsidetests/net/app/src/com/android/cts/net/hostside/PacketReflector.java
new file mode 100644
index 0000000..dd0f792
--- /dev/null
+++ b/hostsidetests/net/app/src/com/android/cts/net/hostside/PacketReflector.java
@@ -0,0 +1,234 @@
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.cts.net.hostside;
+
+import android.system.ErrnoException;
+import android.system.Os;
+import android.util.Log;
+
+import java.io.FileDescriptor;
+import java.io.IOException;
+
+public class PacketReflector extends Thread {
+
+    private static int IPV4_HEADER_LENGTH = 20;
+    private static int IPV6_HEADER_LENGTH = 40;
+
+    private static int IPV4_ADDR_OFFSET = 12;
+    private static int IPV6_ADDR_OFFSET = 8;
+    private static int IPV4_ADDR_LENGTH = 4;
+    private static int IPV6_ADDR_LENGTH = 16;
+
+    private static int IPV4_PROTO_OFFSET = 9;
+    private static int IPV6_PROTO_OFFSET = 6;
+
+    private static final byte IPPROTO_ICMP = 1;
+    private static final byte IPPROTO_TCP = 6;
+    private static final byte IPPROTO_UDP = 17;
+    private static final byte IPPROTO_ICMPV6 = 58;
+
+    private static int ICMP_HEADER_LENGTH = 8;
+    private static int TCP_HEADER_LENGTH = 20;
+    private static int UDP_HEADER_LENGTH = 8;
+
+    private static final byte ICMP_ECHO = 8;
+    private static final byte ICMP_ECHOREPLY = 0;
+    private static final byte ICMPV6_ECHO_REQUEST = (byte) 128;
+    private static final byte ICMPV6_ECHO_REPLY = (byte) 129;
+
+    private static String TAG = "PacketReflector";
+
+    private FileDescriptor mFd;
+    private byte[] mBuf;
+
+    public PacketReflector(FileDescriptor fd, int mtu) {
+        super("PacketReflector");
+        mFd = fd;
+        mBuf = new byte[mtu];
+    }
+
+    private static void swapBytes(byte[] buf, int pos1, int pos2, int len) {
+        for (int i = 0; i < len; i++) {
+            byte b = buf[pos1 + i];
+            buf[pos1 + i] = buf[pos2 + i];
+            buf[pos2 + i] = b;
+        }
+    }
+
+    private static void swapAddresses(byte[] buf, int version) {
+        int addrPos, addrLen;
+        switch(version) {
+            case 4:
+                addrPos = IPV4_ADDR_OFFSET;
+                addrLen = IPV4_ADDR_LENGTH;
+                break;
+            case 6:
+                addrPos = IPV6_ADDR_OFFSET;
+                addrLen = IPV6_ADDR_LENGTH;
+                break;
+            default:
+                throw new IllegalArgumentException();
+        }
+        swapBytes(buf, addrPos, addrPos + addrLen, addrLen);
+    }
+
+    // Reflect TCP packets: swap the source and destination addresses, but don't change the ports.
+    // This is used by the test to "connect to itself" through the VPN.
+    private void processTcpPacket(byte[] buf, int version, int len, int hdrLen) {
+        if (len < hdrLen + TCP_HEADER_LENGTH) {
+            return;
+        }
+
+        // Swap src and dst IP addresses.
+        swapAddresses(buf, version);
+
+        // Send the packet back.
+        writePacket(buf, len);
+    }
+
+    // Echo UDP packets: swap source and destination addresses, and source and destination ports.
+    // This is used by the test to check that the bytes it sends are echoed back.
+    private void processUdpPacket(byte[] buf, int version, int len, int hdrLen) {
+        if (len < hdrLen + UDP_HEADER_LENGTH) {
+            return;
+        }
+
+        // Swap src and dst IP addresses.
+        swapAddresses(buf, version);
+
+        // Swap dst and src ports.
+        int portOffset = hdrLen;
+        swapBytes(buf, portOffset, portOffset + 2, 2);
+
+        // Send the packet back.
+        writePacket(buf, len);
+    }
+
+    private void processIcmpPacket(byte[] buf, int version, int len, int hdrLen) {
+        if (len < hdrLen + ICMP_HEADER_LENGTH) {
+            return;
+        }
+
+        byte type = buf[hdrLen];
+        if (!(version == 4 && type == ICMP_ECHO) &&
+            !(version == 6 && type == ICMPV6_ECHO_REQUEST)) {
+            return;
+        }
+
+        // Save the ping packet we received.
+        byte[] request = buf.clone();
+
+        // Swap src and dst IP addresses, and send the packet back.
+        // This effectively pings the device to see if it replies.
+        swapAddresses(buf, version);
+        writePacket(buf, len);
+
+        // The device should have replied, and buf should now contain a ping response.
+        int received = readPacket(buf);
+        if (received != len) {
+            Log.i(TAG, "Reflecting ping did not result in ping response: " +
+                       "read=" + received + " expected=" + len);
+            return;
+        }
+
+        // Compare the response we got with the original packet.
+        // The only thing that should have changed are addresses, type and checksum.
+        // Overwrite them with the received bytes and see if the packet is otherwise identical.
+        request[hdrLen] = buf[hdrLen];          // Type.
+        request[hdrLen + 2] = buf[hdrLen + 2];  // Checksum byte 1.
+        request[hdrLen + 3] = buf[hdrLen + 3];  // Checksum byte 2.
+        for (int i = 0; i < len; i++) {
+            if (buf[i] != request[i]) {
+                Log.i(TAG, "Received non-matching packet when expecting ping response.");
+                return;
+            }
+        }
+
+        // Now swap the addresses again and reflect the packet. This sends a ping reply.
+        swapAddresses(buf, version);
+        writePacket(buf, len);
+    }
+
+    private void writePacket(byte[] buf, int len) {
+        try {
+            Os.write(mFd, buf, 0, len);
+        } catch (ErrnoException|IOException e) {
+            Log.e(TAG, "Error writing packet: " + e.getMessage());
+        }
+    }
+
+    private int readPacket(byte[] buf) {
+        int len;
+        try {
+            len = Os.read(mFd, buf, 0, buf.length);
+        } catch (ErrnoException|IOException e) {
+            Log.e(TAG, "Error reading packet: " + e.getMessage());
+            len = -1;
+        }
+        return len;
+    }
+
+    // Reads one packet from our mFd, and possibly writes the packet back.
+    private void processPacket() {
+        int len = readPacket(mBuf);
+        if (len < 1) {
+            return;
+        }
+
+        int version = mBuf[0] >> 4;
+        int addrPos, protoPos, hdrLen, addrLen;
+        if (version == 4) {
+            hdrLen = IPV4_HEADER_LENGTH;
+            protoPos = IPV4_PROTO_OFFSET;
+            addrPos = IPV4_ADDR_OFFSET;
+            addrLen = IPV4_ADDR_LENGTH;
+        } else if (version == 6) {
+            hdrLen = IPV6_HEADER_LENGTH;
+            protoPos = IPV6_PROTO_OFFSET;
+            addrPos = IPV6_ADDR_OFFSET;
+            addrLen = IPV6_ADDR_LENGTH;
+        } else {
+            return;
+        }
+
+        if (len < hdrLen) {
+            return;
+        }
+
+        byte proto = mBuf[protoPos];
+        switch (proto) {
+            case IPPROTO_ICMP:
+            case IPPROTO_ICMPV6:
+                processIcmpPacket(mBuf, version, len, hdrLen);
+                break;
+            case IPPROTO_TCP:
+                processTcpPacket(mBuf, version, len, hdrLen);
+                break;
+            case IPPROTO_UDP:
+                processUdpPacket(mBuf, version, len, hdrLen);
+                break;
+        }
+    }
+
+    public void run() {
+        Log.i(TAG, "PacketReflector starting fd=" + mFd + " valid=" + mFd.valid());
+        while (!interrupted() && mFd.valid()) {
+            processPacket();
+        }
+        Log.i(TAG, "PacketReflector exiting fd=" + mFd + " valid=" + mFd.valid());
+    }
+}
diff --git a/hostsidetests/net/app/src/com/android/cts/net/hostside/VpnTest.java b/hostsidetests/net/app/src/com/android/cts/net/hostside/VpnTest.java
new file mode 100755
index 0000000..5045cc2
--- /dev/null
+++ b/hostsidetests/net/app/src/com/android/cts/net/hostside/VpnTest.java
@@ -0,0 +1,488 @@
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.cts.net.hostside;
+
+import static android.system.OsConstants.*;
+
+import android.content.Intent;
+import android.content.pm.PackageManager;
+import android.net.ConnectivityManager;
+import android.net.ConnectivityManager.NetworkCallback;
+import android.net.LinkProperties;
+import android.net.Network;
+import android.net.NetworkCapabilities;
+import android.net.NetworkRequest;
+import android.net.VpnService;
+import android.support.test.uiautomator.UiDevice;
+import android.support.test.uiautomator.UiObject;
+import android.support.test.uiautomator.UiObjectNotFoundException;
+import android.support.test.uiautomator.UiScrollable;
+import android.support.test.uiautomator.UiSelector;
+import android.system.ErrnoException;
+import android.system.Os;
+import android.system.StructPollfd;
+import android.test.InstrumentationTestCase;
+import android.test.MoreAsserts;
+import android.text.TextUtils;
+import android.util.Log;
+
+import java.io.Closeable;
+import java.io.FileDescriptor;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.OutputStream;
+import java.net.DatagramPacket;
+import java.net.DatagramSocket;
+import java.net.Inet6Address;
+import java.net.InetAddress;
+import java.net.InetSocketAddress;
+import java.net.ServerSocket;
+import java.net.Socket;
+import java.util.Random;
+
+/**
+ * Tests for the VpnService API.
+ *
+ * These tests establish a VPN via the VpnService API, and have the service reflect the packets back
+ * to the device without causing any network traffic. This allows testing the local VPN data path
+ * without a network connection or a VPN server.
+ *
+ * Note: in Lollipop, VPN functionality relies on kernel support for UID-based routing. If these
+ * tests fail, it may be due to the lack of kernel support. The necessary patches can be
+ * cherry-picked from the Android common kernel trees:
+ *
+ * android-3.10:
+ *   https://android-review.googlesource.com/#/c/99220/
+ *   https://android-review.googlesource.com/#/c/100545/
+ *
+ * android-3.4:
+ *   https://android-review.googlesource.com/#/c/99225/
+ *   https://android-review.googlesource.com/#/c/100557/
+ *
+ */
+public class VpnTest extends InstrumentationTestCase {
+
+    public static String TAG = "VpnTest";
+    public static int TIMEOUT_MS = 3 * 1000;
+    public static int SOCKET_TIMEOUT_MS = 100;
+
+    private UiDevice mDevice;
+    private MyActivity mActivity;
+    private String mPackageName;
+    private ConnectivityManager mCM;
+    Network mNetwork;
+    NetworkCallback mCallback;
+    final Object mLock = new Object();
+    final Object mLockShutdown = new Object();
+
+    private boolean supportedHardware() {
+        final PackageManager pm = getInstrumentation().getContext().getPackageManager();
+        return !pm.hasSystemFeature("android.hardware.type.television") &&
+               !pm.hasSystemFeature("android.hardware.type.watch");
+    }
+
+    @Override
+    public void setUp() throws Exception {
+        super.setUp();
+
+        mNetwork = null;
+        mCallback = null;
+
+        mDevice = UiDevice.getInstance(getInstrumentation());
+        mActivity = launchActivity(getInstrumentation().getTargetContext().getPackageName(),
+                MyActivity.class, null);
+        mPackageName = mActivity.getPackageName();
+        mCM = (ConnectivityManager) mActivity.getSystemService(mActivity.CONNECTIVITY_SERVICE);
+        mDevice.waitForIdle();
+    }
+
+    @Override
+    public void tearDown() throws Exception {
+        if (mCallback != null) {
+            mCM.unregisterNetworkCallback(mCallback);
+        }
+        Log.i(TAG, "Stopping VPN");
+        stopVpn();
+        mActivity.finish();
+        super.tearDown();
+    }
+
+    private void prepareVpn() throws Exception {
+        final int REQUEST_ID = 42;
+
+        // Attempt to prepare.
+        Log.i(TAG, "Preparing VPN");
+        Intent intent = VpnService.prepare(mActivity);
+
+        if (intent != null) {
+            // Start the confirmation dialog and click OK.
+            mActivity.startActivityForResult(intent, REQUEST_ID);
+            mDevice.waitForIdle();
+
+            String packageName = intent.getComponent().getPackageName();
+            String resourceIdRegex = "android:id/button1$|button_start_vpn";
+            final UiObject okButton = new UiObject(new UiSelector()
+                    .className("android.widget.Button")
+                    .packageName(packageName)
+                    .resourceIdMatches(resourceIdRegex));
+            if (okButton.waitForExists(TIMEOUT_MS) == false) {
+                mActivity.finishActivity(REQUEST_ID);
+                fail("VpnService.prepare returned an Intent for '" + intent.getComponent() + "' " +
+                     "to display the VPN confirmation dialog, but this test could not find the " +
+                     "button to allow the VPN application to connect. Please ensure that the "  +
+                     "component displays a button with a resource ID matching the regexp: '" +
+                     resourceIdRegex + "'.");
+            }
+
+            // Click the button and wait for RESULT_OK.
+            okButton.click();
+            try {
+                int result = mActivity.getResult(TIMEOUT_MS);
+                if (result != MyActivity.RESULT_OK) {
+                    fail("The VPN confirmation dialog did not return RESULT_OK when clicking on " +
+                         "the button matching the regular expression '" + resourceIdRegex +
+                         "' of " + intent.getComponent() + "'. Please ensure that clicking on " +
+                         "that button allows the VPN application to connect. " +
+                         "Return value: " + result);
+                }
+            } catch (InterruptedException e) {
+                fail("VPN confirmation dialog did not return after " + TIMEOUT_MS + "ms");
+            }
+
+            // Now we should be prepared.
+            intent = VpnService.prepare(mActivity);
+            if (intent != null) {
+                fail("VpnService.prepare returned non-null even after the VPN dialog " +
+                     intent.getComponent() + "returned RESULT_OK.");
+            }
+        }
+    }
+
+    private void startVpn(
+            String[] addresses, String[] routes,
+            String allowedApplications, String disallowedApplications) throws Exception {
+
+        prepareVpn();
+
+        // Register a callback so we will be notified when our VPN comes up.
+        final NetworkRequest request = new NetworkRequest.Builder()
+                .addTransportType(NetworkCapabilities.TRANSPORT_VPN)
+                .removeCapability(NetworkCapabilities.NET_CAPABILITY_NOT_VPN)
+                .removeCapability(NetworkCapabilities.NET_CAPABILITY_INTERNET)
+                .build();
+        mCallback = new NetworkCallback() {
+            public void onAvailable(Network network) {
+                synchronized (mLock) {
+                    Log.i(TAG, "Got available callback for network=" + network);
+                    mNetwork = network;
+                    mLock.notify();
+                }
+            }
+        };
+        mCM.registerNetworkCallback(request, mCallback);  // Unregistered in tearDown.
+
+        // Start the service and wait up for TIMEOUT_MS ms for the VPN to come up.
+        Intent intent = new Intent(mActivity, MyVpnService.class)
+                .putExtra(mPackageName + ".cmd", "connect")
+                .putExtra(mPackageName + ".addresses", TextUtils.join(",", addresses))
+                .putExtra(mPackageName + ".routes", TextUtils.join(",", routes))
+                .putExtra(mPackageName + ".allowedapplications", allowedApplications)
+                .putExtra(mPackageName + ".disallowedapplications", disallowedApplications);
+        mActivity.startService(intent);
+        synchronized (mLock) {
+            if (mNetwork == null) {
+                 Log.i(TAG, "bf mLock");
+                 mLock.wait(TIMEOUT_MS);
+                 Log.i(TAG, "af mLock");
+            }
+        }
+
+        if (mNetwork == null) {
+            fail("VPN did not become available after " + TIMEOUT_MS + "ms");
+        }
+
+        // Unfortunately, when the available callback fires, the VPN UID ranges are not yet
+        // configured. Give the system some time to do so. http://b/18436087 .
+        try { Thread.sleep(3000); } catch(InterruptedException e) {}
+    }
+
+    private void stopVpn() {
+        // Register a callback so we will be notified when our VPN comes up.
+        final NetworkRequest request = new NetworkRequest.Builder()
+                .addTransportType(NetworkCapabilities.TRANSPORT_VPN)
+                .removeCapability(NetworkCapabilities.NET_CAPABILITY_NOT_VPN)
+                .removeCapability(NetworkCapabilities.NET_CAPABILITY_INTERNET)
+                .build();
+        mCallback = new NetworkCallback() {
+            public void onLost(Network network) {
+                synchronized (mLockShutdown) {
+                    Log.i(TAG, "Got lost callback for network=" + network + ",mNetwork = " + mNetwork);
+                    if( mNetwork == network){
+                        mLockShutdown.notify();
+                    }
+                }
+            }
+       };
+        mCM.registerNetworkCallback(request, mCallback);  // Unregistered in tearDown.
+        // Simply calling mActivity.stopService() won't stop the service, because the system binds
+        // to the service for the purpose of sending it a revoke command if another VPN comes up,
+        // and stopping a bound service has no effect. Instead, "start" the service again with an
+        // Intent that tells it to disconnect.
+        Intent intent = new Intent(mActivity, MyVpnService.class)
+                .putExtra(mPackageName + ".cmd", "disconnect");
+        mActivity.startService(intent);
+        synchronized (mLockShutdown) {
+            try {
+                 Log.i(TAG, "bf mLockShutdown");
+                 mLockShutdown.wait(TIMEOUT_MS);
+                 Log.i(TAG, "af mLockShutdown");
+            } catch(InterruptedException e) {}
+        }
+    }
+
+    private static void closeQuietly(Closeable c) {
+        if (c != null) {
+            try {
+                c.close();
+            } catch (IOException e) {
+            }
+        }
+    }
+
+    private static void checkPing(String to) throws IOException, ErrnoException {
+        InetAddress address = InetAddress.getByName(to);
+        FileDescriptor s;
+        final int LENGTH = 64;
+        byte[] packet = new byte[LENGTH];
+        byte[] header;
+
+        // Construct a ping packet.
+        Random random = new Random();
+        random.nextBytes(packet);
+        if (address instanceof Inet6Address) {
+            s = Os.socket(AF_INET6, SOCK_DGRAM, IPPROTO_ICMPV6);
+            header = new byte[] { (byte) 0x80, (byte) 0x00, (byte) 0x00, (byte) 0x00 };
+        } else {
+            // Note that this doesn't actually work due to http://b/18558481 .
+            s = Os.socket(AF_INET, SOCK_DGRAM, IPPROTO_ICMP);
+            header = new byte[] { (byte) 0x08, (byte) 0x00, (byte) 0x00, (byte) 0x00 };
+        }
+        System.arraycopy(header, 0, packet, 0, header.length);
+
+        // Send the packet.
+        int port = random.nextInt(65534) + 1;
+        Os.connect(s, address, port);
+        Os.write(s, packet, 0, packet.length);
+
+        // Expect a reply.
+        StructPollfd pollfd = new StructPollfd();
+        pollfd.events = (short) POLLIN;  // "error: possible loss of precision"
+        pollfd.fd = s;
+        int ret = Os.poll(new StructPollfd[] { pollfd }, SOCKET_TIMEOUT_MS);
+        assertEquals("Expected reply after sending ping", 1, ret);
+
+        byte[] reply = new byte[LENGTH];
+        int read = Os.read(s, reply, 0, LENGTH);
+        assertEquals(LENGTH, read);
+
+        // Find out what the kernel set the ICMP ID to.
+        InetSocketAddress local = (InetSocketAddress) Os.getsockname(s);
+        port = local.getPort();
+        packet[4] = (byte) ((port >> 8) & 0xff);
+        packet[5] = (byte) (port & 0xff);
+
+        // Check the contents.
+        if (packet[0] == (byte) 0x80) {
+            packet[0] = (byte) 0x81;
+        } else {
+            packet[0] = 0;
+        }
+        // Zero out the checksum in the reply so it matches the uninitialized checksum in packet.
+        reply[2] = reply[3] = 0;
+        MoreAsserts.assertEquals(packet, reply);
+    }
+
+    // Writes data to out and checks that it appears identically on in.
+    private static void writeAndCheckData(
+            OutputStream out, InputStream in, byte[] data) throws IOException {
+        out.write(data, 0, data.length);
+        out.flush();
+
+        byte[] read = new byte[data.length];
+        int bytesRead = 0, totalRead = 0;
+        do {
+            bytesRead = in.read(read, totalRead, read.length - totalRead);
+            totalRead += bytesRead;
+        } while (bytesRead >= 0 && totalRead < data.length);
+        assertEquals(totalRead, data.length);
+        MoreAsserts.assertEquals(data, read);
+    }
+
+    private static void checkTcpReflection(String to, String expectedFrom) throws IOException {
+        // Exercise TCP over the VPN by "connecting to ourselves". We open a server socket and a
+        // client socket, and connect the client socket to a remote host, with the port of the
+        // server socket. The PacketReflector reflects the packets, changing the source addresses
+        // but not the ports, so our client socket is connected to our server socket, though both
+        // sockets think their peers are on the "remote" IP address.
+
+        // Open a listening socket.
+        ServerSocket listen = new ServerSocket(0, 10, InetAddress.getByName("::"));
+
+        // Connect the client socket to it.
+        InetAddress toAddr = InetAddress.getByName(to);
+        Socket client = new Socket();
+        try {
+            client.connect(new InetSocketAddress(toAddr, listen.getLocalPort()), SOCKET_TIMEOUT_MS);
+            if (expectedFrom == null) {
+                closeQuietly(listen);
+                closeQuietly(client);
+                fail("Expected connection to fail, but it succeeded.");
+            }
+        } catch (IOException e) {
+            if (expectedFrom != null) {
+                closeQuietly(listen);
+                fail("Expected connection to succeed, but it failed.");
+            } else {
+                // We expected the connection to fail, and it did, so there's nothing more to test.
+                return;
+            }
+        }
+
+        // The connection succeeded, and we expected it to succeed. Send some data; if things are
+        // working, the data will be sent to the VPN, reflected by the PacketReflector, and arrive
+        // at our server socket. For good measure, send some data in the other direction.
+        Socket server = null;
+        try {
+            // Accept the connection on the server side.
+            listen.setSoTimeout(SOCKET_TIMEOUT_MS);
+            server = listen.accept();
+
+            // Check that the source and peer addresses are as expected.
+            assertEquals(expectedFrom, client.getLocalAddress().getHostAddress());
+            assertEquals(expectedFrom, server.getLocalAddress().getHostAddress());
+            assertEquals(
+                    new InetSocketAddress(toAddr, client.getLocalPort()),
+                    server.getRemoteSocketAddress());
+            assertEquals(
+                    new InetSocketAddress(toAddr, server.getLocalPort()),
+                    client.getRemoteSocketAddress());
+
+            // Now write some data.
+            final int LENGTH = 32768;
+            byte[] data = new byte[LENGTH];
+            new Random().nextBytes(data);
+
+            // Make sure our writes don't block or time out, because we're single-threaded and can't
+            // read and write at the same time.
+            server.setReceiveBufferSize(LENGTH * 2);
+            client.setSendBufferSize(LENGTH * 2);
+            client.setSoTimeout(SOCKET_TIMEOUT_MS);
+            server.setSoTimeout(SOCKET_TIMEOUT_MS);
+
+            // Send some data from client to server, then from server to client.
+            writeAndCheckData(client.getOutputStream(), server.getInputStream(), data);
+            writeAndCheckData(server.getOutputStream(), client.getInputStream(), data);
+        } finally {
+            closeQuietly(listen);
+            closeQuietly(client);
+            closeQuietly(server);
+        }
+    }
+
+    private static void checkUdpEcho(String to, String expectedFrom) throws IOException {
+        DatagramSocket s;
+        InetAddress address = InetAddress.getByName(to);
+        if (address instanceof Inet6Address) {  // http://b/18094870
+            s = new DatagramSocket(0, InetAddress.getByName("::"));
+        } else {
+            s = new DatagramSocket();
+        }
+        s.setSoTimeout(SOCKET_TIMEOUT_MS);
+
+        Random random = new Random();
+        byte[] data = new byte[random.nextInt(1650)];
+        random.nextBytes(data);
+        DatagramPacket p = new DatagramPacket(data, data.length);
+        s.connect(address, 7);
+
+        if (expectedFrom != null) {
+            assertEquals("Unexpected source address: ",
+                         expectedFrom, s.getLocalAddress().getHostAddress());
+        }
+
+        try {
+            if (expectedFrom != null) {
+                s.send(p);
+                s.receive(p);
+                MoreAsserts.assertEquals(data, p.getData());
+            } else {
+                try {
+                    s.send(p);
+                    s.receive(p);
+                    fail("Received unexpected reply");
+                } catch(IOException expected) {}
+            }
+        } finally {
+            s.close();
+        }
+    }
+
+    private void checkTrafficOnVpn() throws IOException, ErrnoException {
+        checkUdpEcho("192.0.2.251", "192.0.2.2");
+        checkUdpEcho("2001:db8:dead:beef::f00", "2001:db8:1:2::ffe");
+        checkPing("2001:db8:dead:beef::f00");
+        checkTcpReflection("192.0.2.252", "192.0.2.2");
+        checkTcpReflection("2001:db8:dead:beef::f00", "2001:db8:1:2::ffe");
+    }
+
+    private void checkNoTrafficOnVpn() throws IOException, ErrnoException {
+        checkUdpEcho("192.0.2.251", null);
+        checkUdpEcho("2001:db8:dead:beef::f00", null);
+        checkTcpReflection("192.0.2.252", null);
+        checkTcpReflection("2001:db8:dead:beef::f00", null);
+    }
+
+    public void testDefault() throws Exception {
+        if (!supportedHardware()) return;
+
+        startVpn(new String[] {"192.0.2.2/32", "2001:db8:1:2::ffe/128"},
+                 new String[] {"0.0.0.0/0", "::/0"},
+                 "", "");
+
+        checkTrafficOnVpn();
+    }
+
+    public void testAppAllowed() throws Exception {
+        if (!supportedHardware()) return;
+
+        startVpn(new String[] {"192.0.2.2/32", "2001:db8:1:2::ffe/128"},
+                 new String[] {"192.0.2.0/24", "2001:db8::/32"},
+                 mPackageName, "");
+
+        checkTrafficOnVpn();
+    }
+
+    public void testAppDisallowed() throws Exception {
+        if (!supportedHardware()) return;
+
+        startVpn(new String[] {"192.0.2.2/32", "2001:db8:1:2::ffe/128"},
+                 new String[] {"192.0.2.0/24", "2001:db8::/32"},
+                 "", mPackageName);
+
+        checkNoTrafficOnVpn();
+    }
+}
diff --git a/hostsidetests/net/src/com/android/cts/net/HostsideNetworkTests.java b/hostsidetests/net/src/com/android/cts/net/HostsideNetworkTests.java
new file mode 100644
index 0000000..a7698f3
--- /dev/null
+++ b/hostsidetests/net/src/com/android/cts/net/HostsideNetworkTests.java
@@ -0,0 +1,105 @@
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.cts.net;
+
+import com.android.cts.tradefed.build.CtsBuildHelper;
+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.testtype.DeviceTestCase;
+import com.android.tradefed.testtype.IAbi;
+import com.android.tradefed.testtype.IAbiReceiver;
+import com.android.tradefed.testtype.IBuildReceiver;
+import com.android.tradefed.device.DeviceNotAvailableException;
+import com.android.tradefed.device.ITestDevice;
+import com.android.tradefed.result.CollectingTestListener;
+
+import java.util.Map;
+
+public class HostsideNetworkTests extends DeviceTestCase implements IAbiReceiver, IBuildReceiver {
+    private static final String TEST_PKG = "com.android.cts.net.hostside";
+    private static final String TEST_APK = "CtsHostsideNetworkTestsApp.apk";
+
+    private IAbi mAbi;
+    private CtsBuildHelper mCtsBuild;
+
+    @Override
+    public void setAbi(IAbi abi) {
+        mAbi = abi;
+    }
+
+    @Override
+    public void setBuild(IBuildInfo buildInfo) {
+        mCtsBuild = CtsBuildHelper.createBuildHelper(buildInfo);
+    }
+
+    @Override
+    protected void setUp() throws Exception {
+        super.setUp();
+
+        assertNotNull(mAbi);
+        assertNotNull(mCtsBuild);
+
+        getDevice().uninstallPackage(TEST_PKG);
+
+        assertNull(getDevice().installPackage(mCtsBuild.getTestApp(TEST_APK), false));
+    }
+
+    @Override
+    protected void tearDown() throws Exception {
+        super.tearDown();
+
+        getDevice().uninstallPackage(TEST_PKG);
+    }
+
+    public void testVpn() throws Exception {
+        runDeviceTests(TEST_PKG, ".VpnTest");
+    }
+
+    public void runDeviceTests(String packageName, String testClassName)
+           throws DeviceNotAvailableException {
+        RemoteAndroidTestRunner testRunner = new RemoteAndroidTestRunner(packageName,
+                "android.support.test.runner.AndroidJUnitRunner", getDevice().getIDevice());
+
+        final CollectingTestListener listener = new CollectingTestListener();
+        getDevice().runInstrumentationTests(testRunner, listener);
+
+        final TestRunResult result = listener.getCurrentRunResults();
+        if (result.isRunFailure()) {
+            throw new AssertionError("Failed to successfully run device tests for "
+                    + result.getName() + ": " + result.getRunFailureMessage());
+        }
+
+        if (result.hasFailedTests()) {
+            // build a meaningful error message
+            StringBuilder errorBuilder = new StringBuilder("on-device tests failed:\n");
+            for (Map.Entry<TestIdentifier, TestResult> resultEntry :
+                result.getTestResults().entrySet()) {
+                if (!resultEntry.getValue().getStatus().equals(TestStatus.PASSED)) {
+                    errorBuilder.append(resultEntry.getKey().toString());
+                    errorBuilder.append(":\n");
+                    errorBuilder.append(resultEntry.getValue().getStackTrace());
+                }
+            }
+            throw new AssertionError(errorBuilder.toString());
+        }
+    }
+}
diff --git a/hostsidetests/security/src/android/cts/security/SELinuxHostTest.java b/hostsidetests/security/src/android/cts/security/SELinuxHostTest.java
index 96845b1..c93295a 100644
--- a/hostsidetests/security/src/android/cts/security/SELinuxHostTest.java
+++ b/hostsidetests/security/src/android/cts/security/SELinuxHostTest.java
@@ -77,8 +77,7 @@
         /* obtain sepolicy file from running device */
         devicePolicyFile = File.createTempFile("sepolicy", ".tmp");
         devicePolicyFile.deleteOnExit();
-        mDevice.executeAdbCommand("pull", "/sys/fs/selinux/policy",
-                devicePolicyFile.getAbsolutePath());
+        mDevice.pullFile("/sys/fs/selinux/policy", devicePolicyFile);
     }
 
     /**
diff --git a/hostsidetests/theme/app/Android.mk b/hostsidetests/theme/app/Android.mk
index 1be2983..70623cb 100644
--- a/hostsidetests/theme/app/Android.mk
+++ b/hostsidetests/theme/app/Android.mk
@@ -26,8 +26,6 @@
 
 LOCAL_PROGUARD_ENABLED := disabled
 
-LOCAL_STATIC_JAVA_LIBRARIES := android-support-test
-
 LOCAL_SRC_FILES := $(call all-java-files-under, src)
 
 #Flags to tell the Android Asset Packaging Tool not to strip for some densities
diff --git a/hostsidetests/theme/app/AndroidManifest.xml b/hostsidetests/theme/app/AndroidManifest.xml
index 2f8fb3b..81a4d9d 100755
--- a/hostsidetests/theme/app/AndroidManifest.xml
+++ b/hostsidetests/theme/app/AndroidManifest.xml
@@ -24,7 +24,7 @@
     <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
     <application>
         <uses-library android:name="android.test.runner" />
-        <activity android:name=".HoloDeviceActivity" >
+        <activity android:name=".HoloDeviceActivity">
             <intent-filter>
                 <action android:name="android.intent.action.MAIN" />
                 <category android:name="android.intent.category.LAUNCHER" />
@@ -37,13 +37,6 @@
                 <category android:name="android.intent.category.LAUNCHER" />
             </intent-filter>
         </activity>
-        <activity android:name=".CaptureActivity" />
     </application>
 
-    <!--  self-instrumenting test package. -->
-    <instrumentation android:name="android.support.test.runner.AndroidJUnitRunner"
-                     android:targetPackage="android.theme.app"
-                     android:label="Generates Theme reference images"/>
-
 </manifest>
-
diff --git a/hostsidetests/theme/app/res/layout/holo_test.xml b/hostsidetests/theme/app/res/layout/holo_test.xml
index 0aef953..3eed4ba 100644
--- a/hostsidetests/theme/app/res/layout/holo_test.xml
+++ b/hostsidetests/theme/app/res/layout/holo_test.xml
@@ -15,6 +15,8 @@
 -->
 <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
         android:orientation="vertical"
+        android:focusable="true"
+        android:keepScreenOn="true"
         android:layout_width="match_parent"
         android:layout_height="match_parent">
     <android.theme.app.ReferenceViewGroup
diff --git a/hostsidetests/theme/app/src/android/theme/app/CaptureActivity.java b/hostsidetests/theme/app/src/android/theme/app/CaptureActivity.java
deleted file mode 100644
index d241ff6..0000000
--- a/hostsidetests/theme/app/src/android/theme/app/CaptureActivity.java
+++ /dev/null
@@ -1,87 +0,0 @@
-/*
- * Copyright (C) 2014 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package android.theme.app;
-
-import android.app.Activity;
-import android.content.Intent;
-import android.os.Bundle;
-
-import java.util.concurrent.CountDownLatch;
-
-/**
- * Iterates through all themes and all layouts, starting the Activity to capture the images.
- */
-public class CaptureActivity extends Activity {
-
-    private static final int REQUEST_CODE = 1;
-
-    private static final int NUM_THEMES = 24;
-
-    private static final int NUM_LAYOUTS = 47;
-
-    private final CountDownLatch mLatch = new CountDownLatch(1);
-
-    private int mCurrentTheme = 0;
-
-    private int mCurrentLayout = 0;
-
-    @Override
-    protected void onCreate(Bundle savedInstanceState) {
-        super.onCreate(savedInstanceState);
-        generateNextImage();
-    }
-
-    /**
-     * Starts the activity to generate the next image.
-     */
-    private void generateNextImage() {
-        Intent intent = new Intent(this, HoloDeviceActivity.class);
-        intent.setFlags(Intent.FLAG_ACTIVITY_SINGLE_TOP);
-        intent.putExtra(HoloDeviceActivity.EXTRA_THEME, mCurrentTheme);
-        intent.putExtra(HoloDeviceActivity.EXTRA_LAYOUT, mCurrentLayout);
-        startActivityForResult(intent, REQUEST_CODE);
-    }
-
-    @Override
-    protected void onActivityResult(int requestCode, int resultCode, Intent data) {
-        if (requestCode == REQUEST_CODE) {
-            if (resultCode == RESULT_OK) {
-                mCurrentLayout++;
-                if (mCurrentLayout >= NUM_LAYOUTS) {
-                    mCurrentLayout = 0;
-                    mCurrentTheme++;
-                }
-                if (mCurrentTheme < NUM_THEMES) {
-                    generateNextImage();
-                } else {
-                    finish();
-                }
-            } else {
-                finish();
-            }
-        }
-    }
-
-    public void finish() {
-        mLatch.countDown();
-        super.finish();
-    }
-
-    public void waitForCompletion() throws InterruptedException {
-        mLatch.await();
-    }
-}
diff --git a/hostsidetests/theme/app/src/android/theme/app/CaptureHolo.java b/hostsidetests/theme/app/src/android/theme/app/CaptureHolo.java
deleted file mode 100644
index 7e2b2c9..0000000
--- a/hostsidetests/theme/app/src/android/theme/app/CaptureHolo.java
+++ /dev/null
@@ -1,37 +0,0 @@
-/*
- * Copyright (C) 2014 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package android.theme.app;
-
-import android.app.KeyguardManager;
-import android.content.Context;
-import android.test.ActivityInstrumentationTestCase2;
-
-public class CaptureHolo extends ActivityInstrumentationTestCase2<CaptureActivity> {
-
-    public CaptureHolo() {
-        super(CaptureActivity.class);
-    }
-
-    public void testCaptureHolo() throws Exception {
-        setActivityInitialTouchMode(true);
-        CaptureActivity activity = getActivity();
-        KeyguardManager keyguardManager =
-                (KeyguardManager) activity.getSystemService(Context.KEYGUARD_SERVICE);
-        keyguardManager.newKeyguardLock("holo_capture").disableKeyguard();
-        activity.waitForCompletion();
-    }
-}
diff --git a/hostsidetests/theme/app/src/android/theme/app/HoloDeviceActivity.java b/hostsidetests/theme/app/src/android/theme/app/HoloDeviceActivity.java
index 3939979..8ae9fc8 100644
--- a/hostsidetests/theme/app/src/android/theme/app/HoloDeviceActivity.java
+++ b/hostsidetests/theme/app/src/android/theme/app/HoloDeviceActivity.java
@@ -36,6 +36,7 @@
 import android.util.Log;
 import android.view.View;
 import android.widget.CheckBox;
+import android.widget.DatePicker;
 import android.widget.LinearLayout;
 
 import java.io.File;
@@ -50,65 +51,88 @@
 
     public static final String EXTRA_THEME = "holo_theme_extra";
 
-    public static final String EXTRA_LAYOUT = "holo_layout_extra";
-
-    public static final String EXTRA_TIMEOUT = "holo_timeout_extra";
-
     private static final String TAG = HoloDeviceActivity.class.getSimpleName();
 
-    private static final int TIMEOUT = 1 * 1000;//1 sec
+    /**
+     * The duration of the CalendarView adjustement to settle to its final position.
+     */
+    private static final long CALENDAR_VIEW_ADJUSTMENT_DURATION = 540;
 
-    private View mView;
-
-    private String mName;
-
-    private Bitmap mBitmap;
+    private Theme mTheme;
 
     private ReferenceViewGroup mViewGroup;
 
+    private int mLayoutIndex;
+
     @Override
-    public void onCreate(Bundle icicle) {
+    protected void onCreate(Bundle icicle) {
         super.onCreate(icicle);
-        setUpUi(getIntent());
+
+        mTheme = THEMES[getIntent().getIntExtra(EXTRA_THEME, 0)];
+        setTheme(mTheme.mId);
+        setContentView(R.layout.holo_test);
+        mViewGroup = (ReferenceViewGroup) findViewById(R.id.reference_view_group);
     }
 
     @Override
-    public void onNewIntent(Intent intent) {
-        super.onNewIntent(intent);
-        setUpUi(intent);
+    protected void onResume() {
+        super.onResume();
+        setNextLayout();
+    }
+
+    @Override
+    protected void onPause() {
+        if (!isFinishing()) {
+            // The Activity got paused for some reasons, for finish it as the host won't move on to
+            // the next theme otherwise.
+            Log.w(TAG, "onPause called without a call to finish().");
+            finish();
+        }
+        super.onPause();
+    }
+
+    @Override
+    protected void onDestroy() {
+        if (mLayoutIndex != LAYOUTS.length) {
+            Log.w(TAG, "Not all layouts got rendered: " + mLayoutIndex);
+        }
+        Log.i(TAG, "OKAY:" + mTheme.mName);
+        super.onDestroy();
     }
 
     /**
-     * Configures the UI with the given intent
+     * Sets the next layout in the UI.
      */
-    private void setUpUi(Intent intent) {
-        final Theme theme = themes[intent.getIntExtra(EXTRA_THEME, 0)];
-        final Layout layout = layouts[intent.getIntExtra(EXTRA_LAYOUT, 0)];
-        final int timeout = intent.getIntExtra(EXTRA_TIMEOUT, TIMEOUT);
-
-        setTheme(theme.mId);
-        setContentView(R.layout.holo_test);
-
-        mViewGroup = (ReferenceViewGroup) findViewById(R.id.reference_view_group);
-
-        mView = getLayoutInflater().inflate(layout.mId, mViewGroup, false);
-        mViewGroup.addView(mView);
-        if (layout.mModifier != null) {
-            layout.mModifier.modifyView(mView);
+    private void setNextLayout() {
+        if (mLayoutIndex >= LAYOUTS.length) {
+            finish();
+            return;
         }
-        mViewGroup.measure(0, 0);
-        mViewGroup.layout(0, 0, mViewGroup.getMeasuredWidth(), mViewGroup.getMeasuredHeight());
-        mView.setFocusable(false);
-        mName = String.format("%s_%s", theme.mName, layout.mName);
+        final Layout layout = LAYOUTS[mLayoutIndex++];
+        final String layoutName = String.format("%s_%s", mTheme.mName, layout.mName);
 
-        final Handler handler = new Handler();
-        handler.postDelayed(new Runnable() {
+        mViewGroup.removeAllViews();
+        final View view = getLayoutInflater().inflate(layout.mId, mViewGroup, false);
+        if (layout.mModifier != null) {
+            layout.mModifier.modifyView(view);
+        }
+        mViewGroup.addView(view);
+        view.setFocusable(false);
+
+        final Runnable generateBitmapRunnable = new Runnable() {
             @Override
             public void run() {
-                new GenerateBitmapTask().execute();
+                new GenerateBitmapTask(view, layoutName).execute();
             }
-        }, timeout);
-        setResult(RESULT_CANCELED);//On success will be changed to OK
+        };
+
+        if (view instanceof DatePicker) {
+            // DatePicker uses a CalendarView that has a non-configurable adjustment duration of
+            // 540ms
+            view.postDelayed(generateBitmapRunnable, CALENDAR_VIEW_ADJUSTMENT_DURATION);
+        } else {
+            view.post(generateBitmapRunnable);
+        }
     }
 
     /**
@@ -117,12 +141,14 @@
      */
     private class GenerateBitmapTask extends AsyncTask<Void, Void, Boolean> {
 
-        @Override
-        protected void onPreExecute() {
-            final View v = mView;
-            mBitmap = Bitmap.createBitmap(v.getWidth(), v.getHeight(), Bitmap.Config.ARGB_8888);
-            final Canvas canvas = new Canvas(mBitmap);
-            v.draw(canvas);
+        private final View mView;
+
+        private final String mName;
+
+        public GenerateBitmapTask(final View view, final String name) {
+            super();
+            mView = view;
+            mName = name;
         }
 
         @Override
@@ -131,6 +157,16 @@
                 Log.i(TAG, "External storage for saving bitmaps is not mounted");
                 return false;
             }
+            if (mView.getWidth() == 0 || mView.getHeight() == 0) {
+                Log.w(TAG, "Unable to draw View due to incorrect size: " + mName);
+                return false;
+            }
+
+            final Bitmap bitmap = Bitmap.createBitmap(
+                    mView.getWidth(), mView.getHeight(), Bitmap.Config.ARGB_8888);
+            final Canvas canvas = new Canvas(bitmap);
+
+            mView.draw(canvas);
             final File dir = new File(Environment.getExternalStorageDirectory(), "cts-holo-assets");
             dir.mkdirs();
             boolean success = false;
@@ -139,27 +175,23 @@
                 FileOutputStream stream = null;
                 try {
                     stream = new FileOutputStream(file);
-                    mBitmap.compress(CompressFormat.PNG, 100, stream);
+                    success = bitmap.compress(CompressFormat.PNG, 100, stream);
                 } finally {
                     if (stream != null) {
                         stream.close();
                     }
                 }
-                success = true;
             } catch (Exception e) {
                 Log.e(TAG, e.getMessage());
             } finally {
-                mBitmap.recycle();
-                mBitmap = null;
+                bitmap.recycle();
             }
             return success;
         }
 
         @Override
         protected void onPostExecute(Boolean success) {
-            Log.i(TAG, (success ? "OKAY" : "ERROR") + ":" + mName);
-            setResult(RESULT_OK);
-            finish();
+            setNextLayout();
         }
     }
 
@@ -178,7 +210,7 @@
         }
     }
 
-    private static final Theme[] themes = {
+    private static final Theme[] THEMES = {
             new Theme(android.R.style.Theme_Holo,
                     "holo"),
             new Theme(android.R.style.Theme_Holo_Dialog,
@@ -247,7 +279,7 @@
         }
     }
 
-    private static final Layout[] layouts = {
+    private static final Layout[] LAYOUTS = {
             new Layout(R.layout.button, "button", null),
             new Layout(R.layout.button, "button_pressed", new ViewPressedModifier()),
             new Layout(R.layout.checkbox, "checkbox", null),
diff --git a/hostsidetests/theme/app/src/android/theme/app/ReferenceViewGroup.java b/hostsidetests/theme/app/src/android/theme/app/ReferenceViewGroup.java
index 077d8d7..8d2461b 100644
--- a/hostsidetests/theme/app/src/android/theme/app/ReferenceViewGroup.java
+++ b/hostsidetests/theme/app/src/android/theme/app/ReferenceViewGroup.java
@@ -72,10 +72,6 @@
 
     @Override
     protected void onLayout(boolean changed, int l, int t, int r, int b) {
-        if (!changed) {
-            return;
-        }
-
         int childCount = getChildCount();
         for (int i = 0; i < childCount; i++) {
             View child = getChildAt(i);
diff --git a/hostsidetests/theme/assets/22/400dpi.zip b/hostsidetests/theme/assets/22/400dpi.zip
new file mode 100644
index 0000000..6d62e5b
--- /dev/null
+++ b/hostsidetests/theme/assets/22/400dpi.zip
Binary files differ
diff --git a/hostsidetests/theme/assets/22/560dpi.zip b/hostsidetests/theme/assets/22/560dpi.zip
new file mode 100644
index 0000000..eff363c
--- /dev/null
+++ b/hostsidetests/theme/assets/22/560dpi.zip
Binary files differ
diff --git a/hostsidetests/theme/assets/22/hdpi.zip b/hostsidetests/theme/assets/22/hdpi.zip
new file mode 100644
index 0000000..0fa67b7
--- /dev/null
+++ b/hostsidetests/theme/assets/22/hdpi.zip
Binary files differ
diff --git a/hostsidetests/theme/assets/22/ldpi.zip b/hostsidetests/theme/assets/22/ldpi.zip
new file mode 100644
index 0000000..e33f2f0
--- /dev/null
+++ b/hostsidetests/theme/assets/22/ldpi.zip
Binary files differ
diff --git a/hostsidetests/theme/assets/22/mdpi.zip b/hostsidetests/theme/assets/22/mdpi.zip
new file mode 100644
index 0000000..f74739e
--- /dev/null
+++ b/hostsidetests/theme/assets/22/mdpi.zip
Binary files differ
diff --git a/hostsidetests/theme/assets/22/tvdpi.zip b/hostsidetests/theme/assets/22/tvdpi.zip
new file mode 100644
index 0000000..fbe1781
--- /dev/null
+++ b/hostsidetests/theme/assets/22/tvdpi.zip
Binary files differ
diff --git a/hostsidetests/theme/assets/22/xhdpi.zip b/hostsidetests/theme/assets/22/xhdpi.zip
new file mode 100644
index 0000000..de6e2e1
--- /dev/null
+++ b/hostsidetests/theme/assets/22/xhdpi.zip
Binary files differ
diff --git a/hostsidetests/theme/assets/22/xxhdpi.zip b/hostsidetests/theme/assets/22/xxhdpi.zip
new file mode 100644
index 0000000..9f0d778
--- /dev/null
+++ b/hostsidetests/theme/assets/22/xxhdpi.zip
Binary files differ
diff --git a/hostsidetests/theme/src/android/theme/cts/ThemeHostTest.java b/hostsidetests/theme/src/android/theme/cts/ThemeHostTest.java
index da94b15..8326b1f 100644
--- a/hostsidetests/theme/src/android/theme/cts/ThemeHostTest.java
+++ b/hostsidetests/theme/src/android/theme/cts/ThemeHostTest.java
@@ -52,8 +52,6 @@
 
     private static final String TAG = ThemeHostTest.class.getSimpleName();
 
-    private static final int CAPTURE_TIMEOUT = 500;//0.5sec in ms
-
     private static final int ADB_TIMEOUT = 60 * 60 * 1000;//60mins in ms
 
     /** The package name of the APK. */
@@ -69,6 +67,8 @@
     private static final String START_CMD = String.format(
             "am start -W -a android.intent.action.MAIN -n %s/%s.%s", PACKAGE, PACKAGE, CLASS);
 
+    private static final String CLEAR_GENERATED_CMD = "rm -rf /sdcard/cts-holo-assets/*.png";
+
     private static final String STOP_CMD = String.format("am force-stop %s", PACKAGE);
 
     private static final String HARDWARE_TYPE_CMD = "dumpsys | grep android.hardware.type";
@@ -87,10 +87,6 @@
     // Intent extra keys
     private static final String EXTRA_THEME = "holo_theme_extra";
 
-    private static final String EXTRA_LAYOUT = "holo_layout_extra";
-
-    private static final String EXTRA_TIMEOUT = "holo_timeout_extra";
-
     private static final String[] THEMES = {
             "holo",
             "holo_dialog",
@@ -211,7 +207,8 @@
         String[] options = {AbiUtils.createAbiFlag(mAbi.getName())};
         // Install the APK on the device.
         mDevice.installPackage(app, false, options);
-
+        // Remove previously generated images.
+        mDevice.executeShellCommand(CLEAR_GENERATED_CMD);
         final String densityProp;
 
         if (mDevice.getSerialNumber().startsWith("emulator-")) {
@@ -261,6 +258,8 @@
         mExecutionService.shutdown();
         // Remove the APK.
         mDevice.uninstallPackage(PACKAGE);
+        // Remove generated images.
+        mDevice.executeShellCommand(CLEAR_GENERATED_CMD);
         super.tearDown();
     }
 
@@ -272,7 +271,6 @@
             return;
         }
 
-
         if (mReferences.isEmpty()) {
             Log.logAndDisplay(LogLevel.INFO, TAG,
                     "Skipped HoloThemes test due to no reference images");
@@ -282,20 +280,18 @@
         int numTasks = 0;
         for (int i = 0; i < NUM_THEMES; i++) {
             final String themeName = THEMES[i];
+            runCapture(i, themeName);
             for (int j = 0; j < NUM_LAYOUTS; j++) {
                 final String name = String.format("%s_%s", themeName, LAYOUTS[j]);
-                if (runCapture(i, j, name)) {
-                    final File ref = mReferences.get(name + ".png");
-                    if (!ref.exists()) {
-                        Log.logAndDisplay(LogLevel.INFO, TAG,
-                                "Skipping theme test due to missing reference for reference image " + name);
-                        continue;
-                    }
-                    mCompletionService.submit(new ComparisonTask(mDevice, ref, name));
-                    numTasks++;
-                } else {
-                    Log.logAndDisplay(LogLevel.ERROR, TAG, "Capture failed: " + name);
+                final File ref = mReferences.get(name + ".png");
+                if (!ref.exists()) {
+                    Log.logAndDisplay(LogLevel.INFO, TAG,
+                            "Skipping theme test due to missing reference for reference image " +
+                            name);
+                    continue;
                 }
+                mCompletionService.submit(new ComparisonTask(mDevice, ref, name));
+                numTasks++;
             }
         }
         int failures = 0;
@@ -305,11 +301,9 @@
         assertTrue(failures + " failures in theme test", failures == 0);
     }
 
-    private boolean runCapture(int themeId, int layoutId, String imageName) throws Exception {
+    private void runCapture(int themeId, String themeName) throws Exception {
         final StringBuilder sb = new StringBuilder(START_CMD);
         sb.append(String.format(INTENT_INTEGER_EXTRA, EXTRA_THEME, themeId));
-        sb.append(String.format(INTENT_INTEGER_EXTRA, EXTRA_LAYOUT, layoutId));
-        sb.append(String.format(INTENT_INTEGER_EXTRA, EXTRA_TIMEOUT, CAPTURE_TIMEOUT));
         final String startCommand = sb.toString();
         // Clear logcat
         mDevice.executeAdbCommand("logcat", "-c");
@@ -318,9 +312,8 @@
         // Start activity
         mDevice.executeShellCommand(startCommand);
 
-        boolean success = false;
         boolean waiting = true;
-        while (waiting) {
+        do {
             // Dump logcat.
             final String logs = mDevice.executeAdbCommand(
                     "logcat", "-v", "brief", "-d", CLASS + ":I", "*:S");
@@ -331,20 +324,14 @@
                 if (line.startsWith("I/" + CLASS)) {
                     final String[] lineSplit = line.split(":");
                     final String s = lineSplit[1].trim();
-                    final String imageNameGenerated = lineSplit[2].trim();
-                    if (s.equals("OKAY") && imageNameGenerated.equals(imageName)) {
-                        success = true;
-                        waiting = false;
-                    } else if (s.equals("ERROR") && imageNameGenerated.equals(imageName)) {
-                        success = false;
+                    final String themeNameGenerated = lineSplit[2].trim();
+                    if (s.equals("OKAY") && themeNameGenerated.equals(themeName)) {
                         waiting = false;
                     }
                 }
             }
             in.close();
-        }
-
-        return success;
+        } while (waiting);
     }
 
     private static String getDensityBucket(int density) {
diff --git a/libs/deviceutil/src/android/cts/util/MediaUtils.java b/libs/deviceutil/src/android/cts/util/MediaUtils.java
old mode 100644
new mode 100755
index eab4808..5908923
--- a/libs/deviceutil/src/android/cts/util/MediaUtils.java
+++ b/libs/deviceutil/src/android/cts/util/MediaUtils.java
@@ -17,6 +17,7 @@
 
 import android.content.Context;
 import android.content.res.AssetFileDescriptor;
+import android.media.MediaCodec;
 import android.media.MediaCodecInfo;
 import android.media.MediaCodecList;
 import android.media.MediaExtractor;
@@ -37,14 +38,13 @@
 
     private static final MediaCodecList sMCL = new MediaCodecList(MediaCodecList.REGULAR_CODECS);
 
-
     /**
-     * Finds test name (heuristically) and prints out standard skip message.
+     * Returns the test name (heuristically).
      *
      * Since it uses heuristics, this method has only been verified for media
-     * tests. This centralizes the way to signal a skipped test.
+     * tests. This centralizes the way to signal errors during a test.
      */
-    public static void skipTest(String tag, String reason) {
+    public static String getTestName() {
         int bestScore = -1;
         String testName = "test???";
         Map<Thread, StackTraceElement[]> traces = Thread.getAllStackTraces();
@@ -110,8 +110,17 @@
                 }
             }
         }
+        return testName;
+    }
 
-        Log.i(tag, "SKIPPING " + testName + "(): " + reason);
+    /**
+     * Finds test name (heuristically) and prints out standard skip message.
+     *
+     * Since it uses heuristics, this method has only been verified for media
+     * tests. This centralizes the way to signal a skipped test.
+     */
+    public static void skipTest(String tag, String reason) {
+        Log.i(tag, "SKIPPING " + getTestName() + "(): " + reason);
     }
 
     /**
@@ -131,6 +140,17 @@
         return result;
     }
 
+    public static MediaCodec getDecoder(MediaFormat format) {
+        String decoder = sMCL.findDecoderForFormat(format);
+        if (decoder != null) {
+            try {
+                return MediaCodec.createByCodecName(decoder);
+            } catch (IOException e) {
+            }
+        }
+        return null;
+    }
+
     public static boolean canDecode(MediaFormat format) {
         if (sMCL.findDecoderForFormat(format) == null) {
             Log.i(TAG, "no decoder for " + format);
diff --git a/suite/cts/deviceTests/tvproviderperf/Android.mk b/suite/cts/deviceTests/tvproviderperf/Android.mk
new file mode 100644
index 0000000..e268955
--- /dev/null
+++ b/suite/cts/deviceTests/tvproviderperf/Android.mk
@@ -0,0 +1,30 @@
+# Copyright (C) 2014 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#      http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+LOCAL_PATH:= $(call my-dir)
+include $(CLEAR_VARS)
+
+# don't include this package in any target
+LOCAL_MODULE_TAGS := optional
+
+LOCAL_STATIC_JAVA_LIBRARIES := ctsdeviceutil ctstestrunner
+
+LOCAL_SRC_FILES := $(call all-java-files-under, src)
+
+LOCAL_PACKAGE_NAME := CtsDeviceTvProviderPerf
+
+LOCAL_SDK_VERSION := current
+
+include $(BUILD_CTS_PACKAGE)
+
diff --git a/suite/cts/deviceTests/tvproviderperf/AndroidManifest.xml b/suite/cts/deviceTests/tvproviderperf/AndroidManifest.xml
new file mode 100644
index 0000000..d345ab2
--- /dev/null
+++ b/suite/cts/deviceTests/tvproviderperf/AndroidManifest.xml
@@ -0,0 +1,31 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2014 The Android Open Source Project
+
+     Licensed under the Apache License, Version 2.0 (the "License");
+     you may not use this file except in compliance with the License.
+     You may obtain a copy of the License at
+
+          http://www.apache.org/licenses/LICENSE-2.0
+
+     Unless required by applicable law or agreed to in writing, software
+     distributed under the License is distributed on an "AS IS" BASIS,
+     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+     See the License for the specific language governing permissions and
+     limitations under the License.
+-->
+
+<manifest xmlns:android="http://schemas.android.com/apk/res/android"
+        package="com.android.cts.tvproviderperf">
+
+    <uses-permission android:name="android.permission.DISABLE_KEYGUARD" />
+    <uses-permission android:name="com.android.providers.tv.permission.READ_EPG_DATA" />
+    <uses-permission android:name="com.android.providers.tv.permission.WRITE_EPG_DATA" />
+
+    <application>
+        <uses-library android:name="android.test.runner" />
+    </application>
+
+    <instrumentation android:name="android.support.test.runner.AndroidJUnitRunner"
+            android:targetPackage="com.android.cts.tvproviderperf"
+            android:label="TvProvider performance measurement" />
+</manifest>
diff --git a/suite/cts/deviceTests/tvproviderperf/src/com/android/cts/tvproviderperf/TvProviderPerfTest.java b/suite/cts/deviceTests/tvproviderperf/src/com/android/cts/tvproviderperf/TvProviderPerfTest.java
new file mode 100644
index 0000000..f9daa3c
--- /dev/null
+++ b/suite/cts/deviceTests/tvproviderperf/src/com/android/cts/tvproviderperf/TvProviderPerfTest.java
@@ -0,0 +1,375 @@
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.cts.tvproviderperf;
+
+import android.content.ComponentName;
+import android.content.ContentProviderOperation;
+import android.content.ContentProviderResult;
+import android.content.ContentResolver;
+import android.content.ContentUris;
+import android.content.ContentValues;
+import android.content.OperationApplicationException;
+import android.content.pm.PackageManager;
+import android.database.Cursor;
+import android.cts.util.CtsAndroidTestCase;
+import android.media.tv.TvContract;
+import android.media.tv.TvContract.Channels;
+import android.media.tv.TvContract.Programs;
+import android.net.Uri;
+import android.os.RemoteException;
+
+import com.android.cts.util.MeasureRun;
+import com.android.cts.util.MeasureTime;
+import com.android.cts.util.ResultType;
+import com.android.cts.util.ResultUnit;
+import com.android.cts.util.ReportLog;
+import com.android.cts.util.TimeoutReq;
+import com.android.cts.util.Stat;
+
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * Test performance of TvProvider on a device. TvProvider typically handles hundreds of
+ * thousands of records periodically, so it is desirable to have performance under a reasonable
+ * bar.
+ */
+public class TvProviderPerfTest extends CtsAndroidTestCase {
+    private static final int TRANSACTION_RUNS = 100;
+    private static final int QUERY_RUNS = 10;
+
+    private ContentResolver mContentResolver;
+    private String mInputId;
+    private boolean mHasTvInputFramework;
+
+    @Override
+    protected void setUp() throws Exception {
+        super.setUp();
+        mHasTvInputFramework = getContext().getPackageManager().hasSystemFeature(
+                PackageManager.FEATURE_LIVE_TV);
+        if (!mHasTvInputFramework) return;
+        mContentResolver = getContext().getContentResolver();
+        mInputId = TvContract.buildInputId(new ComponentName(getContext(), getClass()));
+    }
+
+    @Override
+    protected void tearDown() throws Exception {
+        try {
+            if (!mHasTvInputFramework) return;
+            mContentResolver.delete(Programs.CONTENT_URI, null, null);
+            mContentResolver.delete(Channels.CONTENT_URI, null, null);
+        } finally {
+            super.tearDown();
+        }
+    }
+
+    @TimeoutReq(minutes = 8)
+    public void testChannels() throws Exception {
+        if (!mHasTvInputFramework) return;
+        double[] averages = new double[5];
+
+        // Insert
+        final ArrayList<ContentProviderOperation> operations = new ArrayList<>();
+        final int TRANSACTION_SIZE = 1000;
+        double[] applyBatchTimes = MeasureTime.measure(TRANSACTION_RUNS, new MeasureRun() {
+            @Override
+            public void run(int i) {
+                operations.clear();
+                for (int j = 0; j < TRANSACTION_SIZE; ++j) {
+                    ContentValues values = new ContentValues();
+                    values.put(Channels.COLUMN_INPUT_ID, mInputId);
+                    values.put(Channels.COLUMN_SERVICE_TYPE,
+                            Channels.SERVICE_TYPE_AUDIO_VIDEO);
+                    values.put(Channels.COLUMN_TYPE, Channels.TYPE_OTHER);
+                    operations.add(
+                            ContentProviderOperation.newInsert(Channels.CONTENT_URI)
+                            .withValues(values).build());
+                }
+                try {
+                    mContentResolver.applyBatch(TvContract.AUTHORITY, operations);
+                } catch (OperationApplicationException | RemoteException e) {
+                    throw new RuntimeException(e);
+                }
+            }
+        });
+        getReportLog().printArray("Elapsed time for insert: ",
+                applyBatchTimes, ResultType.LOWER_BETTER, ResultUnit.MS);
+        averages[0] = Stat.getAverage(applyBatchTimes);
+
+        // Update
+        final String[] projection = { Channels._ID };
+        try (final Cursor cursor = mContentResolver.query(Channels.CONTENT_URI,
+                projection, null, null, null)) {
+            applyBatchTimes = MeasureTime.measure(TRANSACTION_RUNS, new MeasureRun() {
+                @Override
+                public void run(int i) {
+                    operations.clear();
+                    for (int j = 0; j < TRANSACTION_SIZE && cursor.moveToNext(); ++j) {
+                        Uri channelUri = TvContract.buildChannelUri(cursor.getLong(0));
+                        String number = Integer.toString(i * TRANSACTION_SIZE + j);
+                        operations.add(
+                                ContentProviderOperation.newUpdate(channelUri)
+                                .withValue(Channels.COLUMN_DISPLAY_NUMBER, number)
+                                .build());
+                    }
+                    try {
+                        mContentResolver.applyBatch(TvContract.AUTHORITY, operations);
+                    } catch (OperationApplicationException | RemoteException e) {
+                        throw new RuntimeException(e);
+                    }
+                }
+            });
+        }
+        getReportLog().printArray("Elapsed time for update: ",
+                applyBatchTimes, ResultType.LOWER_BETTER, ResultUnit.MS);
+        averages[1] = Stat.getAverage(applyBatchTimes);
+
+        // Query channels
+        applyBatchTimes = MeasureTime.measure(QUERY_RUNS, new MeasureRun() {
+            @Override
+            public void run(int i) {
+                try (Cursor cursor = mContentResolver.query(Channels.CONTENT_URI, null, null,
+                        null, null)) {
+                    while (cursor.moveToNext()) {
+                        // Do nothing. Just iterate all the items.
+                    }
+                }
+            }
+        });
+        getReportLog().printArray("Elapsed time for query (channels): ",
+                applyBatchTimes, ResultType.LOWER_BETTER, ResultUnit.MS);
+        averages[2] = Stat.getAverage(applyBatchTimes);
+
+        // Query a channel
+        try (final Cursor cursor = mContentResolver.query(Channels.CONTENT_URI,
+                projection, null, null, null)) {
+            final Uri channelUri = TvContract.buildChannelUri(cursor.getLong(0));
+            applyBatchTimes = MeasureTime.measure(QUERY_RUNS, new MeasureRun() {
+                @Override
+                public void run(int i) {
+                    assertTrue(cursor.moveToNext());
+                    try (Cursor c = mContentResolver.query(channelUri, null, null, null, null)) {
+                        while (c.moveToNext()) {
+                            // Do nothing. Just iterate all the items.
+                        }
+                    }
+                }
+            });
+        }
+        getReportLog().printArray("Elapsed time for query (a channel): ",
+                applyBatchTimes, ResultType.LOWER_BETTER, ResultUnit.MS);
+        averages[3] = Stat.getAverage(applyBatchTimes);
+
+        // Delete
+        applyBatchTimes = MeasureTime.measure(1, new MeasureRun() {
+            @Override
+            public void run(int i) {
+                mContentResolver.delete(TvContract.buildChannelsUriForInput(mInputId), null, null);
+            }
+        });
+        getReportLog().printArray("Elapsed time for delete: ",
+                applyBatchTimes, ResultType.LOWER_BETTER, ResultUnit.MS);
+        averages[4] = Stat.getAverage(applyBatchTimes);
+
+        getReportLog().printArray("Average elapsed time for insert, update, query (channels), "
+                + "query (a channel), delete: ",
+                averages, ResultType.LOWER_BETTER, ResultUnit.MS);
+    }
+
+    @TimeoutReq(minutes = 12)
+    public void testPrograms() throws Exception {
+        if (!mHasTvInputFramework) return;
+        double[] averages = new double[7];
+
+        // Prepare (insert channels)
+        final ArrayList<ContentProviderOperation> operations = new ArrayList<>();
+        final int TRANSACTION_SIZE = 1000;
+        final int NUM_CHANNELS = 100;
+        final List<Uri> channelUris = new ArrayList<>();
+
+        operations.clear();
+        for (int i = 0; i < NUM_CHANNELS; ++i) {
+            ContentValues values = new ContentValues();
+            values.put(Channels.COLUMN_INPUT_ID, mInputId);
+            values.put(Channels.COLUMN_SERVICE_TYPE,
+                    Channels.SERVICE_TYPE_AUDIO_VIDEO);
+            values.put(Channels.COLUMN_TYPE, Channels.TYPE_OTHER);
+            operations.add(
+                    ContentProviderOperation.newInsert(Channels.CONTENT_URI)
+                    .withValues(values).build());
+        }
+        try {
+            ContentProviderResult[] results =
+                    mContentResolver.applyBatch(TvContract.AUTHORITY, operations);
+            for (ContentProviderResult result : results) {
+                channelUris.add(result.uri);
+            }
+        } catch (OperationApplicationException | RemoteException e) {
+            throw new RuntimeException(e);
+        }
+
+        // Insert
+        double[] applyBatchTimes = MeasureTime.measure(NUM_CHANNELS, new MeasureRun() {
+            @Override
+            public void run(int i) {
+                operations.clear();
+                Uri channelUri = channelUris.get(i);
+                long channelId = ContentUris.parseId(channelUri);
+                for (int j = 0; j < TRANSACTION_SIZE; ++j) {
+                    ContentValues values = new ContentValues();
+                    values.put(Programs.COLUMN_CHANNEL_ID, channelId);
+                    operations.add(
+                            ContentProviderOperation.newInsert(Programs.CONTENT_URI)
+                            .withValues(values).build());
+                }
+                try {
+                    mContentResolver.applyBatch(TvContract.AUTHORITY, operations);
+                } catch (OperationApplicationException | RemoteException e) {
+                    throw new RuntimeException(e);
+                }
+            }
+        });
+        getReportLog().printArray("Elapsed time for insert: ",
+                applyBatchTimes, ResultType.LOWER_BETTER, ResultUnit.MS);
+        averages[0] = Stat.getAverage(applyBatchTimes);
+
+        // Update
+        final long PROGRAM_DURATION_MS = 60 * 1000;
+        final String[] projection = { Programs._ID };
+        applyBatchTimes = MeasureTime.measure(NUM_CHANNELS, new MeasureRun() {
+            @Override
+            public void run(int i) {
+                Uri channelUri = channelUris.get(i);
+                operations.clear();
+                try (Cursor cursor = mContentResolver.query(
+                        TvContract.buildProgramsUriForChannel(channelUri),
+                        projection, null, null, null)) {
+                    long startTimeMs = 0;
+                    long endTimeMs = 0;
+                    while (cursor.moveToNext()) {
+                        Uri programUri = TvContract.buildProgramUri(cursor.getLong(0));
+                        endTimeMs += PROGRAM_DURATION_MS;
+                        operations.add(
+                                ContentProviderOperation.newUpdate(programUri)
+                                .withValue(Programs.COLUMN_START_TIME_UTC_MILLIS, startTimeMs)
+                                .withValue(Programs.COLUMN_END_TIME_UTC_MILLIS, endTimeMs)
+                                .build());
+                        startTimeMs = endTimeMs;
+                    }
+                }
+                try {
+                    mContentResolver.applyBatch(TvContract.AUTHORITY, operations);
+                } catch (OperationApplicationException | RemoteException e) {
+                    throw new RuntimeException(e);
+                }
+            }
+        });
+        getReportLog().printArray("Elapsed time for update: ",
+                applyBatchTimes, ResultType.LOWER_BETTER, ResultUnit.MS);
+        averages[1] = Stat.getAverage(applyBatchTimes);
+
+        // Query programs
+        applyBatchTimes = MeasureTime.measure(QUERY_RUNS, new MeasureRun() {
+            @Override
+            public void run(int i) {
+                try (Cursor cursor = mContentResolver.query(Programs.CONTENT_URI, null, null,
+                        null, null)) {
+                    while (cursor.moveToNext()) {
+                        // Do nothing. Just iterate all the items.
+                    }
+                }
+            }
+        });
+        getReportLog().printArray("Elapsed time for query (programs): ",
+                applyBatchTimes, ResultType.LOWER_BETTER, ResultUnit.MS);
+        averages[2] = Stat.getAverage(applyBatchTimes);
+
+        // Query programs with selection
+        applyBatchTimes = MeasureTime.measure(QUERY_RUNS, new MeasureRun() {
+            @Override
+            public void run(int i) {
+                Uri channelUri = channelUris.get(i);
+                try (Cursor cursor = mContentResolver.query(
+                        TvContract.buildProgramsUriForChannel(
+                                channelUri, 0,
+                                PROGRAM_DURATION_MS * TRANSACTION_SIZE / 2),
+                        null, null, null, null)) {
+                    while (cursor.moveToNext()) {
+                        // Do nothing. Just iterate all the items.
+                    }
+                }
+            }
+        });
+        getReportLog().printArray("Elapsed time for query (programs with selection): ",
+                applyBatchTimes, ResultType.LOWER_BETTER, ResultUnit.MS);
+        averages[3] = Stat.getAverage(applyBatchTimes);
+
+        // Query a program
+        try (final Cursor cursor = mContentResolver.query(Programs.CONTENT_URI,
+                projection, null, null, null)) {
+            final Uri programUri = TvContract.buildProgramUri(cursor.getLong(0));
+            applyBatchTimes = MeasureTime.measure(QUERY_RUNS, new MeasureRun() {
+                @Override
+                public void run(int i) {
+                    assertTrue(cursor.moveToNext());
+                    try (Cursor c = mContentResolver.query(programUri, null, null, null, null)) {
+                        while (c.moveToNext()) {
+                            // Do nothing. Just iterate all the items.
+                        }
+                    }
+                }
+            });
+        }
+        getReportLog().printArray("Elapsed time for query (a program): ",
+                applyBatchTimes, ResultType.LOWER_BETTER, ResultUnit.MS);
+        averages[4] = Stat.getAverage(applyBatchTimes);
+
+        // Delete programs
+        applyBatchTimes = MeasureTime.measure(NUM_CHANNELS, new MeasureRun() {
+            @Override
+            public void run(int i) {
+                Uri channelUri = channelUris.get(i);
+                mContentResolver.delete(
+                        TvContract.buildProgramsUriForChannel(
+                                channelUri,
+                                PROGRAM_DURATION_MS * TRANSACTION_SIZE / 2,
+                                PROGRAM_DURATION_MS * TRANSACTION_SIZE),
+                        null, null);
+            }
+        });
+        getReportLog().printArray("Elapsed time for delete programs: ",
+                applyBatchTimes, ResultType.LOWER_BETTER, ResultUnit.MS);
+        averages[5] = Stat.getAverage(applyBatchTimes);
+
+        // Delete channels
+        applyBatchTimes = MeasureTime.measure(NUM_CHANNELS, new MeasureRun() {
+            @Override
+            public void run(int i) {
+                Uri channelUri = channelUris.get(i);
+                mContentResolver.delete(channelUri, null, null);
+            }
+        });
+        getReportLog().printArray("Elapsed time for delete channels: ",
+                applyBatchTimes, ResultType.LOWER_BETTER, ResultUnit.MS);
+        averages[6] = Stat.getAverage(applyBatchTimes);
+
+        getReportLog().printArray("Average elapsed time for insert, update, query (programs), "
+                + "query (programs with selection), query (a channel), delete (channels), "
+                + "delete (programs): ",
+                averages, ResultType.LOWER_BETTER, ResultUnit.MS);
+    }
+}
diff --git a/suite/cts/deviceTests/videoperf/Android.mk b/suite/cts/deviceTests/videoperf/Android.mk
index cd82dde..a393683 100644
--- a/suite/cts/deviceTests/videoperf/Android.mk
+++ b/suite/cts/deviceTests/videoperf/Android.mk
@@ -17,8 +17,15 @@
 
 # don't include this package in any target
 LOCAL_MODULE_TAGS := optional
+# and when built explicitly put it in the data partition
+LOCAL_MODULE_PATH := $(TARGET_OUT_DATA_APPS)
 
-LOCAL_STATIC_JAVA_LIBRARIES := ctsdeviceutil ctstestrunner
+# include both the 32 and 64 bit versions
+LOCAL_MULTILIB := both
+
+LOCAL_STATIC_JAVA_LIBRARIES := ctsmediautil ctsdeviceutil ctstestrunner
+
+LOCAL_JNI_SHARED_LIBRARIES := libctsmediacodec_jni
 
 LOCAL_SRC_FILES := $(call all-java-files-under, src)
 
diff --git a/suite/cts/deviceTests/videoperf/src/com/android/cts/videoperf/CodecInfo.java b/suite/cts/deviceTests/videoperf/src/com/android/cts/videoperf/CodecInfo.java
index b7d1d27..462794b 100644
--- a/suite/cts/deviceTests/videoperf/src/com/android/cts/videoperf/CodecInfo.java
+++ b/suite/cts/deviceTests/videoperf/src/com/android/cts/videoperf/CodecInfo.java
@@ -66,7 +66,12 @@
                 break;
             }
         }
-        VideoCapabilities vidCap = cap.getVideoCapabilities();
+
+        if (cap.colorFormats.length == 0) {
+            Log.w(TAG, "no supported color format");
+            return null;
+        }
+
         CodecInfo info = new CodecInfo();
         for (int color : cap.colorFormats) {
             if (color == CodecCapabilities.COLOR_FormatYUV420SemiPlanar) {
@@ -77,15 +82,25 @@
             }
         }
         printIntArray("supported colors", cap.colorFormats);
-        //  either YUV420 planar or semiplanar should be supported
-        if (!info.mSupportPlanar && !info.mSupportSemiPlanar) {
-            Log.i(TAG, "no supported color format");
-            return null;
-        }
 
+        VideoCapabilities vidCap = cap.getVideoCapabilities();
         if (mimeType.equals(VIDEO_AVC)) {
             info.mFps = vidCap.getSupportedFrameRatesFor(w, h).getUpper().intValue();
             info.mBitRate = vidCap.getBitrateRange().getUpper();
+
+            // we don't parse bitrate-range on L, so need to adjust bitrate to supported
+            // baseline or main profiles only.
+            int maxBitRate = 0;
+            for (CodecProfileLevel pl : cap.profileLevels) {
+                if (pl.profile == pl.AVCProfileBaseline || pl.profile == pl.AVCProfileMain) {
+                    VideoCapabilities vidCapPL =
+                        CodecCapabilities.createFromProfileLevel(mimeType, pl.profile, pl.level)
+                                .getVideoCapabilities();
+                    maxBitRate = Math.max(maxBitRate, vidCapPL.getBitrateRange().getUpper());
+                }
+            }
+            info.mBitRate = Math.min(info.mBitRate, maxBitRate);
+
             Log.i(TAG, "AVC bit rate " + info.mBitRate + " fps " + info.mFps);
         }
         return info;
diff --git a/suite/cts/deviceTests/videoperf/src/com/android/cts/videoperf/VideoEncoderDecoderTest.java b/suite/cts/deviceTests/videoperf/src/com/android/cts/videoperf/VideoEncoderDecoderTest.java
index aacb7a5..bf02d9c 100644
--- a/suite/cts/deviceTests/videoperf/src/com/android/cts/videoperf/VideoEncoderDecoderTest.java
+++ b/suite/cts/deviceTests/videoperf/src/com/android/cts/videoperf/VideoEncoderDecoderTest.java
@@ -16,7 +16,12 @@
 
 package com.android.cts.videoperf;
 
+import android.graphics.ImageFormat;
 import android.graphics.Point;
+import android.media.cts.CodecImage;
+import android.media.cts.CodecUtils;
+import android.media.Image;
+import android.media.Image.Plane;
 import android.media.MediaCodec;
 import android.media.MediaCodecList;
 import android.media.MediaCodecInfo.CodecCapabilities;
@@ -60,12 +65,10 @@
     private static final int Y_CLAMP_MIN = 16;
     private static final int Y_CLAMP_MAX = 235;
     private static final int YUV_PLANE_ADDITIONAL_LENGTH = 200;
-    private ByteBuffer mYBuffer;
-    private ByteBuffer mUVBuffer;
-    // if input raw data is semi-planar
-    private boolean mSrcSemiPlanar;
-    // if output raw data is semi-planar
-    private boolean mDstSemiPlanar;
+    private ByteBuffer mYBuffer, mYDirectBuffer;
+    private ByteBuffer mUVBuffer, mUVDirectBuffer;
+    private int mSrcColorFormat;
+    private int mDstColorFormat;
     private int mBufferWidth;
     private int mBufferHeight;
     private int mVideoWidth;
@@ -95,6 +98,8 @@
         mEncodedOutputBuffer = null;
         mYBuffer = null;
         mUVBuffer = null;
+        mYDirectBuffer = null;
+        mUVDirectBuffer = null;
         mRandom = null;
         super.tearDown();
     }
@@ -124,6 +129,33 @@
         doTest(VIDEO_AVC, 1920, 1072, NUMBER_OF_REPEAT);
     }
 
+    private boolean isSrcSemiPlanar() {
+        return mSrcColorFormat == CodecCapabilities.COLOR_FormatYUV420SemiPlanar;
+    }
+
+    private boolean isSrcFlexYUV() {
+        return mSrcColorFormat == CodecCapabilities.COLOR_FormatYUV420Flexible;
+    }
+
+    private boolean isDstSemiPlanar() {
+        return mDstColorFormat == CodecCapabilities.COLOR_FormatYUV420SemiPlanar;
+    }
+
+    private boolean isDstFlexYUV() {
+        return mDstColorFormat == CodecCapabilities.COLOR_FormatYUV420Flexible;
+    }
+
+    private static int getColorFormat(CodecInfo info) {
+        if (info.mSupportSemiPlanar) {
+            return CodecCapabilities.COLOR_FormatYUV420SemiPlanar;
+        } else if (info.mSupportPlanar) {
+            return CodecCapabilities.COLOR_FormatYUV420Planar;
+        } else {
+            // FlexYUV must be supported
+            return CodecCapabilities.COLOR_FormatYUV420Flexible;
+        }
+    }
+
     /**
      * Run encoding / decoding test for given mimeType of codec
      * @param mimeType like video/avc
@@ -141,8 +173,14 @@
         assertNotNull(infoDec);
         mVideoWidth = w;
         mVideoHeight = h;
-        initYUVPlane(w + YUV_PLANE_ADDITIONAL_LENGTH, h + YUV_PLANE_ADDITIONAL_LENGTH,
-                infoEnc.mSupportSemiPlanar, infoDec.mSupportSemiPlanar);
+
+        mSrcColorFormat = getColorFormat(infoEnc);
+        mDstColorFormat = getColorFormat(infoDec);
+        Log.i(TAG, "Testing video resolution " + w + "x" + h +
+                   ": enc format " + mSrcColorFormat +
+                   ", dec format " + mDstColorFormat);
+
+        initYUVPlane(w + YUV_PLANE_ADDITIONAL_LENGTH, h + YUV_PLANE_ADDITIONAL_LENGTH);
         double[] encoderFpsResults = new double[numberRepeat];
         double[] decoderFpsResults = new double[numberRepeat];
         double[] totalFpsResults = new double[numberRepeat];
@@ -154,9 +192,7 @@
             format.setInteger(MediaFormat.KEY_BIT_RATE, infoEnc.mBitRate);
             format.setInteger(MediaFormat.KEY_WIDTH, w);
             format.setInteger(MediaFormat.KEY_HEIGHT, h);
-            format.setInteger(MediaFormat.KEY_COLOR_FORMAT,
-                    infoEnc.mSupportSemiPlanar ? CodecCapabilities.COLOR_FormatYUV420SemiPlanar :
-                        CodecCapabilities.COLOR_FormatYUV420Planar);
+            format.setInteger(MediaFormat.KEY_COLOR_FORMAT, mSrcColorFormat);
             format.setInteger(MediaFormat.KEY_FRAME_RATE, infoEnc.mFps);
             mFrameRate = infoEnc.mFps;
             format.setInteger(MediaFormat.KEY_I_FRAME_INTERVAL, KEY_I_FRAME_INTERVAL);
@@ -166,9 +202,7 @@
             format.setString(MediaFormat.KEY_MIME, mimeType);
             format.setInteger(MediaFormat.KEY_WIDTH, w);
             format.setInteger(MediaFormat.KEY_HEIGHT, h);
-            format.setInteger(MediaFormat.KEY_COLOR_FORMAT,
-                    infoDec.mSupportSemiPlanar ? CodecCapabilities.COLOR_FormatYUV420SemiPlanar :
-                        CodecCapabilities.COLOR_FormatYUV420Planar);
+            format.setInteger(MediaFormat.KEY_COLOR_FORMAT, mDstColorFormat);
             double[] decoderResult = runDecoder(VIDEO_AVC, format);
             if (decoderResult == null) {
                 success = false;
@@ -228,7 +262,6 @@
             return Double.NaN;
         }
         codec.start();
-        ByteBuffer[] codecInputBuffers = codec.getInputBuffers();
         ByteBuffer[] codecOutputBuffers = codec.getOutputBuffers();
 
         int numBytesSubmitted = 0;
@@ -241,10 +274,24 @@
             if (inFramesCount < totalFrames) {
                 index = codec.dequeueInputBuffer(VIDEO_CODEC_WAIT_TIME_US /* timeoutUs */);
                 if (index != MediaCodec.INFO_TRY_AGAIN_LATER) {
-                    int size = queueInputBufferEncoder(
-                            codec, codecInputBuffers, index, inFramesCount,
-                            (inFramesCount == (totalFrames - 1)) ?
-                                    MediaCodec.BUFFER_FLAG_END_OF_STREAM : 0);
+                    int size;
+                    // when encoder only supports flexYUV, use Image only; otherwise,
+                    // use ByteBuffer & Image each on half of the frames to test both
+                    if (isSrcFlexYUV() || inFramesCount % 2 == 0) {
+                        Image image = codec.getInputImage(index);
+                        // image should always be available
+                        assertTrue(image != null);
+                        size = queueInputImageEncoder(
+                                codec, image, index, inFramesCount,
+                                (inFramesCount == (totalFrames - 1)) ?
+                                        MediaCodec.BUFFER_FLAG_END_OF_STREAM : 0);
+                    } else {
+                        ByteBuffer buffer = codec.getInputBuffer(index);
+                        size = queueInputBufferEncoder(
+                                codec, buffer, index, inFramesCount,
+                                (inFramesCount == (totalFrames - 1)) ?
+                                        MediaCodec.BUFFER_FLAG_END_OF_STREAM : 0);
+                    }
                     inFramesCount++;
                     numBytesSubmitted += size;
                     if (VERBOSE) {
@@ -290,8 +337,7 @@
      * @return size of enqueued data.
      */
     private int queueInputBufferEncoder(
-            MediaCodec codec, ByteBuffer[] inputBuffers, int index, int frameCount, int flags) {
-        ByteBuffer buffer = inputBuffers[index];
+            MediaCodec codec, ByteBuffer buffer, int index, int frameCount, int flags) {
         buffer.clear();
 
         Point origin = getOrigin(frameCount);
@@ -302,7 +348,7 @@
             buffer.put(yBuffer, srcOffsetY, mVideoWidth);
             srcOffsetY += mBufferWidth;
         }
-        if (mSrcSemiPlanar) {
+        if (isSrcSemiPlanar()) {
             int srcOffsetU = origin.y / 2 * mBufferWidth + origin.x / 2 * 2;
             final byte[] uvBuffer = mUVBuffer.array();
             for (int i = 0; i < mVideoHeight / 2; i++) {
@@ -313,16 +359,161 @@
             int srcOffsetU = origin.y / 2 * mBufferWidth / 2 + origin.x / 2;
             int srcOffsetV = srcOffsetU + mBufferWidth / 2 * mBufferHeight / 2;
             final byte[] uvBuffer = mUVBuffer.array();
-            for (int i = 0; i < mVideoHeight /2; i++) { //U only
+            for (int i = 0; i < mVideoHeight / 2; i++) { //U only
                 buffer.put(uvBuffer, srcOffsetU, mVideoWidth / 2);
                 srcOffsetU += mBufferWidth / 2;
             }
-            for (int i = 0; i < mVideoHeight /2; i++) { //V only
+            for (int i = 0; i < mVideoHeight / 2; i++) { //V only
                 buffer.put(uvBuffer, srcOffsetV, mVideoWidth / 2);
                 srcOffsetV += mBufferWidth / 2;
             }
         }
-        int size = mVideoHeight * mVideoWidth * 3 /2;
+        int size = mVideoHeight * mVideoWidth * 3 / 2;
+        long ptsUsec = computePresentationTime(frameCount);
+
+        codec.queueInputBuffer(index, 0 /* offset */, size, ptsUsec /* timeUs */, flags);
+        if (VERBOSE && (frameCount == 0)) {
+            printByteArray("Y ", mYBuffer.array(), 0, 20);
+            printByteArray("UV ", mUVBuffer.array(), 0, 20);
+            printByteArray("UV ", mUVBuffer.array(), mBufferWidth * 60, 20);
+        }
+        return size;
+    }
+
+    class YUVImage extends CodecImage {
+        private final int mImageWidth;
+        private final int mImageHeight;
+        private final Plane[] mPlanes;
+
+        YUVImage(
+                Point origin,
+                int imageWidth, int imageHeight,
+                int arrayWidth, int arrayHeight,
+                boolean semiPlanar,
+                ByteBuffer bufferY, ByteBuffer bufferUV) {
+            mImageWidth = imageWidth;
+            mImageHeight = imageHeight;
+            ByteBuffer dupY = bufferY.duplicate();
+            ByteBuffer dupUV = bufferUV.duplicate();
+            mPlanes = new Plane[3];
+
+            int srcOffsetY = origin.x + origin.y * arrayWidth;
+
+            mPlanes[0] = new YUVPlane(
+                        mImageWidth, mImageHeight, arrayWidth, 1,
+                        dupY, srcOffsetY);
+
+            if (semiPlanar) {
+                int srcOffsetUV = origin.y / 2 * arrayWidth + origin.x / 2 * 2;
+
+                mPlanes[1] = new YUVPlane(
+                        mImageWidth / 2, mImageHeight / 2, arrayWidth, 2,
+                        dupUV, srcOffsetUV);
+                mPlanes[2] = new YUVPlane(
+                        mImageWidth / 2, mImageHeight / 2, arrayWidth, 2,
+                        dupUV, srcOffsetUV + 1);
+            } else {
+                int srcOffsetU = origin.y / 2 * arrayWidth / 2 + origin.x / 2;
+                int srcOffsetV = srcOffsetU + arrayWidth / 2 * arrayHeight / 2;
+
+                mPlanes[1] = new YUVPlane(
+                        mImageWidth / 2, mImageHeight / 2, arrayWidth / 2, 1,
+                        dupUV, srcOffsetU);
+                mPlanes[2] = new YUVPlane(
+                        mImageWidth / 2, mImageHeight / 2, arrayWidth / 2, 1,
+                        dupUV, srcOffsetV);
+            }
+        }
+
+        @Override
+        public int getFormat() {
+            return ImageFormat.YUV_420_888;
+        }
+
+        @Override
+        public int getWidth() {
+            return mImageWidth;
+        }
+
+        @Override
+        public int getHeight() {
+            return mImageHeight;
+        }
+
+        @Override
+        public long getTimestamp() {
+            return 0;
+        }
+
+        @Override
+        public Plane[] getPlanes() {
+            return mPlanes;
+        }
+
+        @Override
+        public void close() {
+            mPlanes[0] = null;
+            mPlanes[1] = null;
+            mPlanes[2] = null;
+        }
+
+        class YUVPlane extends CodecImage.Plane {
+            private final int mRowStride;
+            private final int mPixelStride;
+            private final ByteBuffer mByteBuffer;
+
+            YUVPlane(int w, int h, int rowStride, int pixelStride,
+                    ByteBuffer buffer, int offset) {
+                mRowStride = rowStride;
+                mPixelStride = pixelStride;
+
+                // only safe to access length bytes starting from buffer[offset]
+                int length = (h - 1) * rowStride + (w - 1) * pixelStride + 1;
+
+                buffer.position(offset);
+                mByteBuffer = buffer.slice();
+                mByteBuffer.limit(length);
+            }
+
+            @Override
+            public int getRowStride() {
+                return mRowStride;
+            }
+
+            @Override
+            public int getPixelStride() {
+                return mPixelStride;
+            }
+
+            @Override
+            public ByteBuffer getBuffer() {
+                return mByteBuffer;
+            }
+        }
+    }
+
+    /**
+     * Fills input image for encoder from YUV buffers.
+     * @return size of enqueued data.
+     */
+    private int queueInputImageEncoder(
+            MediaCodec codec, Image image, int index, int frameCount, int flags) {
+        assertTrue(image.getFormat() == ImageFormat.YUV_420_888);
+
+
+        Point origin = getOrigin(frameCount);
+
+        // Y color first
+        CodecImage srcImage = new YUVImage(
+                origin,
+                mVideoWidth, mVideoHeight,
+                mBufferWidth, mBufferHeight,
+                isSrcSemiPlanar(),
+                mYDirectBuffer, mUVDirectBuffer);
+
+        CodecUtils.copyFlexYUVImage(image, srcImage);
+
+        int size = mVideoHeight * mVideoWidth * 3 / 2;
         long ptsUsec = computePresentationTime(frameCount);
 
         codec.queueInputBuffer(index, 0 /* offset */, size, ptsUsec /* timeUs */, flags);
@@ -368,7 +559,6 @@
         codec.configure(format, null /* surface */, null /* crypto */, 0 /* flags */);
         codec.start();
         ByteBuffer[] codecInputBuffers = codec.getInputBuffers();
-        ByteBuffer[] codecOutputBuffers = codec.getOutputBuffers();
 
         double totalErrorSquared = 0;
 
@@ -407,22 +597,49 @@
 
                 // only do YUV compare on EOS frame if the buffer size is none-zero
                 if (info.size > 0) {
-                    ByteBuffer buf = codecOutputBuffers[outputBufIndex];
-                    if (VERBOSE && (outFrameCount == 0)) {
-                        printByteBuffer("Y ", buf, 0, 20);
-                        printByteBuffer("UV ", buf, mVideoWidth * mVideoHeight, 20);
-                        printByteBuffer("UV ", buf,
-                                mVideoWidth * mVideoHeight + mVideoWidth * 60, 20);
-                    }
                     Point origin = getOrigin(outFrameCount);
-                    for (int i = 0; i < PIXEL_CHECK_PER_FRAME; i++) {
+                    int i;
+
+                    // if decoder supports planar or semiplanar, check output with
+                    // ByteBuffer & Image each on half of the points
+                    int pixelCheckPerFrame = PIXEL_CHECK_PER_FRAME;
+                    if (!isDstFlexYUV()) {
+                        pixelCheckPerFrame /= 2;
+                        ByteBuffer buf = codec.getOutputBuffer(outputBufIndex);
+                        if (VERBOSE && (outFrameCount == 0)) {
+                            printByteBuffer("Y ", buf, 0, 20);
+                            printByteBuffer("UV ", buf, mVideoWidth * mVideoHeight, 20);
+                            printByteBuffer("UV ", buf,
+                                    mVideoWidth * mVideoHeight + mVideoWidth * 60, 20);
+                        }
+                        for (i = 0; i < pixelCheckPerFrame; i++) {
+                            int w = mRandom.nextInt(mVideoWidth);
+                            int h = mRandom.nextInt(mVideoHeight);
+                            getPixelValuesFromYUVBuffers(origin.x, origin.y, w, h, expected);
+                            getPixelValuesFromOutputBuffer(buf, w, h, decoded);
+                            if (VERBOSE) {
+                                Log.i(TAG, outFrameCount + "-" + i + "- th round: ByteBuffer:"
+                                        + " expected "
+                                        + expected.mY + "," + expected.mU + "," + expected.mV
+                                        + " decoded "
+                                        + decoded.mY + "," + decoded.mU + "," + decoded.mV);
+                            }
+                            totalErrorSquared += expected.calcErrorSquared(decoded);
+                        }
+                    }
+
+                    Image image = codec.getOutputImage(outputBufIndex);
+                    assertTrue(image != null);
+                    for (i = 0; i < pixelCheckPerFrame; i++) {
                         int w = mRandom.nextInt(mVideoWidth);
                         int h = mRandom.nextInt(mVideoHeight);
                         getPixelValuesFromYUVBuffers(origin.x, origin.y, w, h, expected);
-                        getPixelValuesFromOutputBuffer(buf, w, h, decoded);
+                        getPixelValuesFromImage(image, w, h, decoded);
                         if (VERBOSE) {
-                            Log.i(TAG, outFrameCount + "-" + i + "- th round expcted " + expected.mY
-                                    + "," + expected.mU + "," + expected.mV + "  decoded "
+                            Log.i(TAG, outFrameCount + "-" + i + "- th round: FlexYUV:"
+                                    + " expcted "
+                                    + expected.mY + "," + expected.mU + "," + expected.mV
+                                    + " decoded "
                                     + decoded.mY + "," + decoded.mU + "," + decoded.mV);
                         }
                         totalErrorSquared += expected.calcErrorSquared(decoded);
@@ -434,23 +651,17 @@
                     Log.d(TAG, "saw output EOS.");
                     sawOutputEOS = true;
                 }
-            } else if (res == MediaCodec.INFO_OUTPUT_BUFFERS_CHANGED) {
-                codecOutputBuffers = codec.getOutputBuffers();
-                Log.d(TAG, "output buffers have changed.");
             } else if (res == MediaCodec.INFO_OUTPUT_FORMAT_CHANGED) {
                 MediaFormat oformat = codec.getOutputFormat();
                 Log.d(TAG, "output format has changed to " + oformat);
                 int colorFormat = oformat.getInteger(MediaFormat.KEY_COLOR_FORMAT);
-                if (colorFormat == CodecCapabilities.COLOR_FormatYUV420SemiPlanar ) {
-                    mDstSemiPlanar = true;
-                } else if (colorFormat == CodecCapabilities.COLOR_FormatYUV420Planar ) {
-                    mDstSemiPlanar = false;
+                if (colorFormat == CodecCapabilities.COLOR_FormatYUV420SemiPlanar
+                        || colorFormat == CodecCapabilities.COLOR_FormatYUV420Planar) {
+                    mDstColorFormat = colorFormat;
                 } else {
+                    mDstColorFormat = CodecCapabilities.COLOR_FormatYUV420Flexible;
                     Log.w(TAG, "output format changed to unsupported one " +
-                            Integer.toHexString(colorFormat));
-                    // give up and return as nothing can be done
-                    codec.release();
-                    return null;
+                            Integer.toHexString(colorFormat) + ", using FlexYUV");
                 }
             }
         }
@@ -491,12 +702,12 @@
      * @param semiPlanarEnc
      * @param semiPlanarDec
      */
-    private void initYUVPlane(int w, int h, boolean semiPlanarEnc, boolean semiPlanarDec) {
+    private void initYUVPlane(int w, int h) {
         int bufferSizeY = w * h;
         mYBuffer = ByteBuffer.allocate(bufferSizeY);
         mUVBuffer = ByteBuffer.allocate(bufferSizeY / 2);
-        mSrcSemiPlanar = semiPlanarEnc;
-        mDstSemiPlanar = semiPlanarDec;
+        mYDirectBuffer = ByteBuffer.allocateDirect(bufferSizeY);
+        mUVDirectBuffer = ByteBuffer.allocateDirect(bufferSizeY / 2);
         mBufferWidth = w;
         mBufferHeight = h;
         final byte[] yArray = mYBuffer.array();
@@ -506,7 +717,7 @@
                 yArray[i * w + j]  = clampY((i + j) & 0xff);
             }
         }
-        if (semiPlanarEnc) {
+        if (isSrcSemiPlanar()) {
             for (int i = 0; i < h/2; i++) {
                 for (int j = 0; j < w/2; j++) {
                     uvArray[i * w + 2 * j]  = (byte) (i & 0xff);
@@ -522,6 +733,10 @@
                 }
             }
         }
+        mYDirectBuffer.put(yArray);
+        mUVDirectBuffer.put(uvArray);
+        mYDirectBuffer.rewind();
+        mUVDirectBuffer.rewind();
     }
 
     /**
@@ -556,7 +771,7 @@
     private void getPixelValuesFromYUVBuffers(int originX, int originY, int x, int y,
             YUVValue result) {
         result.mY = mYBuffer.get((originY + y) * mBufferWidth + (originX + x));
-        if (mSrcSemiPlanar) {
+        if (isSrcSemiPlanar()) {
             int index = (originY + y) / 2 * mBufferWidth + (originX + x) / 2 * 2;
             //Log.d(TAG, "YUV " + originX + "," + originY + "," + x + "," + y + "," + index);
             result.mU = mUVBuffer.get(index);
@@ -577,7 +792,7 @@
      */
     private void getPixelValuesFromOutputBuffer(ByteBuffer buffer, int x, int y, YUVValue result) {
         result.mY = buffer.get(y * mVideoWidth + x);
-        if (mDstSemiPlanar) {
+        if (isDstSemiPlanar()) {
             int index = mVideoWidth * mVideoHeight + y / 2 * mVideoWidth + x / 2 * 2;
             //Log.d(TAG, "Decoded " + x + "," + y + "," + index);
             result.mU = buffer.get(index);
@@ -590,6 +805,22 @@
         }
     }
 
+    private void getPixelValuesFromImage(Image image, int x, int y, YUVValue result) {
+        assertTrue(image.getFormat() == ImageFormat.YUV_420_888);
+
+        Plane[] planes = image.getPlanes();
+        assertTrue(planes.length == 3);
+
+        result.mY = getPixelFromPlane(planes[0], x, y);
+        result.mU = getPixelFromPlane(planes[1], x / 2, y / 2);
+        result.mV = getPixelFromPlane(planes[2], x / 2, y / 2);
+    }
+
+    private byte getPixelFromPlane(Plane plane, int x, int y) {
+        ByteBuffer buf = plane.getBuffer();
+        return buf.get(y * plane.getRowStride() + x * plane.getPixelStride());
+    }
+
     /**
      * Y cannot have full range. clamp it to prevent invalid value.
      */
diff --git a/tests/JobScheduler/src/android/jobscheduler/cts/ConnectivityConstraintTest.java b/tests/JobScheduler/src/android/jobscheduler/cts/ConnectivityConstraintTest.java
index a83f7a9..547b205 100644
--- a/tests/JobScheduler/src/android/jobscheduler/cts/ConnectivityConstraintTest.java
+++ b/tests/JobScheduler/src/android/jobscheduler/cts/ConnectivityConstraintTest.java
@@ -51,6 +51,8 @@
     private boolean mHasWifi;
     /** Whether the device running these tests supports telephony. */
     private boolean mHasTelephony;
+    /** Track whether WiFi was enabled in case we turn it off. */
+    private boolean mInitialWiFiState;
 
     private JobInfo.Builder mBuilder;
 
@@ -67,6 +69,14 @@
         mHasTelephony = packageManager.hasSystemFeature(PackageManager.FEATURE_TELEPHONY);
         mBuilder =
                 new JobInfo.Builder(CONNECTIVITY_JOB_ID, kJobServiceComponent);
+
+        mInitialWiFiState = mWifiManager.isWifiEnabled();
+    }
+
+    @Override
+    public void tearDown() throws Exception {
+        // Ensure that we leave WiFi in its previous state.
+        mWifiManager.setWifiEnabled(mInitialWiFiState);
     }
 
     // --------------------------------------------------------------------------------------------
@@ -202,6 +212,14 @@
         }
     }
 
+    /**
+     * Disconnect from WiFi in an attempt to connect to cellular data. Worth noting that this is
+     * best effort - there are no public APIs to force connecting to cell data. We disable WiFi
+     * and wait for a broadcast that we're connected to cell.
+     * We will not call into this function if the device doesn't support telephony.
+     * @see #mHasTelephony
+     * @see #checkDeviceSupportsMobileData()
+     */
     private void disconnectWifiToConnectToMobile() throws InterruptedException {
         if (mHasWifi && mWifiManager.isWifiEnabled()) {
             ConnectivityActionReceiver connectMobileReceiver =
diff --git a/tests/core/runner/src/com/android/cts/runner/CtsTestRunListener.java b/tests/core/runner/src/com/android/cts/runner/CtsTestRunListener.java
index 080c08e..7564f32 100644
--- a/tests/core/runner/src/com/android/cts/runner/CtsTestRunListener.java
+++ b/tests/core/runner/src/com/android/cts/runner/CtsTestRunListener.java
@@ -29,11 +29,16 @@
 import org.junit.runner.notification.RunListener;
 
 import java.io.File;
+import java.io.IOException;
+import java.io.InputStreamReader;
+import java.lang.Class;
+import java.lang.ReflectiveOperationException;
 import java.lang.reflect.Field;
 import java.lang.reflect.Modifier;
 import java.net.Authenticator;
 import java.net.CookieHandler;
 import java.net.ResponseCache;
+import java.text.DateFormat;
 import java.util.Locale;
 import java.util.Properties;
 import java.util.TimeZone;
@@ -148,11 +153,22 @@
 
     // http://code.google.com/p/vogar/source/browse/trunk/src/vogar/target/TestEnvironment.java
     static class TestEnvironment {
+        private static final Field sDateFormatIs24HourField;
+        static {
+            try {
+                Class<?> dateFormatClass = Class.forName("java.text.DateFormat");
+                sDateFormatIs24HourField = dateFormatClass.getDeclaredField("is24Hour");
+            } catch (ReflectiveOperationException e) {
+                throw new AssertionError("Missing DateFormat.is24Hour", e);
+            }
+        }
+
         private final Locale mDefaultLocale;
         private final TimeZone mDefaultTimeZone;
         private final HostnameVerifier mHostnameVerifier;
         private final SSLSocketFactory mSslSocketFactory;
         private final Properties mProperties = new Properties();
+        private final Boolean mDefaultIs24Hour;
 
         TestEnvironment(Context context) {
             mDefaultLocale = Locale.getDefault();
@@ -167,7 +183,7 @@
             PackageManager pm = context.getPackageManager();
             mProperties.setProperty("android.cts.device.multicast",
                     Boolean.toString(pm.hasSystemFeature(PackageManager.FEATURE_WIFI)));
-
+            mDefaultIs24Hour = getDateFormatIs24Hour();
         }
 
         void reset() {
@@ -180,6 +196,23 @@
             ResponseCache.setDefault(null);
             HttpsURLConnection.setDefaultHostnameVerifier(mHostnameVerifier);
             HttpsURLConnection.setDefaultSSLSocketFactory(mSslSocketFactory);
+            setDateFormatIs24Hour(mDefaultIs24Hour);
+        }
+
+        private static Boolean getDateFormatIs24Hour() {
+            try {
+                return (Boolean) sDateFormatIs24HourField.get(null);
+            } catch (ReflectiveOperationException e) {
+                throw new AssertionError("Unable to get java.text.DateFormat.is24Hour", e);
+            }
+        }
+
+        private static void setDateFormatIs24Hour(Boolean value) {
+            try {
+                sDateFormatIs24HourField.set(null, value);
+            } catch (ReflectiveOperationException e) {
+                throw new AssertionError("Unable to set java.text.DateFormat.is24Hour", e);
+            }
         }
     }
 
diff --git a/tests/expectations/knownfailures.txt b/tests/expectations/knownfailures.txt
index 3abb1f7..76b528b 100644
--- a/tests/expectations/knownfailures.txt
+++ b/tests/expectations/knownfailures.txt
@@ -83,22 +83,18 @@
   bug: 16720689
 },
 {
-  description: "A few WebGL tests are known to fail in WebView",
+  description: "Disable WebGL conformance tests in CTS",
   names: [
-    "android.webgl.cts.WebGLTest#test_conformance_extensions_oes_texture_float_with_video_html",
-    "android.webgl.cts.WebGLTest#test_conformance_renderbuffers_framebuffer_object_attachment_html",
-    "android.webgl.cts.WebGLTest#test_conformance_rendering_multisample_corruption_html",
-    "android.webgl.cts.WebGLTest#test_conformance_textures_tex_image_and_sub_image_2d_with_video_html",
-    "android.webgl.cts.WebGLTest#test_conformance_textures_tex_image_and_sub_image_2d_with_video_rgb565_html",
-    "android.webgl.cts.WebGLTest#test_conformance_textures_tex_image_and_sub_image_2d_with_video_rgba4444_html",
-    "android.webgl.cts.WebGLTest#test_conformance_textures_tex_image_and_sub_image_2d_with_video_rgba5551_html",
-    "android.webgl.cts.WebGLTest#test_conformance_textures_texture_npot_html",
-    "android.webgl.cts.WebGLTest#test_conformance_textures_texture_npot_video_html",
-    "android.webgl.cts.WebGLTest#test_conformance_glsl_misc_empty_main_vert_html",
-    "android.webgl.cts.WebGLTest#test_conformance_glsl_misc_gl_position_unset_vert_html",
-    "android.webgl.cts.WebGLTest#test_conformance_misc_webgl_specific_html"
+    "android.webgl.cts.WebGLTest"
   ],
-  bug: 17748398
+  bug: 20937460
+},
+{
+  description: "WebGL test uniformMatrixBadArgs is too strict. Disabled until it's fixed upstream.",
+  names: [
+    "android.webgl.cts.WebGLTest#test_conformance_more_functions_uniformMatrixBadArgs_html"
+  ],
+  bug: 18638404
 },
 {
   description: "permissions for the API previously used in the test has changed, making it impossible to pass",
@@ -124,37 +120,6 @@
   bug: 17530117
 },
 {
-  description: "this test removes the stay-awake option, causing the screen to turn off during the execution of subsequent tests",
-  names: [
-    "android.admin.cts.DevicePolicyManagerTest#testMaximumTimeToLock"
-  ],
-  bug: 18002490
-},
-{
-  description: "these tests locks the screen with an emtpy password or swipe-to-unlock, blocking subsequent test to dismiss keyguard",
-  names: [
-    "android.admin.cts.DevicePolicyManagerTest#testPasswordQuality_something",
-    "android.admin.cts.DevicePolicyManagerTest#testPasswordQuality_numeric",
-    "android.admin.cts.DevicePolicyManagerTest#testPasswordQuality_alphabetic",
-    "android.admin.cts.DevicePolicyManagerTest#testPasswordQuality_alphanumeric",
-    "android.admin.cts.DevicePolicyManagerTest#testPasswordQuality_complexUpperCase",
-    "android.admin.cts.DevicePolicyManagerTest#testPasswordQuality_complexLowerCase",
-    "android.admin.cts.DevicePolicyManagerTest#testPasswordQuality_complexLetters",
-    "android.admin.cts.DevicePolicyManagerTest#testPasswordQuality_complexNumeric",
-    "android.admin.cts.DevicePolicyManagerTest#testPasswordQuality_complexSymbols",
-    "android.admin.cts.DevicePolicyManagerTest#testPasswordQuality_complexNonLetter",
-    "android.admin.cts.DevicePolicyManagerTest#testGetMaximumFailedPasswordsForWipe"
-  ],
-  bug: 17496766
-},
-{
-  description: "these tests locks the screen with an emtpy password or swipe-to-unlock, blocking subsequent test to dismiss keyguard",
-  names: [
-    "com.android.cts.devicepolicy.DeviceOwnerTest#testKeyManagement"
-  ],
-  bug: 17496766
-},
-{
   description: "Current implementation of uninstallAllUserCaCerts does not throw expected security exception, wait for fix from framework",
   names: [
     "android.admin.cts.DevicePolicyManagerTest#testUninstallAllUserCaCerts_failIfNotProfileOwner"
@@ -162,24 +127,6 @@
   bug: 17508787
 },
 {
-  description: "New tests recently added for Android Enterprise. To be moved out of CTS-staging as soon as they show that they are stable",
-  names: [
-    "com.android.cts.devicepolicy.LauncherAppsMultiUserTest#testGetActivitiesForNonProfileFails",
-    "com.android.cts.devicepolicy.LauncherAppsMultiUserTest#testNoLauncherCallbackPackageAddedSecondaryUser",
-    "com.android.cts.devicepolicy.LauncherAppsProfileTest#testGetActivitiesWithProfile",
-    "com.android.cts.devicepolicy.LauncherAppsProfileTest#testLauncherCallbackPackageAddedProfile",
-    "com.android.cts.devicepolicy.LauncherAppsProfileTest#testLauncherCallbackPackageRemovedProfile",
-    "com.android.cts.devicepolicy.LauncherAppsProfileTest#testLauncherCallbackPackageChangedProfile",
-    "com.android.cts.devicepolicy.LauncherAppsSingleUserTest#testInstallAppMainUser",
-    "com.android.cts.devicepolicy.LauncherAppsSingleUserTest#testLauncherCallbackPackageAddedMainUser",
-    "com.android.cts.devicepolicy.LauncherAppsSingleUserTest#testLauncherCallbackPackageRemovedMainUser",
-    "com.android.cts.devicepolicy.LauncherAppsSingleUserTest#testLauncherCallbackPackageChangedMainUser",
-    "com.android.cts.devicepolicy.LauncherAppsSingleUserTest#testLauncherNonExportedAppFails",
-    "com.android.cts.devicepolicy.LauncherAppsSingleUserTest#testLaunchNonExportActivityFails",
-    "com.android.cts.devicepolicy.LauncherAppsSingleUserTest#testLaunchMainActivity"
-  ]
-},
-{
   description: "These tests fail on some devices.",
   names: [
     "android.uirendering.cts.testclasses.ExactCanvasTests#testBlueRect",
@@ -361,10 +308,7 @@
     "android.hardware.cts.SingleSensorTests#testLinearAcceleration_10hz",
     "android.hardware.cts.SingleSensorTests#testLinearAcceleration_5hz",
     "android.hardware.cts.SingleSensorTests#testLinearAcceleration_1hz",
-    "android.hardware.cts.SensorTest#testValuesForAllSensors",
-    "android.hardware.cts.SensorTest#testSensorTimeStamps",
-    "android.hardware.cts.SensorTest#testBatchAndFlush",
-    "android.hardware.cts.SensorTest#testBatchAndFlushWithHandler"
+    "android.hardware.cts.SensorTest#testSensorTimeStamps"
   ],
   bug: 17675466
 },
@@ -390,5 +334,19 @@
     "com.android.org.conscrypt.SignatureTest#test_getInstance_OpenSSL_ENGINE"
   ],
   bug: 18030049
+},
+{
+  description: "The new recording test is not yet passing on all devices",
+  names: [
+    "android.hardware.camera2.cts.RecordingTest#testRecordingFramerateLowToHigh"
+  ],
+  bug: 18705837
+},
+{
+  description: "The new image reader test is not yet passing on all devices",
+  names: [
+    "android.hardware.camera2.cts.ImageReaderTest#testAllOutputYUVResolutions"
+  ],
+  bug: 18689511
 }
 ]
diff --git a/tests/tests/accessibility/src/android/view/accessibility/cts/AccessibilityNodeInfoTest.java b/tests/tests/accessibility/src/android/view/accessibility/cts/AccessibilityNodeInfoTest.java
index 87f0238..f5e1d48 100644
--- a/tests/tests/accessibility/src/android/view/accessibility/cts/AccessibilityNodeInfoTest.java
+++ b/tests/tests/accessibility/src/android/view/accessibility/cts/AccessibilityNodeInfoTest.java
@@ -35,7 +35,7 @@
 public class AccessibilityNodeInfoTest extends AndroidTestCase {
 
     /** The number of properties of the {@link AccessibilityNodeInfo} class. */
-    private static final int NON_STATIC_FIELD_COUNT = 28;
+    private static final int NON_STATIC_FIELD_COUNT = 30;
 
     @SmallTest
     public void testMarshaling() throws Exception {
@@ -54,7 +54,7 @@
         AccessibilityNodeInfo receivedInfo = AccessibilityNodeInfo.CREATOR.createFromParcel(parcel);
 
         // make sure all fields properly marshaled
-        assertEqualsAccessiblityNodeInfo(sentInfo, receivedInfo);
+        assertEqualsAccessibilityNodeInfo(sentInfo, receivedInfo);
     }
 
     /**
@@ -204,7 +204,7 @@
      * <code>receviedInfo</code> to verify that the received node info is
      * the one that is expected.
      */
-    public static void assertEqualsAccessiblityNodeInfo(AccessibilityNodeInfo expectedInfo,
+    public static void assertEqualsAccessibilityNodeInfo(AccessibilityNodeInfo expectedInfo,
             AccessibilityNodeInfo receivedInfo) {
         Rect expectedBounds = new Rect();
         Rect receivedBounds = new Rect();
diff --git a/tests/tests/accounts/AndroidManifest.xml b/tests/tests/accounts/AndroidManifest.xml
index 22362ab..c671ff0 100644
--- a/tests/tests/accounts/AndroidManifest.xml
+++ b/tests/tests/accounts/AndroidManifest.xml
@@ -35,6 +35,9 @@
             </intent-filter>
         </activity>
 
+        <activity android:name="android.accounts.cts.AccountRemovalDummyActivity" >
+        </activity>
+
         <service android:name="MockAccountService" android:exported="true"
                  android:process="android.accounts.cts">
             <intent-filter>
diff --git a/tests/tests/accounts/src/android/accounts/cts/AccountManagerTest.java b/tests/tests/accounts/src/android/accounts/cts/AccountManagerTest.java
index e5cc45a..b2a48e6 100644
--- a/tests/tests/accounts/src/android/accounts/cts/AccountManagerTest.java
+++ b/tests/tests/accounts/src/android/accounts/cts/AccountManagerTest.java
@@ -26,6 +26,7 @@
 import android.accounts.OperationCanceledException;
 import android.app.Activity;
 import android.content.Context;
+import android.content.Intent;
 import android.os.Bundle;
 import android.os.Handler;
 import android.os.Looper;
@@ -81,6 +82,8 @@
     public static final String USERDATA_VALUE_2 = "user.data.value.2";
 
     public static final Account ACCOUNT = new Account(ACCOUNT_NAME, ACCOUNT_TYPE);
+    public static final Account ACCOUNT_FOR_NEW_REMOVE_ACCOUNT_API = new Account(
+            MockAccountAuthenticator.ACCOUNT_NAME_FOR_NEW_REMOVE_API, ACCOUNT_TYPE);
     public static final Account ACCOUNT_SAME_TYPE = new Account(ACCOUNT_NAME_OTHER, ACCOUNT_TYPE);
 
     private static MockAccountAuthenticator mockAuthenticator;
@@ -122,8 +125,10 @@
         mockAuthenticator.clearData();
 
         // Need to clean up created account
-        assertTrue(removeAccount(am, ACCOUNT, null /* callback */));
-        assertTrue(removeAccount(am, ACCOUNT_SAME_TYPE, null /* callback */));
+        assertTrue(removeAccount(am, ACCOUNT, mActivity, null /* callback */).getBoolean(
+                AccountManager.KEY_BOOLEAN_RESULT));
+        assertTrue(removeAccount(am, ACCOUNT_SAME_TYPE, mActivity, null /* callback */).getBoolean(
+                AccountManager.KEY_BOOLEAN_RESULT));
 
         // need to clean up the authenticator cached data
         mockAuthenticator.clearData();
@@ -174,7 +179,7 @@
         assertTrue(options.containsKey(AccountManager.KEY_CALLER_UID));
         assertTrue(options.containsKey(AccountManager.KEY_CALLER_PID));
     }
-    
+
     private void validateCredentials() {
         assertEquals(ACCOUNT, mockAuthenticator.getAccount());
     }
@@ -229,6 +234,38 @@
         return resultBoolean;
     }
 
+    private Bundle removeAccountWithIntentLaunch(AccountManager am, Account account,
+            Activity activity, AccountManagerCallback<Bundle> callback) throws IOException,
+            AuthenticatorException, OperationCanceledException {
+
+        AccountManagerFuture<Bundle> futureBundle = am.removeAccount(account,
+                activity,
+                callback,
+                null /* handler */);
+        Bundle resultBundle = futureBundle.getResult();
+        assertTrue(futureBundle.isDone());
+
+        return resultBundle;
+    }
+
+    private Bundle removeAccount(AccountManager am, Account account, Activity activity,
+            AccountManagerCallback<Bundle> callback) throws IOException, AuthenticatorException,
+                OperationCanceledException {
+
+        AccountManagerFuture<Bundle> futureBundle = am.removeAccount(account,
+                activity,
+                callback,
+                null /* handler */);
+        Bundle resultBundle = futureBundle.getResult();
+        assertTrue(futureBundle.isDone());
+
+        return resultBundle;
+    }
+
+    private boolean removeAccountExplicitly(AccountManager am, Account account) {
+        return am.removeAccountExplicitly(account);
+    }
+
     private void addAccountExplicitly(Account account, String password, Bundle userdata) {
         assertTrue(am.addAccountExplicitly(account, password, userdata));
     }
@@ -380,6 +417,61 @@
         assertEquals(1 + expectedAccountsCount, accounts.length);
         assertTrue(isAccountPresent(am.getAccounts(), ACCOUNT));
         // Need to clean up
+        assertTrue(removeAccount(am, ACCOUNT, mActivity, null /* callback */).getBoolean(
+                AccountManager.KEY_BOOLEAN_RESULT));
+
+        // and verify that we go back to the initial state
+        accounts = am.getAccounts();
+        assertNotNull(accounts);
+        assertEquals(expectedAccountsCount, accounts.length);
+    }
+
+    /**
+     * Test addAccountExplicitly(), renameAccount() and removeAccount().
+     */
+    public void testAddAccountExplicitlyAndRemoveAccountWithNewApi() throws IOException,
+            AuthenticatorException, OperationCanceledException {
+
+        final int expectedAccountsCount = getAccountsCount();
+
+        addAccountExplicitly(ACCOUNT_FOR_NEW_REMOVE_ACCOUNT_API, ACCOUNT_PASSWORD, null /* userData */);
+
+        // Assert that we have one more account
+        Account[] accounts = am.getAccounts();
+        assertNotNull(accounts);
+        assertEquals(1 + expectedAccountsCount, accounts.length);
+        assertTrue(isAccountPresent(am.getAccounts(), ACCOUNT_FOR_NEW_REMOVE_ACCOUNT_API));
+        // Deprecated API should not work
+        assertFalse(removeAccount(am, ACCOUNT_FOR_NEW_REMOVE_ACCOUNT_API, null /* callback */));
+        accounts = am.getAccounts();
+        assertNotNull(accounts);
+        assertEquals(1 + expectedAccountsCount, accounts.length);
+        assertTrue(isAccountPresent(am.getAccounts(), ACCOUNT_FOR_NEW_REMOVE_ACCOUNT_API));
+        // Check removal of account
+        assertTrue(removeAccountWithIntentLaunch(am, ACCOUNT_FOR_NEW_REMOVE_ACCOUNT_API, mActivity, null /* callback */)
+                .getBoolean(AccountManager.KEY_BOOLEAN_RESULT));
+        // and verify that we go back to the initial state
+        accounts = am.getAccounts();
+        assertNotNull(accounts);
+        assertEquals(expectedAccountsCount, accounts.length);
+    }
+
+    /**
+     * Test addAccountExplicitly(), renameAccount() and removeAccount().
+     */
+    public void testAddAccountExplicitlyAndRemoveAccountWithDeprecatedApi() throws IOException,
+            AuthenticatorException, OperationCanceledException {
+
+        final int expectedAccountsCount = getAccountsCount();
+
+        addAccountExplicitly(ACCOUNT, ACCOUNT_PASSWORD, null /* userData */);
+
+        // Assert that we have one more account
+        Account[] accounts = am.getAccounts();
+        assertNotNull(accounts);
+        assertEquals(1 + expectedAccountsCount, accounts.length);
+        assertTrue(isAccountPresent(am.getAccounts(), ACCOUNT));
+        // Need to clean up
         assertTrue(removeAccount(am, ACCOUNT, null /* callback */));
 
         // and verify that we go back to the initial state
@@ -389,6 +481,30 @@
     }
 
     /**
+     * Test addAccountExplicitly() and removeAccountExplictly().
+     */
+    public void testAddAccountExplicitlyAndRemoveAccountExplicitly() throws IOException,
+            AuthenticatorException, OperationCanceledException {
+
+        final int expectedAccountsCount = getAccountsCount();
+
+        addAccountExplicitly(ACCOUNT, ACCOUNT_PASSWORD, null /* userData */);
+
+        // Assert that we have one more account
+        Account[] accounts = am.getAccounts();
+        assertNotNull(accounts);
+        assertEquals(1 + expectedAccountsCount, accounts.length);
+        assertTrue(isAccountPresent(am.getAccounts(), ACCOUNT));
+        // Need to clean up
+        assertTrue(removeAccountExplicitly(am, ACCOUNT));
+
+        // and verify that we go back to the initial state
+        accounts = am.getAccounts();
+        assertNotNull(accounts);
+        assertEquals(expectedAccountsCount, accounts.length);
+    }
+
+    /**
      * Test setUserData() and getUserData().
      */
     public void testAccountRenameAndGetPreviousName()
@@ -430,7 +546,8 @@
         assertEquals(ACCOUNT.name, am.getPreviousName(renamedAccount));
 
        // Need to clean up
-        assertTrue(removeAccount(am, renamedAccount, null /* callback */));
+        assertTrue(removeAccount(am, renamedAccount, mActivity, null /* callback */).getBoolean(
+                AccountManager.KEY_BOOLEAN_RESULT));
     }
 
     /**
@@ -1148,19 +1265,22 @@
                 false /* updateImmediately */);
 
         // Need to cleanup intermediate state
-        assertTrue(removeAccount(am, ACCOUNT, null /* callback */));
+        assertTrue(removeAccount(am, ACCOUNT, mActivity, null /* callback */).getBoolean(
+                AccountManager.KEY_BOOLEAN_RESULT));
 
         testAddOnAccountsUpdatedListenerWithHandler(null /* handler */,
                 true /* updateImmediately */);
 
         // Need to cleanup intermediate state
-        assertTrue(removeAccount(am, ACCOUNT, null /* callback */));
+        assertTrue(removeAccount(am, ACCOUNT, mActivity, null /* callback */).getBoolean(
+                AccountManager.KEY_BOOLEAN_RESULT));
 
         testAddOnAccountsUpdatedListenerWithHandler(new Handler(Looper.getMainLooper()),
                 false /* updateImmediately */);
 
         // Need to cleanup intermediate state
-        assertTrue(removeAccount(am, ACCOUNT, null /* callback */));
+        assertTrue(removeAccount(am, ACCOUNT, mActivity, null /* callback */).getBoolean(
+                AccountManager.KEY_BOOLEAN_RESULT));
 
         testAddOnAccountsUpdatedListenerWithHandler(new Handler(Looper.getMainLooper()),
                 true /* updateImmediately */);
@@ -1204,7 +1324,8 @@
         testRemoveOnAccountsUpdatedListenerWithHandler(null /* handler */);
 
         // Need to cleanup intermediate state
-        assertTrue(removeAccount(am, ACCOUNT, null /* callback */));
+        assertTrue(removeAccount(am, ACCOUNT, mActivity, null /* callback */).getBoolean(
+                AccountManager.KEY_BOOLEAN_RESULT));
 
         testRemoveOnAccountsUpdatedListenerWithHandler(new Handler(Looper.getMainLooper()));
     }
diff --git a/tests/tests/accounts/src/android/accounts/cts/AccountRemovalDummyActivity.java b/tests/tests/accounts/src/android/accounts/cts/AccountRemovalDummyActivity.java
new file mode 100644
index 0000000..3ad9429
--- /dev/null
+++ b/tests/tests/accounts/src/android/accounts/cts/AccountRemovalDummyActivity.java
@@ -0,0 +1,49 @@
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.accounts.cts;
+
+import android.accounts.AccountAuthenticatorResponse;
+import android.accounts.AccountManager;
+import android.app.Activity;
+import android.content.Context;
+import android.content.Intent;
+import android.os.Bundle;
+
+/**
+ * An activity.
+ */
+public class AccountRemovalDummyActivity extends Activity {
+
+    public static Intent createIntent(Context context) {
+        return new Intent(context, AccountRemovalDummyActivity.class);
+    }
+
+    private AccountAuthenticatorResponse mResponse;
+
+    @Override
+    protected void onCreate(Bundle savedInstanceState) {
+        super.onCreate(savedInstanceState);
+        Bundle input = (savedInstanceState == null) ? getIntent().getExtras() : savedInstanceState;
+        mResponse = input
+                .getParcelable(MockAccountAuthenticator.KEY_ACCOUNT_AUTHENTICATOR_RESPONSE);
+        // Do verification and then remove the account
+        final Bundle result = new Bundle();
+        result.putBoolean(AccountManager.KEY_BOOLEAN_RESULT, true);
+        mResponse.onResult(result);
+        finish();
+    }
+}
diff --git a/tests/tests/accounts/src/android/accounts/cts/MockAccountAuthenticator.java b/tests/tests/accounts/src/android/accounts/cts/MockAccountAuthenticator.java
index 2d5c395..b5804d9 100644
--- a/tests/tests/accounts/src/android/accounts/cts/MockAccountAuthenticator.java
+++ b/tests/tests/accounts/src/android/accounts/cts/MockAccountAuthenticator.java
@@ -22,6 +22,7 @@
 import android.accounts.AccountManager;
 import android.accounts.NetworkErrorException;
 import android.content.Context;
+import android.content.Intent;
 import android.os.Bundle;
 
 import java.util.ArrayList;
@@ -31,21 +32,27 @@
  */
 public class MockAccountAuthenticator extends AbstractAccountAuthenticator {
 
-    private AccountAuthenticatorResponse mResponse;
-    private String mAccountType;
-    private String mAuthTokenType;
-    private String[] mRequiredFeatures;
+    public static String KEY_ACCOUNT_INFO = "key_account_info";
+    public static String KEY_ACCOUNT_AUTHENTICATOR_RESPONSE = "key_account_authenticator_response";
+    public static String ACCOUNT_NAME_FOR_NEW_REMOVE_API = "call new removeAccount api";
+
+    private final Context mContext;
+    AccountAuthenticatorResponse mResponse;
+    String mAccountType;
+    String mAuthTokenType;
+    String[] mRequiredFeatures;
     public Bundle mOptionsUpdateCredentials;
     public Bundle mOptionsConfirmCredentials;
     public Bundle mOptionsAddAccount;
     public Bundle mOptionsGetAuthToken;
-    private Account mAccount;
-    private String[] mFeatures;
+    Account mAccount;
+    String[] mFeatures;
 
-    private final ArrayList<String> mockFeatureList = new ArrayList<String>();
+    final ArrayList<String> mockFeatureList = new ArrayList<String>();
 
     public MockAccountAuthenticator(Context context) {
         super(context);
+        mContext = context;
 
         // Create some mock features
         mockFeatureList.add(AccountManagerTest.FEATURE_1);
@@ -213,4 +220,26 @@
         }
         return result;
     }
+
+    @Override
+    public Bundle getAccountRemovalAllowed(AccountAuthenticatorResponse response,
+            Account account) throws NetworkErrorException {
+        final Bundle result = new Bundle();
+        if (ACCOUNT_NAME_FOR_NEW_REMOVE_API.equals(account.name)) {
+            Intent intent = AccountRemovalDummyActivity.createIntent(mContext);
+            // Pass in the authenticator response, so that account removal can
+            // be
+            // completed
+            intent.putExtra(KEY_ACCOUNT_AUTHENTICATOR_RESPONSE, response);
+            intent.putExtra(KEY_ACCOUNT_INFO, account);
+            result.putParcelable(AccountManager.KEY_INTENT, intent);
+            // Adding this following line to reject account installation
+            // requests
+            // coming from old removeAccount API.
+            result.putBoolean(AccountManager.KEY_BOOLEAN_RESULT, false);
+        } else {
+            result.putBoolean(AccountManager.KEY_BOOLEAN_RESULT, true);
+        }
+        return result;
+    }
 }
\ No newline at end of file
diff --git a/tests/tests/admin/src/android/admin/cts/DevicePolicyManagerTest.java b/tests/tests/admin/src/android/admin/cts/DevicePolicyManagerTest.java
index 1f61b27..513b67e 100644
--- a/tests/tests/admin/src/android/admin/cts/DevicePolicyManagerTest.java
+++ b/tests/tests/admin/src/android/admin/cts/DevicePolicyManagerTest.java
@@ -494,24 +494,6 @@
         }
     }
 
-    public void testMaximumTimeToLock() {
-        if (!mDeviceAdmin) {
-            Log.w(TAG, "Skipping testMaximumTimeToLock");
-            return;
-        }
-        long originalValue = mDevicePolicyManager.getMaximumTimeToLock(mComponent);
-        try {
-            for (long testLength : new long[] {
-                    5000L /* 5 sec */, 60000L /* 1 min */, 1800000 /* 30 min */}) {
-                mDevicePolicyManager.setMaximumTimeToLock(mComponent, testLength);
-                assertEquals(testLength,
-                        mDevicePolicyManager.getMaximumTimeToLock(mComponent));
-            }
-        } finally {
-            mDevicePolicyManager.setMaximumTimeToLock(mComponent, originalValue);
-        }
-    }
-
     public void testCreateUser_failIfNotDeviceOwner() {
         if (!mDeviceAdmin) {
             Log.w(TAG, "Skipping testCreateUser_failIfNotDeviceOwner");
diff --git a/tests/tests/animation/src/android/animation/cts/AnimatorSetTest.java b/tests/tests/animation/src/android/animation/cts/AnimatorSetTest.java
index 35a0b4c..0163a58 100644
--- a/tests/tests/animation/src/android/animation/cts/AnimatorSetTest.java
+++ b/tests/tests/animation/src/android/animation/cts/AnimatorSetTest.java
@@ -16,8 +16,11 @@
 package android.animation.cts;
 
 import java.util.ArrayList;
+import java.util.HashSet;
+import java.util.Set;
 
 import android.animation.Animator;
+import android.animation.AnimatorListenerAdapter;
 import android.animation.AnimatorSet;
 import android.animation.ObjectAnimator;
 import android.animation.TimeInterpolator;
@@ -25,6 +28,7 @@
 import android.test.ActivityInstrumentationTestCase2;
 import android.view.animation.AccelerateDecelerateInterpolator;
 import android.view.animation.AccelerateInterpolator;
+import android.view.animation.LinearInterpolator;
 
 public class AnimatorSetTest extends
         ActivityInstrumentationTestCase2<AnimationActivity> {
@@ -34,6 +38,7 @@
     private Object object;
     private ObjectAnimator yAnimator;
     private ObjectAnimator xAnimator;
+    Set<Integer> identityHashes = new HashSet<Integer>();
 
     public AnimatorSetTest() {
         super(AnimationActivity.class);
@@ -158,4 +163,84 @@
             }
         });
     }
+
+    private void assertUnique(Object object) {
+        assertUnique(object, "");
+    }
+
+    private void assertUnique(Object object, String msg) {
+        final int code = System.identityHashCode(object);
+        assertTrue("object should be unique " + msg + ", obj:" + object, identityHashes.add(code));
+
+    }
+
+    public void testClone() throws Throwable {
+        final AnimatorSet set1 = new AnimatorSet();
+        final AnimatorListenerAdapter setListener = new AnimatorListenerAdapter() {};
+        set1.addListener(setListener);
+        ObjectAnimator animator1 = new ObjectAnimator();
+        animator1.setDuration(100);
+        animator1.setPropertyName("x");
+        animator1.setIntValues(5);
+        animator1.setInterpolator(new LinearInterpolator());
+        AnimatorListenerAdapter listener1 = new AnimatorListenerAdapter(){};
+        AnimatorListenerAdapter listener2 = new AnimatorListenerAdapter(){};
+        animator1.addListener(listener1);
+
+        ObjectAnimator animator2 = new ObjectAnimator();
+        animator2.setDuration(100);
+        animator2.setInterpolator(new LinearInterpolator());
+        animator2.addListener(listener2);
+        animator2.setPropertyName("y");
+        animator2.setIntValues(10);
+
+        set1.playTogether(animator1, animator2);
+
+        AnimateObject target = new AnimateObject();
+        set1.setTarget(target);
+        runTestOnUiThread(new Runnable() {
+            @Override
+            public void run() {
+                set1.start();
+            }
+        });
+        assertTrue(set1.isStarted());
+
+        animator1.getListeners();
+        AnimatorSet set2 = set1.clone();
+        assertFalse(set2.isStarted());
+
+        assertUnique(set1);
+        assertUnique(animator1);
+        assertUnique(animator2);
+
+        assertUnique(set2);
+        assertEquals(2, set2.getChildAnimations().size());
+
+        Animator clone1 = set2.getChildAnimations().get(0);
+        Animator clone2 = set2.getChildAnimations().get(1);
+
+        for (Animator animator : set2.getChildAnimations()) {
+            assertUnique(animator);
+        }
+
+        assertTrue(clone1.getListeners().contains(listener1));
+        assertTrue(clone2.getListeners().contains(listener2));
+
+        assertTrue(set2.getListeners().contains(setListener));
+
+        for (Animator.AnimatorListener listener : set1.getListeners()) {
+            assertTrue(set2.getListeners().contains(listener));
+        }
+
+        assertEquals(animator1.getDuration(), clone1.getDuration());
+        assertEquals(animator2.getDuration(), clone2.getDuration());
+        assertSame(animator1.getInterpolator(), clone1.getInterpolator());
+        assertSame(animator2.getInterpolator(), clone2.getInterpolator());
+    }
+
+    class AnimateObject {
+        int x = 1;
+        int y = 2;
+    }
 }
diff --git a/tests/tests/animation/src/android/animation/cts/AnimatorTest.java b/tests/tests/animation/src/android/animation/cts/AnimatorTest.java
index fac9ff9..a08a5eb 100644
--- a/tests/tests/animation/src/android/animation/cts/AnimatorTest.java
+++ b/tests/tests/animation/src/android/animation/cts/AnimatorTest.java
@@ -182,6 +182,27 @@
         assertNull(listListenersTwo);
     }
 
+    public void testNullObjectAnimator()  throws Throwable {
+        Object object = mActivity.view.newBall;
+        final ObjectAnimator animator = ObjectAnimator.ofFloat(object, "y", 0, 100);
+        MyListener listener = new MyListener();
+        animator.addListener(listener);
+        mActivity.view.newBall.setY(0);
+        startAnimation(animator);
+        int sleepCount = 0;
+        while (mActivity.view.newBall.getY() == 0 && sleepCount++ < 50) {
+            Thread.sleep(1);
+        }
+        assertNotSame(0, mActivity.view.newBall.getY());
+        runTestOnUiThread(new Runnable() {
+            @Override
+            public void run() {
+                animator.setTarget(null);
+            }
+        });
+        assertTrue(listener.mCancel);
+    }
+
     class MyListener implements Animator.AnimatorListener{
         boolean mStart = false;
         boolean mEnd = false;
diff --git a/tests/tests/app.usage/src/android/app/usage/cts/UsageStatsTest.java b/tests/tests/app.usage/src/android/app/usage/cts/UsageStatsTest.java
index 5b1909a..d3890df 100644
--- a/tests/tests/app.usage/src/android/app/usage/cts/UsageStatsTest.java
+++ b/tests/tests/app.usage/src/android/app/usage/cts/UsageStatsTest.java
@@ -132,11 +132,12 @@
                 Activities.ActivityThree.class,
         };
 
+        final long startTime = System.currentTimeMillis() - MINUTE;
+
         // Launch the series of Activities.
         launchSubActivities(activitySequence);
 
         final long endTime = System.currentTimeMillis();
-        final long startTime = endTime - DAY;
         UsageEvents events = mUsageStatsManager.queryEvents(startTime, endTime);
 
         // Consume all the events.
@@ -147,15 +148,26 @@
             eventList.add(event);
         }
 
+        // Find the last Activity's MOVE_TO_FOREGROUND event.
+        int end = eventList.size();
+        while (end > 0) {
+            UsageEvents.Event event = eventList.get(end - 1);
+            if (event.getClassName().equals(activitySequence[activitySequence.length - 1].getName())
+                    && event.getEventType() == UsageEvents.Event.MOVE_TO_FOREGROUND) {
+                break;
+            }
+            end--;
+        }
+
         // We expect 2 events per Activity launched (foreground + background)
         // except for the last Activity, which was in the foreground when
         // we queried the event log.
-        assertTrue(eventList.size() >= (activitySequence.length * 2) - 1);
-        final int offset = eventList.size() - ((activitySequence.length * 2) - 1);
+        final int start = end - ((activitySequence.length * 2) - 1);
+        assertTrue("Not enough events", start >= 0);
 
         final int activityCount = activitySequence.length;
         for (int i = 0; i < activityCount; i++) {
-            int index = offset + (i * 2);
+            final int index = start + (i * 2);
 
             // Check for foreground event.
             UsageEvents.Event event = eventList.get(index);
@@ -163,10 +175,10 @@
             assertEquals(activitySequence[i].getName(), event.getClassName());
             assertEquals(UsageEvents.Event.MOVE_TO_FOREGROUND, event.getEventType());
 
-            index += 1;
+            // Only check for the background event if this is not the
+            // last activity.
             if (i < activityCount - 1) {
-                // Check for background event.
-                event = eventList.get(index);
+                event = eventList.get(index + 1);
                 assertEquals(mTargetPackage, event.getPackageName());
                 assertEquals(activitySequence[i].getName(), event.getClassName());
                 assertEquals(UsageEvents.Event.MOVE_TO_BACKGROUND, event.getEventType());
@@ -182,6 +194,7 @@
     @Ignore
     public void ignore_testStatsAreShiftedInTimeWhenSystemTimeChanges() throws Exception {
         launchSubActivity(Activities.ActivityOne.class);
+        launchSubActivity(Activities.ActivityThree.class);
 
         long endTime = System.currentTimeMillis();
         long startTime = endTime - MINUTE;
@@ -214,18 +227,19 @@
     }
 
     public void testUsageEventsParceling() throws Exception {
-        final long startTime = System.currentTimeMillis();
+        final long startTime = System.currentTimeMillis() - MINUTE;
 
+        // Ensure some data is in the UsageStats log.
         @SuppressWarnings("unchecked")
         Class<? extends Activity>[] activityClasses = new Class[] {
+                Activities.ActivityTwo.class,
                 Activities.ActivityOne.class,
                 Activities.ActivityThree.class,
-                Activities.ActivityTwo.class,
         };
         launchSubActivities(activityClasses);
 
         final long endTime = System.currentTimeMillis();
-        UsageEvents events = mUsageStatsManager.queryEvents(startTime - TIME_DIFF_THRESHOLD, endTime);
+        UsageEvents events = mUsageStatsManager.queryEvents(startTime, endTime);
         assertTrue(events.getNextEvent(new UsageEvents.Event()));
 
         Parcel p = Parcel.obtain();
@@ -255,6 +269,7 @@
 
         // Launch an Activity.
         launchSubActivity(Activities.ActivityFour.class);
+        launchSubActivity(Activities.ActivityThree.class);
 
         final long endTime = System.currentTimeMillis();
 
@@ -287,10 +302,12 @@
     }
 
     public void testNoAccessSilentlyFails() throws Exception {
+        final long startTime = System.currentTimeMillis() - MINUTE;
+
         launchSubActivity(Activities.ActivityOne.class);
+        launchSubActivity(Activities.ActivityThree.class);
 
         final long endTime = System.currentTimeMillis();
-        final long startTime = endTime - MINUTE;
         List<UsageStats> stats = mUsageStatsManager.queryUsageStats(UsageStatsManager.INTERVAL_BEST,
                 startTime, endTime);
         assertFalse(stats.isEmpty());
diff --git a/tests/tests/app/AndroidManifest.xml b/tests/tests/app/AndroidManifest.xml
index 08b0dda..7c09cd2 100644
--- a/tests/tests/app/AndroidManifest.xml
+++ b/tests/tests/app/AndroidManifest.xml
@@ -19,7 +19,7 @@
     package="com.android.cts.app">
 
     <uses-permission android:name="android.permission.DISABLE_KEYGUARD" />
-
+    <uses-permission android:name="android.permission.BODY_SENSORS" />
     <application>
         <uses-library android:name="android.test.runner" />
     </application>
diff --git a/tests/tests/app/src/android/app/cts/ActivityManagerMemoryClassTest.java b/tests/tests/app/src/android/app/cts/ActivityManagerMemoryClassTest.java
index 904a056..5deff5a 100644
--- a/tests/tests/app/src/android/app/cts/ActivityManagerMemoryClassTest.java
+++ b/tests/tests/app/src/android/app/cts/ActivityManagerMemoryClassTest.java
@@ -53,6 +53,7 @@
             expectedMemorySizeForSmallNormalScreen.put(DisplayMetrics.DENSITY_TV, 48);
             expectedMemorySizeForSmallNormalScreen.put(DisplayMetrics.DENSITY_HIGH, 48);
             expectedMemorySizeForSmallNormalScreen.put(DisplayMetrics.DENSITY_XHIGH, 48);
+            expectedMemorySizeForSmallNormalScreen.put(DisplayMetrics.DENSITY_280, 48);
             expectedMemorySizeForSmallNormalScreen.put(DisplayMetrics.DENSITY_400, 96);
             expectedMemorySizeForSmallNormalScreen.put(DisplayMetrics.DENSITY_XXHIGH, 128);
             expectedMemorySizeForSmallNormalScreen.put(DisplayMetrics.DENSITY_560, 192);
@@ -65,6 +66,7 @@
             expectedMemorySizeForLargeScreen.put(DisplayMetrics.DENSITY_TV, 80);
             expectedMemorySizeForLargeScreen.put(DisplayMetrics.DENSITY_HIGH, 80);
             expectedMemorySizeForLargeScreen.put(DisplayMetrics.DENSITY_XHIGH, 128);
+            expectedMemorySizeForLargeScreen.put(DisplayMetrics.DENSITY_280, 96);
             expectedMemorySizeForLargeScreen.put(DisplayMetrics.DENSITY_400, 192);
             expectedMemorySizeForLargeScreen.put(DisplayMetrics.DENSITY_XXHIGH, 256);
             expectedMemorySizeForLargeScreen.put(DisplayMetrics.DENSITY_560, 384);
@@ -77,6 +79,7 @@
             expectedMemorySizeForXLargeScreen.put(DisplayMetrics.DENSITY_TV, 96);
             expectedMemorySizeForXLargeScreen.put(DisplayMetrics.DENSITY_HIGH, 96);
             expectedMemorySizeForXLargeScreen.put(DisplayMetrics.DENSITY_XHIGH, 192);
+            expectedMemorySizeForXLargeScreen.put(DisplayMetrics.DENSITY_280, 144);
             expectedMemorySizeForXLargeScreen.put(DisplayMetrics.DENSITY_400, 288);
             expectedMemorySizeForXLargeScreen.put(DisplayMetrics.DENSITY_XXHIGH, 384);
             expectedMemorySizeForXLargeScreen.put(DisplayMetrics.DENSITY_560, 576);
diff --git a/tests/tests/app/src/android/app/cts/ActivityManagerTest.java b/tests/tests/app/src/android/app/cts/ActivityManagerTest.java
index e633f1f..998a005 100644
--- a/tests/tests/app/src/android/app/cts/ActivityManagerTest.java
+++ b/tests/tests/app/src/android/app/cts/ActivityManagerTest.java
@@ -219,7 +219,8 @@
                 hasTestProcess = true;
             }
         }
-        assertTrue(hasSystemProcess && hasTestProcess);
+        // For security reasons the system process is not exposed.
+        assertTrue(!hasSystemProcess && hasTestProcess);
 
         for (RunningAppProcessInfo ra : list) {
             if (ra.processName.equals("com.android.cts.app.stub:remote")) {
diff --git a/tests/tests/app/src/android/app/cts/AlarmManagerTest.java b/tests/tests/app/src/android/app/cts/AlarmManagerTest.java
index bfc3e1d..b85c616 100644
--- a/tests/tests/app/src/android/app/cts/AlarmManagerTest.java
+++ b/tests/tests/app/src/android/app/cts/AlarmManagerTest.java
@@ -18,14 +18,17 @@
 
 
 import android.app.AlarmManager;
+import android.app.AlarmManager.AlarmClockInfo;
 import android.app.PendingIntent;
 import android.content.Context;
 import android.content.Intent;
 import android.content.IntentFilter;
 import android.cts.util.PollingCheck;
+import android.os.Build;
 import android.os.SystemClock;
 import android.test.AndroidTestCase;
 import android.util.Log;
+import android.test.MoreAsserts;
 
 public class AlarmManagerTest extends AndroidTestCase {
     public static final String MOCKACTION = "android.app.AlarmManagerTest.TEST_ALARMRECEIVER";
@@ -47,6 +50,7 @@
 
     private static final int TIME_DELTA = 1000;
     private static final int TIME_DELAY = 10000;
+    private static final int REPEAT_PERIOD = 60000;
 
     // Receiver registration/unregistration between tests races with the system process, so
     // we add a little buffer time here to allow the system to process before we proceed.
@@ -221,46 +225,54 @@
 
     public void testSetRepeating() throws Exception {
         mMockAlarmReceiver.setAlarmedFalse();
-        mWakeupTime = System.currentTimeMillis() + SNOOZE_DELAY;
-        mAm.setRepeating(AlarmManager.RTC_WAKEUP, mWakeupTime, TIME_DELAY / 2, mSender);
-        new PollingCheck(SNOOZE_DELAY + TIME_DELAY) {
+        mWakeupTime = System.currentTimeMillis() + TEST_ALARM_FUTURITY;
+        mAm.setRepeating(AlarmManager.RTC_WAKEUP, mWakeupTime, REPEAT_PERIOD, mSender);
+
+        // wait beyond the initial alarm's possible delivery window to verify that it fires the first time
+        new PollingCheck(TEST_ALARM_FUTURITY + REPEAT_PERIOD) {
             @Override
             protected boolean check() {
                 return mMockAlarmReceiver.alarmed;
             }
         }.run();
+        assertTrue(mMockAlarmReceiver.alarmed);
+
+        // Now reset the receiver and wait for the intended repeat alarm to fire as expected
         mMockAlarmReceiver.setAlarmedFalse();
-        new PollingCheck(TIME_DELAY) {
+        new PollingCheck(REPEAT_PERIOD*2) {
             @Override
             protected boolean check() {
                 return mMockAlarmReceiver.alarmed;
             }
         }.run();
+        assertTrue(mMockAlarmReceiver.alarmed);
+
         mAm.cancel(mSender);
     }
 
     public void testCancel() throws Exception {
         mMockAlarmReceiver.setAlarmedFalse();
-        mWakeupTime = System.currentTimeMillis() + SNOOZE_DELAY;
-        mAm.setRepeating(AlarmManager.RTC_WAKEUP, mWakeupTime, 1000, mSender);
-        new PollingCheck(SNOOZE_DELAY + TIME_DELAY) {
-            @Override
-            protected boolean check() {
-                return mMockAlarmReceiver.alarmed;
-            }
-        }.run();
-        mMockAlarmReceiver.setAlarmedFalse();
+        mMockAlarmReceiver2.setAlarmedFalse();
+
+        // set two alarms
+        final long when1 = System.currentTimeMillis() + TEST_ALARM_FUTURITY;
+        mAm.setExact(AlarmManager.RTC_WAKEUP, when1, mSender);
+        final long when2 = when1 + TIME_DELTA; // will fire after when1's target time
+        mAm.setExact(AlarmManager.RTC_WAKEUP, when2, mSender2);
+
+        // cancel the earlier one
+        mAm.cancel(mSender);
+
+        // and verify that only the later one fired
         new PollingCheck(TIME_DELAY) {
             @Override
             protected boolean check() {
-                return mMockAlarmReceiver.alarmed;
+                return mMockAlarmReceiver2.alarmed;
             }
         }.run();
-        mAm.cancel(mSender);
-        Thread.sleep(TIME_DELAY);
-        mMockAlarmReceiver.setAlarmedFalse();
-        Thread.sleep(TIME_DELAY * 5);
+
         assertFalse(mMockAlarmReceiver.alarmed);
+        assertTrue(mMockAlarmReceiver2.alarmed);
     }
 
     public void testSetInexactRepeating() throws Exception {
@@ -273,4 +285,66 @@
         // " Unable to open alarm driver: Permission denied". But still fail
         // after tried many permission.
     }
+
+    public void testSetAlarmClock() throws Exception {
+        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
+            mMockAlarmReceiver.setAlarmedFalse();
+            mMockAlarmReceiver2.setAlarmedFalse();
+
+            // Set first alarm clock.
+            final long wakeupTimeFirst = System.currentTimeMillis()
+                    + 2 * TEST_ALARM_FUTURITY;
+            mAm.setAlarmClock(new AlarmClockInfo(wakeupTimeFirst, null), mSender);
+
+            // Verify getNextAlarmClock returns first alarm clock.
+            AlarmClockInfo nextAlarmClock = mAm.getNextAlarmClock();
+            assertEquals(wakeupTimeFirst, nextAlarmClock.getTriggerTime());
+            assertNull(nextAlarmClock.getShowIntent());
+
+            // Set second alarm clock, earlier than first.
+            final long wakeupTimeSecond = System.currentTimeMillis()
+                    + TEST_ALARM_FUTURITY;
+            PendingIntent showIntentSecond = PendingIntent.getBroadcast(getContext(), 0,
+                    new Intent(getContext(), AlarmManagerTest.class).setAction("SHOW_INTENT"), 0);
+            mAm.setAlarmClock(new AlarmClockInfo(wakeupTimeSecond, showIntentSecond),
+                    mSender2);
+
+            // Verify getNextAlarmClock returns second alarm clock now.
+            nextAlarmClock = mAm.getNextAlarmClock();
+            assertEquals(wakeupTimeSecond, nextAlarmClock.getTriggerTime());
+            assertEquals(showIntentSecond, nextAlarmClock.getShowIntent());
+
+            // Cancel second alarm.
+            mAm.cancel(mSender2);
+
+            // Verify getNextAlarmClock returns first alarm clock again.
+            nextAlarmClock = mAm.getNextAlarmClock();
+            assertEquals(wakeupTimeFirst, nextAlarmClock.getTriggerTime());
+            assertNull(nextAlarmClock.getShowIntent());
+
+            // Wait for first alarm to trigger.
+            assertFalse(mMockAlarmReceiver.alarmed);
+            new PollingCheck(2 * TEST_ALARM_FUTURITY + TIME_DELAY) {
+                @Override
+                protected boolean check() {
+                    return mMockAlarmReceiver.alarmed;
+                }
+            }.run();
+
+            // Verify first alarm fired at the right time.
+            assertEquals(mMockAlarmReceiver.rtcTime, wakeupTimeFirst, TIME_DELTA);
+
+            // Verify second alarm didn't fire.
+            assertFalse(mMockAlarmReceiver2.alarmed);
+
+            // Verify the next alarm is not returning neither the first nor the second alarm.
+            nextAlarmClock = mAm.getNextAlarmClock();
+            MoreAsserts.assertNotEqual(wakeupTimeFirst, nextAlarmClock != null
+                    ? nextAlarmClock.getTriggerTime()
+                    : null);
+            MoreAsserts.assertNotEqual(wakeupTimeSecond, nextAlarmClock != null
+                    ? nextAlarmClock.getTriggerTime()
+                    : null);
+        }
+    }
 }
diff --git a/tests/tests/app/src/android/app/cts/DialogTest.java b/tests/tests/app/src/android/app/cts/DialogTest.java
index 6df2eee..feb4940 100644
--- a/tests/tests/app/src/android/app/cts/DialogTest.java
+++ b/tests/tests/app/src/android/app/cts/DialogTest.java
@@ -673,19 +673,36 @@
         mInstrumentation.waitForIdleSync();
     }
 
-    public void testSetFeatureDrawableUri() {
+    public void testSetFeatureDrawableUri() throws Throwable {
         startDialogActivity(DialogStubActivity.TEST_ONSTART_AND_ONSTOP);
-        mActivity.getDialog().setFeatureDrawableUri(0, Uri.parse("http://www.google.com"));
+        runTestOnUiThread(new Runnable() {
+            public void run() {
+                mActivity.getDialog().setFeatureDrawableUri(Window.FEATURE_LEFT_ICON,
+                        Uri.parse("http://www.google.com"));
+            }
+        });
+        mInstrumentation.waitForIdleSync();
     }
 
-    public void testSetFeatureDrawable() {
+    public void testSetFeatureDrawable() throws Throwable {
         startDialogActivity(DialogStubActivity.TEST_ONSTART_AND_ONSTOP);
-        mActivity.getDialog().setFeatureDrawable(0, new MockDrawable());
+        runTestOnUiThread(new Runnable() {
+            public void run() {
+                mActivity.getDialog().setFeatureDrawable(Window.FEATURE_LEFT_ICON, 
+                        new MockDrawable());
+            }
+        });
+        mInstrumentation.waitForIdleSync();
     }
 
-    public void testSetFeatureDrawableAlpha() {
+    public void testSetFeatureDrawableAlpha() throws Throwable {
         startDialogActivity(DialogStubActivity.TEST_ONSTART_AND_ONSTOP);
-        mActivity.getDialog().setFeatureDrawableAlpha(0, 0);
+        runTestOnUiThread(new Runnable() {
+            public void run() {
+                mActivity.getDialog().setFeatureDrawableAlpha(Window.FEATURE_LEFT_ICON, 0);
+            }
+        });
+        mInstrumentation.waitForIdleSync();
     }
 
     public void testGetLayoutInflater() {
diff --git a/tests/tests/app/src/android/app/cts/InstrumentationTest.java b/tests/tests/app/src/android/app/cts/InstrumentationTest.java
index 0c2e9fa..b21148e 100644
--- a/tests/tests/app/src/android/app/cts/InstrumentationTest.java
+++ b/tests/tests/app/src/android/app/cts/InstrumentationTest.java
@@ -196,10 +196,12 @@
 
     public void testInvokeMenuActionSync() throws Exception {
         final int resId = R.id.goto_menu_id;
-        mInstrumentation.invokeMenuActionSync(mActivity, resId, 0);
-        mInstrumentation.waitForIdleSync();
-
-        assertEquals(resId, mActivity.getMenuID());
+        if (mActivity.getWindow().hasFeature(Window.FEATURE_OPTIONS_PANEL)) {
+            mInstrumentation.invokeMenuActionSync(mActivity, resId, 0);
+            mInstrumentation.waitForIdleSync();
+    
+            assertEquals(resId, mActivity.getMenuID());
+        }
     }
 
     public void testCallActivityOnPostCreate() throws Throwable {
diff --git a/tests/tests/app/src/android/app/cts/ServiceTest.java b/tests/tests/app/src/android/app/cts/ServiceTest.java
index b66f4c8..8ba1816 100644
--- a/tests/tests/app/src/android/app/cts/ServiceTest.java
+++ b/tests/tests/app/src/android/app/cts/ServiceTest.java
@@ -448,4 +448,17 @@
             // expected
         }
     }
+
+    @MediumTest
+    public void testImplicitIntentFailsOnApiLevel21() throws Exception {
+        Intent intent = new Intent(LocalService.SERVICE_LOCAL);
+        EmptyConnection conn = new EmptyConnection();
+        try {
+            mContext.bindService(intent, conn, 0);
+            mContext.unbindService(conn);
+            fail("Implicit intents should be disallowed for apps targeting API 21+");
+        } catch (IllegalArgumentException e) {
+            // expected
+        }
+    }
 }
diff --git a/tests/tests/appwidget/src/android/appwidget/cts/AppWidgetTest.java b/tests/tests/appwidget/src/android/appwidget/cts/AppWidgetTest.java
index 5e72a78..a385ebe 100644
--- a/tests/tests/appwidget/src/android/appwidget/cts/AppWidgetTest.java
+++ b/tests/tests/appwidget/src/android/appwidget/cts/AppWidgetTest.java
@@ -95,7 +95,17 @@
     private static final String REVOKE_BIND_APP_WIDGET_PERMISSION_COMMAND =
             "appwidget revokebind --package android.cts.appwidget --user 0";
 
+
+    private boolean hasAppWidgets() {
+        return getInstrumentation().getTargetContext().getPackageManager()
+            .hasSystemFeature(PackageManager.FEATURE_APP_WIDGETS);
+    }
+
     public void testGetAppInstalledProvidersForCurrentUserLegacy() throws Exception {
+        if (!hasAppWidgets()) {
+            return;
+        }
+
         // By default we should get only providers for the current user.
         List<AppWidgetProviderInfo> providers = getAppWidgetManager().getInstalledProviders();
 
@@ -104,6 +114,10 @@
     }
 
     public void testGetAppInstalledProvidersForCurrentUserNewCurrentProfile() throws Exception {
+        if (!hasAppWidgets()) {
+            return;
+        }
+
         // We ask only for providers for the current user.
         List<AppWidgetProviderInfo> providers = getAppWidgetManager()
                 .getInstalledProvidersForProfile(Process.myUserHandle());
@@ -113,6 +127,10 @@
     }
 
     public void testGetAppInstalledProvidersForCurrentUserNewAllProfiles() throws Exception {
+        if (!hasAppWidgets()) {
+            return;
+        }
+
         // We ask only for providers for all current user's profiles
         UserManager userManager = (UserManager) getInstrumentation()
                 .getTargetContext().getSystemService(Context.USER_SERVICE);
@@ -133,6 +151,10 @@
     }
 
     public void testBindAppWidget() throws Exception {
+        if (!hasAppWidgets()) {
+            return;
+        }
+
         // Create a host and start listening.
         AppWidgetHost host = new AppWidgetHost(getInstrumentation().getTargetContext(), 0);
         host.deleteHost();
@@ -170,6 +192,10 @@
     }
 
     public void testAppWidgetProviderCallbacks() throws Exception {
+        if (!hasAppWidgets()) {
+            return;
+        }
+
         AtomicInteger invocationCounter = new AtomicInteger();
 
         // Set a mock to intercept provider callbacks.
@@ -277,6 +303,10 @@
     }
 
     public void testTwoAppWidgetProviderCallbacks() throws Exception {
+        if (!hasAppWidgets()) {
+            return;
+        }
+
         AtomicInteger invocationCounter = new AtomicInteger();
 
         // Set a mock to intercept first provider callbacks.
@@ -368,6 +398,10 @@
     }
 
     public void testGetAppWidgetIds() throws Exception {
+        if (!hasAppWidgets()) {
+            return;
+        }
+
         // We want to bind widgets.
         grantBindAppWidgetPermission();
 
@@ -415,6 +449,10 @@
     }
 
     public void testGetAppWidgetInfo() throws Exception {
+        if (!hasAppWidgets()) {
+            return;
+        }
+
         // We want to bind widgets.
         grantBindAppWidgetPermission();
 
@@ -467,6 +505,10 @@
     }
 
     public void testGetAppWidgetOptions() throws Exception {
+        if (!hasAppWidgets()) {
+            return;
+        }
+
         // We want to bind widgets.
         grantBindAppWidgetPermission();
 
@@ -512,6 +554,10 @@
     }
 
     public void testDeleteHost() throws Exception {
+        if (!hasAppWidgets()) {
+            return;
+        }
+
         // We want to bind widgets.
         grantBindAppWidgetPermission();
 
@@ -553,6 +599,10 @@
     }
 
     public void testDeleteHosts() throws Exception {
+        if (!hasAppWidgets()) {
+            return;
+        }
+
         // We want to bind widgets.
         grantBindAppWidgetPermission();
 
@@ -609,6 +659,10 @@
     }
 
     public void testOnProvidersChanged() throws Exception {
+        if (!hasAppWidgets()) {
+            return;
+        }
+
         // We want to bind widgets.
         grantBindAppWidgetPermission();
 
@@ -671,6 +725,10 @@
     }
 
     public void testUpdateAppWidgetViaComponentName() throws Exception {
+        if (!hasAppWidgets()) {
+            return;
+        }
+
         // We want to bind widgets.
         grantBindAppWidgetPermission();
 
@@ -761,6 +819,10 @@
     }
 
     public void testUpdateAppWidgetViaWidgetId() throws Exception {
+        if (!hasAppWidgets()) {
+            return;
+        }
+
         // We want to bind widgets.
         grantBindAppWidgetPermission();
 
@@ -831,6 +893,10 @@
     }
 
     public void testUpdateAppWidgetViaWidgetIds() throws Exception {
+        if (!hasAppWidgets()) {
+            return;
+        }
+
         // We want to bind widgets.
         grantBindAppWidgetPermission();
 
@@ -923,6 +989,10 @@
     }
 
     public void testPartiallyUpdateAppWidgetViaWidgetId() throws Exception {
+        if (!hasAppWidgets()) {
+            return;
+        }
+
         // We want to bind widgets.
         grantBindAppWidgetPermission();
 
@@ -997,6 +1067,10 @@
     }
 
     public void testPartiallyUpdateAppWidgetViaWidgetIds() throws Exception {
+        if (!hasAppWidgets()) {
+            return;
+        }
+
         // We want to bind widgets.
         grantBindAppWidgetPermission();
 
@@ -1107,6 +1181,10 @@
     }
 
     public void testCollectionWidgets() throws Exception {
+        if (!hasAppWidgets()) {
+            return;
+        }
+
         // We want to bind widgets.
         grantBindAppWidgetPermission();
 
diff --git a/tests/tests/content/src/android/content/cts/HighPriorityBroadcastReceiver.java b/tests/tests/content/src/android/content/cts/HighPriorityBroadcastReceiver.java
index f3a4ba1..187fe06 100644
--- a/tests/tests/content/src/android/content/cts/HighPriorityBroadcastReceiver.java
+++ b/tests/tests/content/src/android/content/cts/HighPriorityBroadcastReceiver.java
@@ -22,13 +22,11 @@
 public class HighPriorityBroadcastReceiver extends ResultReceiver {
 
     @Override
-    public void onReceive(Context context, Intent intent) {
+    public synchronized void onReceive(Context context, Intent intent) {
         super.onReceive(context, intent);
 
         try {
-            synchronized (this) {
-                wait();
-            }
+            wait();
         } catch (InterruptedException e) {
             throw new RuntimeException("Got interrupted during wait()", e);
         }
diff --git a/tests/tests/content/src/android/content/cts/IntentTest.java b/tests/tests/content/src/android/content/cts/IntentTest.java
index d4fac55..0f9a5cc 100644
--- a/tests/tests/content/src/android/content/cts/IntentTest.java
+++ b/tests/tests/content/src/android/content/cts/IntentTest.java
@@ -45,6 +45,7 @@
 import java.io.Serializable;
 import java.net.URISyntaxException;
 import java.util.ArrayList;
+import java.util.Objects;
 import java.util.Set;
 
 public class IntentTest extends AndroidTestCase {
@@ -676,7 +677,7 @@
 
         final String compnent =
                 "component(" + mContext.getPackageName() + "!" + MockActivity.class.getName() + ")";
-        uri = "testdata#action(test)categories(test!test2)type(mtype)launchFlags(1)" + compnent
+        uri = "testdata#action(test)categories(test!test2)type(mtype)launchFlags(5)" + compnent
                 + "extras(Stest=testString!btestbyte=1!"
                 + "Btestboolean=true!ctestchar=a!dtestdouble=1d!"
                 + "itestint=1!ltestlong=1!stestshort=1!ftestfloat=1f)";
@@ -686,7 +687,7 @@
         assertEquals(mComponentName, mIntent.getComponent());
         assertEquals("test", (String) (mIntent.getCategories().toArray()[0]));
         assertEquals("mtype", mIntent.getType());
-        assertEquals(1, mIntent.getFlags());
+        assertEquals(4, mIntent.getFlags());
         assertEquals("testString", mIntent.getStringExtra("test"));
         assertTrue(mIntent.getBooleanExtra("testboolean", false));
         final byte b = 1;
@@ -812,10 +813,13 @@
         target = Intent.getIntent(uri);
         assertEquals(TEST_TYPE, target.getType());
 
-        mIntent.setFlags(1);
+        mIntent.setFlags(Intent.FLAG_ACTIVITY_NEW_DOCUMENT
+                | Intent.FLAG_GRANT_WRITE_URI_PERMISSION | Intent.FLAG_GRANT_READ_URI_PERMISSION
+                | Intent.FLAG_GRANT_PREFIX_URI_PERMISSION
+                | Intent.FLAG_GRANT_PERSISTABLE_URI_PERMISSION);
         uri = mIntent.toURI();
         target = Intent.getIntent(uri);
-        assertEquals(1, target.getFlags());
+        assertEquals(Intent.FLAG_ACTIVITY_NEW_DOCUMENT, target.getFlags());
 
         String stringValue = "testString";
         mIntent.putExtra(TEST_EXTRA_NAME, stringValue);
@@ -869,8 +873,8 @@
     }
 
     public void testToURI() {
-        mIntent.setFlags(0);
-        assertEquals("#Intent;end", mIntent.toURI());
+        mIntent = new Intent();
+        assertEquals("", mIntent.toURI());
 
         mIntent.setData(TEST_URI);
         assertTrue(mIntent.toURI().indexOf(TEST_URI.toString()) != -1);
@@ -937,6 +941,321 @@
         return uri.toString();
     }
 
+    static Intent makeSelector(Intent baseIntent, Intent selectorIntent) {
+        baseIntent.setSelector(selectorIntent);
+        return baseIntent;
+    }
+
+    public void testUris() {
+        checkIntentUri(
+                "intent:#Intent;action=android.test.FOO;end",
+                null,
+                new Intent().setAction("android.test.FOO"));
+        checkIntentUri(
+                "intent:#Intent;category=android.test.FOO;end",
+                null,
+                new Intent().setAction(Intent.ACTION_VIEW).addCategory("android.test.FOO"));
+        checkIntentUri(
+                "intent:#Intent;action=android.test.FOO;launchFlags=0x20;end",
+                null,
+                new Intent().setAction("android.test.FOO").setFlags(0x20));
+        checkIntentUri(
+                "intent://www.example.com/blah#Intent;scheme=http;end",
+                null,
+                new Intent().setAction(Intent.ACTION_VIEW)
+                        .setData(Uri.parse("http://www.example.com/blah")));
+        checkIntentUri(
+                "intent://www.example.com/blah#Intent;scheme=http;component=com.exfoo/com.argh.Bar;end",
+                null,
+                new Intent().setAction(Intent.ACTION_VIEW)
+                        .setData(Uri.parse("http://www.example.com/blah"))
+                        .setComponent(new ComponentName("com.exfoo", "com.argh.Bar")));
+        checkIntentUri(
+                "intent://www.example.com/blah#fragment#Intent;scheme=http;end",
+                null,
+                new Intent().setAction(Intent.ACTION_VIEW)
+                        .setData(Uri.parse("http://www.example.com/blah#fragment")));
+        checkIntentUri(
+                "intent://www.example.com/blah#Intent;scheme=http;action=android.test.foo;end",
+                null,
+                new Intent().setAction("android.test.foo")
+                        .setData(Uri.parse("http://www.example.com/blah")));
+        checkIntentUri(
+                "intent:foo#Intent;scheme=mailto;type=image/foo;end",
+                null,
+                new Intent().setAction(Intent.ACTION_VIEW)
+                        .setDataAndType(Uri.parse("mailto:foo"), "image/foo"));
+        checkIntentUri(
+                "intent:foo#Intent;scheme=mailto;S.string=text;end",
+                null,
+                new Intent().setAction(Intent.ACTION_VIEW)
+                        .setData(Uri.parse("mailto:foo"))
+                        .putExtra("string", "text"));
+        checkIntentUri(
+                "intent:#Intent;action=android.test.FOO;S.string=text;end",
+                null,
+                new Intent().setAction("android.test.FOO").putExtra("string", "text"));
+        checkIntentUri(
+                "intent:foo#Intent;scheme=mailto;i.int=1000;end",
+                null,
+                new Intent().setAction(Intent.ACTION_VIEW)
+                        .setData(Uri.parse("mailto:foo"))
+                        .putExtra("int", 1000));
+        checkIntentUri(
+                "intent:foo#Intent;scheme=mailto;l.long=1000;end",
+                null,
+                new Intent().setAction(Intent.ACTION_VIEW)
+                        .setData(Uri.parse("mailto:foo"))
+                        .putExtra("long", (long) 1000));
+        checkIntentUri(
+                "intent:foo#Intent;scheme=mailto;B.boolean=true;end",
+                null,
+                new Intent().setAction(Intent.ACTION_VIEW)
+                        .setData(Uri.parse("mailto:foo"))
+                        .putExtra("boolean", true));
+        checkIntentUri(
+                "intent:foo#Intent;scheme=mailto;f.float=10.4;end",
+                null,
+                new Intent().setAction(Intent.ACTION_VIEW)
+                        .setData(Uri.parse("mailto:foo"))
+                        .putExtra("float", 10.4f));
+        checkIntentUri(
+                "intent:foo#Intent;scheme=mailto;d.double=10.4;end",
+                null,
+                new Intent().setAction(Intent.ACTION_VIEW)
+                        .setData(Uri.parse("mailto:foo"))
+                        .putExtra("double", (double) 10.4));
+        checkIntentUri(
+                "intent:#Intent;S.string=text;i.int=1000;l.long=1000;B.boolean=true;f.float=10.4;end",
+                null,
+                new Intent().setAction(Intent.ACTION_VIEW).putExtra("string", "text")
+                        .putExtra("int", 1000).putExtra("long", (long) 1000)
+                        .putExtra("boolean", true).putExtra("float", 10.4f));
+        checkIntentUri(
+                "intent:foo#Intent;scheme=mailto;SEL;scheme=foobar;action=android.test.FOO;end",
+                null,
+                makeSelector(new Intent(Intent.ACTION_VIEW).setData(Uri.parse("mailto:foo")),
+                        new Intent("android.test.FOO").setData(Uri.parse("foobar:"))));
+        checkIntentUri(
+                "intent:foo#Intent;scheme=mailto;SEL;action=android.test.FOO;package=com.myapp;end",
+                null,
+                makeSelector(new Intent(Intent.ACTION_VIEW).setData(Uri.parse("mailto:foo")),
+                        new Intent("android.test.FOO").setPackage("com.myapp")));
+        checkIntentUri(
+                "intent:foo#Intent;scheme=mailto;SEL;action=android.test.FOO;component=com.exfoo/com.argh.Bar;end",
+                null,
+                makeSelector(new Intent(Intent.ACTION_VIEW).setData(Uri.parse("mailto:foo")),
+                        new Intent("android.test.FOO")
+                                .setComponent(new ComponentName("com.exfoo", "com.argh.Bar"))));
+
+        checkIntentUri(
+                "intent:#Intent;action=android.test.FOO;package=com.myapp;end",
+                "android-app://com.myapp#Intent;action=android.test.FOO;end",
+                new Intent().setAction("android.test.FOO").setPackage("com.myapp"));
+        checkIntentUri(
+                "intent:#Intent;action=android.intent.action.MAIN;package=com.myapp;end",
+                "android-app://com.myapp",
+                new Intent().setAction(Intent.ACTION_MAIN).setPackage("com.myapp"));
+        checkIntentUri(
+                "intent:#Intent;package=com.myapp;end",
+                "android-app://com.myapp#Intent;action=android.intent.action.VIEW;end",
+                new Intent().setAction(Intent.ACTION_VIEW).setPackage("com.myapp"));
+        checkIntentUri(
+                "intent:#Intent;category=android.test.FOO;package=com.myapp;end",
+                "android-app://com.myapp#Intent;action=android.intent.action.VIEW;category=android.test.FOO;end",
+                new Intent().setAction(Intent.ACTION_VIEW).addCategory("android.test.FOO")
+                        .setPackage("com.myapp"));
+        checkIntentUri(
+                "intent:#Intent;action=android.test.FOO;launchFlags=0x20;package=com.myapp;end",
+                "android-app://com.myapp#Intent;action=android.test.FOO;launchFlags=0x20;end",
+                new Intent().setAction("android.test.FOO").setFlags(0x20)
+                        .setPackage("com.myapp"));
+        checkIntentUri(
+                "intent://www.example.com/blah#Intent;scheme=http;package=com.myapp;end",
+                "android-app://com.myapp/http/www.example.com/blah",
+                new Intent().setAction(Intent.ACTION_VIEW)
+                        .setData(Uri.parse("http://www.example.com/blah"))
+                        .setPackage("com.myapp"));
+        checkIntentUri(
+                "intent://www.example.com/blah#Intent;scheme=http;package=com.myapp;component=com.exfoo/com.argh.Bar;end",
+                "android-app://com.myapp/http/www.example.com/blah#Intent;component=com.exfoo/com.argh.Bar;end",
+                new Intent().setAction(Intent.ACTION_VIEW)
+                        .setData(Uri.parse("http://www.example.com/blah"))
+                        .setComponent(new ComponentName("com.exfoo", "com.argh.Bar"))
+                        .setPackage("com.myapp"));
+        checkIntentUri(
+                "intent://www.example.com/blah#fragment#Intent;scheme=http;package=com.myapp;end",
+                "android-app://com.myapp/http/www.example.com/blah#fragment",
+                new Intent().setAction(Intent.ACTION_VIEW)
+                        .setData(Uri.parse("http://www.example.com/blah#fragment"))
+                        .setPackage("com.myapp"));
+        checkIntentUri(
+                "intent://www.example.com/blah#fragment#Intent;scheme=http;action=android.test.FOO;package=com.myapp;end",
+                "android-app://com.myapp/http/www.example.com/blah#fragment#Intent;action=android.test.FOO;end",
+                new Intent().setAction("android.test.FOO")
+                        .setData(Uri.parse("http://www.example.com/blah#fragment"))
+                        .setPackage("com.myapp"));
+        checkIntentUri(
+                "intent://www.example.com/blah#Intent;scheme=http;package=com.myapp;end",
+                "android-app://com.myapp/http/www.example.com/blah",
+                new Intent().setAction(Intent.ACTION_VIEW)
+                        .setData(Uri.parse("http://www.example.com/blah"))
+                        .setPackage("com.myapp"));
+        checkIntentUri(
+                "intent:#Intent;scheme=mailto;type=image/foo;package=com.myapp;end",
+                "android-app://com.myapp/mailto#Intent;type=image/foo;end",
+                new Intent().setAction(Intent.ACTION_VIEW)
+                        .setDataAndType(Uri.parse("mailto:"), "image/foo")
+                        .setPackage("com.myapp"));
+        checkIntentUri(
+                "intent:#Intent;scheme=mailto;package=com.myapp;S.string=text;end",
+                "android-app://com.myapp/mailto#Intent;S.string=text;end",
+                new Intent().setAction(Intent.ACTION_VIEW).putExtra("string", "text")
+                        .setData(Uri.parse("mailto:")).setPackage("com.myapp"));
+        checkIntentUri(
+                "intent:#Intent;action=android.test.FOO;package=com.myapp;S.string=text;end",
+                "android-app://com.myapp#Intent;action=android.test.FOO;S.string=text;end",
+                new Intent().setAction("android.test.FOO").putExtra("string", "text")
+                        .setPackage("com.myapp"));
+        checkIntentUri(
+                "intent:#Intent;scheme=mailto;package=com.myapp;i.int=1000;end",
+                "android-app://com.myapp/mailto#Intent;i.int=1000;end",
+                new Intent().setAction(Intent.ACTION_VIEW).putExtra("int", 1000)
+                        .setData(Uri.parse("mailto:")).setPackage("com.myapp"));
+        checkIntentUri(
+                "intent:#Intent;scheme=mailto;package=com.myapp;l.long=1000;end",
+                "android-app://com.myapp/mailto#Intent;l.long=1000;end",
+                new Intent().setAction(Intent.ACTION_VIEW).putExtra("long", (long) 1000)
+                        .setData(Uri.parse("mailto:")).setPackage("com.myapp"));
+        checkIntentUri(
+                "intent:#Intent;scheme=mailto;package=com.myapp;B.boolean=true;end",
+                "android-app://com.myapp/mailto#Intent;B.boolean=true;end",
+                new Intent().setAction(Intent.ACTION_VIEW).putExtra("boolean", true)
+                        .setData(Uri.parse("mailto:")).setPackage("com.myapp"));
+        checkIntentUri(
+                "intent:#Intent;scheme=mailto;package=com.myapp;f.float=10.4;end",
+                "android-app://com.myapp/mailto#Intent;f.float=10.4;end",
+                new Intent().setAction(Intent.ACTION_VIEW).putExtra("float", 10.4f)
+                        .setData(Uri.parse("mailto:")).setPackage("com.myapp"));
+        checkIntentUri(
+                "intent:#Intent;scheme=mailto;package=com.myapp;d.double=10.4;end",
+                "android-app://com.myapp/mailto#Intent;d.double=10.4;end",
+                new Intent().setAction(Intent.ACTION_VIEW).putExtra("double", (double) 10.4)
+                        .setData(Uri.parse("mailto:")).setPackage("com.myapp"));
+        checkIntentUri(
+                "intent:#Intent;package=com.myapp;S.string=text;i.int=1000;l.long=1000;B.boolean=true;f.float=10.4;end",
+                "android-app://com.myapp#Intent;action=android.intent.action.VIEW;S.string=text;i.int=1000;l.long=1000;B.boolean=true;f.float=10.4;end",
+                new Intent().setAction(Intent.ACTION_VIEW).putExtra("string", "text")
+                        .putExtra("int", 1000).putExtra("long", (long) 1000)
+                        .putExtra("boolean", true).putExtra("float", 10.4f)
+                        .setPackage("com.myapp"));
+    }
+
+    private boolean compareIntents(Intent expected, Intent actual) {
+        if (!Objects.equals(expected.getAction(), actual.getAction())) {
+            return false;
+        }
+        if (!Objects.equals(expected.getData(), actual.getData())) {
+            return false;
+        }
+        if (!Objects.equals(expected.getType(), actual.getType())) {
+            return false;
+        }
+        if (!Objects.equals(expected.getPackage(), actual.getPackage())) {
+            return false;
+        }
+        if (!Objects.equals(expected.getComponent(), actual.getComponent())) {
+            return false;
+        }
+        if (expected.getFlags() != actual.getFlags()) {
+            return false;
+        }
+        Set<String> expectedCat = expected.getCategories();
+        Set<String> actualCat = actual.getCategories();
+        if (expectedCat != actualCat) {
+            if (expectedCat == null || actualCat == null) {
+                return false;
+            }
+            for (String cat : expectedCat) {
+                if (!actual.hasCategory(cat)) {
+                    return false;
+                }
+            }
+            for (String cat : actualCat) {
+                if (!expected.hasCategory(cat)) {
+                    return false;
+                }
+            }
+        }
+        Bundle extras1 = expected.getExtras();
+        Bundle extras2 = actual.getExtras();
+        if (extras1 != extras2) {
+            if (extras1 == null || extras2 == null) {
+                return false;
+            }
+            for (String key : extras1.keySet()) {
+                if (!Objects.equals(extras1.get(key), extras2.get(key))) {
+                    return false;
+                }
+            }
+            for (String key : extras2.keySet()) {
+                if (!Objects.equals(extras1.get(key), extras2.get(key))) {
+                    return false;
+                }
+            }
+        }
+        return true;
+    }
+
+    private void assertEqualsIntent(String msg, Intent expected, Intent actual) {
+        if (!compareIntents(expected, actual)) {
+            failNotEquals(msg, expected, actual);
+        }
+        Intent expectedSel = expected.getSelector();
+        Intent actualSel = actual.getSelector();
+        if (expectedSel != actualSel) {
+            if (expectedSel == null || actualSel == null) {
+                failNotEquals(msg, expected, actual);
+            }
+            if (!compareIntents(expectedSel, actualSel)) {
+                failNotEquals(msg, expected, actual);
+            }
+        }
+    }
+
+    private void checkIntentUri(String intentSchemeUri, String androidAppSchemeUri, Intent intent) {
+        if (intentSchemeUri != null) {
+            try {
+                Intent genIntent = Intent.parseUri(intentSchemeUri, 0);
+                assertEqualsIntent("Implicitly converting " + intentSchemeUri + " to Intent",
+                        intent, genIntent);
+                genIntent = Intent.parseUri(intentSchemeUri, Intent.URI_INTENT_SCHEME);
+                assertEqualsIntent("Explicitly converting " + intentSchemeUri + " to Intent",
+                        intent, genIntent);
+            } catch (URISyntaxException e) {
+                fail("Failure parsing " + intentSchemeUri + ": " + e);
+            }
+            String genUri = intent.toUri(Intent.URI_INTENT_SCHEME);
+            assertEquals("Converting " + intent + " to intent: uri",
+                    intentSchemeUri, genUri);
+        }
+        if (androidAppSchemeUri != null) {
+            try {
+                Intent genIntent = Intent.parseUri(androidAppSchemeUri, 0);
+                assertEqualsIntent("Implicitly converting " + androidAppSchemeUri + " to Intent",
+                        intent, genIntent);
+                genIntent = Intent.parseUri(intentSchemeUri, Intent.URI_ANDROID_APP_SCHEME);
+                assertEqualsIntent("Explicitly converting " + androidAppSchemeUri + " to Intent",
+                        intent, genIntent);
+            } catch (URISyntaxException e) {
+                fail("Failure parsing " + androidAppSchemeUri + ": " + e);
+            }
+            String genUri = intent.toUri(Intent.URI_ANDROID_APP_SCHEME);
+            assertEquals("Converting " + intent + " to android-app: uri",
+                    androidAppSchemeUri, genUri);
+        }
+    }
+
     public void testAccessFlags() {
         int expected = 1;
         mIntent.setFlags(expected);
diff --git a/tests/tests/content/src/android/content/res/cts/PrivateAttributeTest.java b/tests/tests/content/src/android/content/res/cts/PrivateAttributeTest.java
new file mode 100644
index 0000000..35466be
--- /dev/null
+++ b/tests/tests/content/src/android/content/res/cts/PrivateAttributeTest.java
@@ -0,0 +1,56 @@
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.content.res.cts;
+
+import android.content.res.Resources;
+import android.test.AndroidTestCase;
+import android.util.TypedValue;
+
+/**
+ * Tests that private attributes are correctly placed in a separate type to
+ * prevent future releases from stomping over private attributes with new public ones.
+ */
+public class PrivateAttributeTest extends AndroidTestCase {
+
+    private static final int sLastPublicAttr = 0x010104d5;
+
+    public void testNoAttributesAfterLastPublicAttribute() throws Exception {
+        final Resources res = getContext().getResources();
+
+        final String lastPublicName;
+        try {
+            lastPublicName = res.getResourceEntryName(sLastPublicAttr);
+        } catch (Resources.NotFoundException e) {
+            throw new AssertionError("Last public resource was not found", e);
+        }
+
+        int currentAttr = sLastPublicAttr;
+        while (currentAttr < 0x0101ffff) {
+            currentAttr++;
+            try {
+                final String name = res.getResourceEntryName(currentAttr);
+                throw new AssertionError("Found attribute '" + name + "'"
+                        + " (0x" + Integer.toHexString(currentAttr) + ")"
+                        + " after last public framework attribute "
+                        + "'" + lastPublicName + "'"
+                        + " (0x" + Integer.toHexString(sLastPublicAttr) + ")");
+            } catch (Resources.NotFoundException e) {
+                // continue
+            }
+        }
+    }
+}
diff --git a/tests/tests/display/src/android/display/cts/DisplayTest.java b/tests/tests/display/src/android/display/cts/DisplayTest.java
index 4c7116d..bea99ed 100644
--- a/tests/tests/display/src/android/display/cts/DisplayTest.java
+++ b/tests/tests/display/src/android/display/cts/DisplayTest.java
@@ -28,10 +28,11 @@
 public class DisplayTest extends AndroidTestCase {
     // This test is called from DisplayTestRunner which brings up an overlay display on the target
     // device. The overlay display parameters must match the ones defined there which are
-    // 1281x721/214 (wxh/dpi).
+    // 181x161/214 (wxh/dpi).  It only matters that these values are different from any real
+    // display.
 
-    private static final int SECONDARY_DISPLAY_WIDTH = 1281;
-    private static final int SECONDARY_DISPLAY_HEIGHT = 721;
+    private static final int SECONDARY_DISPLAY_WIDTH = 181;
+    private static final int SECONDARY_DISPLAY_HEIGHT = 161;
     private static final int SECONDARY_DISPLAY_DPI = 214;
     private static final float SCALE_DENSITY_LOWER_BOUND =
             (float)(SECONDARY_DISPLAY_DPI - 1) / DisplayMetrics.DENSITY_DEFAULT;
diff --git a/tests/tests/dpi/src/android/dpi/cts/ConfigurationTest.java b/tests/tests/dpi/src/android/dpi/cts/ConfigurationTest.java
index 82e3204..238abec 100644
--- a/tests/tests/dpi/src/android/dpi/cts/ConfigurationTest.java
+++ b/tests/tests/dpi/src/android/dpi/cts/ConfigurationTest.java
@@ -57,6 +57,7 @@
         allowedDensities.add(DisplayMetrics.DENSITY_MEDIUM);
         allowedDensities.add(DisplayMetrics.DENSITY_TV);
         allowedDensities.add(DisplayMetrics.DENSITY_HIGH);
+        allowedDensities.add(DisplayMetrics.DENSITY_280);
         allowedDensities.add(DisplayMetrics.DENSITY_XHIGH);
         allowedDensities.add(DisplayMetrics.DENSITY_400);
         allowedDensities.add(DisplayMetrics.DENSITY_XXHIGH);
diff --git a/tests/tests/graphics/res/xml/anim_list_missing_list_attrs.xml b/tests/tests/graphics/res/xml/anim_list_missing_list_attrs.xml
deleted file mode 100644
index 25d2dfe..0000000
--- a/tests/tests/graphics/res/xml/anim_list_missing_list_attrs.xml
+++ /dev/null
@@ -1,24 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
-/*
- * Copyright (C) 2008 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License"); you may not
- * use this file except in compliance with the License. You may obtain a copy of
- * the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
- * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
- * License for the specific language governing permissions and limitations under
- * the License.
- */
--->
-
-<animation-list xmlns:android="http://schemas.android.com/apk/res/android" >
-    <item android:drawable="@drawable/testimage"
-        android:duration="2000" />
-</animation-list>
-
diff --git a/tests/tests/graphics/src/android/graphics/drawable/cts/AnimationDrawableTest.java b/tests/tests/graphics/src/android/graphics/drawable/cts/AnimationDrawableTest.java
index c278ed2..28a3c6b 100644
--- a/tests/tests/graphics/src/android/graphics/drawable/cts/AnimationDrawableTest.java
+++ b/tests/tests/graphics/src/android/graphics/drawable/cts/AnimationDrawableTest.java
@@ -76,7 +76,7 @@
         // Check the values set in the constructor
         assertNotNull(mAnimationDrawable.getConstantState());
         assertFalse(mAnimationDrawable.isRunning());
-        assertTrue(mAnimationDrawable.isOneShot());
+        assertFalse(mAnimationDrawable.isOneShot());
     }
 
     public void testSetVisible() throws Throwable {
@@ -277,68 +277,62 @@
         assertStoppedAnimation(THIRD_FRAME_INDEX, THIRD_FRAME_DURATION);
     }
 
-    public void testInflate() throws XmlPullParserException, IOException {
-        mAnimationDrawable = new AnimationDrawable();
-        DrawableContainerState drawableContainerState =
-            (DrawableContainerState) mAnimationDrawable.getConstantState();
-
+    public void testInflateCorrect() throws XmlPullParserException, IOException {
         XmlResourceParser parser = getResourceParser(R.xml.anim_list_correct);
-        mAnimationDrawable.inflate(mResources, parser, Xml.asAttributeSet(parser));
+        AnimationDrawable dr = new AnimationDrawable();
+        dr.inflate(mResources, parser, Xml.asAttributeSet(parser));
         // android:visible="false"
-        assertFalse(mAnimationDrawable.isVisible());
+        assertFalse(dr.isVisible());
         // android:oneShot="true"
-        assertTrue(mAnimationDrawable.isOneShot());
+        assertTrue(dr.isOneShot());
         // android:variablePadding="true"
-        assertNull(drawableContainerState.getConstantPadding());
-        assertEquals(2, mAnimationDrawable.getNumberOfFrames());
-        assertEquals(2000, mAnimationDrawable.getDuration(0));
-        assertEquals(1000, mAnimationDrawable.getDuration(1));
-        assertSame(mAnimationDrawable.getFrame(0), mAnimationDrawable.getCurrent());
+        DrawableContainerState state =
+                (DrawableContainerState) dr.getConstantState();
+        assertNull(state.getConstantPadding());
+        assertEquals(2, dr.getNumberOfFrames());
+        assertEquals(2000, dr.getDuration(0));
+        assertEquals(1000, dr.getDuration(1));
+        assertSame(dr.getFrame(0), dr.getCurrent());
+    }
 
-        parser = getResourceParser(R.xml.anim_list_missing_list_attrs);
-        mAnimationDrawable.inflate(mResources, parser, Xml.asAttributeSet(parser));
-        // use current the visibility
-        assertFalse(mAnimationDrawable.isVisible());
-        // default value of android:oneShot is false
-        assertFalse(mAnimationDrawable.isOneShot());
-        // default value of android:variablePadding is false
-        // TODO: its not clear what the value of constant padding should be when variablePadding
-        // is false
-        //assertNotNull(drawableContainerState.getConstantPadding());
-        // add a new frame from xml
-        assertEquals(3, mAnimationDrawable.getNumberOfFrames());
-        assertEquals(2000, mAnimationDrawable.getDuration(0));
-        assertEquals(1000, mAnimationDrawable.getDuration(1));
-        assertEquals(2000, mAnimationDrawable.getDuration(2));
-        assertSame(mAnimationDrawable.getFrame(0), mAnimationDrawable.getCurrent());
-
-        parser = getResourceParser(R.xml.anim_list_missing_item_drawable);
+    public void testInflateMissingDrawable() throws XmlPullParserException, IOException {
+        XmlResourceParser parser = getResourceParser(R.xml.anim_list_missing_item_drawable);
+        AnimationDrawable dr = new AnimationDrawable();
         try {
-            mAnimationDrawable.inflate(mResources, parser, Xml.asAttributeSet(parser));
+            dr.inflate(mResources, parser, Xml.asAttributeSet(parser));
             fail("Should throw XmlPullParserException if drawable of item is missing");
         } catch (XmlPullParserException e) {
             // expected
         }
     }
 
-    public void testInflateWithNullParameters() throws XmlPullParserException, IOException {
+    public void testInflateNullResources() throws XmlPullParserException, IOException {
         XmlResourceParser parser = getResourceParser(R.drawable.animationdrawable);
+        AnimationDrawable dr = new AnimationDrawable();
         try {
-            mAnimationDrawable.inflate(null, parser, Xml.asAttributeSet(parser));
+            dr.inflate(null, parser, Xml.asAttributeSet(parser));
             fail("Should throw NullPointerException if resource is null");
         } catch (NullPointerException e) {
             // expected
         }
+    }
 
+    public void testInflateNullXmlPullParser() throws XmlPullParserException, IOException {
+        XmlResourceParser parser = getResourceParser(R.drawable.animationdrawable);
+        AnimationDrawable dr = new AnimationDrawable();
         try {
-            mAnimationDrawable.inflate(mResources, null, Xml.asAttributeSet(parser));
+            dr.inflate(mResources, null, Xml.asAttributeSet(parser));
             fail("Should throw NullPointerException if parser is null");
         } catch (NullPointerException e) {
             // expected
         }
+    }
 
+    public void testInflateNullAttributeSet() throws XmlPullParserException, IOException {
+        XmlResourceParser parser = getResourceParser(R.drawable.animationdrawable);
+        AnimationDrawable dr = new AnimationDrawable();
         try {
-            mAnimationDrawable.inflate(mResources, parser, null);
+            dr.inflate(mResources, parser, null);
             fail("Should throw NullPointerException if AttributeSet is null");
         } catch (NullPointerException e) {
             // expected
diff --git a/tests/tests/graphics/src/android/graphics/drawable/cts/ScaleDrawableTest.java b/tests/tests/graphics/src/android/graphics/drawable/cts/ScaleDrawableTest.java
index 190fffa..6281582 100644
--- a/tests/tests/graphics/src/android/graphics/drawable/cts/ScaleDrawableTest.java
+++ b/tests/tests/graphics/src/android/graphics/drawable/cts/ScaleDrawableTest.java
@@ -413,8 +413,8 @@
         attrs = DrawableTestUtils.getAttributeSet(parser, "scale_nodrawable");
         try {
             scaleDrawable.inflate(res, parser, attrs);
-            fail("Should throw IllegalArgumentException");
-        } catch (IllegalArgumentException e) {
+            fail("Should throw XmlPullParserException");
+        } catch (XmlPullParserException e) {
         }
 
         try {
diff --git a/tests/tests/hardware/src/android/hardware/camera2/cts/AllocationTest.java b/tests/tests/hardware/src/android/hardware/camera2/cts/AllocationTest.java
index 92a5e58..d4fb235 100644
--- a/tests/tests/hardware/src/android/hardware/camera2/cts/AllocationTest.java
+++ b/tests/tests/hardware/src/android/hardware/camera2/cts/AllocationTest.java
@@ -310,7 +310,7 @@
      */
     private static float[] convertPixelYuvToRgb(byte[] yuvData) {
         final int CHANNELS = 3; // yuv
-        final float COLOR_RANGE = 256f;
+        final float COLOR_RANGE = 255f;
 
         assertTrue("YUV pixel must be at least 3 bytes large", CHANNELS <= yuvData.length);
 
diff --git a/tests/tests/hardware/src/android/hardware/camera2/cts/BurstCaptureTest.java b/tests/tests/hardware/src/android/hardware/camera2/cts/BurstCaptureTest.java
new file mode 100644
index 0000000..5d0841e
--- /dev/null
+++ b/tests/tests/hardware/src/android/hardware/camera2/cts/BurstCaptureTest.java
@@ -0,0 +1,353 @@
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.hardware.camera2.cts;
+
+import static android.hardware.camera2.cts.CameraTestUtils.*;
+
+import android.graphics.ImageFormat;
+import android.hardware.camera2.CameraDevice;
+import android.hardware.camera2.CaptureRequest;
+import android.hardware.camera2.CaptureResult;
+import android.hardware.camera2.CameraCharacteristics;
+import android.hardware.camera2.cts.testcases.Camera2SurfaceViewTestCase;
+import android.hardware.camera2.params.StreamConfigurationMap;
+import android.util.Log;
+import android.util.Range;
+import android.util.Size;
+
+import java.util.List;
+import java.util.ArrayList;
+
+public class BurstCaptureTest extends Camera2SurfaceViewTestCase {
+    private static final String TAG = "BurstCaptureTest";
+    private static final boolean VERBOSE = Log.isLoggable(TAG, Log.VERBOSE);
+    private static final boolean DEBUG = Log.isLoggable(TAG, Log.DEBUG);
+
+    /**
+     * Test YUV burst capture with full-AUTO control.
+     * Also verifies sensor settings operation if READ_SENSOR_SETTINGS is available.
+     */
+    public void testYuvBurst() throws Exception {
+        for (int i = 0; i < mCameraIds.length; i++) {
+            try {
+                String id = mCameraIds[i];
+                Log.i(TAG, "Testing YUV Burst for camera " + id);
+                openDevice(id);
+
+                if (mStaticInfo.isHardwareLevelLegacy()) {
+                    Log.i(TAG, "Skip burst test on legacy devices.");
+                    continue;
+                }
+
+                yuvBurstTestByCamera(id);
+            } finally {
+                closeDevice();
+                closeImageReader();
+            }
+        }
+    }
+
+    private void yuvBurstTestByCamera(String cameraId) throws Exception {
+        // Parameters
+        final int MAX_CONVERGENCE_FRAMES = 150; // 5 sec at 30fps
+        final long MAX_PREVIEW_RESULT_TIMEOUT_MS = 1000;
+        final int BURST_SIZE = 100;
+        final float FRAME_DURATION_MARGIN_FRACTION = 0.1f;
+
+        // Find a good preview size (bound to 1080p)
+        final Size previewSize = mOrderedPreviewSizes.get(0);
+
+        // Get maximum YUV_420_888 size
+        final Size stillSize = getMaxPreviewSize(cameraId, mCameraManager);
+
+        // Find max pipeline depth and sync latency
+        final int maxPipelineDepth = mStaticInfo.getCharacteristics().get(
+            CameraCharacteristics.REQUEST_PIPELINE_MAX_DEPTH);
+        final int maxSyncLatency = mStaticInfo.getCharacteristics().get(
+            CameraCharacteristics.SYNC_MAX_LATENCY);
+
+        // Find minimum frame duration for full-res YUV_420_888
+        StreamConfigurationMap config = mStaticInfo.getCharacteristics().get(
+            CameraCharacteristics.SCALER_STREAM_CONFIGURATION_MAP);
+        final long minStillFrameDuration =
+                config.getOutputMinFrameDuration(ImageFormat.YUV_420_888, stillSize);
+
+        // Find suitable target FPS range - as high as possible
+        Range<Integer>[] fpsRanges = mStaticInfo.getAeAvailableTargetFpsRangesChecked();
+        int minBurstFps = (int) Math.floor(1e9 / minStillFrameDuration);
+        Range<Integer> targetRange = null;
+        for (Range<Integer> candidateRange : fpsRanges) {
+            if (candidateRange.getLower() >= minBurstFps) {
+                if (targetRange == null) {
+                    targetRange = candidateRange;
+                } else if (candidateRange.getLower() > targetRange.getLower()) {
+                    targetRange = candidateRange;
+                } else if (candidateRange.getUpper() > targetRange.getUpper()) {
+                    targetRange = candidateRange;
+                }
+            }
+        }
+        assertTrue(String.format("Cam %s: No target FPS range found with minimum FPS above " +
+                        " 1/minFrameDuration (%d fps, duration %d ns) for full-resolution YUV",
+                cameraId, minBurstFps, minStillFrameDuration),
+            targetRange != null);
+
+        Log.i(TAG, String.format("Selected frame rate range %d - %d for YUV burst",
+                        targetRange.getLower(), targetRange.getUpper()));
+
+        // Check if READ_SENSOR_SETTINGS is supported
+        final boolean checkSensorSettings = mStaticInfo.isCapabilitySupported(
+            CameraCharacteristics.REQUEST_AVAILABLE_CAPABILITIES_READ_SENSOR_SETTINGS);
+
+        // Configure basic preview and burst settings
+
+        CaptureRequest.Builder previewBuilder =
+            mCamera.createCaptureRequest(CameraDevice.TEMPLATE_PREVIEW);
+        CaptureRequest.Builder burstBuilder =
+            mCamera.createCaptureRequest(CameraDevice.TEMPLATE_PREVIEW);
+
+        previewBuilder.set(CaptureRequest.CONTROL_AE_TARGET_FPS_RANGE,
+                targetRange);
+        burstBuilder.set(CaptureRequest.CONTROL_AE_TARGET_FPS_RANGE,
+                targetRange);
+        burstBuilder.set(CaptureRequest.CONTROL_AE_LOCK, true);
+        burstBuilder.set(CaptureRequest.CONTROL_AWB_LOCK, true);
+
+        // Create session and start up preview
+
+        SimpleCaptureCallback resultListener = new SimpleCaptureCallback();
+        ImageDropperListener imageDropper = new ImageDropperListener();
+
+        prepareCaptureAndStartPreview(
+            previewBuilder, burstBuilder,
+            previewSize, stillSize,
+            ImageFormat.YUV_420_888, resultListener,
+            /*maxNumImages*/ 3, imageDropper);
+
+        // Create burst
+
+        List<CaptureRequest> burst = new ArrayList<>();
+        for (int i = 0; i < BURST_SIZE; i++) {
+            burst.add(burstBuilder.build());
+        }
+
+        // Converge AE/AWB
+
+        int frameCount = 0;
+        while (true) {
+            CaptureResult result = resultListener.getCaptureResult(MAX_PREVIEW_RESULT_TIMEOUT_MS);
+            int aeState = result.get(CaptureResult.CONTROL_AE_STATE);
+            int awbState = result.get(CaptureResult.CONTROL_AWB_STATE);
+
+            if (DEBUG) {
+                Log.d(TAG, "aeState: " + aeState + ". awbState: " + awbState);
+            }
+
+            if ((aeState == CaptureResult.CONTROL_AE_STATE_CONVERGED ||
+                    aeState == CaptureResult.CONTROL_AE_STATE_FLASH_REQUIRED) &&
+                    awbState == CaptureResult.CONTROL_AWB_STATE_CONVERGED) {
+                break;
+            }
+            frameCount++;
+            assertTrue(String.format("Cam %s: Can not converge AE and AWB within %d frames",
+                    cameraId, MAX_CONVERGENCE_FRAMES),
+                frameCount < MAX_CONVERGENCE_FRAMES);
+        }
+
+        // Lock AF if there's a focuser
+
+        if (mStaticInfo.hasFocuser()) {
+            previewBuilder.set(CaptureRequest.CONTROL_AF_TRIGGER,
+                CaptureRequest.CONTROL_AF_TRIGGER_START);
+            mSession.capture(previewBuilder.build(), resultListener, mHandler);
+            previewBuilder.set(CaptureRequest.CONTROL_AF_TRIGGER,
+                CaptureRequest.CONTROL_AF_TRIGGER_IDLE);
+
+            frameCount = 0;
+            while (true) {
+                CaptureResult result = resultListener.getCaptureResult(MAX_PREVIEW_RESULT_TIMEOUT_MS);
+                int afState = result.get(CaptureResult.CONTROL_AF_STATE);
+
+                if (DEBUG) {
+                    Log.d(TAG, "afState: " + afState);
+                }
+
+                if (afState == CaptureResult.CONTROL_AF_STATE_FOCUSED_LOCKED ||
+                    afState == CaptureResult.CONTROL_AF_STATE_NOT_FOCUSED_LOCKED) {
+                    break;
+                }
+                frameCount++;
+                assertTrue(String.format("Cam %s: Cannot lock AF within %d frames", cameraId,
+                        MAX_CONVERGENCE_FRAMES),
+                    frameCount < MAX_CONVERGENCE_FRAMES);
+            }
+        }
+
+        // Lock AE/AWB
+
+        previewBuilder.set(CaptureRequest.CONTROL_AE_LOCK, true);
+        previewBuilder.set(CaptureRequest.CONTROL_AWB_LOCK, true);
+
+        CaptureRequest lockedRequest = previewBuilder.build();
+        mSession.setRepeatingRequest(lockedRequest, resultListener, mHandler);
+
+        // Wait for first result with locking
+
+        CaptureResult lockedResult =
+                resultListener.getCaptureResultForRequest(lockedRequest, maxPipelineDepth);
+
+        int pipelineDepth = lockedResult.get(CaptureResult.REQUEST_PIPELINE_DEPTH);
+
+        // Then start waiting on results to get the first result that should be synced
+        // up, and also fire the burst as soon as possible
+
+        if (maxSyncLatency == CameraCharacteristics.SYNC_MAX_LATENCY_PER_FRAME_CONTROL) {
+            // The locked result we have is already synchronized so start the burst
+            mSession.captureBurst(burst, resultListener, mHandler);
+        } else {
+            // Need to get a synchronized result, and may need to start burst later to
+            // be synchronized correctly
+
+            boolean burstSent = false;
+
+            // Calculate how many requests we need to still send down to camera before we
+            // know the settings have settled for the burst
+
+            int numFramesWaited = maxSyncLatency;
+            if (numFramesWaited == CameraCharacteristics.SYNC_MAX_LATENCY_UNKNOWN) {
+                numFramesWaited = NUM_FRAMES_WAITED_FOR_UNKNOWN_LATENCY;
+            }
+
+            int requestsNeededToSync = numFramesWaited - pipelineDepth;
+            for (int i = 0; i < numFramesWaited; i++) {
+                if (!burstSent && requestsNeededToSync <= 0) {
+                    mSession.captureBurst(burst, resultListener, mHandler);
+                    burstSent = true;
+                }
+                lockedResult = resultListener.getCaptureResult(MAX_PREVIEW_RESULT_TIMEOUT_MS);
+                requestsNeededToSync--;
+            }
+
+            assertTrue("Cam " + cameraId + ": Burst failed to fire!", burstSent);
+        }
+
+        // Read in locked settings if supported
+
+        long burstExposure = 0;
+        long burstFrameDuration = 0;
+        int burstSensitivity = 0;
+        if (checkSensorSettings) {
+            burstExposure = lockedResult.get(CaptureResult.SENSOR_EXPOSURE_TIME);
+            burstFrameDuration = lockedResult.get(CaptureResult.SENSOR_FRAME_DURATION);
+            burstSensitivity = lockedResult.get(CaptureResult.SENSOR_SENSITIVITY);
+
+            assertTrue(String.format("Cam %s: Frame duration %d ns too short compared to " +
+                    "exposure time %d ns", cameraId, burstFrameDuration, burstExposure),
+                burstFrameDuration >= burstExposure);
+
+            assertTrue(String.format("Cam %s: Exposure time is not valid: %d",
+                    cameraId, burstExposure),
+                burstExposure > 0);
+            assertTrue(String.format("Cam %s: Frame duration is not valid: %d",
+                    cameraId, burstFrameDuration),
+                burstFrameDuration > 0);
+            assertTrue(String.format("Cam %s: Sensitivity is not valid: %d",
+                    cameraId, burstSensitivity),
+                burstSensitivity > 0);
+        }
+
+        // Process burst results
+        int burstIndex = 0;
+        CaptureResult burstResult =
+                resultListener.getCaptureResultForRequest(burst.get(burstIndex),
+                    maxPipelineDepth + 1);
+        long prevTimestamp = -1;
+        final long frameDurationBound = (long)
+                (minStillFrameDuration * (1 + FRAME_DURATION_MARGIN_FRACTION) );
+
+        List<Long> frameDurations = new ArrayList<>();
+
+        while(true) {
+            // Verify the result
+            assertTrue("Cam " + cameraId + ": Result doesn't match expected request",
+                    burstResult.getRequest() == burst.get(burstIndex));
+
+            // Verify locked settings
+            if (checkSensorSettings) {
+                long exposure = burstResult.get(CaptureResult.SENSOR_EXPOSURE_TIME);
+                int sensitivity = burstResult.get(CaptureResult.SENSOR_SENSITIVITY);
+                assertTrue("Cam " + cameraId + ": Exposure not locked!",
+                    exposure == burstExposure);
+                assertTrue("Cam " + cameraId + ": Sensitivity not locked!",
+                    sensitivity == burstSensitivity);
+            }
+
+            // Collect inter-frame durations
+            long timestamp = burstResult.get(CaptureResult.SENSOR_TIMESTAMP);
+            if (prevTimestamp != -1) {
+                long frameDuration = timestamp - prevTimestamp;
+                frameDurations.add(frameDuration);
+                if (DEBUG) {
+                    Log.i(TAG, String.format("Frame %03d    Duration %.2f ms", burstIndex,
+                            frameDuration/1e6));
+                }
+            }
+            prevTimestamp = timestamp;
+
+            // Get next result
+            burstIndex++;
+            if (burstIndex == BURST_SIZE) break;
+            burstResult = resultListener.getCaptureResult(MAX_PREVIEW_RESULT_TIMEOUT_MS);
+        }
+
+        // Verify inter-frame durations
+
+        long meanFrameSum = 0;
+        for (Long duration : frameDurations) {
+            meanFrameSum += duration;
+        }
+        float meanFrameDuration = (float) meanFrameSum / frameDurations.size();
+
+        float stddevSum = 0;
+        for (Long duration : frameDurations) {
+            stddevSum += (duration - meanFrameDuration) * (duration - meanFrameDuration);
+        }
+        float stddevFrameDuration = (float)
+                Math.sqrt(1.f / (frameDurations.size() - 1 ) * stddevSum);
+
+        Log.i(TAG, String.format("Cam %s: Burst frame duration mean: %.1f, stddev: %.1f", cameraId,
+                meanFrameDuration, stddevFrameDuration));
+
+        assertTrue(
+            String.format("Cam %s: Burst frame duration mean %.1f ns is larger than acceptable, " +
+                "expecting below %d ns, allowing below %d", cameraId,
+                meanFrameDuration, minStillFrameDuration, frameDurationBound),
+            meanFrameDuration <= frameDurationBound);
+
+        // Calculate upper 97.5% bound (assuming durations are normally distributed...)
+        float limit95FrameDuration = meanFrameDuration + 2 * stddevFrameDuration;
+
+        // Don't enforce this yet, but warn
+        if (limit95FrameDuration > frameDurationBound) {
+            Log.w(TAG,
+                String.format("Cam %s: Standard deviation is too large compared to limit: " +
+                    "mean: %.1f ms, stddev: %.1f ms: 95%% bound: %f ms", cameraId,
+                    meanFrameDuration/1e6, stddevFrameDuration/1e6,
+                    limit95FrameDuration/1e6));
+        }
+    }
+}
diff --git a/tests/tests/hardware/src/android/hardware/camera2/cts/Camera2SurfaceViewCtsActivity.java b/tests/tests/hardware/src/android/hardware/camera2/cts/Camera2SurfaceViewCtsActivity.java
index 936883e..8a217fd 100644
--- a/tests/tests/hardware/src/android/hardware/camera2/cts/Camera2SurfaceViewCtsActivity.java
+++ b/tests/tests/hardware/src/android/hardware/camera2/cts/Camera2SurfaceViewCtsActivity.java
@@ -19,6 +19,7 @@
 import android.app.Activity;
 import android.os.Bundle;
 import android.os.ConditionVariable;
+import android.os.SystemClock;
 import android.util.Log;
 import android.view.SurfaceHolder;
 import android.view.SurfaceView;
@@ -31,6 +32,7 @@
     private SurfaceView mSurfaceView;
     private int currentWidth = 0;
     private int currentHeight = 0;
+    private final Object sizeLock = new Object();
 
     @Override
     protected void onCreate(Bundle savedInstanceState) {
@@ -54,10 +56,16 @@
                             timeOutMs, expectWidth, expectHeight));
         }
 
+        synchronized(sizeLock) {
+            if (expectWidth == currentWidth && expectHeight == currentHeight) {
+                return true;
+            }
+        }
+
         int waitTimeMs = timeOutMs;
         boolean changeSucceeded = false;
         while (!changeSucceeded && waitTimeMs > 0) {
-            long startTimeMs = System.currentTimeMillis();
+            long startTimeMs = SystemClock.elapsedRealtime();
             changeSucceeded = surfaceChangedDone.block(waitTimeMs);
             if (!changeSucceeded) {
                 Log.e(TAG, "Wait for surface change timed out after " + timeOutMs + " ms");
@@ -72,7 +80,7 @@
                 // again.
                 changeSucceeded = false;
             }
-            waitTimeMs -= (System.currentTimeMillis() - startTimeMs);
+            waitTimeMs -= (SystemClock.elapsedRealtime() - startTimeMs);
         }
 
         // Couldn't get expected surface size change.
@@ -86,8 +94,10 @@
     @Override
     public void surfaceChanged(SurfaceHolder holder, int format, int width, int height) {
         Log.i(TAG, "Surface Changed to: " + width + "x" + height);
-        currentWidth = width;
-        currentHeight = height;
+        synchronized (sizeLock) {
+            currentWidth = width;
+            currentHeight = height;
+        }
         surfaceChangedDone.open();
     }
 
diff --git a/tests/tests/hardware/src/android/hardware/camera2/cts/CameraDeviceTest.java b/tests/tests/hardware/src/android/hardware/camera2/cts/CameraDeviceTest.java
index 9f50b43..29c7362 100644
--- a/tests/tests/hardware/src/android/hardware/camera2/cts/CameraDeviceTest.java
+++ b/tests/tests/hardware/src/android/hardware/camera2/cts/CameraDeviceTest.java
@@ -28,6 +28,7 @@
 import android.hardware.camera2.CameraCaptureSession;
 import android.hardware.camera2.CameraCharacteristics;
 import android.hardware.camera2.CameraDevice;
+import android.hardware.camera2.CameraMetadata;
 import android.hardware.camera2.CaptureFailure;
 import android.hardware.camera2.CaptureRequest;
 import android.hardware.camera2.CaptureResult;
@@ -1014,6 +1015,24 @@
         }
     }
 
+    private void checkAntiBandingMode(CaptureRequest.Builder request, int template) {
+        if (template == CameraDevice.TEMPLATE_MANUAL) {
+            return;
+        }
+
+        List<Integer> availableAntiBandingModes =
+                Arrays.asList(toObject(mStaticInfo.getAeAvailableAntiBandingModesChecked()));
+
+        if (availableAntiBandingModes.contains(CameraMetadata.CONTROL_AE_ANTIBANDING_MODE_AUTO)) {
+            mCollector.expectKeyValueEquals(request, CONTROL_AE_ANTIBANDING_MODE,
+                    CameraMetadata.CONTROL_AE_ANTIBANDING_MODE_AUTO);
+        } else {
+            mCollector.expectKeyValueIsIn(request, CONTROL_AE_ANTIBANDING_MODE,
+                    CameraMetadata.CONTROL_AE_ANTIBANDING_MODE_50HZ,
+                    CameraMetadata.CONTROL_AE_ANTIBANDING_MODE_60HZ);
+        }
+    }
+
     /**
      * <p>Check if 3A metering settings are "up to HAL" in request template</p>
      *
@@ -1058,6 +1077,7 @@
 
         checkAfMode(request, template, props);
         checkFpsRange(request, template, props);
+        checkAntiBandingMode(request, template);
 
         if (template == CameraDevice.TEMPLATE_MANUAL) {
             mCollector.expectKeyValueEquals(request, CONTROL_MODE, CaptureRequest.CONTROL_MODE_OFF);
@@ -1068,8 +1088,6 @@
         } else {
             mCollector.expectKeyValueEquals(request, CONTROL_AE_MODE,
                     CaptureRequest.CONTROL_AE_MODE_ON);
-            mCollector.expectKeyValueNotEquals(request, CONTROL_AE_ANTIBANDING_MODE,
-                    CaptureRequest.CONTROL_AE_ANTIBANDING_MODE_OFF);
             mCollector.expectKeyValueEquals(request, CONTROL_AE_EXPOSURE_COMPENSATION, 0);
             mCollector.expectKeyValueEquals(request, CONTROL_AE_LOCK, false);
             mCollector.expectKeyValueEquals(request, CONTROL_AE_PRECAPTURE_TRIGGER,
diff --git a/tests/tests/hardware/src/android/hardware/camera2/cts/CameraManagerTest.java b/tests/tests/hardware/src/android/hardware/camera2/cts/CameraManagerTest.java
index 192fb85..27ff6d1 100644
--- a/tests/tests/hardware/src/android/hardware/camera2/cts/CameraManagerTest.java
+++ b/tests/tests/hardware/src/android/hardware/camera2/cts/CameraManagerTest.java
@@ -41,7 +41,9 @@
 
 import java.util.ArrayList;
 import java.util.Arrays;
+import java.util.HashSet;
 import java.util.List;
+import java.util.concurrent.LinkedBlockingQueue;
 
 /**
  * <p>Basic test for CameraManager class.</p>
@@ -485,7 +487,132 @@
         mCameraManager.registerAvailabilityCallback(mListener, mHandler);
         mCameraManager.unregisterAvailabilityCallback(mListener);
         mCameraManager.unregisterAvailabilityCallback(mListener);
-
-        // TODO: test the listener callbacks
     }
+
+    /**
+     * Test that the availability callbacks fire when expected
+     */
+    public void testCameraManagerListenerCallbacks() throws Exception {
+        final int AVAILABILITY_TIMEOUT_MS = 10;
+
+        final LinkedBlockingQueue<String> availableEventQueue = new LinkedBlockingQueue<>();
+        final LinkedBlockingQueue<String> unavailableEventQueue = new LinkedBlockingQueue<>();
+
+        CameraManager.AvailabilityCallback ac = new CameraManager.AvailabilityCallback() {
+            @Override
+            public void onCameraAvailable(String cameraId) {
+                availableEventQueue.offer(cameraId);
+            }
+
+            @Override
+            public void onCameraUnavailable(String cameraId) {
+                unavailableEventQueue.offer(cameraId);
+            }
+        };
+
+        mCameraManager.registerAvailabilityCallback(ac, mHandler);
+        String[] cameras = mCameraManager.getCameraIdList();
+
+        if (cameras.length == 0) {
+            Log.i(TAG, "No cameras present, skipping test");
+            return;
+        }
+
+        // Verify we received available for all cameras' initial state in a reasonable amount of time
+        HashSet<String> expectedAvailableCameras = new HashSet<String>(Arrays.asList(cameras));
+        while (expectedAvailableCameras.size() > 0) {
+            String id = availableEventQueue.poll(AVAILABILITY_TIMEOUT_MS,
+                    java.util.concurrent.TimeUnit.MILLISECONDS);
+            assertTrue("Did not receive initial availability notices for some cameras",
+                       id != null);
+            expectedAvailableCameras.remove(id);
+        }
+        // Verify no unavailable cameras were reported
+        assertTrue("Some camera devices are initially unavailable",
+                unavailableEventQueue.size() == 0);
+
+        // Verify transitions for individual cameras
+        for (String id : cameras) {
+            MockStateCallback mockListener = MockStateCallback.mock();
+            mCameraListener = new BlockingStateCallback(mockListener);
+
+            mCameraManager.openCamera(id, mCameraListener, mHandler);
+
+            // Block until opened
+            mCameraListener.waitForState(BlockingStateCallback.STATE_OPENED,
+                    CameraTestUtils.CAMERA_IDLE_TIMEOUT_MS);
+            // Then verify only open happened, and get the camera handle
+            CameraDevice camera = verifyCameraStateOpened(id, mockListener);
+
+            // Verify that we see the expected 'unavailable' event.
+            String candidateId = unavailableEventQueue.poll(AVAILABILITY_TIMEOUT_MS,
+                    java.util.concurrent.TimeUnit.MILLISECONDS);
+            assertTrue(String.format("Received unavailability notice for wrong ID " +
+                            "(expected %s, got %s)", id, candidateId),
+                    id == candidateId);
+            assertTrue("Availability events received unexpectedly",
+                    availableEventQueue.size() == 0);
+
+            // Verify that we see the expected 'available' event after closing the camera
+
+            camera.close();
+
+            mCameraListener.waitForState(BlockingStateCallback.STATE_CLOSED,
+                    CameraTestUtils.CAMERA_CLOSE_TIMEOUT_MS);
+
+            candidateId = availableEventQueue.poll(AVAILABILITY_TIMEOUT_MS,
+                    java.util.concurrent.TimeUnit.MILLISECONDS);
+            assertTrue(String.format("Received availability notice for wrong ID " +
+                            "(expected %s, got %s)", id, candidateId),
+                    id == candidateId);
+            assertTrue("Unavailability events received unexpectedly",
+                    unavailableEventQueue.size() == 0);
+
+        }
+
+        // Verify that we can unregister the listener and see no more events
+        assertTrue("Availability events received unexpectedly",
+                availableEventQueue.size() == 0);
+        assertTrue("Unavailability events received unexpectedly",
+                    unavailableEventQueue.size() == 0);
+
+        mCameraManager.unregisterAvailabilityCallback(ac);
+
+        {
+            // Open an arbitrary camera and make sure we don't hear about it
+
+            MockStateCallback mockListener = MockStateCallback.mock();
+            mCameraListener = new BlockingStateCallback(mockListener);
+
+            mCameraManager.openCamera(cameras[0], mCameraListener, mHandler);
+
+            // Block until opened
+            mCameraListener.waitForState(BlockingStateCallback.STATE_OPENED,
+                    CameraTestUtils.CAMERA_IDLE_TIMEOUT_MS);
+            // Then verify only open happened, and close the camera
+            CameraDevice camera = verifyCameraStateOpened(cameras[0], mockListener);
+
+            camera.close();
+
+            mCameraListener.waitForState(BlockingStateCallback.STATE_CLOSED,
+                    CameraTestUtils.CAMERA_CLOSE_TIMEOUT_MS);
+
+            // No unavailability or availability callback should have occured
+            String candidateId = unavailableEventQueue.poll(AVAILABILITY_TIMEOUT_MS,
+                    java.util.concurrent.TimeUnit.MILLISECONDS);
+            assertTrue(String.format("Received unavailability notice for ID %s unexpectedly ",
+                            candidateId),
+                    candidateId == null);
+
+            candidateId = availableEventQueue.poll(AVAILABILITY_TIMEOUT_MS,
+                    java.util.concurrent.TimeUnit.MILLISECONDS);
+            assertTrue(String.format("Received availability notice for ID %s unexpectedly ",
+                            candidateId),
+                    candidateId == null);
+
+
+        }
+
+    } // testCameraManagerListenerCallbacks
+
 }
diff --git a/tests/tests/hardware/src/android/hardware/camera2/cts/CameraTestUtils.java b/tests/tests/hardware/src/android/hardware/camera2/cts/CameraTestUtils.java
index 0c36832..a17041d 100644
--- a/tests/tests/hardware/src/android/hardware/camera2/cts/CameraTestUtils.java
+++ b/tests/tests/hardware/src/android/hardware/camera2/cts/CameraTestUtils.java
@@ -18,6 +18,7 @@
 
 import static com.android.ex.camera2.blocking.BlockingStateCallback.*;
 
+import android.graphics.Bitmap;
 import android.graphics.BitmapFactory;
 import android.graphics.ImageFormat;
 import android.graphics.PointF;
@@ -40,7 +41,9 @@
 import android.media.Image.Plane;
 import android.os.Handler;
 import android.util.Log;
+import android.view.Display;
 import android.view.Surface;
+import android.view.WindowManager;
 
 import com.android.ex.camera2.blocking.BlockingCameraManager;
 import com.android.ex.camera2.blocking.BlockingCameraManager.BlockingOpenException;
@@ -63,6 +66,7 @@
 import java.util.List;
 import java.util.concurrent.LinkedBlockingQueue;
 import java.util.concurrent.TimeUnit;
+import java.util.concurrent.atomic.AtomicLong;
 
 /**
  * A package private utility class for wrapping up the camera2 cts test common utility functions
@@ -199,6 +203,7 @@
     public static class SimpleCaptureCallback extends CameraCaptureSession.CaptureCallback {
         private final LinkedBlockingQueue<CaptureResult> mQueue =
                 new LinkedBlockingQueue<CaptureResult>();
+        private AtomicLong mNumFramesArrived = new AtomicLong(0);
 
         @Override
         public void onCaptureStarted(CameraCaptureSession session, CaptureRequest request,
@@ -210,6 +215,7 @@
         public void onCaptureCompleted(CameraCaptureSession session, CaptureRequest request,
                 TotalCaptureResult result) {
             try {
+                mNumFramesArrived.incrementAndGet();
                 mQueue.put(result);
             } catch (InterruptedException e) {
                 throw new UnsupportedOperationException(
@@ -227,6 +233,10 @@
                 long frameNumber) {
         }
 
+        public long getTotalNumFrames() {
+            return mNumFramesArrived.get();
+        }
+
         public CaptureResult getCaptureResult(long timeout) {
             try {
                 CaptureResult result = mQueue.poll(timeout, TimeUnit.MILLISECONDS);
@@ -439,22 +449,26 @@
             assertTrue("rowStride " + rowStride + " should be >= width " + w , rowStride >= w);
             for (int row = 0; row < h; row++) {
                 int bytesPerPixel = ImageFormat.getBitsPerPixel(format) / 8;
+                int length;
                 if (pixelStride == bytesPerPixel) {
                     // Special case: optimized read of the entire row
-                    int length = w * bytesPerPixel;
+                    length = w * bytesPerPixel;
                     buffer.get(data, offset, length);
-                    // Advance buffer the remainder of the row stride
-                    buffer.position(buffer.position() + rowStride - length);
                     offset += length;
                 } else {
                     // Generic case: should work for any pixelStride but slower.
                     // Use intermediate buffer to avoid read byte-by-byte from
                     // DirectByteBuffer, which is very bad for performance
-                    buffer.get(rowData, 0, rowStride);
+                    length = (w - 1) * pixelStride + bytesPerPixel;
+                    buffer.get(rowData, 0, length);
                     for (int col = 0; col < w; col++) {
                         data[offset++] = rowData[col * pixelStride];
                     }
                 }
+                // Advance buffer the remainder of the row stride
+                if (row < h - 1) {
+                    buffer.position(buffer.position() + rowStride - length);
+                }
             }
             if (VERBOSE) Log.v(TAG, "Finished reading data from plane " + i);
             buffer.rewind();
@@ -485,6 +499,23 @@
         }
     }
 
+    public static void dumpFile(String fileName, Bitmap data) {
+        FileOutputStream outStream;
+        try {
+            Log.v(TAG, "output will be saved as " + fileName);
+            outStream = new FileOutputStream(fileName);
+        } catch (IOException ioe) {
+            throw new RuntimeException("Unable to create debug output file " + fileName, ioe);
+        }
+
+        try {
+            data.compress(Bitmap.CompressFormat.JPEG, /*quality*/90, outStream);
+            outStream.close();
+        } catch (IOException ioe) {
+            throw new RuntimeException("failed writing data to file " + fileName, ioe);
+        }
+    }
+
     public static void dumpFile(String fileName, byte[] data) {
         FileOutputStream outStream;
         try {
@@ -541,7 +572,27 @@
      */
     static public List<Size> getSupportedPreviewSizes(String cameraId,
             CameraManager cameraManager, Size bound) throws CameraAccessException {
-        return getSortedSizesForFormat(cameraId, cameraManager, ImageFormat.YUV_420_888, bound);
+        CameraCharacteristics props = cameraManager.getCameraCharacteristics(cameraId);
+        StreamConfigurationMap config =
+                props.get(CameraCharacteristics.SCALER_STREAM_CONFIGURATION_MAP);
+        Size[] rawSizes = config.getOutputSizes(android.view.SurfaceHolder.class);
+        assertArrayNotEmpty(rawSizes,
+                "Available sizes for SurfaceHolder class should not be empty");
+        if (VERBOSE) {
+            Log.v(TAG, "Supported sizes are: " + Arrays.deepToString(rawSizes));
+        }
+
+        if (bound == null) {
+            return getAscendingOrderSizes(Arrays.asList(rawSizes), /*ascending*/false);
+        }
+
+        List<Size> sizes = new ArrayList<Size>();
+        for (Size sz: rawSizes) {
+            if (sz.getWidth() <= bound.getWidth() && sz.getHeight() <= bound.getHeight()) {
+                sizes.add(sz);
+            }
+        }
+        return getAscendingOrderSizes(sizes, /*ascending*/false);
     }
 
     /**
@@ -576,7 +627,7 @@
      * Get sorted (descending order) size list for given format. Remove the sizes larger than
      * the bound. If the bound is null, don't do the size bound filtering.
      */
-    static private List<Size> getSortedSizesForFormat(String cameraId,
+    static public List<Size> getSortedSizesForFormat(String cameraId,
             CameraManager cameraManager, int format, Size bound) throws CameraAccessException {
         Comparator<Size> comparator = new SizeComparator();
         Size[] sizes = getSupportedSizeForFormat(format, cameraId, cameraManager);
@@ -610,7 +661,27 @@
      */
     static public List<Size> getSupportedVideoSizes(String cameraId,
             CameraManager cameraManager, Size bound) throws CameraAccessException {
-        return getSortedSizesForFormat(cameraId, cameraManager, ImageFormat.YUV_420_888, bound);
+        CameraCharacteristics props = cameraManager.getCameraCharacteristics(cameraId);
+        StreamConfigurationMap config =
+                props.get(CameraCharacteristics.SCALER_STREAM_CONFIGURATION_MAP);
+        Size[] rawSizes = config.getOutputSizes(android.media.MediaRecorder.class);
+        assertArrayNotEmpty(rawSizes,
+                "Available sizes for MediaRecorder class should not be empty");
+        if (VERBOSE) {
+            Log.v(TAG, "Supported sizes are: " + Arrays.deepToString(rawSizes));
+        }
+
+        if (bound == null) {
+            return getAscendingOrderSizes(Arrays.asList(rawSizes), /*ascending*/false);
+        }
+
+        List<Size> sizes = new ArrayList<Size>();
+        for (Size sz: rawSizes) {
+            if (sz.getWidth() <= bound.getWidth() && sz.getHeight() <= bound.getHeight()) {
+                sizes.add(sz);
+            }
+        }
+        return getAscendingOrderSizes(sizes, /*ascending*/false);
     }
 
     /**
@@ -675,6 +746,21 @@
     }
 
     /**
+     * Returns true if the given {@code array} contains the given element.
+     *
+     * @param array {@code array} to check for {@code elem}
+     * @param elem {@code elem} to test for
+     * @return {@code true} if the given element is contained
+     */
+    public static boolean contains(int[] array, int elem) {
+        if (array == null) return false;
+        for (int i = 0; i < array.length; i++) {
+            if (elem == array[i]) return true;
+        }
+        return false;
+    }
+
+    /**
      * Get object array from byte array.
      *
      * @param array Input byte array to be converted
@@ -772,6 +858,7 @@
                 validateJpegData(data, width, height, filePath);
                 break;
             case ImageFormat.YUV_420_888:
+            case ImageFormat.YV12:
                 validateYuvData(data, width, height, format, image.getTimestamp(), filePath);
                 break;
             case ImageFormat.RAW_SENSOR:
@@ -973,4 +1060,22 @@
         }
         return resultRegions;
     }
+
+    public static Size getPreviewSizeBound(WindowManager windowManager, Size bound) {
+        Display display = windowManager.getDefaultDisplay();
+
+        int width = display.getWidth();
+        int height = display.getHeight();
+
+        if (height > width) {
+            height = width;
+            width = display.getHeight();
+        }
+
+        if (bound.getWidth() <= width &&
+            bound.getHeight() <= height)
+            return bound;
+        else
+            return new Size(width, height);
+    }
 }
diff --git a/tests/tests/hardware/src/android/hardware/camera2/cts/CaptureRequestTest.java b/tests/tests/hardware/src/android/hardware/camera2/cts/CaptureRequestTest.java
index f7d95f6..be29a8e 100644
--- a/tests/tests/hardware/src/android/hardware/camera2/cts/CaptureRequestTest.java
+++ b/tests/tests/hardware/src/android/hardware/camera2/cts/CaptureRequestTest.java
@@ -147,7 +147,8 @@
                 changeExposure(requestBuilder, DEFAULT_EXP_TIME_NS, DEFAULT_SENSITIVITY);
 
                 Size previewSz =
-                        getMaxPreviewSize(mCamera.getId(), mCameraManager, PREVIEW_SIZE_BOUND);
+                        getMaxPreviewSize(mCamera.getId(), mCameraManager,
+                        getPreviewSizeBound(mWindowManager, PREVIEW_SIZE_BOUND));
 
                 startPreview(requestBuilder, previewSz, listener);
                 waitForSettingsApplied(listener, NUM_FRAMES_WAITED_FOR_UNKNOWN_LATENCY);
@@ -202,7 +203,8 @@
                         STATISTICS_LENS_SHADING_MAP_MODE_ON);
 
                 Size previewSz =
-                        getMaxPreviewSize(mCamera.getId(), mCameraManager, PREVIEW_SIZE_BOUND);
+                        getMaxPreviewSize(mCamera.getId(), mCameraManager,
+                        getPreviewSizeBound(mWindowManager, PREVIEW_SIZE_BOUND));
 
                 listener = new SimpleCaptureCallback();
                 startPreview(requestBuilder, previewSz, listener);
@@ -256,7 +258,8 @@
                 int[] modes = mStaticInfo.getAeAvailableAntiBandingModesChecked();
 
                 Size previewSz =
-                        getMaxPreviewSize(mCamera.getId(), mCameraManager, PREVIEW_SIZE_BOUND);
+                        getMaxPreviewSize(mCamera.getId(), mCameraManager,
+                        getPreviewSizeBound(mWindowManager, PREVIEW_SIZE_BOUND));
 
                 for (int mode : modes) {
                     antiBandingTestByMode(previewSz, mode);
@@ -351,11 +354,6 @@
             try {
                 openDevice(mCameraIds[i]);
 
-                if (mStaticInfo.isHardwareLevelLegacy()) {
-                    Log.i(TAG, "Skipping test on legacy devices");
-                    continue;
-                }
-
                 faceDetectionTestByCamera();
             } finally {
                 closeDevice();
@@ -471,6 +469,11 @@
             try {
                 openDevice(id);
 
+                if (mStaticInfo.isHardwareLevelLegacy()) {
+                    Log.i(TAG, "Skipping test on legacy devices");
+                    continue;
+                }
+
                 awbModeAndLockTestByCamera();
             } finally {
                 closeDevice();
@@ -1165,7 +1168,9 @@
                 changeExposure(requestBuilder, expTimes[i], sensitivities[j]);
                 mSession.capture(requestBuilder.build(), listener, mHandler);
 
-                CaptureResult result = listener.getCaptureResult(WAIT_FOR_RESULT_TIMEOUT_MS);
+                // make sure timeout is long enough for long exposure time
+                long timeout = WAIT_FOR_RESULT_TIMEOUT_MS + expTimes[i];
+                CaptureResult result = listener.getCaptureResult(timeout);
                 long resultExpTime = getValueNotNull(result, CaptureResult.SENSOR_EXPOSURE_TIME);
                 int resultSensitivity = getValueNotNull(result, CaptureResult.SENSOR_SENSITIVITY);
                 validateExposureTime(expTimes[i], resultExpTime);
diff --git a/tests/tests/hardware/src/android/hardware/camera2/cts/DngCreatorTest.java b/tests/tests/hardware/src/android/hardware/camera2/cts/DngCreatorTest.java
index 9ec649e..67fad4e 100644
--- a/tests/tests/hardware/src/android/hardware/camera2/cts/DngCreatorTest.java
+++ b/tests/tests/hardware/src/android/hardware/camera2/cts/DngCreatorTest.java
@@ -16,20 +16,29 @@
 
 package android.hardware.camera2.cts;
 
+import android.content.Context;
+import android.graphics.Bitmap;
+import android.graphics.BitmapFactory;
 import android.graphics.ImageFormat;
 import android.graphics.Rect;
+import android.graphics.RectF;
 import android.hardware.camera2.CameraCaptureSession;
 import android.hardware.camera2.CameraCharacteristics;
 import android.hardware.camera2.CameraDevice;
 import android.hardware.camera2.CaptureRequest;
 import android.hardware.camera2.CaptureResult;
 import android.hardware.camera2.DngCreator;
+import android.hardware.camera2.TotalCaptureResult;
 import android.hardware.camera2.cts.helpers.StaticMetadata;
+import android.hardware.camera2.cts.rs.BitmapUtils;
+import android.hardware.camera2.cts.rs.RawConverter;
+import android.hardware.camera2.cts.rs.RenderScriptSingleton;
 import android.hardware.camera2.cts.testcases.Camera2AndroidTestCase;
 import android.location.Location;
 import android.media.ExifInterface;
 import android.media.Image;
 import android.media.ImageReader;
+import android.os.ConditionVariable;
 import android.util.Log;
 import android.util.Pair;
 import android.util.Size;
@@ -37,10 +46,12 @@
 
 import java.io.ByteArrayOutputStream;
 import java.io.FileOutputStream;
+import java.nio.channels.FileChannel;
 import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Collections;
 import java.util.List;
 
-import static android.hardware.camera2.cts.CameraTestUtils.configureCameraSession;
 import static android.hardware.camera2.cts.helpers.AssertHelpers.*;
 
 /**
@@ -51,6 +62,10 @@
     private static final boolean VERBOSE = Log.isLoggable(TAG, Log.VERBOSE);
     private static final String DEBUG_DNG_FILE = "raw16.dng";
 
+    private static final double IMAGE_DIFFERENCE_TOLERANCE = 65;
+    private static final int DEFAULT_PATCH_DIMEN = 512;
+    private static final int AE_TIMEOUT_MS = 2000;
+
     @Override
     protected void setUp() throws Exception {
         super.setUp();
@@ -61,6 +76,13 @@
         super.tearDown();
     }
 
+    @Override
+    public synchronized void setContext(Context context) {
+        super.setContext(context);
+
+        RenderScriptSingleton.setContext(context);
+    }
+
     /**
      * Test basic raw capture and DNG saving functionality for each of the available cameras.
      *
@@ -111,7 +133,7 @@
                 captureReader = createImageReader(activeArraySize, ImageFormat.RAW_SENSOR, 2,
                         captureListener);
                 Pair<Image, CaptureResult> resultPair = captureSingleRawShot(activeArraySize,
-                        captureReader, captureListener);
+                        /*waitForAe*/false, captureReader, captureListener);
                 CameraCharacteristics characteristics = mStaticInfo.getCharacteristics();
 
                 // Test simple writeImage, no header checks
@@ -120,14 +142,15 @@
                 dngCreator.writeImage(outputStream, resultPair.first);
 
                 if (VERBOSE) {
-                    String filePath = DEBUG_FILE_NAME_BASE + "camera_" + deviceId + "_" +
+                    // Write DNG to file
+                    String dngFilePath = DEBUG_FILE_NAME_BASE + "/camera_basic_" + deviceId + "_" +
                             DEBUG_DNG_FILE;
                     // Write out captured DNG file for the first camera device if setprop is enabled
-                    fileStream = new FileOutputStream(filePath);
+                    fileStream = new FileOutputStream(dngFilePath);
                     fileStream.write(outputStream.toByteArray());
                     fileStream.flush();
                     fileStream.close();
-                    Log.v(TAG, "Test DNG file for camera " + deviceId + " saved to " + filePath);
+                    Log.v(TAG, "Test DNG file for camera " + deviceId + " saved to " + dngFilePath);
                 }
             } finally {
                 closeDevice(deviceId);
@@ -212,7 +235,7 @@
                 captureListeners.add(previewListener);
 
                 Pair<List<Image>, CaptureResult> resultPair = captureSingleRawShot(activeArraySize,
-                        captureReaders, captureListeners);
+                        captureReaders, /*waitForAe*/false, captureListeners);
                 CameraCharacteristics characteristics = mStaticInfo.getCharacteristics();
 
                 // Test simple writeImage, no header checks
@@ -231,7 +254,7 @@
                 dngCreator.writeImage(outputStream, resultPair.first.get(0));
 
                 if (VERBOSE) {
-                    String filePath = DEBUG_FILE_NAME_BASE + "camera_" + deviceId + "_" +
+                    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
                     fileStream = new FileOutputStream(filePath);
@@ -257,33 +280,260 @@
         }
     }
 
-    private Pair<Image, CaptureResult> captureSingleRawShot(Size s, ImageReader captureReader,
+    /**
+     * Test basic RAW capture, and ensure that the rendered RAW output is similar to the JPEG
+     * created for the same frame.
+     *
+     * <p>
+     * This test renders the RAW buffer into an RGB bitmap using a rendering pipeline
+     * similar to one in the Adobe DNG validation tool.  JPEGs produced by the vendor hardware may
+     * have different tonemapping and saturation applied than the RGB bitmaps produced
+     * from this DNG rendering pipeline, and this test allows for fairly wide variations
+     * between the histograms for the RAW and JPEG buffers to avoid false positives.
+     * </p>
+     *
+     * <p>
+     * To ensure more subtle errors in the colorspace transforms returned for the HAL's RAW
+     * metadata, the DNGs and JPEGs produced here should also be manually compared using external
+     * DNG rendering tools.  The DNG, rendered RGB bitmap, and JPEG buffer for this test can be
+     * dumped to the SD card for further examination by enabling the 'verbose' mode for this test
+     * using:
+     * adb shell setprop log.tag.DngCreatorTest VERBOSE
+     * </p>
+     */
+    public void testRaw16JpegConsistency() throws Exception {
+        for (int i = 0; i < mCameraIds.length; i++) {
+            String deviceId = mCameraIds[i];
+            List<ImageReader> captureReaders = new ArrayList<ImageReader>();
+            List<CameraTestUtils.SimpleImageReaderListener> captureListeners =
+                    new ArrayList<CameraTestUtils.SimpleImageReaderListener>();
+            FileOutputStream fileStream = null;
+            ByteArrayOutputStream outputStream = null;
+            FileChannel fileChannel = null;
+            try {
+                openDevice(deviceId);
+
+                if (!mStaticInfo.isCapabilitySupported(
+                        CameraCharacteristics.REQUEST_AVAILABLE_CAPABILITIES_RAW)) {
+                    Log.i(TAG, "RAW capability is not supported in camera " + mCameraIds[i] +
+                            ". Skip the test.");
+                    continue;
+                }
+
+                Size[] targetCaptureSizes =
+                        mStaticInfo.getAvailableSizesForFormatChecked(ImageFormat.RAW_SENSOR,
+                                StaticMetadata.StreamDirection.Output);
+
+                assertTrue("No capture sizes available for RAW format!",
+                        targetCaptureSizes.length != 0);
+                Rect activeArray = mStaticInfo.getActiveArraySizeChecked();
+                Size activeArraySize = new Size(activeArray.width(), activeArray.height());
+                assertTrue("Active array has invalid size!", activeArray.width() > 0 &&
+                        activeArray.height() > 0);
+                // TODO: Allow PixelArraySize also.
+                assertArrayContains("Available sizes for RAW format must include ActiveArraySize",
+                        targetCaptureSizes, activeArraySize);
+
+                // Get largest jpeg size
+                Size[] targetJpegSizes =
+                        mStaticInfo.getAvailableSizesForFormatChecked(ImageFormat.JPEG,
+                                StaticMetadata.StreamDirection.Output);
+
+                Size largestJpegSize = Collections.max(Arrays.asList(targetJpegSizes),
+                        new CameraTestUtils.SizeComparator());
+
+                // Create raw image reader and capture listener
+                CameraTestUtils.SimpleImageReaderListener rawListener
+                        = new CameraTestUtils.SimpleImageReaderListener();
+                captureReaders.add(createImageReader(activeArraySize, ImageFormat.RAW_SENSOR, 2,
+                        rawListener));
+                captureListeners.add(rawListener);
+
+
+                // Create jpeg image reader and capture listener
+                CameraTestUtils.SimpleImageReaderListener jpegListener
+                        = new CameraTestUtils.SimpleImageReaderListener();
+                captureReaders.add(createImageReader(largestJpegSize, ImageFormat.JPEG, 2,
+                        jpegListener));
+                captureListeners.add(jpegListener);
+
+                Pair<List<Image>, CaptureResult> resultPair = captureSingleRawShot(activeArraySize,
+                        captureReaders, /*waitForAe*/true, captureListeners);
+                CameraCharacteristics characteristics = mStaticInfo.getCharacteristics();
+                Image raw = resultPair.first.get(0);
+                Image jpeg = resultPair.first.get(1);
+
+                Bitmap rawBitmap = Bitmap.createBitmap(raw.getWidth(), raw.getHeight(),
+                        Bitmap.Config.ARGB_8888);
+                byte[] rawPlane = new byte[raw.getPlanes()[0].getRowStride() * raw.getHeight()];
+
+                // Render RAW image to a bitmap
+                raw.getPlanes()[0].getBuffer().get(rawPlane);
+                raw.getPlanes()[0].getBuffer().rewind();
+                RawConverter.convertToSRGB(RenderScriptSingleton.getRS(), raw.getWidth(),
+                        raw.getHeight(), raw.getPlanes()[0].getRowStride(), rawPlane,
+                        characteristics, resultPair.second, /*offsetX*/0, /*offsetY*/0,
+                        /*out*/rawBitmap);
+
+                // Decompress JPEG image to a bitmap
+                byte[] compressedJpegData = CameraTestUtils.getDataFromImage(jpeg);
+
+                BitmapFactory.Options opt = new BitmapFactory.Options();
+                opt.inPreferredConfig = Bitmap.Config.ARGB_8888;
+                Bitmap fullSizeJpegBmap = BitmapFactory.decodeByteArray(compressedJpegData,
+                        /*offset*/0, compressedJpegData.length, /*inout*/opt);
+                Rect jpegDimens = new Rect(0, 0, fullSizeJpegBmap.getWidth(),
+                        fullSizeJpegBmap.getHeight());
+
+                if (VERBOSE) {
+                    // Generate DNG file
+                    DngCreator dngCreator = new DngCreator(characteristics, resultPair.second);
+                    outputStream = new ByteArrayOutputStream();
+                    dngCreator.writeImage(outputStream, raw);
+
+                    // Write DNG to file
+                    String dngFilePath = DEBUG_FILE_NAME_BASE + "/camera_" + deviceId + "_" +
+                            DEBUG_DNG_FILE;
+                    // Write out captured DNG file for the first camera device if setprop is enabled
+                    fileStream = new FileOutputStream(dngFilePath);
+                    fileStream.write(outputStream.toByteArray());
+                    fileStream.flush();
+                    fileStream.close();
+                    Log.v(TAG, "Test DNG file for camera " + deviceId + " saved to " + dngFilePath);
+
+                    // Write JPEG to file
+                    String jpegFilePath = DEBUG_FILE_NAME_BASE + "/camera_" + deviceId + "_jpeg.jpg";
+                    // Write out captured DNG file for the first camera device if setprop is enabled
+                    fileChannel = new FileOutputStream(jpegFilePath).getChannel();
+                    fileChannel.write(jpeg.getPlanes()[0].getBuffer());
+                    fileChannel.close();
+                    Log.v(TAG, "Test JPEG file for camera " + deviceId + " saved to " +
+                            jpegFilePath);
+
+                    // Write jpeg generated from demosaiced RAW frame to file
+                    String rawFilePath = DEBUG_FILE_NAME_BASE + "/camera_" + deviceId + "_raw.jpg";
+                    // Write out captured DNG file for the first camera device if setprop is enabled
+                    fileStream = new FileOutputStream(rawFilePath);
+                    rawBitmap.compress(Bitmap.CompressFormat.JPEG, 90, fileStream);
+                    fileStream.flush();
+                    fileStream.close();
+                    Log.v(TAG, "Test converted RAW file for camera " + deviceId + " saved to " +
+                            rawFilePath);
+                }
+
+                Size rawBitmapSize = new Size(rawBitmap.getWidth(), rawBitmap.getHeight());
+                assertTrue("Raw bitmap size must be equal to active array size.",
+                        rawBitmapSize.equals(activeArraySize));
+
+                // Get square center patch from JPEG and RAW bitmaps
+                RectF jpegRect = new RectF(jpegDimens);
+                RectF rawRect = new RectF(0, 0, rawBitmap.getWidth(), rawBitmap.getHeight());
+                int sideDimen = Math.min(Math.min(Math.min(Math.min(DEFAULT_PATCH_DIMEN,
+                        jpegDimens.width()), jpegDimens.height()), rawBitmap.getWidth()),
+                        rawBitmap.getHeight());
+
+                RectF jpegIntermediate = new RectF(0, 0, sideDimen, sideDimen);
+                jpegIntermediate.offset(jpegRect.centerX() - jpegIntermediate.centerX(),
+                        jpegRect.centerY() - jpegIntermediate.centerY());
+                RectF rawIntermediate = new RectF(0, 0, sideDimen, sideDimen);
+                rawIntermediate.offset(rawRect.centerX() - rawIntermediate.centerX(),
+                        rawRect.centerY() - rawIntermediate.centerY());
+                Rect jpegFinal = new Rect();
+                jpegIntermediate.roundOut(jpegFinal);
+                Rect rawFinal = new Rect();
+                rawIntermediate.roundOut(rawFinal);
+
+                Bitmap jpegPatch = Bitmap.createBitmap(fullSizeJpegBmap, jpegFinal.left,
+                        jpegFinal.top, jpegFinal.width(), jpegFinal.height());
+                Bitmap rawPatch = Bitmap.createBitmap(rawBitmap, rawFinal.left, rawFinal.top,
+                        rawFinal.width(), rawFinal.height());
+
+                // Compare center patch from JPEG and rendered RAW bitmap
+                double difference = BitmapUtils.calcDifferenceMetric(jpegPatch, rawPatch);
+                if (difference > IMAGE_DIFFERENCE_TOLERANCE) {
+                    // Write JPEG patch to file
+                    String jpegFilePath = DEBUG_FILE_NAME_BASE + "/camera_" + deviceId +
+                            "_jpeg_patch.jpg";
+                    fileStream = new FileOutputStream(jpegFilePath);
+                    jpegPatch.compress(Bitmap.CompressFormat.JPEG, 90, fileStream);
+                    fileStream.flush();
+                    fileStream.close();
+                    Log.e(TAG, "Failed JPEG patch file for camera " + deviceId + " saved to " +
+                            jpegFilePath);
+
+                    // Write RAW patch to file
+                    String rawFilePath = DEBUG_FILE_NAME_BASE + "/camera_" + deviceId +
+                            "_raw_patch.jpg";
+                    fileStream = new FileOutputStream(rawFilePath);
+                    rawPatch.compress(Bitmap.CompressFormat.JPEG, 90, fileStream);
+                    fileStream.flush();
+                    fileStream.close();
+                    Log.e(TAG, "Failed RAW patch file for camera " + deviceId + " saved to " +
+                            rawFilePath);
+
+                    fail("Camera " + mCamera.getId() + ": RAW and JPEG image at  for the same " +
+                            "frame are not similar, center patches have difference metric of " +
+                            difference);
+                }
+
+            } finally {
+                closeDevice(deviceId);
+                for (ImageReader r : captureReaders) {
+                    closeImageReader(r);
+                }
+
+                if (fileChannel != null) {
+                    fileChannel.close();
+                }
+
+                if (outputStream != null) {
+                    outputStream.close();
+                }
+
+                if (fileStream != null) {
+                    fileStream.close();
+                }
+            }
+        }
+    }
+
+    private Pair<Image, CaptureResult> captureSingleRawShot(Size s, boolean waitForAe,
+            ImageReader captureReader,
             CameraTestUtils.SimpleImageReaderListener captureListener) throws Exception {
         List<ImageReader> readers = new ArrayList<ImageReader>();
         readers.add(captureReader);
         List<CameraTestUtils.SimpleImageReaderListener> listeners =
                 new ArrayList<CameraTestUtils.SimpleImageReaderListener>();
         listeners.add(captureListener);
-        Pair<List<Image>, CaptureResult> res = captureSingleRawShot(s, readers, listeners);
+        Pair<List<Image>, CaptureResult> res = captureSingleRawShot(s, readers, waitForAe,
+                listeners);
         return new Pair<Image, CaptureResult>(res.first.get(0), res.second);
     }
 
+    private Pair<List<Image>, CaptureResult> captureSingleRawShot(Size s,
+            List<ImageReader> captureReaders, boolean waitForAe,
+            List<CameraTestUtils.SimpleImageReaderListener> captureListeners) throws Exception {
+        return captureRawShots(s, captureReaders, waitForAe, captureListeners, 1).get(0);
+    }
+
     /**
-     * Capture a single raw image.
+     * Capture raw images.
      *
-     * <p>Capture an raw image for a given size.</p>
+     * <p>Capture raw images for a given size.</p>
      *
      * @param s The size of the raw image to capture.  Must be one of the available sizes for this
      *          device.
-     * @return a pair containing the {@link Image} and {@link CaptureResult} used for this capture.
+     * @return a list of pairs containing a {@link Image} and {@link CaptureResult} used for
+     *          each capture.
      */
-    private Pair<List<Image>, CaptureResult> captureSingleRawShot(Size s, List<ImageReader> captureReaders,
-            List<CameraTestUtils.SimpleImageReaderListener> captureListeners) throws Exception {
+    private List<Pair<List<Image>, CaptureResult>> captureRawShots(Size s,
+            List<ImageReader> captureReaders, boolean waitForAe,
+            List<CameraTestUtils.SimpleImageReaderListener> captureListeners,
+            int numShots) throws Exception {
         if (VERBOSE) {
             Log.v(TAG, "captureSingleRawShot - Capturing raw image.");
         }
 
-        Size maxYuvSz = mOrderedPreviewSizes.get(0);
         Size[] targetCaptureSizes =
                 mStaticInfo.getAvailableSizesForFormatChecked(ImageFormat.RAW_SENSOR,
                         StaticMetadata.StreamDirection.Output);
@@ -298,37 +548,102 @@
         }
         assertTrue("Capture size is supported.", validSize);
 
-
         // Capture images.
-        List<Surface> outputSurfaces = new ArrayList<Surface>();
+        final List<Surface> outputSurfaces = new ArrayList<Surface>();
         for (ImageReader captureReader : captureReaders) {
             Surface captureSurface = captureReader.getSurface();
             outputSurfaces.add(captureSurface);
         }
 
-        CaptureRequest.Builder request = prepareCaptureRequestForSurfaces(outputSurfaces);
+        // Set up still capture template targeting JPEG/RAW outputs
+        CaptureRequest.Builder request =
+                mCamera.createCaptureRequest(CameraDevice.TEMPLATE_STILL_CAPTURE);
+        assertNotNull("Fail to get captureRequest", request);
+        for (Surface surface : outputSurfaces) {
+            request.addTarget(surface);
+        }
+
+        ImageReader previewReader = null;
+        if (waitForAe) {
+            // Also setup a small YUV output for AE metering if needed
+            Size yuvSize = (mOrderedPreviewSizes.size() == 0) ? null :
+                    mOrderedPreviewSizes.get(mOrderedPreviewSizes.size() - 1);
+            assertNotNull("Must support at least one small YUV size.", yuvSize);
+            previewReader = createImageReader(yuvSize, ImageFormat.YUV_420_888,
+                        /*maxNumImages*/2, new CameraTestUtils.ImageDropperListener());
+            outputSurfaces.add(previewReader.getSurface());
+        }
+
+        createSession(outputSurfaces);
+
+        if (waitForAe) {
+            CaptureRequest.Builder precaptureRequest =
+                    mCamera.createCaptureRequest(CameraDevice.TEMPLATE_PREVIEW);
+            assertNotNull("Fail to get captureRequest", precaptureRequest);
+            precaptureRequest.addTarget(previewReader.getSurface());
+            precaptureRequest.set(CaptureRequest.CONTROL_MODE,
+                    CaptureRequest.CONTROL_MODE_AUTO);
+            precaptureRequest.set(CaptureRequest.CONTROL_AE_MODE,
+                    CaptureRequest.CONTROL_AE_MODE_ON);
+
+            final ConditionVariable waitForAeCondition = new ConditionVariable(/*isOpen*/false);
+            CameraCaptureSession.CaptureCallback captureCallback =
+                    new CameraCaptureSession.CaptureCallback() {
+                @Override
+                public void onCaptureProgressed(CameraCaptureSession session,
+                        CaptureRequest request, CaptureResult partialResult) {
+                    if (partialResult.get(CaptureResult.CONTROL_AE_STATE) ==
+                            CaptureRequest.CONTROL_AE_STATE_CONVERGED) {
+                        waitForAeCondition.open();
+                    }
+                }
+
+                @Override
+                public void onCaptureCompleted(CameraCaptureSession session,
+                        CaptureRequest request, TotalCaptureResult result) {
+                    if (result.get(CaptureResult.CONTROL_AE_STATE) ==
+                            CaptureRequest.CONTROL_AE_STATE_CONVERGED) {
+                        waitForAeCondition.open();
+                    }
+                }
+            };
+            startCapture(precaptureRequest.build(), /*repeating*/true, captureCallback, mHandler);
+
+            precaptureRequest.set(CaptureRequest.CONTROL_AE_PRECAPTURE_TRIGGER,
+                    CaptureRequest.CONTROL_AE_PRECAPTURE_TRIGGER_START);
+            startCapture(precaptureRequest.build(), /*repeating*/false, captureCallback, mHandler);
+            assertTrue("Timeout out waiting for AE to converge",
+                    waitForAeCondition.block(AE_TIMEOUT_MS));
+        }
+
         request.set(CaptureRequest.STATISTICS_LENS_SHADING_MAP_MODE,
                 CaptureRequest.STATISTICS_LENS_SHADING_MAP_MODE_ON);
         CameraTestUtils.SimpleCaptureCallback resultListener =
                 new CameraTestUtils.SimpleCaptureCallback();
 
-        startCapture(request.build(), /*repeating*/false, resultListener, mHandler);
+        CaptureRequest request1 = request.build();
+        for (int i = 0; i < numShots; i++) {
+            startCapture(request1, /*repeating*/false, resultListener, mHandler);
+        }
+        List<Pair<List<Image>, CaptureResult>> ret = new ArrayList<>();
+        for (int i = 0; i < numShots; i++) {
+            // Verify capture result and images
+            CaptureResult result = resultListener.getCaptureResult(CAPTURE_WAIT_TIMEOUT_MS);
 
-        // Verify capture result and images
-        CaptureResult result = resultListener.getCaptureResult(CAPTURE_WAIT_TIMEOUT_MS);
-
-        List<Image> resultImages = new ArrayList<Image>();
-        for (CameraTestUtils.SimpleImageReaderListener captureListener : captureListeners) {
-            Image captureImage = captureListener.getImage(CAPTURE_WAIT_TIMEOUT_MS);
+            List<Image> resultImages = new ArrayList<Image>();
+            for (CameraTestUtils.SimpleImageReaderListener captureListener : captureListeners) {
+                Image captureImage = captureListener.getImage(CAPTURE_WAIT_TIMEOUT_MS);
 
             /*CameraTestUtils.validateImage(captureImage, s.getWidth(), s.getHeight(),
                     ImageFormat.RAW_SENSOR, null);*/
-            resultImages.add(captureImage);
+                resultImages.add(captureImage);
+            }
+            ret.add(new Pair<List<Image>, CaptureResult>(resultImages, result));
         }
         // Stop capture, delete the streams.
         stopCapture(/*fast*/false);
 
-        return new Pair<List<Image>, CaptureResult>(resultImages, result);
+        return ret;
     }
 
     private CaptureRequest.Builder prepareCaptureRequestForSurfaces(List<Surface> surfaces)
@@ -336,7 +651,7 @@
         createSession(surfaces);
 
         CaptureRequest.Builder captureBuilder =
-                mCamera.createCaptureRequest(CameraDevice.TEMPLATE_PREVIEW);
+                mCamera.createCaptureRequest(CameraDevice.TEMPLATE_STILL_CAPTURE);
         assertNotNull("Fail to get captureRequest", captureBuilder);
         for (Surface surface : surfaces) {
             captureBuilder.addTarget(surface);
diff --git a/tests/tests/hardware/src/android/hardware/camera2/cts/ExtendedCameraCharacteristicsTest.java b/tests/tests/hardware/src/android/hardware/camera2/cts/ExtendedCameraCharacteristicsTest.java
index f08b60a..8619f73 100644
--- a/tests/tests/hardware/src/android/hardware/camera2/cts/ExtendedCameraCharacteristicsTest.java
+++ b/tests/tests/hardware/src/android/hardware/camera2/cts/ExtendedCameraCharacteristicsTest.java
@@ -18,6 +18,8 @@
 
 import android.content.Context;
 import android.graphics.ImageFormat;
+import android.graphics.Rect;
+import android.graphics.SurfaceTexture;
 import android.hardware.camera2.CameraCharacteristics;
 import android.hardware.camera2.CameraCharacteristics.Key;
 import android.hardware.camera2.CameraManager;
@@ -25,10 +27,13 @@
 import android.hardware.camera2.params.BlackLevelPattern;
 import android.hardware.camera2.params.ColorSpaceTransform;
 import android.hardware.camera2.params.StreamConfigurationMap;
+import android.media.ImageReader;
 import android.test.AndroidTestCase;
 import android.util.Log;
 import android.util.Rational;
+import android.util.Range;
 import android.util.Size;
+import android.view.Surface;
 
 import java.util.ArrayList;
 import java.util.Arrays;
@@ -269,7 +274,8 @@
         int counter = 0;
         for (CameraCharacteristics c : mCharacteristics) {
             int[] actualCapabilities = c.get(CameraCharacteristics.REQUEST_AVAILABLE_CAPABILITIES);
-            assertNotNull("android.request.availableCapabilities must never be null");
+            assertNotNull("android.request.availableCapabilities must never be null",
+                    actualCapabilities);
             if (!arrayContains(actualCapabilities,
                     CameraCharacteristics.REQUEST_AVAILABLE_CAPABILITIES_RAW)) {
                 Log.i(TAG, "RAW capability is not supported in camera " + counter++ +
@@ -341,6 +347,289 @@
     }
 
     /**
+     * Test values for static metadata used by the BURST capability.
+     */
+    public void testStaticBurstCharacteristics() {
+        int counter = 0;
+        final float SIZE_ERROR_MARGIN = 0.03f;
+        for (CameraCharacteristics c : mCharacteristics) {
+            int[] actualCapabilities = c.get(CameraCharacteristics.REQUEST_AVAILABLE_CAPABILITIES);
+            assertNotNull("android.request.availableCapabilities must never be null",
+                    actualCapabilities);
+
+            // Check if the burst capability is defined
+            boolean haveBurstCapability = arrayContains(actualCapabilities,
+                    CameraCharacteristics.REQUEST_AVAILABLE_CAPABILITIES_BURST_CAPTURE);
+
+            StreamConfigurationMap config =
+                    c.get(CameraCharacteristics.SCALER_STREAM_CONFIGURATION_MAP);
+            assertNotNull(String.format("No stream configuration map found for: ID %s",
+                    mIds[counter]), config);
+            Rect activeRect = c.get(CameraCharacteristics.SENSOR_INFO_ACTIVE_ARRAY_SIZE);
+            Size sensorSize = new Size(activeRect.width(), activeRect.height());
+
+            // Ensure that max YUV size matches max JPEG size
+            Size maxYuvSize = CameraTestUtils.getMaxSize(
+                    config.getOutputSizes(ImageFormat.YUV_420_888));
+            Size maxJpegSize = CameraTestUtils.getMaxSize(config.getOutputSizes(ImageFormat.JPEG));
+
+            boolean haveMaxYuv = maxYuvSize != null ?
+                (maxJpegSize.getWidth() <= maxYuvSize.getWidth() &&
+                        maxJpegSize.getHeight() <= maxYuvSize.getHeight()) : false;
+
+            boolean maxYuvMatchSensor =
+                    (maxYuvSize.getWidth() <= sensorSize.getWidth() * (1.0 + SIZE_ERROR_MARGIN) &&
+                     maxYuvSize.getWidth() >= sensorSize.getWidth() * (1.0 - SIZE_ERROR_MARGIN) &&
+                     maxYuvSize.getHeight() <= sensorSize.getHeight() * (1.0 + SIZE_ERROR_MARGIN) &&
+                     maxYuvSize.getHeight() >= sensorSize.getHeight() * (1.0 - SIZE_ERROR_MARGIN));
+
+            // Ensure that YUV output is fast enough - needs to be at least 20 fps
+
+            long maxYuvRate =
+                config.getOutputMinFrameDuration(ImageFormat.YUV_420_888, maxYuvSize);
+            final long MIN_DURATION_BOUND_NS = 50000000; // 50 ms, 20 fps
+
+            boolean haveMaxYuvRate = maxYuvRate <= MIN_DURATION_BOUND_NS;
+
+            // Ensure that there's an FPS range that's fast enough to capture at above
+            // minFrameDuration, for full-auto bursts
+            Range[] fpsRanges = c.get(CameraCharacteristics.CONTROL_AE_AVAILABLE_TARGET_FPS_RANGES);
+            float minYuvFps = 1.f / maxYuvRate;
+
+            boolean haveFastAeTargetFps = false;
+            for (Range<Integer> r : fpsRanges) {
+                if (r.getLower() >= minYuvFps) {
+                    haveFastAeTargetFps = true;
+                    break;
+                }
+            }
+
+            // Ensure that maximum sync latency is small enough for fast setting changes, even if
+            // it's not quite per-frame
+
+            Integer maxSyncLatencyValue = c.get(CameraCharacteristics.SYNC_MAX_LATENCY);
+            assertNotNull(String.format("No sync latency declared for ID %s", mIds[counter]),
+                    maxSyncLatencyValue);
+
+            int maxSyncLatency = maxSyncLatencyValue;
+            final long MAX_LATENCY_BOUND = 4;
+            boolean haveFastSyncLatency =
+                (maxSyncLatency <= MAX_LATENCY_BOUND) && (maxSyncLatency >= 0);
+
+            if (haveBurstCapability) {
+                assertTrue(
+                        String.format("BURST-capable camera device %s does not have maximum YUV " +
+                                "size that is at least max JPEG size",
+                                mIds[counter]),
+                        haveMaxYuv);
+                assertTrue(
+                        String.format("BURST-capable camera device %s YUV frame rate is too slow" +
+                                "(%d ns min frame duration reported, less than %d ns expected)",
+                                mIds[counter], maxYuvRate, MIN_DURATION_BOUND_NS),
+                        haveMaxYuvRate);
+                assertTrue(
+                        String.format("BURST-capable camera device %s does not list an AE target " +
+                                " FPS range with min FPS >= %f, for full-AUTO bursts",
+                                mIds[counter], minYuvFps),
+                        haveFastAeTargetFps);
+                assertTrue(
+                        String.format("BURST-capable camera device %s YUV sync latency is too long" +
+                                "(%d frames reported, [0, %d] frames expected)",
+                                mIds[counter], maxSyncLatency, MAX_LATENCY_BOUND),
+                        haveFastSyncLatency);
+                assertTrue(
+                        "Active array size and max YUV size should be similar",
+                        maxYuvMatchSensor);
+            } else {
+                assertTrue(
+                        String.format("Camera device %s has all the requirements for BURST" +
+                                " capability but does not report it!", mIds[counter]),
+                        !(haveMaxYuv && haveMaxYuvRate && haveFastAeTargetFps &&
+                                haveFastSyncLatency && maxYuvMatchSensor));
+            }
+
+            counter++;
+        }
+    }
+
+    /**
+     * Cross-check StreamConfigurationMap output
+     */
+    public void testStreamConfigurationMap() {
+        int counter = 0;
+        for (CameraCharacteristics c : mCharacteristics) {
+            Log.i(TAG, "testStreamConfigurationMap: Testing camera ID " + mIds[counter]);
+            StreamConfigurationMap config =
+                    c.get(CameraCharacteristics.SCALER_STREAM_CONFIGURATION_MAP);
+            assertNotNull(String.format("No stream configuration map found for: ID %s",
+                            mIds[counter]), config);
+
+            assertTrue("ImageReader must be supported",
+                    config.isOutputSupportedFor(android.media.ImageReader.class));
+            assertTrue("MediaRecorder must be supported",
+                    config.isOutputSupportedFor(android.media.MediaRecorder.class));
+            assertTrue("MediaCodec must be supported",
+                    config.isOutputSupportedFor(android.media.MediaCodec.class));
+            assertTrue("Allocation must be supported",
+                    config.isOutputSupportedFor(android.renderscript.Allocation.class));
+            assertTrue("SurfaceHolder must be supported",
+                    config.isOutputSupportedFor(android.view.SurfaceHolder.class));
+            assertTrue("SurfaceTexture must be supported",
+                    config.isOutputSupportedFor(android.graphics.SurfaceTexture.class));
+
+            assertTrue("YUV_420_888 must be supported",
+                    config.isOutputSupportedFor(ImageFormat.YUV_420_888));
+            assertTrue("JPEG must be supported",
+                    config.isOutputSupportedFor(ImageFormat.JPEG));
+
+            // Legacy YUV formats should not be listed
+            assertTrue("NV21 must not be supported",
+                    !config.isOutputSupportedFor(ImageFormat.NV21));
+
+            int[] actualCapabilities = c.get(CameraCharacteristics.REQUEST_AVAILABLE_CAPABILITIES);
+            assertNotNull("android.request.availableCapabilities must never be null",
+                    actualCapabilities);
+            if (arrayContains(actualCapabilities,
+                    CameraCharacteristics.REQUEST_AVAILABLE_CAPABILITIES_RAW)) {
+                assertTrue("RAW_SENSOR must be supported if RAW capability is advertised",
+                    config.isOutputSupportedFor(ImageFormat.RAW_SENSOR));
+            }
+
+            // Cross check public formats and sizes
+
+            int[] supportedFormats = config.getOutputFormats();
+            for (int format : supportedFormats) {
+                assertTrue("Format " + format + " fails cross check",
+                        config.isOutputSupportedFor(format));
+                Size[] supportedSizes = config.getOutputSizes(format);
+                assertTrue("Supported format " + format + " has no sizes listed",
+                        supportedSizes.length > 0);
+                for (Size size : supportedSizes) {
+                    if (VERBOSE) {
+                        Log.v(TAG,
+                                String.format("Testing camera %s, format %d, size %s",
+                                        mIds[counter], format, size.toString()));
+                    }
+
+                    long stallDuration = config.getOutputStallDuration(format, size);
+                    switch(format) {
+                        case ImageFormat.YUV_420_888:
+                            assertTrue("YUV_420_888 may not have a non-zero stall duration",
+                                    stallDuration == 0);
+                            break;
+                        default:
+                            assertTrue("Negative stall duration for format " + format,
+                                    stallDuration >= 0);
+                            break;
+                    }
+                    long minDuration = config.getOutputMinFrameDuration(format, size);
+                    if (arrayContains(actualCapabilities,
+                            CameraCharacteristics.REQUEST_AVAILABLE_CAPABILITIES_MANUAL_SENSOR)) {
+                        assertTrue("MANUAL_SENSOR capability, need positive min frame duration for"
+                                + "format " + format + " for size " + size + " minDuration " +
+                                minDuration,
+                                minDuration > 0);
+                    } else {
+                        assertTrue("Need non-negative min frame duration for format " + format,
+                                minDuration >= 0);
+                    }
+
+                    ImageReader testReader = ImageReader.newInstance(
+                        size.getWidth(),
+                        size.getHeight(),
+                        format,
+                        1);
+                    Surface testSurface = testReader.getSurface();
+
+                    assertTrue(
+                        String.format("isOutputSupportedFor fails for config %s, format %d",
+                                size.toString(), format),
+                        config.isOutputSupportedFor(testSurface));
+
+                    testReader.close();
+
+                } // sizes
+
+                // Try an invalid size in this format, should round
+                Size invalidSize = findInvalidSize(supportedSizes);
+                // WAR: the intended threshold is 1920, but to counter the bug
+                // in Lollipop framework, we need to set it to 1080 here.
+                // The threshold will be changed back to 1920 in next Android release.
+                int MAX_ROUNDING_WIDTH = 1080;
+                if (invalidSize.getWidth() <= MAX_ROUNDING_WIDTH) {
+                    ImageReader testReader = ImageReader.newInstance(
+                                                                     invalidSize.getWidth(),
+                                                                     invalidSize.getHeight(),
+                                                                     format,
+                                                                     1);
+                    Surface testSurface = testReader.getSurface();
+
+                    assertTrue(
+                               String.format("isOutputSupportedFor fails for config %s, %d",
+                                       invalidSize.toString(), format),
+                               config.isOutputSupportedFor(testSurface));
+
+                    testReader.close();
+                }
+            } // formats
+
+            // Cross-check opaque format and sizes
+
+            SurfaceTexture st = new SurfaceTexture(1);
+            Surface surf = new Surface(st);
+
+            Size[] opaqueSizes = config.getOutputSizes(SurfaceTexture.class);
+            assertTrue("Opaque format has no sizes listed",
+                    opaqueSizes.length > 0);
+            for (Size size : opaqueSizes) {
+                long stallDuration = config.getOutputStallDuration(SurfaceTexture.class, size);
+                assertTrue("Opaque output may not have a non-zero stall duration",
+                        stallDuration == 0);
+
+                long minDuration = config.getOutputMinFrameDuration(SurfaceTexture.class, size);
+                if (arrayContains(actualCapabilities,
+                                CameraCharacteristics.REQUEST_AVAILABLE_CAPABILITIES_MANUAL_SENSOR)) {
+                    assertTrue("MANUAL_SENSOR capability, need positive min frame duration for"
+                            + "opaque format",
+                            minDuration > 0);
+                } else {
+                    assertTrue("Need non-negative min frame duration for opaque format ",
+                            minDuration >= 0);
+                }
+                st.setDefaultBufferSize(size.getWidth(), size.getHeight());
+
+                assertTrue(
+                    String.format("isOutputSupportedFor fails for SurfaceTexture config %s",
+                            size.toString()),
+                    config.isOutputSupportedFor(surf));
+
+            } // opaque sizes
+
+            // Try invalid opaque size, should get rounded
+            Size invalidSize = findInvalidSize(opaqueSizes);
+            st.setDefaultBufferSize(invalidSize.getWidth(), invalidSize.getHeight());
+            assertTrue(
+                String.format("isOutputSupportedFor fails for SurfaceTexture config %s",
+                        invalidSize.toString()),
+                config.isOutputSupportedFor(surf));
+
+            counter++;
+        } // mCharacteristics
+
+    }
+
+    /**
+     * Create an invalid size that's close to one of the good sizes in the list, but not one of them
+     */
+    private Size findInvalidSize(Size[] goodSizes) {
+        Size invalidSize = new Size(goodSizes[0].getWidth() + 1, goodSizes[0].getHeight());
+        while(arrayContains(goodSizes, invalidSize)) {
+            invalidSize = new Size(invalidSize.getWidth() + 1, invalidSize.getHeight());
+        }
+        return invalidSize;
+    }
+
+    /**
      * Check key is present in characteristics if the hardware level is at least {@code hwLevel};
      * check that the key is present if the actual capabilities are one of {@code capabilities}.
      *
@@ -353,7 +642,8 @@
         assertNotNull("android.info.supportedHardwareLevel must never be null", actualHwLevel);
 
         int[] actualCapabilities = c.get(CameraCharacteristics.REQUEST_AVAILABLE_CAPABILITIES);
-        assertNotNull("android.request.availableCapabilities must never be null");
+        assertNotNull("android.request.availableCapabilities must never be null",
+                actualCapabilities);
 
         List<Key<?>> allKeys = c.getKeys();
 
@@ -412,6 +702,20 @@
         return false;
     }
 
+    private static <T> boolean arrayContains(T[] arr, T needle) {
+        if (arr == null) {
+            return false;
+        }
+
+        for (T elem : arr) {
+            if (elem.equals(needle)) {
+                return true;
+            }
+        }
+
+        return false;
+    }
+
     private static boolean arrayContainsAnyOf(int[] arr, int[] needles) {
         for (int needle : needles) {
             if (arrayContains(arr, needle)) {
diff --git a/tests/tests/hardware/src/android/hardware/camera2/cts/ImageReaderTest.java b/tests/tests/hardware/src/android/hardware/camera2/cts/ImageReaderTest.java
index 61f25fb..a410775 100644
--- a/tests/tests/hardware/src/android/hardware/camera2/cts/ImageReaderTest.java
+++ b/tests/tests/hardware/src/android/hardware/camera2/cts/ImageReaderTest.java
@@ -16,26 +16,40 @@
 
 package android.hardware.camera2.cts;
 
-import static android.hardware.camera2.cts.CameraTestUtils.*;
-
 import android.content.Context;
+import android.graphics.Bitmap;
+import android.graphics.BitmapFactory;
+import android.graphics.BitmapRegionDecoder;
+import android.graphics.Color;
 import android.graphics.ImageFormat;
-import android.hardware.camera2.CameraDevice;
+import android.graphics.Matrix;
+import android.graphics.Rect;
+import android.graphics.RectF;
 import android.hardware.camera2.CameraCharacteristics;
+import android.hardware.camera2.CameraDevice;
 import android.hardware.camera2.CaptureRequest;
 import android.hardware.camera2.CaptureResult;
-import android.util.Size;
 import android.hardware.camera2.cts.helpers.StaticMetadata;
+import android.hardware.camera2.cts.rs.BitmapUtils;
 import android.hardware.camera2.cts.testcases.Camera2AndroidTestCase;
+import android.hardware.camera2.params.StreamConfigurationMap;
 import android.media.Image;
 import android.media.ImageReader;
 import android.os.ConditionVariable;
 import android.util.Log;
+import android.util.Size;
 import android.view.Surface;
 
+import java.nio.ByteBuffer;
 import java.util.ArrayList;
 import java.util.List;
 
+import static android.hardware.camera2.cts.CameraTestUtils.CAPTURE_RESULT_TIMEOUT_MS;
+import static android.hardware.camera2.cts.CameraTestUtils.SimpleCaptureCallback;
+import static android.hardware.camera2.cts.CameraTestUtils.SimpleImageReaderListener;
+import static android.hardware.camera2.cts.CameraTestUtils.dumpFile;
+import static android.hardware.camera2.cts.CameraTestUtils.getValueNotNull;
+
 /**
  * <p>Basic test for ImageReader APIs. It uses CameraDevice as producer, camera
  * sends the data to the surface provided by imageReader. Below image formats
@@ -49,10 +63,16 @@
 public class ImageReaderTest extends Camera2AndroidTestCase {
     private static final String TAG = "ImageReaderTest";
     private static final boolean VERBOSE = Log.isLoggable(TAG, Log.VERBOSE);
+    private static final boolean DEBUG = Log.isLoggable(TAG, Log.DEBUG);
+
     // number of frame (for streaming requests) to be verified.
     private static final int NUM_FRAME_VERIFIED = 2;
     // Max number of images can be accessed simultaneously from ImageReader.
     private static final int MAX_NUM_IMAGES = 5;
+    // Max difference allowed between YUV and JPEG patches. This tolerance is intentionally very
+    // generous to avoid false positives due to punch/saturation operations vendors apply to the
+    // JPEG outputs.
+    private static final double IMAGE_DIFFERENCE_TOLERANCE = 30;
 
     private SimpleImageListener mListener;
 
@@ -88,10 +108,6 @@
             try {
                 Log.v(TAG, "Testing jpeg capture for Camera " + id);
                 openDevice(id);
-                if (mStaticInfo.isHardwareLevelLegacy()) {
-                    Log.i(TAG, "Skipping test on legacy devices");
-                    continue;
-                }
                 bufferFormatTestByCamera(ImageFormat.JPEG, /*repeating*/false);
             } finally {
                 closeDevice(id);
@@ -117,10 +133,6 @@
             try {
                 Log.v(TAG, "Testing repeating jpeg capture for Camera " + id);
                 openDevice(id);
-                if (mStaticInfo.isHardwareLevelLegacy()) {
-                    Log.i(TAG, "Skipping test on legacy devices");
-                    continue;
-                }
                 bufferFormatTestByCamera(ImageFormat.JPEG, /*repeating*/true);
             } finally {
                 closeDevice(id);
@@ -141,9 +153,29 @@
         }
     }
 
-    public void testInvalidAccessTest() {
-        // TODO: test invalid access case, see if we can receive expected
-        // exceptions
+    /**
+     * Test invalid access of image byte buffers: when an image is closed, further access
+     * of the image byte buffers will get an IllegalStateException. The basic assumption of
+     * this test is that the ImageReader always gives direct byte buffer, which is always true
+     * for camera case. For if the produced image byte buffer is not direct byte buffer, there
+     * is no guarantee to get an ISE for this invalid access case.
+     */
+    public void testInvalidAccessTest() throws Exception {
+        // Test byte buffer access after an image is released, it should throw ISE.
+        for (String id : mCameraIds) {
+            try {
+                Log.v(TAG, "Testing invalid image access for Camera " + id);
+                openDevice(id);
+                bufferAccessAfterRelease();
+                fail("ImageReader should throw IllegalStateException when accessing a byte buffer"
+                        + " after the image is closed");
+            } catch (IllegalStateException e) {
+                // Expected.
+            } finally {
+                closeDevice(id);
+            }
+        }
+
     }
 
     /**
@@ -156,10 +188,7 @@
             try {
                 Log.v(TAG, "YUV and JPEG testing for camera " + id);
                 openDevice(id);
-                if (mStaticInfo.isHardwareLevelLegacy()) {
-                    Log.i(TAG, "Skipping test on legacy devices");
-                    continue;
-                }
+
                 bufferFormatWithYuvTestByCamera(ImageFormat.JPEG);
             } finally {
                 closeDevice(id);
@@ -184,6 +213,302 @@
         }
     }
 
+    /**
+     * Check that the center patches for YUV and JPEG outputs for the same frame match for each YUV
+     * resolution and format supported.
+     */
+    public void testAllOutputYUVResolutions() throws Exception {
+        for (String id : mCameraIds) {
+            try {
+                Log.v(TAG, "Testing all YUV image resolutions for camera " + id);
+                openDevice(id);
+
+                // Skip warmup on FULL mode devices.
+                int warmupCaptureNumber = (mStaticInfo.isHardwareLevelLegacy()) ?
+                        MAX_NUM_IMAGES - 1 : 0;
+
+                // NV21 isn't supported by ImageReader.
+                final int[] YUVFormats = new int[] {ImageFormat.YUV_420_888, ImageFormat.YV12};
+
+                CameraCharacteristics.Key<StreamConfigurationMap> key =
+                        CameraCharacteristics.SCALER_STREAM_CONFIGURATION_MAP;
+                StreamConfigurationMap config = mStaticInfo.getValueFromKeyNonNull(key);
+                int[] supportedFormats = config.getOutputFormats();
+                List<Integer> supportedYUVFormats = new ArrayList<>();
+                for (int format : YUVFormats) {
+                    if (CameraTestUtils.contains(supportedFormats, format)) {
+                        supportedYUVFormats.add(format);
+                    }
+                }
+
+                Size[] jpegSizes = mStaticInfo.getAvailableSizesForFormatChecked(ImageFormat.JPEG,
+                        StaticMetadata.StreamDirection.Output);
+                assertFalse("JPEG output not supported for camera " + id +
+                        ", at least one JPEG output is required.", jpegSizes.length == 0);
+
+                Size maxJpegSize = CameraTestUtils.getMaxSize(jpegSizes);
+
+                for (int format : supportedYUVFormats) {
+                    Size[] targetCaptureSizes =
+                            mStaticInfo.getAvailableSizesForFormatChecked(format,
+                            StaticMetadata.StreamDirection.Output);
+
+                    for (Size captureSz : targetCaptureSizes) {
+                        if (VERBOSE) {
+                            Log.v(TAG, "Testing yuv size " + captureSz + " and jpeg size "
+                                    + maxJpegSize + " for camera " + mCamera.getId());
+                        }
+
+                        ImageReader jpegReader = null;
+                        ImageReader yuvReader = null;
+                        try {
+                            // Create YUV image reader
+                            SimpleImageReaderListener yuvListener = new SimpleImageReaderListener();
+                            yuvReader = createImageReader(captureSz, format, MAX_NUM_IMAGES,
+                                    yuvListener);
+                            Surface yuvSurface = yuvReader.getSurface();
+
+                            // Create JPEG image reader
+                            SimpleImageReaderListener jpegListener =
+                                    new SimpleImageReaderListener();
+                            jpegReader = createImageReader(maxJpegSize,
+                                    ImageFormat.JPEG, MAX_NUM_IMAGES, jpegListener);
+                            Surface jpegSurface = jpegReader.getSurface();
+
+                            // Setup session
+                            List<Surface> outputSurfaces = new ArrayList<Surface>();
+                            outputSurfaces.add(yuvSurface);
+                            outputSurfaces.add(jpegSurface);